BR.Export = L.Class.extend({ latLngs: [], options: { shortcut: { export: 88, // char code for 'x' }, }, initialize: function (router, pois, profile) { this.router = router; this.pois = pois; this.profile = profile; this.exportButton = $('#exportButton'); var trackname = (this.trackname = document.getElementById('trackname')); this.tracknameAllowedChars = BR.conf.tracknameAllowedChars; if (this.tracknameAllowedChars) { this.tracknameMessage = document.getElementById('trackname-message'); var patternRegex = new RegExp('[' + this.tracknameAllowedChars + ']+'); // warn about special characters getting removed by server quick fix (#194) trackname.pattern = patternRegex.toString().slice(1, -1); trackname.addEventListener('input', L.bind(this._validationMessage, this)); } this.exportButton.on('click', L.bind(this._generateTrackname, this)); L.DomUtil.get('submitExport').onclick = L.bind(this._export, this); L.DomEvent.addListener(document, 'keydown', this._keydownListener, this); this.update([]); }, update: function (latLngs, segments) { this.latLngs = latLngs; this.segments = segments; if (latLngs.length < 2) { this.exportButton.addClass('disabled'); } else { this.exportButton.removeClass('disabled'); } }, _export: function (e) { var exportForm = document.forms['export']; var format = exportForm['format'].value || $('#export-format input:radio:checked').val(); var name = exportForm['trackname'].value; var nameUri = encodeURIComponent(name); var includeWaypoints = exportForm['include-waypoints'].checked; e.preventDefault(); if (true) { var uri = this.router.getUrl(this.latLngs, this.pois.getMarkers(), null, format, nameUri, includeWaypoints); // var evt = document.createEvent('MouseEvents'); // evt.initMouseEvent('click', true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null); // var link = document.createElement('a'); // link.href = uri; // link.dispatchEvent(evt); //} else { const track = this._formatTrack(format, name, includeWaypoints); console.log('track: ', track); BR.Diff.diff(uri, track, format); } }, _formatTrack: function (format, name, includeWaypoints) { const track = BR.Export._concatTotalTrack(this.segments); if (name) { track.features[0].properties.name = name; } this._addPois(track); if (includeWaypoints) { this._addRouteWaypoints(track); } switch (format) { case 'gpx': const turnInstructionMode = +this.profile.getProfileVar('turnInstructionMode'); const transportMode = this.profile.getTransportMode(); return BR.Gpx.format(track, turnInstructionMode, transportMode); case 'kml': return BR.Kml.format(track); case 'geojson': return JSON.stringify(track, null, 2); case 'csv': return BR.Csv.format(track); default: break; } console.error('Export format not implemented: ' + format); }, _addPois: function (track) { const markers = this.pois.getMarkers(); for (const poi of markers) { const properties = { name: poi.name, type: 'poi' }; const point = turf.point([poi.latlng.lng, poi.latlng.lat], properties); track.features.push(point); } }, _addRouteWaypoints: function (track) { for (const [i, latLng] of this.latLngs.entries()) { let name = 'via' + i; let type = 'via'; if (i === 0) { name = 'from'; type = 'from'; } else if (i === this.latLngs.length - 1) { name = 'to'; type = 'to'; } const properties = { name, type }; const point = turf.point([latLng.lng, latLng.lat], properties); track.features.push(point); } }, _validationMessage: function () { var trackname = this.trackname; var replaceRegex = new RegExp('[^' + this.tracknameAllowedChars + ']', 'g'); if (trackname.validity.patternMismatch) { var replaced = trackname.value.replace(replaceRegex, ''); var patternStr = this.tracknameAllowedChars.replace(/\\/g, ''); this.tracknameMessage.textContent = '[' + patternStr + '] --> ' + replaced; } else { this.tracknameMessage.textContent = ''; } }, _generateTrackname: function () { return; // TODO remove var trackname = this.trackname; this._getCityAtPosition( this.latLngs[0], L.bind(function (from) { this._getCityAtPosition( this.latLngs[this.latLngs.length - 1], L.bind(function (to) { var distance = document.getElementById('distance').innerHTML; if (this.tracknameAllowedChars) { distance = distance.replace(',', '.'); // temp. fix (#202) } if (!from || !to) { trackname.value = null; } else if (from === to) { trackname.value = i18next.t('export.route-loop', { from: from, distance: distance, }); } else { trackname.value = i18next.t('export.route-from-to', { from: from, to: to, distance: distance, }); } if (this.tracknameAllowedChars) { // temp. fix: replace and remove characters that will get removed by server quick fix (#194) trackname.value = trackname.value.replace(/[>)]/g, '').replace(/ \(/g, ' - '); this._validationMessage(); } }, this) ); }, this) ); }, _getCityAtPosition: function (lonlat, cb) { var url = L.Util.template( 'https://nominatim.openstreetmap.org/reverse?lon={lng}&lat={lat}&format=json', lonlat ); BR.Util.get( url, L.bind(function (err, response) { try { var addr = JSON.parse(response).address; cb(addr.village || addr.town || addr.hamlet || addr.city_district || addr.city); } catch (err) { BR.message.showError('Error getting position city "' + lonlat + '": ' + err); return cb(null); } }) ); }, _keydownListener: function (e) { if ( BR.Util.keyboardShortcutsAllowed(e) && e.keyCode === this.options.shortcut.export && !this.exportButton.hasClass('disabled') ) { this._generateTrackname(); $('#export').modal('show'); } }, }); BR.export = function () { return new BR.Export(); }; BR.Export._concatTotalTrack = function (segments) { const sumProperties = (p, fp, keys) => { for (const key of keys) { p[key] = (+p[key] + +fp[key]).toString(); } }; let coordinates = []; let properties; //console.time('_concatTotalTrack'); for (const [segmentIndex, segment] of segments.entries()) { const feature = segment.feature; if (!feature) continue; const coordOffset = coordinates.length > 0 ? coordinates.length - 1 : 0; if (properties) { const p = properties; const fp = feature.properties; sumProperties(p, fp, [ 'cost', 'filtered ascend', 'plain-ascend', 'total-energy', 'total-time', 'track-length', ]); p.messages = p.messages.concat(fp.messages.slice(1)); if (p.times && fp.times) { const lastTime = p.times[p.times.length - 1]; for (const [timeIndex, time] of fp.times.entries()) { if (timeIndex > 0) { p.times.push(+(lastTime + time).toFixed(3)); } } } if (fp.voicehints) { if (!p.voicehints) p.voicehints = []; for (const fpHint of fp.voicehints) { const hint = fpHint.slice(); hint[0] += coordOffset; p.voicehints.push(hint); } } } else { // clone properties = Object.assign({}, feature.properties); if (properties.voicehints) { properties.voicehints = properties.voicehints.slice(); } if (properties.times) { properties.times = properties.times.slice(); } } let featureCoordinates = feature.geometry.coordinates; if (segmentIndex > 0) { // remove first segment coordinate, same as previous last featureCoordinates = featureCoordinates.slice(1); } coordinates = coordinates.concat(featureCoordinates); } //console.timeEnd('_concatTotalTrack'); return turf.featureCollection([turf.lineString(coordinates, properties)]); };