Created
May 10, 2018 18:13
-
-
Save seyfro/9e3162ae5985d7168ffd97f5bec79f10 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* GPX plugin, Copyright (C) 2011-2012 Pavel Shramov, Copyright (C) 2013 Maxime Petazzoni <maxime.petazzoni@bulix.org> | |
* Last commit included: 4/8/2013 (Merge pull request #8 from rkusa/bugfix) - https://github.com/mpetazzoni/leaflet-gpx/commits/master | |
* added for Maps Marker Pro v3.1: 22.1.2017: replaced clickable with interactive due to leaflet 1.0.2 update - https://github.com/Leaflet/Leaflet/pull/2838 | |
* not used anymore with Maps Marker Pro 4.0! | |
*/ | |
var _SECOND_IN_MILLIS = 1000; | |
var _MINUTE_IN_MILLIS = 60 * _SECOND_IN_MILLIS; | |
var _HOUR_IN_MILLIS = 60 * _MINUTE_IN_MILLIS; | |
L.GPX = L.FeatureGroup.extend({ | |
initialize: function(gpx, options) { | |
L.Util.setOptions(this, options); | |
if (mapsmarkerjspro.gpx_icons_status == 'show') { //info: added by RH | |
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._prepare_parsing(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; | |
}, | |
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_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_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'; | |
}); | |
}); | |
}, | |
//**************************************************************************/ | |
// Private methods | |
//**************************************************************************/ | |
_htmlspecialchars_decode: function htmlspecialchars_decode (string, quote_style) { | |
// http://kevin.vanzonneveld.net | |
// * example 1: htmlspecialchars_decode("<p>this -> "</p>", 'ENT_NOQUOTES'); | |
// * returns 1: '<p>this -> "</p>' | |
// * example 2: htmlspecialchars_decode("&quot;"); | |
// * returns 2: '"' | |
var optTemp = 0, | |
i = 0, | |
noquotes = false; | |
if (typeof quote_style === 'undefined') { | |
quote_style = 2; | |
} | |
string = string.toString().replace(/</g, '<').replace(/>/g, '>'); | |
var OPTS = { | |
'ENT_NOQUOTES': 0, | |
'ENT_HTML_QUOTE_SINGLE': 1, | |
'ENT_HTML_QUOTE_DOUBLE': 2, | |
'ENT_COMPAT': 2, | |
'ENT_QUOTES': 3, | |
'ENT_IGNORE': 4 | |
}; | |
if (quote_style === 0) { | |
noquotes = true; | |
} | |
if (typeof quote_style !== 'number') { // Allow for a single string or an array of string flags | |
quote_style = [].concat(quote_style); | |
for (i = 0; i < quote_style.length; i++) { | |
// Resolve string input to bitwise e.g. 'PATHINFO_EXTENSION' becomes 4 | |
if (OPTS[quote_style[i]] === 0) { | |
noquotes = true; | |
} else if (OPTS[quote_style[i]]) { | |
optTemp = optTemp | OPTS[quote_style[i]]; | |
} | |
} | |
quote_style = optTemp; | |
} | |
if (quote_style && OPTS.ENT_HTML_QUOTE_SINGLE) { | |
string = string.replace(/�*39;/g, "'"); // PHP doesn't currently escape if more than one 0, but it should | |
// string = string.replace(/'|�*27;/g, "'"); // This would also be useful here, but not a part of PHP | |
} | |
if (!noquotes) { | |
string = string.replace(/"/g, '"'); | |
} | |
// Put this in last place to avoid escape being double-decoded | |
string = string.replace(/&/g, '&'); | |
return string; | |
}, | |
_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; | |
}, | |
_prepare_parsing: 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); | |
setTimeout(function(){ _this.fire('gpx_loaded') }, 0); | |
} | |
if (async == undefined) async = this.options.async; | |
if (options == undefined) options = this.options; | |
var gpx_content_to_parse = this._htmlspecialchars_decode(this.options.gpx_content); | |
var xmlDoc = this._parse_xml(gpx_content_to_parse); | |
if(xmlDoc){ | |
cb(this._parse_xml(gpx_content_to_parse), options); | |
} else{ | |
if (window.console) { console.log('error parsing xml'); } | |
} | |
}, | |
_parse_xml: function(xmlStr){ | |
if (typeof window.DOMParser != "undefined") { | |
return ( new window.DOMParser() ).parseFromString(xmlStr, "text/xml"); | |
} else if (typeof window.ActiveXObject != "undefined" && new window.ActiveXObject("Microsoft.XMLDOM")) { | |
var xmlDoc = new window.ActiveXObject("Microsoft.XMLDOM"); | |
xmlDoc.async = "false"; | |
xmlDoc.loadXML(xmlStr); | |
return xmlDoc; | |
} else { | |
throw new Error("No XML parser found"); | |
} | |
}, | |
_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 || name[0].text || name[0].innerText; | |
} | |
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 (mapsmarkerjspro.gpx_icons_status == 'show') { //info: added by RH | |
// add start pin | |
var p = new L.Marker(coords[0], { | |
interactive: false, | |
icon: new L.GPXTrackIcon({iconUrl: options.marker_options.startIconUrl}) | |
}); | |
this.fire('addpoint', { point: p }); | |
layers.push(p); | |
// add end pin | |
p = new L.Marker(coords[coords.length-1], { | |
interactive: 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; | |
}, | |
_parseDate_for_IE8: function(input) { | |
var iso = /^(\d{4})(?:-?W(\d+)(?:-?(\d+)D?)?|(?:-(\d+))?-(\d+))(?:[T ](\d+):(\d+)(?::(\d+)(?:\.(\d+))?)?)?(?:Z(-?\d*))?$/; | |
var parts = input.match(iso); | |
if (parts == null) { | |
throw new Error("Invalid Date"); | |
} | |
var year = Number(parts[1]); | |
if (typeof parts[2] != "undefined") { | |
/* Convert weeks to days, months 0 */ | |
var weeks = Number(parts[2]) - 1; | |
var days = Number(parts[3]); | |
if (typeof days == "undefined") { | |
days = 0; | |
} | |
days += weeks * 7; | |
var months = 0; | |
} else { | |
if (typeof parts[4] != "undefined") { | |
var months = Number(parts[4]) - 1; | |
} else { | |
/* it's an ordinal date... */ | |
var months = 0; | |
} | |
var days = Number(parts[5]); | |
} | |
if (typeof parts[6] != "undefined" && | |
typeof parts[7] != "undefined") | |
{ | |
var hours = Number(parts[6]); | |
var minutes = Number(parts[7]); | |
if (typeof parts[8] != "undefined") { | |
var seconds = Number(parts[8]); | |
if (typeof parts[9] != "undefined") { | |
var fractional = Number(parts[9]); | |
var milliseconds = fractional / 100; | |
} else { | |
var milliseconds = 0 | |
} | |
} else { | |
var seconds = 0; | |
var milliseconds = 0; | |
} | |
} | |
else { | |
var hours = 0; | |
var minutes = 0; | |
var seconds = 0; | |
var fractional = 0; | |
var milliseconds = 0; | |
} | |
if (typeof parts[10] != "undefined") { | |
/* Timezone adjustment, offset the minutes appropriately */ | |
var localzone = -(new Date().getTimezoneOffset()); | |
var timezone = parts[10] * 60; | |
minutes = Number(minutes) + (timezone - localzone); | |
} | |
return new Date(year, months, days, hours, minutes, seconds, milliseconds); | |
}, | |
_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) { | |
if (window.addEventListener) { | |
var time_temp = Date.parse(_[0].textContent); | |
ll.meta.time = new Date(time_temp); | |
} else { //IE8 | |
ll.meta.time = this._parseDate_for_IE8(_[0].text); | |
} | |
} | |
_ = el[i].getElementsByTagName('ele'); | |
if (_.length > 0) { | |
ll.meta.ele = parseFloat(_[0].textContent || _[0].text || _[0].innerText); //IE8 | |
} | |
/*IE9+only _ = el[i].getElementsByTagNameNS('*', 'hr');*/ | |
_ = el[i].getElementsByTagName('hr'); | |
if (_.length > 0) { | |
ll.meta.hr = parseInt(_[0].textContent || _[0].text || _[0].innerText); //IE8 | |
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; | |
}, | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment