




















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 网站上获取,地址如下:
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。