C# 程序自动批量生成 google maps 的KML文件

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

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)
   2:          {
   3:              Generator kmlGenerator = new Generator("Test");
   4:   
   5:              kmlGenerator.Generate(new AddressDescription("1600 Amphitheatre Parkway, Mountain View, CA 94043",
   6:                  "Google"));
   7:   
   8:              if (!string.IsNullOrEmpty(kmlGenerator.LastErrorOrWarning))
   9:              {
  10:                  Console.WriteLine(kmlGenerator.LastErrorOrWarning);
  11:              }
  12:   
  13:              kmlGenerator.Generate(new AddressDescription("1 Microsoft Way, Redmond, WA 98052",
  14:                  "Microsoft"));
  15:   
  16:              if (!string.IsNullOrEmpty(kmlGenerator.LastErrorOrWarning))
  17:              {
  18:                  Console.WriteLine(kmlGenerator.LastErrorOrWarning);
  19:              }
  20:   
  21:              kmlGenerator.Generate(new AddressDescription("1601 S. California Ave., Palo Alto, CA 95304",
  22:                  "Facebook"));
  23:   
  24:              if (!string.IsNullOrEmpty(kmlGenerator.LastErrorOrWarning))
  25:              {
  26:                  Console.WriteLine(kmlGenerator.LastErrorOrWarning);
  27:              }
  28:   
  29:              kmlGenerator.Generate(new AddressDescription("701 First Ave, Sunnyvale, CA 94089",
  30:                  "Yahoo"));
  31:   
  32:              if (!string.IsNullOrEmpty(kmlGenerator.LastErrorOrWarning))
  33:              {
  34:                  Console.WriteLine(kmlGenerator.LastErrorOrWarning);
  35:              }
  36:   
  37:              kmlGenerator.Save("test.kml");
  38:          }
  39:      }
 
第三行,实例化 KML 生成器,并指定一个名字,这个名字对于 kml 文档中的 name 字段。
第五行,在kml 文件中标注 google 总部的地址
第八行,判断是否有最新的错误,每次执行第五行的Generate 方法,会将最新错误清空,所以这里永远是得到最近一次调用 Generate 方法的错误或警告。
后面以此类推了。
最后 Save 到一个kml文件中就OK了。
 
最后,我们可以把这个 kml 文件导入到我们自己创建的 google map 中。这个在 google maps 里面有相应的导入功能,这里就不介绍了。
 
完整源码下载
 
 
注意源码中 app.config 文件中
 
    <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# Windows
C#程序如何编译成Native代码
【10月更文挑战第15天】在C#中,可以通过.NET Native和第三方工具(如Ngen.exe)将程序编译成Native代码,以提升性能和启动速度。.NET Native适用于UWP应用,而Ngen.exe则通过预编译托管程序集为本地机器代码来加速启动。不过,这些方法也可能增加编译时间和部署复杂度。
926 2
|
12月前
|
存储 监控 算法
基于 C# 的局域网计算机监控系统文件变更实时监测算法设计与实现研究
本文介绍了一种基于C#语言的局域网文件变更监控算法,通过事件驱动与批处理机制结合,实现高效、低负载的文件系统实时监控。核心内容涵盖监控机制选择(如事件触发机制)、数据结构设计(如监控文件列表、事件队列)及批处理优化策略。文章详细解析了C#实现的核心代码,并提出性能优化与可靠性保障措施,包括批量处理、事件过滤和异步处理等技术。最后,探讨了该算法在企业数据安全监控、文件同步备份等场景的应用潜力,以及未来向智能化扩展的方向,如文件内容分析、智能告警机制和分布式监控架构。
285 3
|
存储 SQL 数据库连接
C#程序调用Sql Server存储过程异常处理:调用存储过程后不返回、不抛异常的解决方案
本文分析了C#程序操作Sql Server数据库时偶发的不返回、不抛异常问题,并提出了解决思路。首先解析了一个执行存储过程的函数`ExecuteProcedure`,其功能是调用存储过程并返回影响行数。针对代码执行被阻塞但无异常的情况,文章总结了可能原因,如死锁、无限循环或网络问题等。随后提供了多种解决方案:1) 增加日志定位问题;2) 使用异步操作提升响应性;3) 设置超时机制避免阻塞;4) 利用线程池分离主线程;5) 通过信号量同步线程;6) 监控数据库连接状态确保可用性。这些方法可有效应对数据库操作中的潜在问题,保障程序稳定性。
832 11
基于 C# 编写的 Visual Studio 文件编码显示与修改扩展插件
基于 C# 编写的 Visual Studio 文件编码显示与修改扩展插件
469 9
|
算法 Java 测试技术
Benchmark.NET:让 C# 测试程序性能变得既酷又简单
Benchmark.NET是一款专为 .NET 平台设计的性能基准测试框架,它可以帮助你测量代码的执行时间、内存使用情况等性能指标。它就像是你代码的 "健身教练",帮助你找到瓶颈,优化性能,让你的应用跑得更快、更稳!希望这个小教程能让你在追求高性能的路上越走越远,享受编程带来的无限乐趣!
1003 13
|
存储 C#
【C#】大批量判断文件是否存在的两种方法效率对比
【C#】大批量判断文件是否存在的两种方法效率对比
541 1
|
安全 API C#
C# 如何让程序后台进程不被Windows任务管理器强制结束
C# 如何让程序后台进程不被Windows任务管理器强制结束
890 0
|
API C#
C#实现Winform程序右下角弹窗消息提示
C#实现Winform程序右下角弹窗消息提示
1058 1
|
C# 容器
C#中的命名空间与程序集管理
在C#编程中,`命名空间`和`程序集`是组织代码的关键概念,有助于提高代码的可维护性和复用性。本文从基础入手,详细解释了命名空间的逻辑组织方式及其基本语法,展示了如何使用`using`指令访问其他命名空间中的类型,并提供了常见问题的解决方案。接着介绍了程序集这一.NET框架的基本单位,包括其创建、引用及高级特性如强名称和延迟加载等。通过具体示例,展示了如何创建和使用自定义程序集,并提出了针对版本不匹配和性能问题的有效策略。理解并善用这些概念,能显著提升开发效率和代码质量。
583 4
|
Linux C# 开发者
Uno Platform 驱动的跨平台应用开发:从零开始的全方位资源指南与定制化学习路径规划,助您轻松上手并精通 C# 与 XAML 编程技巧,打造高效多端一致用户体验的移动与桌面应用程序
【9月更文挑战第8天】Uno Platform 的社区资源与学习路径推荐旨在为初学者和开发者提供全面指南,涵盖官方文档、GitHub 仓库及社区支持,助您掌握使用 C# 和 XAML 创建跨平台原生 UI 的技能。从官网入门教程到进阶技巧,再到活跃社区如 Discord,本指南带领您逐步深入了解 Uno Platform,并提供实用示例代码,帮助您在 Windows、iOS、Android、macOS、Linux 和 WebAssembly 等平台上高效开发。建议先熟悉 C# 和 XAML 基础,然后实践官方教程,研究 GitHub 示例项目,并积极参与社区讨论,不断提升技能。
661 2