switch to GeoJSON response format instead of GPX:

- use BRouter track stats
- remove leaflet-gpx plugin
This commit is contained in:
Norbert Renner 2014-08-22 10:10:07 +02:00
parent d260414f6c
commit 726cf4bba6
16 changed files with 50 additions and 598 deletions

View file

@ -50,9 +50,6 @@ Copyright (c) 2013 Felix Bache; [MIT License](https://github.com/MrMufflon/Leafl
Copyright (c) 2013, Michael Bostock. All rights reserved.; [3-clause BSD License](https://github.com/mbostock/d3/blob/master/LICENSE)
* [Leaflet.draw](https://github.com/Leaflet/Leaflet.draw)
Copyright 2012 Jacob Toye; [MIT License](https://github.com/Leaflet/Leaflet.draw/blob/master/MIT-LICENCE.txt)
* [leaflet-gpx](https://github.com/mpetazzoni/leaflet-gpx)
Copyright (C) 2011-2012 Pavel Shramov, Copyright (C) 2013 Maxime Petazzoni
All rights reserved. [2-clause BSD License](https://github.com/mpetazzoni/leaflet-gpx/blob/master/LICENSE)
* [Leaflet.Control.Search](https://github.com/stefanocudini/leaflet-search)
Copyright (c) 2013 Stefano Cudini; [MIT License](https://github.com/stefanocudini/leaflet-search/blob/master/LICENSE.txt)
* [leaflet-plugins](https://github.com/shramov/leaflet-plugins)

View file

@ -8,7 +8,6 @@
],
"dependencies": {
"normalize-css": "*",
"leaflet-gpx": "mpetazzoni/leaflet-gpx",
"leaflet-search": "*",
"leaflet-plugins": "*",
"leaflet-routing": "Turistforeningen/leaflet-routing#gh-pages",

View file

@ -1,13 +0,0 @@
{
"name": "leaflet-gpx",
"homepage": "https://github.com/mpetazzoni/leaflet-gpx",
"_release": "ad9d5adf0a",
"_resolution": {
"type": "branch",
"branch": "master",
"commit": "ad9d5adf0ae5450bc5bc60e1e86307beb7667772"
},
"_source": "git://github.com/mpetazzoni/leaflet-gpx.git",
"_target": "*",
"_originalSource": "mpetazzoni/leaflet-gpx"
}

View file

@ -1,24 +0,0 @@
Copyright (C) 2011-2012 Pavel Shramov
Copyright (C) 2013 Maxime Petazzoni <maxime.petazzoni@bulix.org>
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
- Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice, this
list of conditions and the following disclaimer in the documentation and/or
other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View file

@ -1,147 +0,0 @@
GPX plugin for Leaflet
======================
[Leaflet](http://www.leafletjs.com) is a Javascript library for displaying
interactive maps. This plugin, based on the work of [Pavel
Shramov](http://github.com/shramov) and his
[leaflet-plugins](http://github.com/shramov/leaflet-plugins), it allows for the
analysis and parsing of a GPX track in order to display it as a Leaflet map
layer. As it parses the GPX data, it will record information about the recorded
track, including total time, moving time, total distance, elevation stats and
heart-rate.
GPX parsing will automatically handle pauses in the track with a default
tolerance interval of 15 seconds between points. You can configure this
interval by setting `max_point_interval`, in milliseconds, in the options
passed to the `GPX` constructor.
I've put together a complete example as a
[demo](http://mpetazzoni.github.com/leaflet-gpx/).
License
-------
`leaflet-gpx` is under the *BSD 2-clause license*. Please refer to the
attached LICENSE file and/or to the copyright header in gpx.js for more
information.
Usage
-----
Usage is very simple. Let's consider we have a Leaflet map:
```javascript
var map = L.map('map');
L.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: 'Map data &copy; <a href="http://www.osm.org">OpenStreetMap</a>'
}).addTo(map);
```
Displaying the GPX track is as easy as:
```javascript
var gpx = '...'; // URL to your GPX file or the GPX itself
new L.GPX(gpx, {async: true}).on('loaded', function(e) {
map.fitBounds(e.target.getBounds());
}).addTo(map);
```
If you want to display additional information about the GPX track, you can do
so in the 'loaded' event handler, calling one of the following methods on the
`GPX` object `e.target`:
* `get_name()`: returns the name of the GPX track
* `get_distance()`: returns the total track distance, in meters
* `get_start_time()`: returns a Javascript `Date` object representing the
starting time
* `get_end_time()`: returns a Javascript `Date` object representing when the
last point was recorded
* `get_moving_time()`: returns the moving time, in milliseconds
* `get_total_time()`: returns the total track time, in milliseconds
* `get_moving_pace()`: returns the average moving pace in milliseconds per km
* `get_moving_speed()`: returns the average moving speed in km per hour
* `get_elevation_gain()`: returns the cumulative elevation gain, in meters
* `get_elevation_loss()`: returns the cumulative elevation loss, in meters
* `get_average_hr()`: returns the average heart rate (if available)
If you're not a fan of the metric system, you also have the following methods
at your disposal:
* `get_distance_imp()`: returns the total track distance in miles
* `get_moving_pace_imp()`: returns the average moving pace in milliseconds per
hour
* `get_moving_speed()`: returns the average moving pace in miles per
hour
The reason why these methods return milliseconds is that you have at your
disposal a nice helper method to format a duration in milliseconds into a cool
string like `3:07'48"` or `59'32.431`:
* `get_duration_string(duration, hidems)`, where `duration` is in
milliseconds and `hidems` is an optional boolean you can use to request never
to display millisecond precision.
You can also get full elevation and heartrate data with:
* `get_elevation_data()` and `get_elevation_data_imp()`
* `get_heartrate_data()` and `get_heartrate_data_imp()`
These methods all return an array of points `[distance, value, tooltip]` where
the distance is either in kilometers or in miles and the elevation in meters of
feet, depending on whether you use the `_imp` variant or not. Heart rate,
obviously, doesn't change.
You can reload remote gpx file every 5 seconds with:
```javascript
var gpxLayer = new L.GPX(gpxFile);
setInterval(function() {
gpxLayer.reload();
},5000);
```
About marker icons
------------------
By default `gpx.js` will use `pin-icon-start.png`, `pin-icon-end.png` and
`pin-shadow.png` as the marker icons URLs for, respectively, the start marker,
the end marker and their drop shadow. Since it might not be convenient that
these images have to reside under the same directory as your HTML page, it is
possible to override the marker icon URLs and sizes by passing a
`marker_options` object to the `GPX` options object.
The field names are the same as for custom Leaflet icons, as explained in the
[Markers with custom icons](http://leafletjs.com/examples/custom-icons.html)
page in Leaflet's documentation. The only difference is that instead of
`iconUrl` you should specify `startIconUrl` and `endIconUrl` for the start and
end markers, respectively.
Note that you do not need to override all the marker icon options as `gpx.js`
will use sensible defaults with sizes matching the provided icon images. Here
is how you would override the URL of the provided icons if you decided to place
them in an `images/` directory:
```javascript
var url = '...'; // URL to your GPX file
new L.GPX(url, {
async: true,
marker_options: {
startIconUrl: 'images/pin-icon-start.png',
endIconUrl: 'images/pin-icon-end.png',
shadowUrl: 'images/pin-shadow.png'
}
}).on('loaded', function(e) {
map.fitBounds(e.target.getBounds());
}).addTo(map);
```
Caveats
-------
* Distance calculation is relatively accurate, but elevation change
calculation is not topographically adjusted, so the total elevation
gain/loss/change might appear inaccurate in some situations.
* Currently doesn't seem to work in IE8/9. See #9 and #11 for
discussion.

View file

@ -1,361 +0,0 @@
/**
* Copyright (C) 2011-2012 Pavel Shramov
* Copyright (C) 2013 Maxime Petazzoni <maxime.petazzoni@bulix.org>
* All Rights Reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/*
* Thanks to Pavel Shramov who provided the initial implementation and Leaflet
* integration. Original code was at https://github.com/shramov/leaflet-plugins.
*
* It was then cleaned-up and modified to record and make available more
* information about the GPX track while it is being parsed so that the result
* can be used to display additional information about the track that is
* rendered on the Leaflet map.
*/
var _MAX_POINT_INTERVAL_MS = 15000;
var _SECOND_IN_MILLIS = 1000;
var _MINUTE_IN_MILLIS = 60 * _SECOND_IN_MILLIS;
var _HOUR_IN_MILLIS = 60 * _MINUTE_IN_MILLIS;
var _DEFAULT_MARKER_OPTS = {
startIconUrl: 'pin-icon-start.png',
endIconUrl: 'pin-icon-end.png',
shadowUrl: 'pin-shadow.png',
iconSize: [33, 50],
shadowSize: [50, 50],
iconAnchor: [16, 45],
shadowAnchor: [16, 47]
};
var _DEFAULT_POLYLINE_OPTS = {
color:'blue'
};
L.GPX = L.FeatureGroup.extend({
initialize: function(gpx, options) {
options.max_point_interval = options.max_point_interval || _MAX_POINT_INTERVAL_MS;
options.marker_options = this._merge_objs(
_DEFAULT_MARKER_OPTS,
options.marker_options || {});
options.polyline_options = this._merge_objs(
_DEFAULT_POLYLINE_OPTS,
options.polyline_options || {});
L.Util.setOptions(this, options);
// Base icon class for track pins.
L.GPXTrackIcon = L.Icon.extend({ options: options.marker_options });
this._gpx = gpx;
this._layers = {};
this._info = {
name: null,
length: 0.0,
elevation: {gain: 0.0, loss: 0.0, _points: []},
hr: {avg: 0, _total: 0, _points: []},
duration: {start: null, end: null, moving: 0, total: 0},
};
if (gpx) {
this._parse(gpx, options, this.options.async);
}
},
get_duration_string: function(duration, hidems) {
var s = '';
if (duration >= _HOUR_IN_MILLIS) {
s += Math.floor(duration / _HOUR_IN_MILLIS) + ':';
duration = duration % _HOUR_IN_MILLIS;
}
var mins = Math.floor(duration / _MINUTE_IN_MILLIS);
duration = duration % _MINUTE_IN_MILLIS;
if (mins < 10) s += '0';
s += mins + '\'';
var secs = Math.floor(duration / _SECOND_IN_MILLIS);
duration = duration % _SECOND_IN_MILLIS;
if (secs < 10) s += '0';
s += secs;
if (!hidems && duration > 0) s += '.' + Math.round(Math.floor(duration)*1000)/1000;
else s += '"';
return s;
},
// Public methods
to_miles: function(v) { return v / 1.60934; },
to_ft: function(v) { return v * 3.28084; },
m_to_km: function(v) { return v / 1000; },
m_to_mi: function(v) { return v / 1609.34; },
get_name: function() { return this._info.name; },
get_desc: function() { return this._info.desc; },
get_author: function() { return this._info.author; },
get_copyright: function() { return this._info.copyright; },
get_desc: function() { return this._info.desc; },
get_distance: function() { return this._info.length; },
get_distance_imp: function() { return this.to_miles(this.m_to_km(this.get_distance())); },
get_start_time: function() { return this._info.duration.start; },
get_end_time: function() { return this._info.duration.end; },
get_moving_time: function() { return this._info.duration.moving; },
get_total_time: function() { return this._info.duration.total; },
get_moving_pace: function() { return this.get_moving_time() / this.m_to_km(this.get_distance()); },
get_moving_pace_imp: function() { return this.get_moving_time() / this.get_distance_imp(); },
get_moving_speed: function() { return this.m_to_km(this.get_distance()) / (this.get_moving_time() / (3600 * 1000)) ; },
get_moving_speed_imp:function() { return this.to_miles(this.m_to_km(this.get_distance())) / (this.get_moving_time() / (3600 * 1000)) ; },
get_elevation_gain: function() { return this._info.elevation.gain; },
get_elevation_loss: function() { return this._info.elevation.loss; },
get_elevation_data: function() {
var _this = this;
return this._info.elevation._points.map(
function(p) { return _this._prepare_data_point(p, _this.m_to_km, null,
function(a, b) { return a.toFixed(2) + ' km, ' + b.toFixed(0) + ' m'; });
});
},
get_elevation_data_imp: function() {
var _this = this;
return this._info.elevation._points.map(
function(p) { return _this._prepare_data_point(p, _this.m_to_mi, _this.to_ft,
function(a, b) { return a.toFixed(2) + ' mi, ' + b.toFixed(0) + ' ft'; });
});
},
get_average_hr: function() { return this._info.hr.avg; },
get_heartrate_data: function() {
var _this = this;
return this._info.hr._points.map(
function(p) { return _this._prepare_data_point(p, _this.m_to_km, null,
function(a, b) { return a.toFixed(2) + ' km, ' + b.toFixed(0) + ' bpm'; });
});
},
get_heartrate_data_imp: function() {
var _this = this;
return this._info.hr._points.map(
function(p) { return _this._prepare_data_point(p, _this.m_to_mi, null,
function(a, b) { return a.toFixed(2) + ' mi, ' + b.toFixed(0) + ' bpm'; });
});
},
reload: function() {
this.clearLayers();
this._parse(this._gpx, this.options, this.options.async);
},
// Private methods
_merge_objs: function(a, b) {
var _ = {};
for (var attr in a) { _[attr] = a[attr]; }
for (var attr in b) { _[attr] = b[attr]; }
return _;
},
_prepare_data_point: function(p, trans1, trans2, trans_tooltip) {
var r = [trans1 && trans1(p[0]) || p[0], trans2 && trans2(p[1]) || p[1]];
r.push(trans_tooltip && trans_tooltip(r[0], r[1]) || (r[0] + ': ' + r[1]));
return r;
},
_load_xml: 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);
},
_parse: function(input, options, async) {
var _this = this;
var cb = function(gpx, options) {
var layers = _this._parse_gpx_data(gpx, options);
if (!layers) return;
_this.addLayer(layers);
_this.fire('loaded');
}
if (input.substr(0,1)==='<') { // direct XML has to start with a <
var parser = new DOMParser();
setTimeout(function() {
cb(parser.parseFromString(input, "text/xml"), options);
});
} else {
this._load_xml(input, cb, options, async);
}
},
_parse_gpx_data: function(xml, options) {
var j, i, el, layers = [];
var tags = [['rte','rtept'], ['trkseg','trkpt']];
var name = xml.getElementsByTagName('name');
if (name.length > 0) {
this._info.name = name[0].textContent;
}
var desc = xml.getElementsByTagName('desc');
if (desc.length > 0) {
this._info.desc = desc[0].textContent;
}
var author = xml.getElementsByTagName('author');
if (author.length > 0) {
this._info.author = author[0].textContent;
}
var copyright = xml.getElementsByTagName('copyright');
if (copyright.length > 0) {
this._info.copyright = copyright[0].textContent;
}
for (j = 0; j < tags.length; j++) {
el = xml.getElementsByTagName(tags[j][0]);
for (i = 0; i < el.length; i++) {
var coords = this._parse_trkseg(el[i], xml, options, tags[j][1]);
if (coords.length === 0) continue;
// add track
var l = new L.Polyline(coords, options.polyline_options);
this.fire('addline', { line: l })
layers.push(l);
if (options.marker_options.startIconUrl) {
// add start pin
var p = new L.Marker(coords[0], {
clickable: false,
icon: new L.GPXTrackIcon({iconUrl: options.marker_options.startIconUrl})
});
this.fire('addpoint', { point: p });
layers.push(p);
}
if (options.marker_options.endIconUrl) {
// add end pin
p = new L.Marker(coords[coords.length-1], {
clickable: false,
icon: new L.GPXTrackIcon({iconUrl: options.marker_options.endIconUrl})
});
this.fire('addpoint', { point: p });
layers.push(p);
}
}
}
this._info.hr.avg = Math.round(this._info.hr._total / this._info.hr._points.length);
if (!layers.length) return;
var layer = layers[0];
if (layers.length > 1)
layer = new L.FeatureGroup(layers);
return layer;
},
_parse_trkseg: function(line, xml, options, tag) {
var el = line.getElementsByTagName(tag);
if (!el.length) return [];
var coords = [];
var last = null;
for (var i = 0; i < el.length; i++) {
var _, ll = new L.LatLng(
el[i].getAttribute('lat'),
el[i].getAttribute('lon'));
ll.meta = { time: null, ele: null, hr: null };
_ = el[i].getElementsByTagName('time');
if (_.length > 0) {
ll.meta.time = new Date(Date.parse(_[0].textContent));
}
_ = el[i].getElementsByTagName('ele');
if (_.length > 0) {
ll.meta.ele = parseFloat(_[0].textContent);
}
_ = el[i].getElementsByTagNameNS('*', 'hr');
if (_.length > 0) {
ll.meta.hr = parseInt(_[0].textContent);
this._info.hr._points.push([this._info.length, ll.meta.hr]);
this._info.hr._total += ll.meta.hr;
}
this._info.elevation._points.push([this._info.length, ll.meta.ele]);
this._info.duration.end = ll.meta.time;
if (last != null) {
this._info.length += this._dist3d(last, ll);
var t = ll.meta.ele - last.meta.ele;
if (t > 0) this._info.elevation.gain += t;
else this._info.elevation.loss += Math.abs(t);
t = Math.abs(ll.meta.time - last.meta.time);
this._info.duration.total += t;
if (t < options.max_point_interval) this._info.duration.moving += t;
} else {
this._info.duration.start = ll.meta.time;
}
last = ll;
coords.push(ll);
}
return coords;
},
_dist2d: function(a, b) {
var R = 6371000;
var dLat = this._deg2rad(b.lat - a.lat);
var dLon = this._deg2rad(b.lng - a.lng);
var r = Math.sin(dLat/2) *
Math.sin(dLat/2) +
Math.cos(this._deg2rad(a.lat)) *
Math.cos(this._deg2rad(b.lat)) *
Math.sin(dLon/2) *
Math.sin(dLon/2);
var c = 2 * Math.atan2(Math.sqrt(r), Math.sqrt(1-r));
var d = R * c;
return d;
},
_dist3d: function(a, b) {
var planar = this._dist2d(a, b);
var height = Math.abs(b.meta.ele - a.meta.ele);
return Math.sqrt(Math.pow(planar, 2) + Math.pow(height, 2));
},
_deg2rad: function(deg) {
return deg * Math.PI / 180;
}
});

Binary file not shown.

Before

Width:  |  Height:  |  Size: 842 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 945 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

View file

@ -74,7 +74,7 @@ div.elevation {
}
select {
max-width: 180px;
max-width: 176px;
/* normalize height, for absolute Profile control positioning */
height: 24px;
}
@ -96,7 +96,7 @@ td {
.heading, tr > td:first-child, .label {
/* 1/4 of net info control width (370), so that values start at 50% */
width: 92.5px;
width: 95px;
}
.routing-draw-enabled {
@ -128,7 +128,7 @@ td {
.leaflet-leftpane .leaflet-control:last-child {
position: absolute;
top: 441px;
top: 461px;
bottom: 0px;
}

View file

@ -61,7 +61,6 @@
<script src="http://cdn.leafletjs.com/leaflet-0.7.3/leaflet-src.js"></script>
<script src="http://leaflet.github.io/Leaflet.draw/leaflet.draw-src.js"></script>
<script src="bower_components/leaflet-gpx/gpx.js"></script>
<!--
<script src="lib/spin.min.js"></script>
<script src="lib/leaflet.spin.js"></script>

View file

@ -9,39 +9,35 @@ BR.TrackStats = BR.Control.extend({
return container;
},
update: function (polyline) {
var stats = this.calcStats(polyline),
update: function (polyline, segments) {
var stats = this.calcStats(polyline, segments),
html = '';
html += '<table id="stats">';
html += '<tr><td>Length: </td><td>' + L.Util.formatNum(stats.distance/1000,1) + '</td><td>km</td></tr>';
html += '<tr><td>Ascent: </td><td>' + Math.round(stats.elevationGain) + '</td><td>m</td></tr>';
html += '<tr><td>Descent: </td><td>' + Math.round(stats.elevationLoss) + '</td><td>m</td></tr>';
html += '<tr><td>Length: </td><td>' + L.Util.formatNum(stats.trackLength/1000,1) + '</td><td>km</td></tr>';
html += '<tr><td>Ascent filtered:</td><td>' + stats.filteredAscend + '</td><td>m</td></tr>';
html += '<tr><td>Ascent plain:</td><td>' + stats.plainAscend + '</td><td>m</td></tr>';
html += '<tr><td>Cost: </td><td>' + stats.cost + '</td><td></td></tr>';
html += '</table>';
this._content.innerHTML = html;
},
calcStats: function(polyline) {
calcStats: function(polyline, segments) {
var stats = {
distance: 0,
elevationGain: 0,
elevationLoss: 0
trackLength: 0,
filteredAscend: 0,
plainAscend: 0,
cost: 0
};
var i, props;
var latLngs = polyline ? polyline.getLatLngs() : [];
for (var i = 0, current, next, eleDiff; i < latLngs.length - 1; i++) {
current = latLngs[i];
next = latLngs[i + 1];
stats.distance += current.distanceTo(next);
// from Leaflet.gpx plugin (writes to LatLng.meta.ele, LatLng now supports ele)
eleDiff = (next.ele || next.meta.ele) - (current.ele || current.meta.ele);
if (eleDiff > 0) {
stats.elevationGain += eleDiff;
} else {
stats.elevationLoss += Math.abs(eleDiff);
}
for (i = 0; segments && i < segments.length; i++) {
props = segments[i].feature.properties;
stats.trackLength += +props['track-length'];
stats.filteredAscend += +props['filtered ascend'];
stats.plainAscend += +props['plain-ascend'];
stats.cost += +props['cost'];
}
return stats;

View file

@ -170,11 +170,12 @@
}
var track = routing.toPolyline(),
segments = routing.getSegments(),
latLngs = routing.getWaypoints(),
urls = {};
elevation.update(track);
stats.update(track);
stats.update(track, segments);
if (latLngs.length > 1) {
urls.gpx = router.getUrl(latLngs, 'gpx');

View file

@ -14,7 +14,7 @@ BR.Elevation = L.Control.Elevation.extend({
update: function(track) {
this.clear();
if (track && track.getLatLngs().length > 0) {
this.addData(track);
this.addData(track.toGeoJSON());
}
}
});

View file

@ -95,4 +95,14 @@ BR.Routing = L.Routing.extend({
cb(err, data);
}, this));
}
,getSegments: function() {
var segments = [];
this._eachSegment(function(m1, m2, line) {
segments.push(line);
});
return segments;
}
});

View file

@ -10,7 +10,7 @@ L.BRouter = L.Class.extend({
},
options: {
format: 'gpx'
format: 'geojson'
},
initialize: function (options) {
@ -83,8 +83,7 @@ L.BRouter = L.Class.extend({
getRoute: function(latLngs, cb) {
var url = this.getUrl(latLngs),
xhr = new XMLHttpRequest(),
gpx;
xhr = new XMLHttpRequest();
if (!url) {
return cb(new Error('Error getting route URL'));
@ -99,23 +98,20 @@ L.BRouter = L.Class.extend({
},
_handleRouteResponse: function(xhr, cb) {
var gpx = xhr.responseXML;
var layer,
geojson;
if (xhr.status === 200
&& xhr.responseText
// application error when not GeoJSON format (text/plain for errors)
&& xhr.getResponseHeader('Content-Type').split(';')[0] === 'application/vnd.geo+json') {
if (xhr.status === 200 && gpx) {
// L.GPX has no XHR error handling, and expects either URL or text (not document),
// so bypass by passing null and call internal _parse_gpx_data directly
var gpxLayer = new L.GPX(null, {
polyline_options: {
opacity: 0.6
},
marker_options: {
startIconUrl: null,
endIconUrl: null
}
});
var layer = gpxLayer._parse_gpx_data(gpx, gpxLayer.options);
// leaflet.spin
//gpxLayer.fire('data:loaded');
geojson = JSON.parse(xhr.responseText);
layer = L.geoJson(geojson).getLayers()[0];
return cb(null, layer);
} else {
cb(this._getError(xhr));
@ -147,8 +143,7 @@ L.BRouter = L.Class.extend({
},
_handleProfileResponse: function(xhr, cb) {
var response,
profile;
var response;
if (xhr.status === 200 && xhr.responseText && xhr.responseText.length > 0) {
response = JSON.parse(xhr.responseText);