UNPKG

4.23 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3const tools_1 = require("@toba/tools");
4const piDeg = Math.PI / 180.0;
5const radiusMiles = 3958.756;
6const earthRadius = radiusMiles;
7var Unit;
8(function (Unit) {
9 Unit[Unit["English"] = 0] = "English";
10 Unit[Unit["Metric"] = 1] = "Metric";
11})(Unit = exports.Unit || (exports.Unit = {}));
12const toRadians = (deg) => deg * piDeg;
13const toDegrees = (rad) => (rad * 180) / Math.PI;
14const sameLocation = (p1, p2) => tools_1.is.array(p1) &&
15 tools_1.is.array(p2) &&
16 p1[1] == p2[1] &&
17 p1[0] == p2[0];
18function pointDistance(p1, p2) {
19 if (sameLocation(p1, p2) || !tools_1.is.array(p1) || !tools_1.is.array(p2)) {
20 return 0;
21 }
22 const radLat1 = toRadians(p1[1]);
23 const radLat2 = toRadians(p2[1]);
24 const latDistance = toRadians(p2[1] - p1[1]);
25 const lonDistance = toRadians(p2[0] - p1[0]);
26 const a = Math.sin(latDistance / 2) ** 2 +
27 Math.cos(radLat1) * Math.cos(radLat2) * Math.sin(lonDistance / 2) ** 2;
28 const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
29 return earthRadius * c;
30}
31const length = (points) => points.reduce((total, p, i) => total + (i > 0 ? pointDistance(points[i - 1], p) : 0), 0);
32function speed(p1, p2) {
33 const t = Math.abs(p1[3] - p2[3]);
34 const d = pointDistance(p1, p2);
35 return t > 0 && d > 0 ? d / (t / 3600000) : 0;
36}
37function duration(line) {
38 const firstPoint = line[0];
39 const lastPoint = line[line.length - 1];
40 return (lastPoint[3] - firstPoint[3]) / 3600000;
41}
42function pointLineDistance(p, p1, p2) {
43 let x = p1[0];
44 let y = p1[1];
45 let Δx = p2[0] - x;
46 let Δy = p2[1] - y;
47 if (Δx !== 0 || Δy !== 0) {
48 const t = ((p[0] - x) * Δx + (p[1] - y) * Δy) /
49 (Δx * Δx + Δy * Δy);
50 if (t > 1) {
51 x = p2[0];
52 y = p2[1];
53 }
54 else if (t > 0) {
55 x += Δx * t;
56 y += Δy * t;
57 }
58 }
59 Δx = p[0] - x;
60 Δy = p[1] - y;
61 return Δx * Δx + Δy * Δy;
62}
63function centroid(points) {
64 const count = points.length;
65 if (count == 0) {
66 return null;
67 }
68 if (count == 1) {
69 return {
70 lon: points[0][0],
71 lat: points[0][1]
72 };
73 }
74 const location = { lon: 0, lat: 0 };
75 let x = 0;
76 let y = 0;
77 let z = 0;
78 points.forEach(p => {
79 const radLat = toRadians(p[1]);
80 const radLon = toRadians(p[0]);
81 x += Math.cos(radLat) * Math.cos(radLon);
82 y += Math.cos(radLat) * Math.sin(radLon);
83 z += Math.sin(radLat);
84 });
85 x /= count;
86 y /= count;
87 z /= count;
88 const lon = Math.atan2(y, x);
89 const hyp = Math.sqrt(x * x + y * y);
90 const lat = Math.atan2(z, hyp);
91 location.lat = toDegrees(lat);
92 location.lon = toDegrees(lon);
93 return location;
94}
95function simplify(points, maxPointDeviationFeet = 0) {
96 if (maxPointDeviationFeet <= 0) {
97 return points;
98 }
99 const yard = 3;
100 const mile = yard * 1760;
101 const equatorFeet = mile * radiusMiles;
102 const len = points.length;
103 const keep = new Uint8Array(len);
104 const tolerance = maxPointDeviationFeet / equatorFeet;
105 let first = 0;
106 let last = len - 1;
107 const stack = [];
108 let maxDistance = 0;
109 let distance = 0;
110 let index = 0;
111 keep[first] = 1;
112 keep[last] = 1;
113 while (last > 0) {
114 maxDistance = 0;
115 for (let i = first + 1; i < last; i++) {
116 distance = pointLineDistance(points[i], points[first], points[last]);
117 if (distance > maxDistance) {
118 index = i;
119 maxDistance = distance;
120 }
121 }
122 if (maxDistance > tolerance) {
123 keep[index] = 1;
124 stack.push(first, index, index, last);
125 }
126 let i = stack.pop();
127 if (i !== undefined) {
128 last = i;
129 i = stack.pop();
130 if (i !== undefined) {
131 first = i;
132 }
133 }
134 }
135 return points.filter((_p, i) => keep[i] == 1);
136}
137exports.measure = {
138 speed,
139 length,
140 centroid,
141 duration,
142 toRadians,
143 toDegrees,
144 sameLocation,
145 pointDistance,
146 simplify
147};