Google地图API和KML文件本地开发选项

15

谷歌地图JavaScript版本3 API库文档清楚地解释了

谷歌地图API支持KML和GeoRSS数据格式来显示地理信息。这些数据格式可以使用KmlLayer对象在地图上显示,它的构造函数需要一个公开可访问的KML或GeoRSS文件的URL。

甚至有几个Stack Overflow问题关于如何加载本地数据:

一些答案指向了第三方库,可以在不需要文件公开的情况下本地解析KML:

当你需要保护数据私密性时,这些解决方案是不错的选择。但我只是想让开发变得更加容易。当本地运行时,我无法解析我的KML文件,因此失去了我正在尝试测试的功能。我在一个公共网站上发布了一个通用的KML文件,但是当真正运行时,必须有不同的开发代码来呈现一件事情与另一件事情。

对于本地开发而言,我有哪些选项可以呈现动态生成的公共可用的KML文件?

2个回答

11

看起来你已经很好地列出了选项:

如果你想使用本地数据而不涉及公开的 Web 服务器,那么你需要使用基于 JavaScript 的方法来解析 KML 并将其加载到地图上。虽然这样做无法完全复制 Google 的功能,但如果你只关心显示 KML 特征的话,这可能已经足够进行初步开发了。在这种情况下,我可能会设置一个存根类,如下所示:

    // I'll assume you have a global namespace called MyProject
    MyProject.LOCAL_KML = true;

    MyProject.KmlLayer = function(url) {
        // parse the KML, maybe caching an array of markers or polygons,
        // using one of the libraries you list in your question
    };

    // now stub out the methods you care about, based on
    // http://code.google.com/apis/maps/documentation/javascript/reference.html#KmlLayer
    MyProject.KmlLayer.prototype.setMap = function(map) {
        // add the markers and polygons to the map, or remove them if !map
    }
    // etc

现在,要么在代码中加入一个开关,要么注释/取消注释,要么使用构建脚本来切换,或者使用您当前的切换开发和生产代码的流程:

    var kmlPath = "/my.kml";
    var kmlLayer =  MyProject.LOCAL_KML ?
        new MyProject.KmlLayer(MyProject.LOCAL_KML_HOST + kmlPath) :
        new google.maps.KmlLayer(MyProject.PRODUCTION_KML_HOST + kmlPath);
    kmlLayer.setMap(myMap);

如果你需要 Google KmlLayer 的所有功能,或者想要确保它在生产环境中能够正常工作,或者不想麻烦地存根出 Google 提供的功能,那么你需要将其上传到公共可用服务器上,这样 Google 可以进行其服务器端处理。

除了显而易见的选项(FTP、命令行脚本上传新的 KML 文件等),其中大部分都需要你在加载地图页面之前手动执行一些操作,你可以考虑将更新构建到正在加载的页面中。根据你使用的平台,在后端或前端执行这个操作可能更容易;关键是在公共服务器上拥有一个允许更新 KML 的脚本:

  1. 从 request.POST 获取 KML 字符串
  2. 验证 KML 字符串(只是为了防止攻击)
  3. 写入单个文件,例如 "my.kml"

然后,当你查看地图页面时,基于来自 localhost 的数据更新远程 KML。这是一个客户端版本的示例,使用 jQuery:

// again, you'd probably have a way to kill this block in production
if (MyProject.UPDATE_KML_FROM_LOCALHOST) {
    // get localhost KML
    $.get(MyProject.LOCAL_KML_HOST + kmlPath, function(data) {
        // now post it to the remote server
        $.post(
            MyProject.DEV_KML_HOST + '/update_kml.php', 
            { kml: data }, 
            function() {
                // after the post completes, get the KML layer from Google
                var kmlLayer new google.maps.KmlLayer(MyProject.DEV_KML_HOST + kmlPath);
                kmlLayer.setMap(myMap);
            }
        );
    })
}

诚然,这里会有很多往返(页面 -> 本地主机,页面 -> 远程服务器,Google -> 远程服务器,Google -> 页面),所以这会慢一些。但是这将允许您正确渲染本地主机产生的动态KML数据的Google代码,而无需每次重新加载页面时都进行单独的手动步骤。


0

毫无疑问,Google Maps KmlLayer 是为了让你把数据发送给他们而设计的。 https://developers.google.com/maps/documentation/javascript/kml

请看以下日志。

//console
var src = 'https://developers.google.com/maps/documentation/javascript/examples/kml/westcampus.kml';

var kmlLayer = new google.maps.KmlLayer(src, {
  suppressInfoWindows: true,
  preserveViewport: false,
  map: your_gmap_object
});

创建标记、多边形,它们都是浏览器端解析和渲染

从下一个网络日志中可以看出,KmlLayer类将源URL发送到Google服务器进行解析并(在其端执行某些操作),然后将解析结果发送回您的浏览器进行渲染。

//REQUEST from browser

https://maps.googleapis.com/maps/api/js/KmlOverlayService.GetOverlays?1shttps%3A%2F%2Fdevelopers.google.com%2Fmaps%2Fdocumentation%2Fjavascript%2Fexamples%2Fkml%2Fwestcampus.kml&callback=_xdc_._lidt3k&key=AIzaSyBeLTP20qMgxsQFz1mwLlzNuhrS5xD_a_U&token=103685

//RESPONSE from google server

/**/_xdc_._lidt3k && _xdc_._lidt3k( [0,"kml:cXOw0bjKUSmlnTN2l67v0Sai6WfXhSSWuyNaDD0mAzh6xfi2fYnBo78Y2Eg","|ks:;dc:tg;ts:51385071|kv:3|api:3",...
["KmlFile"],[[37.423017,-122.0927],[37.424194,-122.091498]],[["g74cf1503d602f2e5"],["g58e8cf8fd6da8d29"],["ge39d22e72437b02e"]],1,[["client","2"]],-21505,[["ks",";dc:tg;ts:51385071"],["kv","3"],["api","3"]]] )

如@capdragon所述,最好自己解析KML。

更新

这里是紧凑的KML解析器代码。仅适用于Google.maps标记和多边形。

HTML

<input type='file' accept=".kml,.kmz" onchange="fileChanged()">

脚本,我使用TypeScript,但它与JavaScript非常相似

  file: any
  fileChanged(e) {
    this.file = e.target.files[0]
    this.parseDocument(this.file)
  }
  parseDocument(file) {
    let fileReader = new FileReader()
    fileReader.onload = async (e: any) => {
      let result = await this.extractGoogleCoords(e.target.result)

      //CREATE MARKER OR POLYGON WITH result here
      console.log(result)

    }
    fileReader.readAsText(file)
  }

  async extractGoogleCoords(plainText) {
    let parser = new DOMParser()
    let xmlDoc = parser.parseFromString(plainText, "text/xml")
    let googlePolygons = []
    let googleMarkers = []

    if (xmlDoc.documentElement.nodeName == "kml") {

      for (const item of xmlDoc.getElementsByTagName('Placemark') as any) {
        let placeMarkName = item.getElementsByTagName('name')[0].childNodes[0].nodeValue.trim()
        let polygons = item.getElementsByTagName('Polygon')
        let markers = item.getElementsByTagName('Point')

        /** POLYGONS PARSE **/        
        for (const polygon of polygons) {
          let coords = polygon.getElementsByTagName('coordinates')[0].childNodes[0].nodeValue.trim()
          let points = coords.split(" ")

          let googlePolygonsPaths = []
          for (const point of points) {
            let coord = point.split(",")
            googlePolygonsPaths.push({ lat: +coord[1], lng: +coord[0] })
          }
          googlePolygons.push(googlePolygonsPaths)
        }

        /** MARKER PARSE **/    
        for (const marker of markers) {
          var coords = marker.getElementsByTagName('coordinates')[0].childNodes[0].nodeValue.trim()
          let coord = coords.split(",")
          googleMarkers.push({ lat: +coord[1], lng: +coord[0] })
        }
      }
    } else {
      throw "error while parsing"
    }

    return { markers: googleMarkers, polygons: googlePolygons }

  }

输出

markers: Array(3)
0: {lat: 37.42390182131783, lng: -122.0914977709329}
...

polygons: Array(1)
0: Array(88)
0: {lat: -37.79825999283025, lng: 144.9165994157198}
...

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接