1

I'm using Leaflet maps together with "Plugins by Pavel Shramov", which allows google usage and GPX file use.

This bit was easy enough, but trying now to use markerClusterer. I've spent a few hours playing around with things but complete stabs in the dark, and no joy yet.

Most of the examples I'm seeing are JSON, not GPX, and I don't think are really relevant?

My test page is up here: http://www.talesfromthesaddle.com/mapproject/map.html

If anyone knows of similar usage examples, please let me know so I can take a look. Or, if you want to head over to the test page and give me a few pointers, also welcome!

This is the GPX parser, which might provide some insight:

L.GPX = L.FeatureGroup.extend({
initialize: function(gpx, options) {
    L.Util.setOptions(this, options);
    this._gpx = gpx;
    this._layers = {};

    if (gpx) {
        this.addGPX(gpx, options, this.options.async);
    }
},

loadXML: function(url, cb, options, async) {
    if (async === undefined) async = this.options.async;
    if (options === undefined) options = this.options;

    var req = new window.XMLHttpRequest();
    req.open('GET', url, async);
    try {
        req.overrideMimeType('text/xml'); // unsupported by IE
    } catch(e) {}
    req.onreadystatechange = function() {
        if (req.readyState !== 4) return;
        if(req.status === 200) cb(req.responseXML, options);
    };
    req.send(null);
},

_humanLen: function(l) {
    if (l < 2000)
        return l.toFixed(0) + ' m';
    else
        return (l/1000).toFixed(1) + ' km';
},

_polylineLen: function(line)//line is a L.Polyline()
{
    var ll = line._latlngs;
    var d = 0, p = null;
    for (var i = 0; i < ll.length; i++)
    {
        if(i && p)
            d += p.distanceTo(ll[i]);
        p = ll[i];
    }
    return d;
},

addGPX: function(url, options, async) {
    var _this = this;
    var cb = function(gpx, options) { _this._addGPX(gpx, options); };
    this.loadXML(url, cb, options, async);
},

_addGPX: function(gpx, options) {
    var layers = this.parseGPX(gpx, options);
    if (!layers) return;
    this.addLayer(layers);
    this.fire('loaded');
},  

parseGPX: function(xml, options) {
    var j, i, el, layers = [];
    var named = false, tags = [['rte','rtept'], ['trkseg','trkpt']];

    for (j = 0; j < tags.length; j++) {
        el = xml.getElementsByTagName(tags[j][0]);
        for (i = 0; i < el.length; i++) {
            var l = this.parse_trkseg(el[i], xml, options, tags[j][1]);
            for (var k = 0; k < l.length; k++) {
                if (this.parse_name(el[i], l[k])) named = true;
                layers.push(l[k]);
            }
        }
    }

    el = xml.getElementsByTagName('wpt');
    if (options.display_wpt !== false) {
        for (i = 0; i < el.length; i++) {
            var marker = this.parse_wpt(el[i], xml, options);
            if (!marker) continue;
            if (this.parse_name(el[i], marker)) named = true;
            layers.push(marker);
        }
    }

    if (!layers.length) return;
    var layer = layers[0];
    if (layers.length > 1) 
        layer = new L.FeatureGroup(layers);
    if (!named) this.parse_name(xml, layer);
    return layer;
},

parse_name: function(xml, layer) {
    var i, el, txt='', name, descr='', len=0;
    el = xml.getElementsByTagName('name');
    if (el.length)
        name = el[0].childNodes[0].nodeValue;
    el = xml.getElementsByTagName('desc');
    for (i = 0; i < el.length; i++) {
        for (var j = 0; j < el[i].childNodes.length; j++)
            descr = descr + el[i].childNodes[j].nodeValue;
    }

    if(layer instanceof L.Path)
        len = this._polylineLen(layer);

    if (name) txt += '<h2>' + name + '</h2>' + descr;
    if (len) txt += '<p>' + this._humanLen(len) + '</p>';

    if (layer && layer._popup === undefined) layer.bindPopup(txt);
    return txt;
},

parse_trkseg: function(line, xml, options, tag) {
    var el = line.getElementsByTagName(tag);
    if (!el.length) return [];
    var coords = [];
    for (var i = 0; i < el.length; i++) {
        var ll = new L.LatLng(el[i].getAttribute('lat'),
                    el[i].getAttribute('lon'));
        ll.meta = {};
        for (var j in el[i].childNodes) {
            var e = el[i].childNodes[j];
            if (!e.tagName) continue;
            ll.meta[e.tagName] = e.textContent;
        }
        coords.push(ll);
    }
    var l = [new L.Polyline(coords, options)];
    this.fire('addline', {line:l});
    return l;
},

parse_wpt: function(e, xml, options) {
    var m = new L.Marker(new L.LatLng(e.getAttribute('lat'),
                    e.getAttribute('lon')), options);
    this.fire('addpoint', {point:m});
    return m;
}  });
0

GeoJSON or GPX doesn't really matter, you just need a way to construct a marker from each coordinate pair, and add it to a MarkerCluster group.

Here is the start of a solution; ugly as it is, it works (for clustering the hundreds of gpx points), and it should point you in the right direction to a cleaner solution.

  1. Add a max zoom to the map. Not sure why, but L.MarkerCluster complains if you don't

    var map = new L.Map('map', {center: new L.LatLng(45, 0), zoom: 2, maxZoom:12});

  2. Create a marker cluster group before you start parsing the gpx

    var markerClusters = new L.MarkerClusterGroup();

  3. Drop the on.('loaded') function from this line

    var track = new L.GPX("./map.gpx", {async: true});

  4. Don't add the 'track' to the map. Instead, get GPX.js to add the markers to the marker cluster group, by changing the parseWPT() function as shown below. (Basically, use the existing methods in the GPX.js plugin to construct markers from the lat and log attributes, and add them to the clusters)

    parse_wpt: function(e, xml, options) { var m = new L.Marker(new L.LatLng(e.getAttribute('lat'), e.getAttribute('lon')), options); markerClusters.addLayer(m); }

  5. Finally, add the markerCluster to the map

    map.addLayer(markerClusters)

Note, this is not adding your tracks to the map. If you need these, I suggest separating the points from the tracks in your gpx file, and working from there, with a second call to GPX.js.

  • Toms! What a boss! I can't tell you how grateful I am, I really appreciate your time! I wasn't a million miles off with my attempts (but an inch is as good as a mile right), though I was trying to do everything from the GPX.js file. Anyways, thanks Toms :) – Nick Jones Nov 22 '14 at 17:29
  • Glad it helped! I don't particularly like hacking the GPX plugin, but it works to get these two libraries talking to each other. I'm sure there are more elegant ways, say by writing your own, simplified version of a GPX parser that just returns L.markers – toms Nov 22 '14 at 21:42

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.