惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

月光博客
月光博客
L
LangChain Blog
Jina AI
Jina AI
WordPress大学
WordPress大学
人人都是产品经理
人人都是产品经理
S
Secure Thoughts
T
The Exploit Database - CXSecurity.com
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
博客园 - 聂微东
小众软件
小众软件
Apple Machine Learning Research
Apple Machine Learning Research
C
Cyber Attacks, Cyber Crime and Cyber Security
Project Zero
Project Zero
T
Threat Research - Cisco Blogs
量子位
G
GRAHAM CLULEY
腾讯CDC
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
C
CERT Recently Published Vulnerability Notes
The Hacker News
The Hacker News
C
Cisco Blogs
Scott Helme
Scott Helme
Spread Privacy
Spread Privacy
宝玉的分享
宝玉的分享
V
V2EX
博客园 - 三生石上(FineUI控件)
T
Tor Project blog
P
Proofpoint News Feed
雷峰网
雷峰网
D
Darknet – Hacking Tools, Hacker News & Cyber Security
V
Vulnerabilities – Threatpost
PCI Perspectives
PCI Perspectives
博客园_首页
L
LINUX DO - 最新话题
IT之家
IT之家
有赞技术团队
有赞技术团队
博客园 - Franky
Hacker News: Ask HN
Hacker News: Ask HN
Last Week in AI
Last Week in AI
The Cloudflare Blog
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
美团技术团队
博客园 - 【当耐特】
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Security Archives - TechRepublic
Security Archives - TechRepublic
L
LINUX DO - 热门话题
AWS News Blog
AWS News Blog
S
Security Affairs
T
Tailwind CSS Blog

博客园 - eaglet

HubbleDotNet 的注册码生成器 文件名通配符匹配的代码 .net 下如何将文档文件(Word, Pdf等) 中的文本提取出来 NTCPMSG 开源高性能TCP消息发送组件 文件在不同文件系统间拷贝文件时间改变的问题 文件是否真的写入了磁盘? 如何自动延长 windows 2008 试用版的试用期 获取ALL USER 的特殊目录的类 i—比 i++ 快? 多线程环境下调用 HttpWebRequest 并发连接限制 HubbleDotNet+Mongodb 构建高性能搜索引擎--概述 C# 重启计算机的问题 HubbleDotNet 索引分词的测试方法和分词技巧 Windows 启动顺序详解 记录windows操作系统启动日志 通过 Windows 7 共享上网 正确理解 SqlConnection 的连接池机制 Windows 2008 server + IIS 7 设置身份模拟(ASP.NET impersonation) IIS 7.5 配置 FTP Passive 模式
C# 程序自动批量生成 google maps 的KML文件
eaglet · 2011-12-05 · via 博客园 - eaglet

google maps 的 KML 文件可以用于静态的地图标注,在某些应用中,我们手上往往有成百上千个地址,我们需要把这些地址和描述批量标注到 google maps 上去,如果手工来做,太耗时间,在这里我写了一个程序批量来生成这个 KML 文件。

首先看一下 KML 文件的格式:

<?xml version="1.0" encoding="UTF-8"?>
<kml xmlns="http://www.google.com/earth/kml/2">
<Document>
    <name>kml_sample2.kml</name>

    <Style id="red">
      <IconStyle>
        <Icon>
          <href>http://www.google.com/intl/en_us/mapfiles/ms/icons/red-dot.png</href>
        </Icon>
      </IconStyle>
    </Style>

    <Style id="green">
      <IconStyle>
        <Icon>
          <href>http://www.google.com/intl/en_us/mapfiles/ms/icons/green-dot.png</href>
        </Icon>
      </IconStyle>
    </Style>

    <Style id="blue">
      <IconStyle>
        <Icon>
          <href>http://www.google.com/intl/en_us/mapfiles/ms/icons/blue-dot.png</href>
        </Icon>
      </IconStyle>
    </Style>

  <Placemark>
    <name>Google Inc.</name>
    <description><![CDATA[
      Google Inc.<br />
      1600 Amphitheatre Parkway<br />
      Mountain View, CA 94043<br />
      Phone: +1 650-253-0000<br />
      Fax: +1 650-253-0001<br />
      <p>Home page: <a href="http://www.google.com">www.google.com</a></p>
    ]]>
    </description>
    <styleUrl>#red</styleUrl> 
    <Point>
      <coordinates>-122.0841430, 37.4219720, 0</coordinates>
    </Point>
  </Placemark>

  <Placemark>
    <name>Yahoo! Inc.</name>
    <description><![CDATA[
      Yahoo! Inc.<br />
      701 First Avenue<br />
      Sunnyvale, CA 94089<br />
      Tel: (408) 349-3300<br />
      Fax: (408) 349-3301<br />
      <p>Home page: <a href="http://yahoo.com">http://yahoo.com</a></p>
      ]]>
    </description>
    <styleUrl>#green</styleUrl> 
    <Point>
      <coordinates>-122.0250403,37.4163228</coordinates>
    </Point>
  </Placemark>

</Document>
</kml>

这个是一个典型的用于google maps 的 KML 文件,(注意不同应用的KML 格式会有所不同,比如 google earth 的 kml 格式就复杂得多)

从这个kml 文件格式来看,其实它就是一个 xml 文件,我们只要自动生成这个文件中各个元素的信息就可以得到这个xml  文件。这里其实最大的问题是如何自动通过地址获取经纬度坐标。值得庆幸的是 google 提供了这方面的 api 函数。google api 获取地理坐标的官方例子见:geocodingapi

我的实现稍微复杂一些,因为我需要在函数中为不同的位置自动分配颜色

   1:          /// <summary>
   2:          /// Generate placemark by address description
   3:          /// </summary>
   4:          /// <param name="addrDescription">address and description</param>
   5:          /// <returns>if no matched, return false</returns>
   6:          public bool Generate(AddressDescription addrDescription)
   7:          {
   8:              _LastErrorOrWarning = null;
   9:   
  10:              Thread.Sleep(DelayInMs);
  11:   
  12:              List<GeographicCoordinate> coordinates = Geocoding.Geocode(addrDescription.Address);
  13:   
  14:              if (coordinates.Count == 0)
  15:              {
  16:                  _LastErrorOrWarning = string.Format("Address:{0}, Description:{1} does not find the coordinates, please make sure the address is correctly.",
  17:                      addrDescription.Address, addrDescription.Description);
  18:   
  19:                  return false;
  20:              }
  21:   
  22:              if (coordinates.Count > 1)
  23:              {
  24:                  _LastErrorOrWarning = string.Format("Address:{0}, Description:{1} has more than one coordinates.",
  25:                      addrDescription.Address, addrDescription.Description);
  26:              }
  27:   
  28:              string colorId = Colors[_ColorIndex];
  29:   
  30:              _ColorIndex++;
  31:   
  32:              if (_ColorIndex >= Colors.Count)
  33:              {
  34:                  _ColorIndex = 0;
  35:              }
  36:   
  37:              _Kml.Document.Add(new Placemark(addrDescription.Address, addrDescription.Description, colorId,
  38:                  coordinates[0].Latitude, coordinates[0].Longitude));
  39:   
  40:              return true;
  41:          }

第32行有个bug,应该是 >=  ,我原来写成 > 了,博客中我改过来了,源码我就不改了。

如上代码,第12行就是通过GeocodingApi 获取指定地址的物理坐标,由于有时候获取不到坐标,有时候由于地址不确切,有多个坐标,所以我加了一个错误和警告的属性,用于调用者得到相关的信息。

_Kml 这个对象是一个 Kml 类的实例,这个类用于生成 KML 文件结构,并可以保存到KML文件中。这个类在后面介绍。

下面的 _Color 部分是自动的顺序分配标注点的颜色,我为了省事,在代码中写死了4种颜色,你也可以修改代码增加颜色或其他图标。

标注颜色这里其实还有一个问题,就是如果让相邻的节点显示不同颜色,这个算法比较复杂了,我没有实现,各位如果有兴趣可以思考一下这个怎么做。

好了,最大的问题解决了,剩下就是写 xml  文件了,这个很简单,我就不深入讲了,直接把代码贴出来。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
using System.IO;

namespace GenerateKML
{
    public class Placemark
    {
        public class KMLPoint
        {
            public KMLPoint()
            {
            }

            public KMLPoint(double latitude, double longitude)
            {
                SetCoordinates(latitude, longitude);
            }

            private string _coordinates;

            public void SetCoordinates(double latitude, double longitude)
            {
                _coordinates = longitude.ToString() + "," + latitude.ToString();
            }

            public string coordinates
            {
                get
                {
                    return _coordinates;
                }

                set
                {
                    _coordinates = value;
                }
            }
        }

        [XmlElement("name")]
        public string Name { get; set; }

        [XmlElement("description")]
        public string Description { get; set; }

        [XmlElement("styleUrl")]
        public string StyleUrl { get; set; }

        public KMLPoint Point { get; set; }

        public Placemark()
        {
        }

        public Placemark(string name, string description, string styleUrl,
            double latitude, double longitude)
        {
            Name = name;
            Description = description;
            StyleUrl = styleUrl;

            Point = new KMLPoint(latitude, longitude);
        }

    }

    public class kml
    {
        [XmlIgnore]
        string Name { get; set; }


        List<Placemark> _Placemarks = new List<Placemark>();

        [XmlArray()]
        public List<Placemark> Document
        {
            get
            {
                return _Placemarks;
            }

            set
            {
                _Placemarks = value;
            }
        }

        public kml()
        {
        }

        public kml(string name)
        {
            Name = name;
        }

        private XmlNode GetColorStyle(XmlDocument xmlDoc,  string color)
        {
            XmlNode style = xmlDoc.CreateNode(XmlNodeType.Element, "Style", "");
            XmlAttribute attr = style.OwnerDocument.CreateAttribute("id");
            attr.Value = color;
            style.Attributes.Append(attr);

            XmlNode iconStyle = xmlDoc.CreateNode(XmlNodeType.Element, "IconStyle", "");
            XmlNode icon = xmlDoc.CreateNode(XmlNodeType.Element, "Icon", "");
            XmlNode href = xmlDoc.CreateNode(XmlNodeType.Element, "href", "");
            href.InnerText = string.Format("http://www.google.com/intl/en_us/mapfiles/ms/icons/{0}-dot.png",
                color);
            
            
            style.AppendChild(iconStyle);
            iconStyle.AppendChild(icon);
            icon.AppendChild(href);

            return style;
        }


        public void SaveToFile(string xml)
        {
            using (FileStream fs = new FileStream(xml, FileMode.Create, FileAccess.ReadWrite))
            {
                using (StreamWriter sw = new StreamWriter(fs, System.Text.Encoding.UTF8))
                {
                    XmlSerializer serializer = new XmlSerializer(this.GetType());
                    serializer.Serialize(sw, this);
                }
            }


            XmlDocument xmlDoc = new XmlDocument();
            xmlDoc.Load(xml);
            xmlDoc.CreateXmlDeclaration("1.0", "utf-8", null);

            XmlNode documentNode = xmlDoc.SelectSingleNode(@"/kml/Document");

            XmlNode nameNode = xmlDoc.CreateNode(XmlNodeType.Element, "name", "");
            nameNode.InnerText = this.Name;

            XmlNode placeMarkNode = documentNode.FirstChild;
            documentNode.InsertBefore(nameNode, placeMarkNode);

            documentNode.InsertBefore(GetColorStyle(xmlDoc, "red"), placeMarkNode);
            documentNode.InsertBefore(GetColorStyle(xmlDoc, "green"), placeMarkNode);
            documentNode.InsertBefore(GetColorStyle(xmlDoc, "blue"), placeMarkNode);
            documentNode.InsertBefore(GetColorStyle(xmlDoc, "yellow"), placeMarkNode);

            XmlNode kmlNode = xmlDoc.SelectSingleNode(@"/kml");

            XmlAttribute attr = kmlNode.OwnerDocument.CreateAttribute("xmlns");
            attr.Value = "http://earth.google.com/kml/2.0";

            kmlNode.Attributes.Append(attr);

            xmlDoc.Save(xml);
        }
    }

}

下面看一下调用的方法,使用者如果不想仔细研究细节,那就关注这个就可以了,调用方法非常简单

   1:          static void Main(string[] args)
   3:              Generator kmlGenerator = new Generator("Test");
   5:              kmlGenerator.Generate(new AddressDescription("1600 Amphitheatre Parkway, Mountain View, CA 94043",
   8:              if (!string.IsNullOrEmpty(kmlGenerator.LastErrorOrWarning))
  10:                  Console.WriteLine(kmlGenerator.LastErrorOrWarning);
  13:              kmlGenerator.Generate(new AddressDescription("1 Microsoft Way, Redmond, WA 98052",
  16:              if (!string.IsNullOrEmpty(kmlGenerator.LastErrorOrWarning))
  18:                  Console.WriteLine(kmlGenerator.LastErrorOrWarning);
  21:              kmlGenerator.Generate(new AddressDescription("1601 S. California Ave., Palo Alto, CA 95304",
  24:              if (!string.IsNullOrEmpty(kmlGenerator.LastErrorOrWarning))
  26:                  Console.WriteLine(kmlGenerator.LastErrorOrWarning);
  29:              kmlGenerator.Generate(new AddressDescription("701 First Ave, Sunnyvale, CA 94089",
  32:              if (!string.IsNullOrEmpty(kmlGenerator.LastErrorOrWarning))
  34:                  Console.WriteLine(kmlGenerator.LastErrorOrWarning);
  37:              kmlGenerator.Save("test.kml");
第三行,实例化 KML 生成器,并指定一个名字,这个名字对于 kml 文档中的 name 字段。
第五行,在kml 文件中标注 google 总部的地址
第八行,判断是否有最新的错误,每次执行第五行的Generate 方法,会将最新错误清空,所以这里永远是得到最近一次调用 Generate 方法的错误或警告。
最后,我们可以把这个 kml 文件导入到我们自己创建的 google map 中。这个在 google maps 里面有相应的导入功能,这里就不介绍了。
    <add key="GeocodingApi.Key" value="google api key" />
    <add key="GeocodingApi.Url" value="http://maps.google.com/maps/geo?" />

GeocodingApi.key 这里要填写你自己的 google api key,你可以在 google  网站上获取,地址如下:

http://code.google.com/apis/maps/signup.html