commit 1a82ed9b9ce060392c16da41f683ae77c4490bd3
parent 6e04000168a0b2fc3937afb2f50b1bac9bfc7703
Author: Sergej Orlov <wladimirych@gmail.com>
Date: Wed, 17 Oct 2018 23:03:44 +0200
[track list] split track by 180 meridian when exporting to kml and gpx #125
Diffstat:
1 file changed, 83 insertions(+), 47 deletions(-)
diff --git a/src/lib/leaflet.control.track-list/lib/geo_file_exporters.js b/src/lib/leaflet.control.track-list/lib/geo_file_exporters.js
@@ -1,11 +1,55 @@
+import L from 'leaflet';
import utf8 from 'utf8';
import escapeHtml from 'escape-html';
import {saveNktk} from './nktk';
+function getSegmentLatForLng(latLng1, latLng2, lng) {
+ const deltaLat = latLng2.lat - latLng1.lat;
+ const deltaLng = latLng2.lng - latLng1.lng;
+ return latLng1.lat + deltaLat / deltaLng * (lng - latLng1.lng);
+}
+
+function splitLineAt180Meridian(latLngs) {
+ const wrappedLatLngs = latLngs.map(ll => ll.wrap());
+ const newLines = [];
+ if (latLngs.length < 2) {
+ return newLines;
+ }
+
+ let newLine = [wrappedLatLngs[0]];
+ newLines.push(newLine);
+ for (let i = 1; i < wrappedLatLngs.length; i++) {
+ let latLng = wrappedLatLngs[i];
+ let prevLatLng = wrappedLatLngs[i - 1];
+ if (Math.abs(latLng.lng - prevLatLng.lng) <= 180) {
+ newLine.push(latLng);
+ } else {
+ let positiveLng = L.Util.wrapNum(latLng.lng, [0, 360]);
+ let positivePrevLng = L.Util.wrapNum(prevLatLng.lng, [0, 360]);
+ let splitLng = 180 - 0.000001 * Math.sign(latLng.lng);
+ let splitPrevLng = 180 - 0.000001 * Math.sign(prevLatLng.lng);
+ let splitLat = getSegmentLatForLng(L.latLng(prevLatLng.lat, positivePrevLng), L.latLng(latLng.lat, positiveLng), splitLng);
+ let splitPrevLat = getSegmentLatForLng(L.latLng(prevLatLng.lat, positivePrevLng), L.latLng(latLng.lat, positiveLng), splitPrevLng);
+ newLine.push(L.latLng(splitPrevLat, splitPrevLng).wrap());
+ newLine = [L.latLng(splitLat, splitLng).wrap(), latLng];
+ newLines.push(newLine);
+ }
+ }
+ return newLines;
+}
+
+function normalizeLines(lines) {
+ return (lines || [])
+ .map((segment) => splitLineAt180Meridian(segment))
+ .reduce((acc, cur) => {
+ acc.push(...cur);
+ return acc;
+ }, []);
+}
+
function saveGpx(segments, name, points) {
- var gpx = [],
- x, y,
- fakeTime = '1970-01-01T00:00:01.000Z';
+ const gpx = [];
+ const fakeTime = '1970-01-01T00:00:01.000Z';
gpx.push('<?xml version="1.0" encoding="UTF-8" standalone="no" ?>');
gpx.push(
@@ -15,43 +59,38 @@ function saveGpx(segments, name, points) {
var label = marker.label;
label = escapeHtml(label);
label = utf8.encode(label);
- gpx.push('\t<wpt lat="' + marker.latlng.lat.toFixed(6) + '" lon="' + marker.latlng.lng.toFixed(6) + '">');
- gpx.push('\t\t<name>' + label + '</name>');
+ gpx.push(`\t<wpt lat="${marker.latlng.lat.toFixed(6)}" lon="${marker.latlng.lng.toFixed(6)}">`);
+ gpx.push(`\t\t<name>${label}</name>`);
gpx.push('\t</wpt>');
}
);
- if (segments.length) {
+ const normalizedSegments = normalizeLines(segments);
+ if (normalizedSegments.length > 0) {
name = name || 'Track';
name = escapeHtml(name);
name = utf8.encode(name);
gpx.push('\t<trk>');
gpx.push('\t\t<name>' + name + '</name>');
- segments.forEach(function(points) {
- if (points.length > 1) {
- gpx.push('\t\t<trkseg>');
- points.forEach(function(p) {
- x = p.lng.toFixed(6);
- y = p.lat.toFixed(6);
- //time element is not necessary, added for compatibility to Garmin Connect only
- gpx.push('\t\t\t<trkpt lat="' + y + '" lon="' + x + '"><time>' + fakeTime + '</time></trkpt>');
- }
- );
- gpx.push('\t\t</trkseg>');
- }
+ for (let segment of normalizedSegments) {
+ gpx.push('\t\t<trkseg>');
+ for (let point of segment) {
+ let x = point.lng.toFixed(6);
+ let y = point.lat.toFixed(6);
+ //time element is not necessary, added for compatibility to Garmin Connect only
+ gpx.push(`\t\t\t<trkpt lat="${y}" lon="${x}"><time>${fakeTime}</time></trkpt>`);
}
- );
+ gpx.push('\t\t</trkseg>');
+ }
gpx.push('\t</trk>');
}
gpx.push('</gpx>');
- gpx = gpx.join('\n');
- return gpx;
+ return gpx.join('\n');
}
function saveKml(segments, name, points) {
- var kml = [],
- x, y;
+ const kml = [];
name = name || 'Track';
name = escapeHtml(name);
@@ -60,29 +99,27 @@ function saveKml(segments, name, points) {
kml.push('<?xml version="1.0" encoding="UTF-8"?>');
kml.push('<kml xmlns="http://www.opengis.net/kml/2.2">');
kml.push('\t<Document>');
- kml.push('\t\t<name>' + name + '</name>');
-
- segments.forEach(function(points, i) {
- if (points.length > 1) {
- kml.push('\t\t<Placemark>',
- '\t\t\t<name>Line ' + (i + 1) + '</name>',
- '\t\t\t<LineString>',
- '\t\t\t\t<tessellate>1</tessellate>',
- '\t\t\t\t<coordinates>'
- );
- points.forEach(function(p) {
- x = p.lng.toFixed(6);
- y = p.lat.toFixed(6);
- kml.push('\t\t\t\t\t' + x + ',' + y);
- }
- );
- kml.push('\t\t\t\t</coordinates>',
- '\t\t\t</LineString>',
- '\t\t</Placemark>'
- );
- }
+ kml.push(`\t\t<name>${name}</name>`);
+
+ const normalizedSegments = normalizeLines(segments);
+ for (let [i, segment] of normalizedSegments.entries()) {
+ kml.push('\t\t<Placemark>');
+ kml.push(`\t\t\t<name>Line ${(i + 1)}</name>`);
+ kml.push('\t\t\t<LineString>');
+ kml.push('\t\t\t\t<tessellate>1</tessellate>');
+ kml.push('\t\t\t\t<coordinates>');
+
+ for (let point of segment) {
+ let x = point.lng.toFixed(6);
+ let y = point.lat.toFixed(6);
+ kml.push(`\t\t\t\t\t${x},${y}`);
}
- );
+
+ kml.push('\t\t\t\t</coordinates>');
+ kml.push('\t\t\t</LineString>');
+ kml.push('\t\t</Placemark>');
+ }
+
points.forEach(function(marker) {
var label = marker.label;
label = escapeHtml(label);
@@ -101,8 +138,7 @@ function saveKml(segments, name, points) {
kml.push('\t</Document>');
kml.push('\t</kml>');
- kml = kml.join('\n');
- return kml;
+ return kml.join('\n');
}
export default {saveGpx, saveKml, saveToString: saveNktk};