Chris Chris - 22 days ago 5
Javascript Question

Use Google maps API to draw a polyline that changes color

I'm working on a project that plots a track from GPX file onto google maps, and color-code it with an attribute (let's call it

Score
). I've done some editing that now there is an additional tag
Score
for each coordinate in this GPX file. Here is a snippet of my modified GPX file.

<trkpt lat="50.834048" lon="-0.127354"> <ele>31.0</ele>
<Score>-0.64730385</Score>
<time>2016-07-07T14:31:51Z</time>
</trkpt>

<trkpt lat="50.833833" lon="-0.127600"> <ele>31.0</ele>
<Score>-0.647203</Score>
<time>2016-07-07T14:32:09Z</time>
</trkpt>


I'm learning Google maps' javascript API to do the plotting part. My question is, how to create a ployline that changes color based on
Score
? I understand that I can draw thousands of mini polylines with just two points, and set a color for each of them. But it feels very primitive. Are there better ways of doing it? Also, I could write my own function to parse this GPX file, but why doesn't google maps support GPX?

If it helps to illustrate my purpose, this is what I try to achieve:
enter image description here

Answer

One option would be to make a separate polyline for each line segment and assign it a color based on the Score in the first point of that segment.

for (var i=0;i<(trkpts.length-1);i++) {
  var coord1 = new google.maps.LatLng(parseFloat(trkpts[i].getAttribute("lat")),
                                      parseFloat(trkpts[i].getAttribute("lon")));
  var coord2 = new google.maps.LatLng(parseFloat(trkpts[i+1].getAttribute("lat")),
                                      parseFloat(trkpts[i+1].getAttribute("lon")));
  var score = parseFloat(nodeValue(trkpts[i].getElementsByTagName("Score")[0]));
  var polyline = new google.maps.Polyline({
    map: map,
    path: [coord1, coord2],
    strokeColor: rgb(-1,1,score),
    strokeWeight: 4,
    strokeOpacity: 1.0
  });
}

proof of concept fiddle

picture of fiddle

code snippet:

var map;
var bounds = new google.maps.LatLngBounds();

function initialize() {
  map = new google.maps.Map(
    document.getElementById("map_canvas"), {
      center: new google.maps.LatLng(34, 108),
      zoom: 13,
      mapTypeId: google.maps.MapTypeId.ROADMAP
    });
  var gpxStr = '<gpx><trk><trkseg><trkpt lat="50.834048" lon="-0.127354"> <ele>31.0</ele><Score>-0.64730385</Score><time>2016-07-07T14:31:51Z</time></trkpt><trkpt lat="50.833833" lon="-0.127600"> <ele>31.0</ele><Score>-0.647203</Score><time>2016-07-07T14:32:09Z</time></trkpt><trkpt lat="50.833715" lon="-0.127768"> <ele>31.0</ele><Score>0.647203</Score><time>2016-07-07T14:32:50Z</time></trkpt><trkpt lat="50.833171" lon="-0.128468"> <ele>31.0</ele><Score>0.99609375</Score><time>2016-07-07T14:32:50Z</time></trkpt><trkpt lat="50.832951" lon="-0.128771"> <ele>31.0</ele><Score>-0.5</Score><time>2016-07-07T14:32:50Z</time></trkpt></trkseg></trk></gpx>';

  var xml = parseXml(gpxStr);
  var trkpts = xml.getElementsByTagName("trkpt");
  var bounds = new google.maps.LatLngBounds();
  for (var i = 0; i < (trkpts.length - 1); i++) {
    var coord1 = new google.maps.LatLng(parseFloat(trkpts[i].getAttribute("lat")),
      parseFloat(trkpts[i].getAttribute("lon")));
    bounds.extend(coord1);
    var coord2 = new google.maps.LatLng(parseFloat(trkpts[i + 1].getAttribute("lat")),
      parseFloat(trkpts[i + 1].getAttribute("lon")));
    bounds.extend(coord2);

    var score = parseFloat(nodeValue(trkpts[i].getElementsByTagName("Score")[0]));
    var polyline = new google.maps.Polyline({
      map: map,
      path: [coord1, coord2],
      strokeColor: rgb(-1, 1, score),
      strokeWeight: 4,
      strokeOpacity: 1.0
    })

  }
  map.fitBounds(bounds);
}
google.maps.event.addDomListener(window, "load", initialize);

function rgb(minimum, maximum, value) {
  var ratio = 2 * (value - minimum) / (maximum - minimum);
  b = Math.floor(Math.max(0, 255 * (1 - ratio)));
  r = Math.floor(Math.max(0, 255 * (ratio - 1)));
  g = 255 - b - r;

  var hexStr = ("00" + r.toString(16)).slice(-2);
  hexStr += ("00" + g.toString(16)).slice(-2);
  hexStr += ("00" + b.toString(16)).slice(-2);
  hexStr = "#" + hexStr;
  return hexStr
}
function parseXml(str) {
    if (window.ActiveXObject) {
      var doc = new ActiveXObject('MicrosoftXMLDOM');
      doc.loadXML(str);
      return doc;
    } else if (window.DOMParser) {
      return (new DOMParser()).parseFromString(str, 'text/xml');
    }
  }
  //nodeValue: Extract the text value of a DOM node, with leading and trailing whitespace trimmed

function nodeValue(node, defVal) {
  var retStr = "";
  if (!node) {
    return (typeof defVal === 'undefined' || defVal === null) ? '' : defVal;
  }
  if (node.nodeType == 3 || node.nodeType == 4 || node.nodeType == 2) {
    retStr += node.nodeValue;
  } else if (node.nodeType == 1 || node.nodeType == 9 || node.nodeType == 11) {
    for (var i = 0; i < node.childNodes.length; ++i) {
      retStr += arguments.callee(node.childNodes[i]);
    }
  }
  return retStr;
};
html,
body,
#map_canvas {
  height: 100%;
  width: 100%;
  margin: 0px;
  padding: 0px
}
<script src="https://maps.googleapis.com/maps/api/js"></script>
<div id="map_canvas" style="border: 2px solid #3872ac;"></div>