1 | "use strict";
|
2 | Object.defineProperty(exports, "__esModule", { value: true });
|
3 | const tools_1 = require("@toba/tools");
|
4 | const xmldom_1 = require("xmldom");
|
5 | const index_1 = require("./index");
|
6 | var Type;
|
7 | (function (Type) {
|
8 | Type["Feature"] = "Feature";
|
9 | Type["Collection"] = "FeatureCollection";
|
10 | Type["Point"] = "Point";
|
11 | Type["Line"] = "LineString";
|
12 | Type["MultiLine"] = "MultiLineString";
|
13 | })(Type = exports.Type || (exports.Type = {}));
|
14 | const features = () => ({
|
15 | type: Type.Collection,
|
16 | features: []
|
17 | });
|
18 | const geometry = (type, coordinates) => ({
|
19 | type,
|
20 | coordinates
|
21 | });
|
22 | function trackFromGPX(node, maxPossibleSpeed = 0) {
|
23 | let count = 0;
|
24 | let topSpeed = 0;
|
25 | let totalTime = 0;
|
26 | let totalSpeed = 0;
|
27 | let totalDistance = 0;
|
28 | const points = Array.from(node.getElementsByTagName('trkseg'))
|
29 | .map(segment => index_1.gpx.line(segment, 'trkpt'))
|
30 | .filter(line => line !== null && line[0].length > 0);
|
31 | const track = points.map(line => {
|
32 | totalTime += index_1.measure.duration(line);
|
33 | totalDistance += index_1.measure.length(line);
|
34 | return index_1.measure.simplify(line.map(point => {
|
35 | const speed = point[4];
|
36 | if (maxPossibleSpeed === 0 || speed < maxPossibleSpeed) {
|
37 | count++;
|
38 | totalSpeed += speed;
|
39 | if (speed > topSpeed) {
|
40 | topSpeed = parseFloat(speed.toFixed(1));
|
41 | }
|
42 | }
|
43 | return point.slice(0, 3);
|
44 | }));
|
45 | });
|
46 | return track.length === 0 || track[0].length === 0
|
47 | ? null
|
48 | : {
|
49 | type: Type.Feature,
|
50 | properties: Object.assign(index_1.gpx.properties(node), {
|
51 | topSpeed,
|
52 | avgSpeed: parseFloat((totalSpeed / count).toFixed(1)),
|
53 | duration: totalTime,
|
54 | distance: parseFloat(totalDistance.toFixed(2))
|
55 | }),
|
56 | geometry: track.length === 1
|
57 | ? geometry(Type.Line, track[0])
|
58 | : geometry(Type.MultiLine, track)
|
59 | };
|
60 | }
|
61 | const routeFromGPX = (node) => ({
|
62 | type: Type.Feature,
|
63 | properties: index_1.gpx.properties(node),
|
64 | geometry: geometry(Type.Line, index_1.gpx.line(node, 'rtept'))
|
65 | });
|
66 | const pointFromGPX = (node) => ({
|
67 | type: Type.Feature,
|
68 | properties: index_1.gpx.properties(node, ['sym']),
|
69 | geometry: geometry(Type.Point, index_1.gpx.location(node))
|
70 | });
|
71 | function pointFromKML(node) {
|
72 | const location = index_1.kml.location(node);
|
73 | return location == null
|
74 | ? null
|
75 | : {
|
76 | type: Type.Feature,
|
77 | properties: index_1.kml.properties(node, ['sym']),
|
78 | geometry: geometry(Type.Point, location)
|
79 | };
|
80 | }
|
81 | const lineFeature = (type, node, lines) => ({
|
82 | type: Type.Feature,
|
83 | properties: index_1.kml.properties(node),
|
84 | geometry: geometry(type, lines)
|
85 | });
|
86 | function lineFromKML(node) {
|
87 | const lines = index_1.kml.line(node);
|
88 | return lines != null
|
89 | ? lines.length > 1
|
90 | ? lineFeature(Type.MultiLine, node, lines)
|
91 | : lineFeature(Type.Line, node, lines[0])
|
92 | : null;
|
93 | }
|
94 | const parseNodes = (doc, name, parser) => Array.from(doc.getElementsByTagName(name))
|
95 | .map(parser)
|
96 | .filter(f => tools_1.is.value(f));
|
97 | function featuresFromGPX(gpxString) {
|
98 | const geo = features();
|
99 | let gpx = null;
|
100 | try {
|
101 | gpx = new xmldom_1.DOMParser().parseFromString(gpxString);
|
102 | }
|
103 | catch (err) {
|
104 | console.error(err);
|
105 | return null;
|
106 | }
|
107 | const tracks = parseNodes(gpx, 'trk', trackFromGPX);
|
108 | const routes = parseNodes(gpx, 'rte', routeFromGPX);
|
109 | const points = parseNodes(gpx, 'wpt', pointFromGPX);
|
110 | geo.features = geo.features.concat(tracks, routes, points);
|
111 | return geo;
|
112 | }
|
113 | function postProcess(features, transformer = null) {
|
114 | if (transformer !== null) {
|
115 | features.forEach(f => {
|
116 | f.properties = transformer(f.properties);
|
117 | });
|
118 | }
|
119 | return features;
|
120 | }
|
121 | const featuresFromKML = (kml, transformer = null) => {
|
122 | const geo = features();
|
123 | let doc = null;
|
124 | if (tools_1.is.text(kml)) {
|
125 | kml = kml.replace(/[\r\n]/g, '').replace(/>\s+</g, '><');
|
126 | try {
|
127 | doc = new xmldom_1.DOMParser().parseFromString(kml);
|
128 | }
|
129 | catch (err) {
|
130 | console.error(err);
|
131 | return null;
|
132 | }
|
133 | }
|
134 | else {
|
135 | doc = kml;
|
136 | }
|
137 | if (doc !== null) {
|
138 | const lines = parseNodes(doc, 'Placemark', lineFromKML);
|
139 | const points = parseNodes(doc, 'Placemark', pointFromKML);
|
140 | geo.features = postProcess(geo.features.concat(lines, points), transformer);
|
141 | }
|
142 | return geo;
|
143 | };
|
144 | exports.geoJSON = {
|
145 | Type,
|
146 | features,
|
147 | geometry,
|
148 | featuresFromGPX,
|
149 | featuresFromKML
|
150 | };
|