Dynamic KML layers with Google Maps Javascript API

by on 2nd August 2016

About two years ago we created the Messages in the Deep project, which included a mapping interactive powered by Google’s Maps and Fusion Tables APIs. However, we always had an issue with it – the 25,000 daily requests limit to the Fusion Tables API. We repeatedly blew past that. The solution we used at the time was round-robin load balancing against a variety of Fusion Tables, using different tables and credentials.

Needless to say, it was not a great way of handling the issue. As part of the redesign of our site, we decided to take time to go back and rebuild the mapping application to no longer rely on Fusion Tables. Here’s some of what we learned as a result of this.

What We’ll Be Making

Generating KML for Google Maps JavaScript API v3

All the following is as relates to creating Google Maps for a React.JS component. As such, some of the detail will be specific to that use case, which I’ll point out as and when.

Instantiating the Google Maps API is fairly simple:

var MapOptions = {
  scrollwheel: false,
  streetViewControl: !1,
  mapTypeControl: !1,
  maxZoom: 10,
  minZoom: 2,
  zoom: 3,
  mapTypeId: google.maps.MapTypeId.ROADMAP,
  center: new google.maps.LatLng(30, 30),
  zoomControl: true,
  zoomControlOptions: {
    position: google.maps.ControlPosition.LEFT_CENTER
} = new google.maps.Map(this.refs.mapCanvas, MapOptions)

Firstly, this.refs.mapCanvas maps to a React.JS element which looks like this:

React.createElement("div", { 
  ref: "mapCanvas", 
  id: "mapCanvas", 
  style: { 
    display: 'block', 
    height: '100%' 

This element is going to receive the map. Its parent sets the width and height for the map, and this element then expands to fill that, with the map appearing inside. We use refs for this, to allow us to not re-render constantly and hold the map, rather than re-instantiating it every time anything changes.

We also set the map itself (and the KML layer in a minute) to persistent variables, so we can re-access them later on. The reason for this will become clear when we start playing the the KML later.

MapOptions doesn’t require all of the options above. However, these are the ones that the map we created uses, so I’ve left them in for completeness. Once the map has been created, adding a KML layer is also pretty simple:

  var url = '';
this.kmlLayer = new google.maps.KmlLayer(url, { 
  preserveViewport: true, 
  suppressInfoWindows: true, 

Note that the URL for this looks a little odd. This is because it appears that the Google Maps API call logic seems buggy. When we tried to give it a URL with parameters, it would fail to include anything after the ?. Instead, it stripped off any parameters attached to the end of the string provided as a URL. This made it impossible to pass GET or POST parameters to the script that creates the KML. This doesn’t seem to be intended behaviour, but it was the case for us.

To get around this, I’ve set up a rewrite rule to map this URL to the following location: If more parameters are required, these come as a CSV list, in the form of param=value. However, If you access this URL you’ll see it works, but providing it via the Google Maps KML Layer API doesn’t. When using the getStatus() method to debug, it returns an INVALID_DOCUMENT status.

If anyone knows why this is, feel free to let me know.

Changing KML Request URLs

With the old v2 API, if you wanted to set a different URL for KML, you needed to set the map for the KML to null, and add a new layer with new KML on top. With the v3 API however, we helpfully have the new setUrl() method. As a result, loading new data is as simple as calling:

if ( !== null && this.kmlLayer !== null && this.kmlLayer.setMap)
  this.kmlLayer.setUrl('' + props.params);

For the sake of not firing this all the time however, we add in a debounce to the function that calls this. This means that if someone’s changing a bunch of things at the same time, we don’t ask for the KML until we’re sure they’re ready. Alternatively, you could have this as a function that only runs on the user specifying that it should update – either will work perfectly well either way.

This is also however why we need to use variables which will persist, rather than being cleared when the component refreshes – it allows us to re-access the KML layer to update data directly, rather than having to tear down the old layer, set the map on it to null, and create another one. The whole thing becomes simpler and cleaner, and lets us use the setUrl() method to best effect.

Dynamic Components from KML

It’s worth also noting that you can intercept events triggered by the KML layer. For example, adding the following code:

var self = this;

google.maps.event.addListener(this.kmlLayer, 'click', function(kmlEvent) {
  self.props.someFunction({ value: kmlEvent.featureData })

…allows us to call a function on a click on an element rendered by the KML (markers, lines and so on). This gives you the option of marrying data in your KML to data held somewhere else, perhaps behind an API or ready loaded from somewhere else. We use this in the Messages map to show information on a specific cable when clicked, but it could also be triggered on place markers or anything else you can render onto the map.

We're hiring – check out our careers page Careers

Get insights straight to your inbox

Stay one step ahead of the competition with our monthly Inner Circle email full of resources, industry developments and opinions from around the web.