Loading a local .kml file using google maps?

GisGoogle Maps-Api-3KmlKmz

Gis Problem Overview


I created a hello world program to load a local kml file (borrowed from google's docs):

var ctaLayer = new google.maps.KmlLayer("http://localhost:8080/kml/cta.kml");

This does not work (nothing gets loaded).

However, when I change that line to:

  var ctaLayer = new google.maps.KmlLayer("http://gmaps-samples.googlecode.com/svn/trunk/ggeoxml/cta.kml");

it loads properly. Both kml files are identical. What do I need to do to get it to load when serving it myself? (I tried both absolute and relative paths, and I know the paths I am using are correct...)

Also I added the correct mime type to my appserver's config file:

<mime-mapping>
    <extension>kml</extension>
<mime-type>application/vnd.google-earth.kml+xml</mime-type>
</mime-mapping>
<mime-mapping>
    <extension>kmz</extension>
    <mime-type>application/vnd.google-earth.kmz</mime-type>
</mime-mapping>

But it still doesn't load.

I found this in google's docs:

> The Google Maps API supports the KML and GeoRSS data formats for displaying geographic information. These data formats are displayed on a map using a KmlLayer object, whose constructor takes the URL of a publicly accessible KML or GeoRSS file.

So I guess what I am trying to do is not possible without serving the kml from a publicly accessible url...unless someone can prove otherwise

Gis Solutions


Solution 1 - Gis

The KML can't be accessed since it's on your local machine and google can't access that since it doesn't know how to get to localhost:8080

Solution 2 - Gis

Unfortunately you cannot use "localhost". You have two choices:

  1. place the kml on a publically available domain. (if google cannot access it, it won't work)
  2. Use geoxml3 which basically does what google does but allows you to downlaod and host the parser JS file youself. It will allow you to load a LOCALHOST KML and parse it out for you (objects accessible via JSON) (http://code.google.com/p/geoxml3/).

Choice #1 might not be an option for those working on defense contracts and deal with sensitive information as the kml is sent to google in the background and rendered on the map.

Solution 3 - Gis

This website, display-kml.appspot.com, requires that you copy/paste the entire KML file into the website. Alternatively, you can use Dropbox to host the KML file using your public folder. Within the public Dropbox folder, there is a right-click context menu that allows you to copy the URL.

Update:

The appspot website has a history of being unstable. As of January 2019, the website appears to be working.

REFERENCES:
  1. http://display-kml.appspot.com/
  2. https://www.dropbox.com/

Solution 4 - Gis

Definitely, Google Maps KmlLayer is designed for you to send your data to them. https://developers.google.com/maps/documentation/javascript/kml

Have a look the following log.

//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
});

Creating Marker, Polygon, they are all browser side parsing and rendering.

As you can see from next network log, KmlLayer class send source URL to Google Server to parse it and (do something in their end) and send the parsed result back to your browser to render.

//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"]]] )

As @capdragon mentioned above, it would be better parse KML by yourself.

UPDATE

Here is compact KML parser code. This only for google.maps Marker and Polygon.

html

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

script, I used typescript but it is pretty same with 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 }

  }

output

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

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

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionhdddView Question on Stackoverflow
Solution 1 - GisMartin MurphyView Answer on Stackoverflow
Solution 2 - GiscapdragonView Answer on Stackoverflow
Solution 3 - Gisj.raymondView Answer on Stackoverflow
Solution 4 - GisJohnView Answer on Stackoverflow