-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy patharcs.js
149 lines (122 loc) · 5.7 KB
/
arcs.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
const utils = require('./geo-utils');
const vf = require('./vector_features');
const ol = require('openlayers');
class Arc {
/*
* Принимает координаты концов отрезка в виде долготы и широты
* */
constructor(a, b) {
this.cartesianA = vf.convertLongLatToCartesian(a.map((x) => x * Math.PI / 180));
this.cartesianB = vf.convertLongLatToCartesian(b.map((x) => x * Math.PI / 180));
[this.coordinatesPart1, this.coordinatesPart2] = this._computeCoordinates(a, b);
this.a = a;
this.b = b;
}
draw(vectorSource, lineColor="blue") {
const f1 = new ol.Feature(new ol.geom.LineString(this.coordinatesPart1));
const f2 = new ol.Feature(new ol.geom.LineString(this.coordinatesPart2));
f1.setStyle(new ol.style.Style({
stroke: new ol.style.Stroke({
color: lineColor,
width: lineColor === "blue" ? 1 : 2
})
}));
f2.setStyle(new ol.style.Style({
stroke: new ol.style.Stroke({
color: lineColor,
width: lineColor === "blue" ? 1 : 2
})
}));
vectorSource.addFeature(f1);
vectorSource.addFeature(f2);
}
findIntersection(other) {
const n1 = vf.crossProduct(this.cartesianA, this.cartesianB);
const n2 = vf.crossProduct(other.cartesianA, other.cartesianB);
const n = vf.normalizeVector(vf.crossProduct(n1, n2));
const s1 = - Math.sign(vf.scalarProduct(vf.crossProduct(this.cartesianA, n1), n));
const s2 = Math.sign(vf.scalarProduct(vf.crossProduct(this.cartesianB, n1), n));
const s3 = - Math.sign(vf.scalarProduct(vf.crossProduct(other.cartesianA, n2), n));
const s4 = Math.sign(vf.scalarProduct(vf.crossProduct(other.cartesianB, n2), n));
const sign = s1 + s2 + s3 + s4;
if (sign === 4) {
return vf.convertCartesianToLongLat(n).map((x) => x * 180 / Math.PI);
} else if (sign === -4) {
return vf.convertCartesianToLongLat(vf.reverseVector(n)).map((x) => x * 180 / Math.PI);
} else {
return null;
}
}
getMiddlePoint() {
const middlePointIndex = Math.floor((this.coordinatesPart1.length + this.coordinatesPart2.length) / 2);
if (this.coordinatesPart2.length > this.coordinatesPart1.length) {
return ol.proj.transform(this.coordinatesPart2[middlePointIndex - this.coordinatesPart1.length], 'EPSG:3857', 'EPSG:4326');
} else {
return ol.proj.transform(this.coordinatesPart1[middlePointIndex], 'EPSG:3857', 'EPSG:4326');
}
}
toString() {
return this.a + "/" + this.b;
}
/*
* Принимает координаты концов отрезка в EPSG:4326, возвращает координаты точек геодезического отрезка в EPSG:3857
* Возвращаемые координаты делятся на два списка, один из которых может быть пустым
* */
_computeCoordinates(longLatA, longLatB) {
const offset = 30;
const MaxLong = 180 - offset;
const circleDistance = this._computeCircleDistance(longLatA, longLatB);
const dotCount = 500;
const delta = 1 / (dotCount - 1);
const part1 = [];
const part2 = [];
var border = false;
var prevPoint = null;
utils.range(dotCount).forEach((i) => {
const p = this._intermediatePoint(i * delta, circleDistance, longLatA, longLatB);
// граничный случай: линия продолжается по параллели, выходящей с одной стороны карты, и продолжающейся с другой
// устранение горизонтальной линии, отрисовывающейся через всю карту одним вызовом LineString
// координаты делятся на два списка, каждый из которых отрисовывается по отдельности
if (prevPoint != null && ((p[0] > MaxLong && prevPoint[0] < -MaxLong) || (p[0] < -MaxLong && prevPoint[0] > MaxLong))) {
border = true;
const ratio = (180 - prevPoint[0]) / (p[0] - prevPoint[0]);
const y = ratio * p[1] + (1 - ratio) * prevPoint[1];
part1.push(ol.proj.transform([prevPoint[0] > -MaxLong ? 180 : -180, y], 'EPSG:4326', 'EPSG:3857'));
part2.push(ol.proj.transform([prevPoint[0] > -MaxLong ? -180 : 180, y], 'EPSG:4326', 'EPSG:3857'));
}
if (!border) {
part1.push(ol.proj.transform(p, 'EPSG:4326', 'EPSG:3857'));
} else {
part2.push(ol.proj.transform(p, 'EPSG:4326', 'EPSG:3857'));
}
prevPoint = p;
});
return [part1, part2];
}
/*
* Вычисляет долготу и широту промежуточной точки i на отрезке a, b (координаты в виде долготы и широты) на отрезке с углом дуги d
* */
_intermediatePoint(i, d, a, b) {
const A = Math.sin((1 - i) * d) / Math.sin(d);
const B = Math.sin(i * d) / Math.sin(d);
const [x1, y1, z1] = this.cartesianA;
const [x2, y2, z2] = this.cartesianB;
const x = A * x1 + B * x2;
const y = A * y1 + B * y2;
const z = A * z1 + B * z2;
const longLat = vf.convertCartesianToLongLat([x, y, z]).map((x) => x * 180 / Math.PI);
return longLat;
}
/*
* Принимает координаты концов отрезка в виде долготы и широты, возвращает угол дуги на большом круге
* */
_computeCircleDistance(a, b) {
const [long1, lat1] = a;
const [long2, lat2] = b;
return 2 * Math.asin(Math.sqrt(
Math.pow(Math.sin((lat1 - lat2) / 2), 2) +
Math.cos(lat1) * Math.cos(lat2) * Math.pow(Math.sin((long1 - long2) / 2), 2)
));
}
}
module.exports = Arc;