Google Maps API 和 KML 文件 LocalHost 开发选项

IT技术 javascript google-maps localhost kml
2021-02-28 19:45:34

Google Maps JavaScript 版本 3 API库文档清楚地解释了

Google Maps API 支持用于显示地理信息的 KML 和 GeoRSS 数据格式。这些数据格式使用 KmlLayer 对象显示在地图上,该对象的构造函数采用可公开访问的 KML 或 GeoRSS 文件的 URL

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

一些答案指向第三方库,它们可以在本地解析 KML,而无需公开文件:

如果您需要将数据保密,这些解决方案是很好的,但我只是想让开发更容易。在本地运行时,我显然无法解析我的 KML,因此失去了我试图测试的功能。我在一个公开可用的站点上发布了一个通用 KML 文件,但是在真正运行时必须使用不同的开发代码来呈现一件事与另一件事。

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

2个回答

您似乎已经很好地概述了这些选项:

如果您想处理本地数据,而不涉及可公开访问的网络服务器,则需要使用基于 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”

然后,当您查看地图页面时,根据来自 的数据更新远程 KML localhost这是一个使用 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);
            }
        );
    })
}

诚然,这里有很多往返(页面 -> localhost,页面 -> 远程服务器,谷歌 -> 远程服务器,谷歌 -> 页面),所以这会很但它允许您让 Google 的代码正确呈现在本地主机上生成的动态 KML 数据,而无需在每次重新加载页面时执行单独的手动步骤。

毫无疑问,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
});

创建Marker、Polygon,都是浏览器端解析渲染

正如您从下一个网络日志中看到的那样,KmlLayer 类将源 URL 发送到 Google Server以对其进行解析并(在他们的最后做一些事情)并将解析的结果发送回您的浏览器以进行渲染。

//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}
...