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
C# 程序自动批量生成 google maps 的KML文件