dgeoutils 2.4.41 → 2.4.44
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -4
- package/dist/cjs/DCircle.d.ts +0 -18
- package/dist/cjs/DCircle.js +0 -102
- package/dist/cjs/DLine.d.ts +0 -39
- package/dist/cjs/DLine.js +0 -343
- package/dist/cjs/DNumbers.d.ts +0 -8
- package/dist/cjs/DNumbers.js +0 -30
- package/dist/cjs/DPlane.d.ts +0 -25
- package/dist/cjs/DPlane.js +0 -132
- package/dist/cjs/DPoint.d.ts +0 -138
- package/dist/cjs/DPoint.js +0 -803
- package/dist/cjs/DPolygon.d.ts +0 -137
- package/dist/cjs/DPolygon.js +0 -1745
- package/dist/cjs/DPolygonLoop.d.ts +0 -60
- package/dist/cjs/DPolygonLoop.js +0 -439
- package/dist/cjs/FastSearch.d.ts +0 -6
- package/dist/cjs/FastSearch.js +0 -53
- package/dist/cjs/InterpolationMatrix.d.ts +0 -24
- package/dist/cjs/InterpolationMatrix.js +0 -173
- package/dist/cjs/TraceMatrix.d.ts +0 -22
- package/dist/cjs/TraceMatrix.js +0 -285
- package/dist/cjs/index.d.ts +0 -11
- package/dist/cjs/index.js +0 -37
- package/dist/cjs/utils.d.ts +0 -49
- package/dist/cjs/utils.js +0 -280
- package/dist/es2015/DCircle.js +0 -87
- package/dist/es2015/DLine.js +0 -290
- package/dist/es2015/DNumbers.js +0 -22
- package/dist/es2015/DPlane.js +0 -105
- package/dist/es2015/DPoint.js +0 -676
- package/dist/es2015/DPolygon.js +0 -1193
- package/dist/es2015/DPolygonLoop.js +0 -430
- package/dist/es2015/FastSearch.js +0 -25
- package/dist/es2015/InterpolationMatrix.js +0 -128
- package/dist/es2015/TraceMatrix.js +0 -229
- package/dist/es2015/index.js +0 -11
- package/dist/es2015/utils.js +0 -207
- package/dist/esm/DCircle.js +0 -99
- package/dist/esm/DLine.js +0 -340
- package/dist/esm/DNumbers.js +0 -27
- package/dist/esm/DPlane.js +0 -129
- package/dist/esm/DPoint.js +0 -800
- package/dist/esm/DPolygon.js +0 -1742
- package/dist/esm/DPolygonLoop.js +0 -436
- package/dist/esm/FastSearch.js +0 -50
- package/dist/esm/InterpolationMatrix.js +0 -170
- package/dist/esm/TraceMatrix.js +0 -282
- package/dist/esm/index.js +0 -11
- package/dist/esm/utils.js +0 -265
- package/dist/umd/dgeoutils.js +0 -4347
- package/dist/umd/dgeoutils.min.js +0 -1
- package/dist/umd/dgeoutils.min.js.map +0 -1
package/dist/es2015/DPolygon.js
DELETED
|
@@ -1,1193 +0,0 @@
|
|
|
1
|
-
import { DPoint } from './DPoint';
|
|
2
|
-
import { DLine } from './DLine';
|
|
3
|
-
import { DCircle } from './DCircle';
|
|
4
|
-
import { DNumbers } from './DNumbers';
|
|
5
|
-
import { io as jstsIo, operation } from 'jsts';
|
|
6
|
-
import { DPolygonLoop } from './DPolygonLoop';
|
|
7
|
-
import { DGeo, isDefAndNotNull } from './utils';
|
|
8
|
-
const { buffer: { BufferParameters: { CAP_ROUND, CAP_FLAT, CAP_SQUARE } } } = operation;
|
|
9
|
-
export const MIN_POINTS_IN_VALID_POLYGON = 3;
|
|
10
|
-
const APPROXIMATION_VALUE = 0.1;
|
|
11
|
-
const MAX_CONVEX_ITERATIONS = 100;
|
|
12
|
-
const CLOSE_TO_INTERSECTION_DISTANCE = 0.001;
|
|
13
|
-
const triangleCenter = (triangle) => {
|
|
14
|
-
const p0 = triangle.at(0);
|
|
15
|
-
const p1 = triangle.at(1);
|
|
16
|
-
const p2 = triangle.at(2);
|
|
17
|
-
return new DPoint((p0.x + p1.x + p2.x) / 3, (p0.y + p1.y + p2.y) / 3);
|
|
18
|
-
};
|
|
19
|
-
const containCalculator = (poly, p) => {
|
|
20
|
-
const hasSamePoint = poly.points.some((point) => point.equal(p));
|
|
21
|
-
if (hasSamePoint) {
|
|
22
|
-
return true;
|
|
23
|
-
}
|
|
24
|
-
for (const [, , polygonLine] of poly.loopPointsGenerator(true)()) {
|
|
25
|
-
const onBorder = polygonLine.x(p).equal(p) && polygonLine.inRange(p);
|
|
26
|
-
if (onBorder) {
|
|
27
|
-
return true;
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
let totalFi = 0;
|
|
31
|
-
for (const [{ x, y }, { x: a, y: b }] of poly.loopPointsGenerator()()) {
|
|
32
|
-
const line1 = new DLine(x - p.x, y - p.y, 0);
|
|
33
|
-
const line2 = new DLine(a - p.x, b - p.y, 0);
|
|
34
|
-
const fiDif = line1.findFi(line2);
|
|
35
|
-
if (line1.vectorProduct(line2).c > 0) {
|
|
36
|
-
totalFi += fiDif;
|
|
37
|
-
}
|
|
38
|
-
else {
|
|
39
|
-
totalFi -= fiDif;
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
const eps = Math.PI / 10000;
|
|
43
|
-
let result = false;
|
|
44
|
-
const absTotalFi = Math.abs(totalFi);
|
|
45
|
-
if (absTotalFi < eps) {
|
|
46
|
-
result = false;
|
|
47
|
-
}
|
|
48
|
-
else if (Math.abs(2 * Math.PI - absTotalFi) < eps) {
|
|
49
|
-
result = true;
|
|
50
|
-
}
|
|
51
|
-
else {
|
|
52
|
-
throw new Error('contains2 faild');
|
|
53
|
-
}
|
|
54
|
-
return result;
|
|
55
|
-
};
|
|
56
|
-
export class DPolygon {
|
|
57
|
-
constructor(pPoints = []) {
|
|
58
|
-
this.pPoints = pPoints;
|
|
59
|
-
this.properties = {};
|
|
60
|
-
this.holes = [];
|
|
61
|
-
this.searchStore = {};
|
|
62
|
-
}
|
|
63
|
-
static arrayOfTrianglesToVertices(triangles, height) {
|
|
64
|
-
return triangles.map((v) => (isDefAndNotNull(height) ? v
|
|
65
|
-
.loop()
|
|
66
|
-
.height(height)
|
|
67
|
-
.run() : v)
|
|
68
|
-
.toArrayOfCoords())
|
|
69
|
-
.flat(2);
|
|
70
|
-
}
|
|
71
|
-
static minAreaRectangleSize(poly) {
|
|
72
|
-
const { first, second, last } = poly.clone().open();
|
|
73
|
-
return new DPoint(first.distance(second), first.distance(last));
|
|
74
|
-
}
|
|
75
|
-
static toDash(poly) {
|
|
76
|
-
let p = new DPolygon();
|
|
77
|
-
const result = [p];
|
|
78
|
-
let trigger = true;
|
|
79
|
-
for (const point of poly.points) {
|
|
80
|
-
if (trigger) {
|
|
81
|
-
p.push(point.clone());
|
|
82
|
-
}
|
|
83
|
-
if (point.properties.pieceBorder) {
|
|
84
|
-
trigger = !trigger;
|
|
85
|
-
if (trigger) {
|
|
86
|
-
p = new DPolygon();
|
|
87
|
-
result.push(p);
|
|
88
|
-
p.push(point.clone());
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
return result;
|
|
93
|
-
}
|
|
94
|
-
static minAreaRectangleDirection(poly) {
|
|
95
|
-
const { first, second, last } = poly.clone().open();
|
|
96
|
-
if (!first || !second || !last) {
|
|
97
|
-
return 0;
|
|
98
|
-
}
|
|
99
|
-
if (first.distance(second) > first.distance(last)) {
|
|
100
|
-
return first.findLine(second).getFi();
|
|
101
|
-
}
|
|
102
|
-
return first.findLine(last).getFi();
|
|
103
|
-
}
|
|
104
|
-
static parseFromWKT(wkt) {
|
|
105
|
-
const data = wkt.trim().toUpperCase();
|
|
106
|
-
let res = new DPolygon();
|
|
107
|
-
if (data.indexOf('POLYGON') === 0) {
|
|
108
|
-
const regexp = /POLYGON \(\((?<data>(?:(?!\)\)$).)*?)\)\)$/miu;
|
|
109
|
-
const reg = regexp.exec(data);
|
|
110
|
-
const [path, ...holes] = reg.groups.data
|
|
111
|
-
.split('), (')
|
|
112
|
-
.map((p) => new DPolygon(p.split(', ')
|
|
113
|
-
.map((pares) => DPoint.parse(pares.split(' ').map(Number)))));
|
|
114
|
-
if (holes && holes.length) {
|
|
115
|
-
path.holes = holes;
|
|
116
|
-
}
|
|
117
|
-
res = path;
|
|
118
|
-
}
|
|
119
|
-
if (data.indexOf('LINESTRING') === 0) {
|
|
120
|
-
const regexp = /LINESTRING \((?<data>(?:(?!\)$).)*?)\)$/miu;
|
|
121
|
-
const reg = regexp.exec(data);
|
|
122
|
-
res = new DPolygon(reg.groups.data
|
|
123
|
-
.split(', ').map((t) => DPoint.parse(t.split(' ').map(Number))));
|
|
124
|
-
}
|
|
125
|
-
if (data.indexOf('POINT') === 0) {
|
|
126
|
-
res = new DPolygon([DPoint.parseFromWKT(data)]);
|
|
127
|
-
}
|
|
128
|
-
return res;
|
|
129
|
-
}
|
|
130
|
-
static createSquareBySize(size) {
|
|
131
|
-
return new DPolygon([DPoint.zero(), size.clone().setX(0), size.clone(), size.clone().setY(0)]).close();
|
|
132
|
-
}
|
|
133
|
-
loop() {
|
|
134
|
-
return new DPolygonLoop(this);
|
|
135
|
-
}
|
|
136
|
-
set points(p) {
|
|
137
|
-
this.pPoints = p;
|
|
138
|
-
}
|
|
139
|
-
get points() {
|
|
140
|
-
return this.pPoints;
|
|
141
|
-
}
|
|
142
|
-
get maxX() {
|
|
143
|
-
return this.reduce((a, r) => Math.max(a, r.x), -Infinity);
|
|
144
|
-
}
|
|
145
|
-
get minX() {
|
|
146
|
-
return this.reduce((a, r) => Math.min(a, r.x), Infinity);
|
|
147
|
-
}
|
|
148
|
-
get maxY() {
|
|
149
|
-
return this.reduce((a, r) => Math.max(a, r.y), -Infinity);
|
|
150
|
-
}
|
|
151
|
-
get minY() {
|
|
152
|
-
return this.reduce((a, r) => Math.min(a, r.y), Infinity);
|
|
153
|
-
}
|
|
154
|
-
get innerCenter() {
|
|
155
|
-
const { center } = this;
|
|
156
|
-
return this.toTriangles().map((t) => {
|
|
157
|
-
const c = triangleCenter(t);
|
|
158
|
-
c.properties.score = Math.min(c.distance(t.at(0)), c.distance(t.at(1)), c.distance(t.at(2))) + c.distance(center);
|
|
159
|
-
return c;
|
|
160
|
-
})
|
|
161
|
-
.sort((a, b) => a.properties.score - b.properties.score)[0];
|
|
162
|
-
}
|
|
163
|
-
get center() {
|
|
164
|
-
return this.leftTop.move(this.size.divide(2));
|
|
165
|
-
}
|
|
166
|
-
get h() {
|
|
167
|
-
return this.maxY - this.minY;
|
|
168
|
-
}
|
|
169
|
-
get w() {
|
|
170
|
-
return this.maxX - this.minX;
|
|
171
|
-
}
|
|
172
|
-
get dY() {
|
|
173
|
-
return this.h;
|
|
174
|
-
}
|
|
175
|
-
get dX() {
|
|
176
|
-
return this.w;
|
|
177
|
-
}
|
|
178
|
-
get extend() {
|
|
179
|
-
const { minX, minY, maxX, maxY } = this;
|
|
180
|
-
return new DPolygon([
|
|
181
|
-
new DPoint(minX, minY),
|
|
182
|
-
new DPoint(maxX, minY),
|
|
183
|
-
new DPoint(maxX, maxY),
|
|
184
|
-
new DPoint(minX, maxY),
|
|
185
|
-
new DPoint(minX, minY)
|
|
186
|
-
]);
|
|
187
|
-
}
|
|
188
|
-
get size() {
|
|
189
|
-
const { w, h } = this;
|
|
190
|
-
return new DPoint(w, h);
|
|
191
|
-
}
|
|
192
|
-
get leftTop() {
|
|
193
|
-
const { minX, minY } = this;
|
|
194
|
-
return new DPoint(minX, minY);
|
|
195
|
-
}
|
|
196
|
-
get minPoint() {
|
|
197
|
-
return this.leftTop;
|
|
198
|
-
}
|
|
199
|
-
get rightBottom() {
|
|
200
|
-
const { maxX, maxY } = this;
|
|
201
|
-
return new DPoint(maxX, maxY);
|
|
202
|
-
}
|
|
203
|
-
get maxPoint() {
|
|
204
|
-
return this.rightBottom;
|
|
205
|
-
}
|
|
206
|
-
get length() {
|
|
207
|
-
return this.pPoints.length;
|
|
208
|
-
}
|
|
209
|
-
get fullLength() {
|
|
210
|
-
return this.clone().open().perimeter;
|
|
211
|
-
}
|
|
212
|
-
get perimeter() {
|
|
213
|
-
let p = 0;
|
|
214
|
-
for (const [p1, p2] of this.loopPointsGenerator()()) {
|
|
215
|
-
p += p1.distance(p2);
|
|
216
|
-
}
|
|
217
|
-
return p;
|
|
218
|
-
}
|
|
219
|
-
get area() {
|
|
220
|
-
let sum = 0;
|
|
221
|
-
for (const [{ x, y }, { x: a, y: b }] of this.deintersection.loopPointsGenerator()()) {
|
|
222
|
-
sum += x * b - y * a;
|
|
223
|
-
}
|
|
224
|
-
return Math.abs(sum / 2) - this.holes.reduce((a, hole) => a + hole.area, 0);
|
|
225
|
-
}
|
|
226
|
-
get filledDeintersection() {
|
|
227
|
-
const p = this.clone().deintersection.removeDuplicates().clockWise.open();
|
|
228
|
-
const findSameCorners = (startIndex = 0) => {
|
|
229
|
-
const store = {};
|
|
230
|
-
for (let i = startIndex; i < p.length; i++) {
|
|
231
|
-
const key = p.at(i).toString();
|
|
232
|
-
if (typeof store[key] === 'number') {
|
|
233
|
-
return [store[key], i];
|
|
234
|
-
}
|
|
235
|
-
store[key] = i;
|
|
236
|
-
}
|
|
237
|
-
return undefined;
|
|
238
|
-
};
|
|
239
|
-
let indexes = findSameCorners();
|
|
240
|
-
const holes = [];
|
|
241
|
-
while (indexes) {
|
|
242
|
-
const a = new DPolygon(p.clone().removePart(indexes[0], indexes[1] - indexes[0]));
|
|
243
|
-
if (a.isClockwise) {
|
|
244
|
-
indexes = findSameCorners(indexes[0] + 1);
|
|
245
|
-
}
|
|
246
|
-
else {
|
|
247
|
-
holes.push(a.close());
|
|
248
|
-
p.removePart(indexes[0], indexes[1] - indexes[0]);
|
|
249
|
-
indexes = findSameCorners();
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
p.holes = holes;
|
|
253
|
-
return p.close();
|
|
254
|
-
}
|
|
255
|
-
get deintersection() {
|
|
256
|
-
let p = this.clone().close();
|
|
257
|
-
const store = {};
|
|
258
|
-
for (let i = 0; i < p.length - 1; i++) {
|
|
259
|
-
const k = p.at(i).toString();
|
|
260
|
-
store[k] = store[k] || [];
|
|
261
|
-
store[k].push(i);
|
|
262
|
-
for (let j = i + 2; j < p.length - 1; j++) {
|
|
263
|
-
const firstLine = p.at(i).findLine(p.at(i + 1));
|
|
264
|
-
const secondLine = p.at(j).findLine(p.at(j + 1));
|
|
265
|
-
const intersectionPoint = firstLine.intersection(secondLine);
|
|
266
|
-
if (intersectionPoint &&
|
|
267
|
-
![...firstLine.points, ...secondLine.points].some((t) => t.like(intersectionPoint))) {
|
|
268
|
-
const part = p.removePart(i, j - i).reverse();
|
|
269
|
-
p.insertAfter(i, ...part);
|
|
270
|
-
p.insertAfter(j, intersectionPoint);
|
|
271
|
-
p.insertAfter(i, intersectionPoint);
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
for (const key of Object.keys(store)) {
|
|
276
|
-
const record = store[key];
|
|
277
|
-
if (record.length > 1) {
|
|
278
|
-
for (let j = record.length - 1; j > 0; j--) {
|
|
279
|
-
const origin = p.clone();
|
|
280
|
-
const d = record[j] - record[j - 1];
|
|
281
|
-
if (d > 1) {
|
|
282
|
-
const part = new DPolygon(origin.removePart(record[j - 1], d));
|
|
283
|
-
const allInside = part.reduce((a, e) => a && containCalculator(origin, e), true);
|
|
284
|
-
if (allInside && origin.isClockwise === part.isClockwise) {
|
|
285
|
-
origin.insertAfter(record[j - 1] - 1, ...part.reverse().points);
|
|
286
|
-
p = origin;
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
return p;
|
|
293
|
-
}
|
|
294
|
-
get valid() {
|
|
295
|
-
return this.length > MIN_POINTS_IN_VALID_POLYGON;
|
|
296
|
-
}
|
|
297
|
-
get first() {
|
|
298
|
-
return this.at(0);
|
|
299
|
-
}
|
|
300
|
-
get second() {
|
|
301
|
-
return this.at(1);
|
|
302
|
-
}
|
|
303
|
-
get last() {
|
|
304
|
-
return this.at(this.length - 1);
|
|
305
|
-
}
|
|
306
|
-
get minAreaRectangle() {
|
|
307
|
-
const p = this.convex;
|
|
308
|
-
let resultPolygon = new DPolygon();
|
|
309
|
-
let resultArea = Infinity;
|
|
310
|
-
for (const [, , l] of p.loopPointsGenerator(true)()) {
|
|
311
|
-
let maxWidth = 0;
|
|
312
|
-
let maxWidthPoint1 = null;
|
|
313
|
-
let maxWidthPoint2 = null;
|
|
314
|
-
let maxHeight = 0;
|
|
315
|
-
let maxHeightPoint = null;
|
|
316
|
-
for (const [z, , , i] of p.loopPointsGenerator()()) {
|
|
317
|
-
const p1 = l.findPoint(l.findPerpendicular(z));
|
|
318
|
-
const h = p1.distance(z);
|
|
319
|
-
if (h >= maxHeight) {
|
|
320
|
-
maxHeight = h;
|
|
321
|
-
maxHeightPoint = z;
|
|
322
|
-
}
|
|
323
|
-
for (let j = i; j < p.length - 1; j++) {
|
|
324
|
-
const p2 = l.findPoint(l.findPerpendicular(p.at(j)));
|
|
325
|
-
const w = p1.distance(p2);
|
|
326
|
-
if (w >= maxWidth) {
|
|
327
|
-
maxWidth = w;
|
|
328
|
-
maxWidthPoint1 = p1;
|
|
329
|
-
maxWidthPoint2 = p2;
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
|
-
if (!maxWidthPoint1 || !maxWidthPoint2 || !maxHeightPoint) {
|
|
334
|
-
continue;
|
|
335
|
-
}
|
|
336
|
-
const widthLine = maxWidthPoint1.findLine(maxWidthPoint2);
|
|
337
|
-
const perpendicular1 = widthLine.findPerpendicular(maxWidthPoint1);
|
|
338
|
-
const perpendicular2 = widthLine.findPerpendicular(maxWidthPoint2);
|
|
339
|
-
const tempPolygon = new DPolygon([
|
|
340
|
-
maxWidthPoint1,
|
|
341
|
-
maxWidthPoint2,
|
|
342
|
-
perpendicular2.findPoint(perpendicular2.findPerpendicular(maxHeightPoint)),
|
|
343
|
-
perpendicular1.findPoint(perpendicular1.findPerpendicular(maxHeightPoint))
|
|
344
|
-
]).close();
|
|
345
|
-
if (tempPolygon.area < resultArea) {
|
|
346
|
-
resultPolygon = tempPolygon;
|
|
347
|
-
resultArea = tempPolygon.area;
|
|
348
|
-
}
|
|
349
|
-
}
|
|
350
|
-
return resultPolygon;
|
|
351
|
-
}
|
|
352
|
-
get convex() {
|
|
353
|
-
let p = this.clone().open();
|
|
354
|
-
const { isClockwise } = p;
|
|
355
|
-
if (!isClockwise) {
|
|
356
|
-
p.reverse();
|
|
357
|
-
}
|
|
358
|
-
let l = 0;
|
|
359
|
-
do {
|
|
360
|
-
const p1 = p.last;
|
|
361
|
-
const p2 = p.first;
|
|
362
|
-
const p3 = p.second;
|
|
363
|
-
const d = p2.findInnerAngle(p1, p3);
|
|
364
|
-
if (d > Math.PI || DNumbers.likeZero(DNumbers.rad2Deg(d)) || DNumbers.likePI(d) || DNumbers.like2PI(d)) {
|
|
365
|
-
p.removePart(-1, 1);
|
|
366
|
-
}
|
|
367
|
-
else {
|
|
368
|
-
break;
|
|
369
|
-
}
|
|
370
|
-
} while (p.length);
|
|
371
|
-
p.close();
|
|
372
|
-
let iteration = 0;
|
|
373
|
-
do {
|
|
374
|
-
p = p.deintersection;
|
|
375
|
-
l = p.length;
|
|
376
|
-
for (let i = 1; i < p.length - 1; i++) {
|
|
377
|
-
const p1 = p.at(i - 1);
|
|
378
|
-
const p2 = p.at(i);
|
|
379
|
-
const p3 = p.at(i + 1);
|
|
380
|
-
const d = p2.findInnerAngle(p1, p3);
|
|
381
|
-
if (d > Math.PI || DNumbers.likeZero(DNumbers.rad2Deg(d)) || DNumbers.likePI(d) || DNumbers.like2PI(d)) {
|
|
382
|
-
p.removePart(--i, 1);
|
|
383
|
-
}
|
|
384
|
-
}
|
|
385
|
-
iteration++;
|
|
386
|
-
} while (p.length !== l && iteration < MAX_CONVEX_ITERATIONS);
|
|
387
|
-
if (!isClockwise) {
|
|
388
|
-
p.reverse();
|
|
389
|
-
}
|
|
390
|
-
return p;
|
|
391
|
-
}
|
|
392
|
-
get isClockwise() {
|
|
393
|
-
let sum = 0;
|
|
394
|
-
for (const [{ x, y }, { x: a, y: b }] of this.clone().close()
|
|
395
|
-
.loopPointsGenerator()()) {
|
|
396
|
-
sum += (a - x) * (b + y);
|
|
397
|
-
}
|
|
398
|
-
return sum < 0;
|
|
399
|
-
}
|
|
400
|
-
get clockWise() {
|
|
401
|
-
if (this.isClockwise) {
|
|
402
|
-
return this.clone();
|
|
403
|
-
}
|
|
404
|
-
return this.clone().reverse();
|
|
405
|
-
}
|
|
406
|
-
get noHoles() {
|
|
407
|
-
const res = this.clone();
|
|
408
|
-
res.holes = [];
|
|
409
|
-
return res;
|
|
410
|
-
}
|
|
411
|
-
reduce(f, v) {
|
|
412
|
-
return this.pPoints.reduce(f, v);
|
|
413
|
-
}
|
|
414
|
-
intersection(l, includeOnly = false) {
|
|
415
|
-
const res = [];
|
|
416
|
-
for (const [, , line] of this.loopPointsGenerator(true)()) {
|
|
417
|
-
const intersect = line.intersection(l, 0, includeOnly);
|
|
418
|
-
if (intersect) {
|
|
419
|
-
res.push(intersect);
|
|
420
|
-
}
|
|
421
|
-
}
|
|
422
|
-
return res;
|
|
423
|
-
}
|
|
424
|
-
setCenter(newCenter) {
|
|
425
|
-
return this.loop()
|
|
426
|
-
.move(newCenter.clone().move(this.center.minus()))
|
|
427
|
-
.run();
|
|
428
|
-
}
|
|
429
|
-
toWKT(type = DPolygon.WKT_POLYGON, withZ = false) {
|
|
430
|
-
if (type === DPolygon.WKT_POLYGON) {
|
|
431
|
-
let h = '';
|
|
432
|
-
if (this.holes && this.holes.length) {
|
|
433
|
-
h = `, ${this.holes.map((hole) => hole.toString())
|
|
434
|
-
.join(', ')}`;
|
|
435
|
-
}
|
|
436
|
-
return `POLYGON ((${this.deintersection.mapArray((r) => `${r.x} ${r.y}${withZ ? ` ${r.z}` : ''}`)
|
|
437
|
-
.join(', ')})${h})`;
|
|
438
|
-
}
|
|
439
|
-
return `LINESTRING (${this.mapArray((r) => `${r.x} ${r.y}${withZ ? ` ${r.z}` : ''}`)
|
|
440
|
-
.join(', ')})`;
|
|
441
|
-
}
|
|
442
|
-
filter(f) {
|
|
443
|
-
this.pPoints = this.pPoints.filter(f);
|
|
444
|
-
return this;
|
|
445
|
-
}
|
|
446
|
-
map(f) {
|
|
447
|
-
this.pPoints = this.mapArray(f);
|
|
448
|
-
this.holes = this.holes.map((h) => h.map(f));
|
|
449
|
-
return this;
|
|
450
|
-
}
|
|
451
|
-
mapArray(f) {
|
|
452
|
-
return this.pPoints.map(f);
|
|
453
|
-
}
|
|
454
|
-
sort(f) {
|
|
455
|
-
this.points.sort(f);
|
|
456
|
-
return this;
|
|
457
|
-
}
|
|
458
|
-
at(index) {
|
|
459
|
-
const { length } = this;
|
|
460
|
-
return this.points[(index % length + length) % length];
|
|
461
|
-
}
|
|
462
|
-
pop() {
|
|
463
|
-
return this.pPoints.pop();
|
|
464
|
-
}
|
|
465
|
-
push(...args) {
|
|
466
|
-
return this.pPoints.push(...args);
|
|
467
|
-
}
|
|
468
|
-
shift() {
|
|
469
|
-
return this.pPoints.shift();
|
|
470
|
-
}
|
|
471
|
-
unshift(...args) {
|
|
472
|
-
return this.pPoints.unshift(...args);
|
|
473
|
-
}
|
|
474
|
-
reverse() {
|
|
475
|
-
this.pPoints = this.pPoints.reverse();
|
|
476
|
-
this.holes = this.holes.map((h) => h.reverse());
|
|
477
|
-
return this;
|
|
478
|
-
}
|
|
479
|
-
getValue() {
|
|
480
|
-
return (this.pPoints.map((r) => r.getValue()) + this.holes
|
|
481
|
-
.reduce((a, h) => a + h.getValue(), ''));
|
|
482
|
-
}
|
|
483
|
-
toString() {
|
|
484
|
-
return `(${this.mapArray((r) => r.toString()).join(', ')})`;
|
|
485
|
-
}
|
|
486
|
-
close() {
|
|
487
|
-
const p0 = this.first;
|
|
488
|
-
if (p0 && !this.closed) {
|
|
489
|
-
this.push(p0.clone());
|
|
490
|
-
}
|
|
491
|
-
return this;
|
|
492
|
-
}
|
|
493
|
-
open() {
|
|
494
|
-
const p = this.first;
|
|
495
|
-
if (this.length > 2 && p && this.closed) {
|
|
496
|
-
this.pop();
|
|
497
|
-
}
|
|
498
|
-
return this;
|
|
499
|
-
}
|
|
500
|
-
add(poly) {
|
|
501
|
-
const res = new DPolygon([...this.points, ...poly.points]).close();
|
|
502
|
-
res.holes = [...this.holes, ...poly.holes].map((h) => h.clone());
|
|
503
|
-
return res;
|
|
504
|
-
}
|
|
505
|
-
has(p) {
|
|
506
|
-
return this.pPoints.some((q) => q.equal(p));
|
|
507
|
-
}
|
|
508
|
-
clone() {
|
|
509
|
-
const res = new DPolygon(this.points.map((r) => r.clone()));
|
|
510
|
-
res.holes = this.holes.map((h) => h.clone());
|
|
511
|
-
res.properties = Object.assign({}, this.properties);
|
|
512
|
-
return res;
|
|
513
|
-
}
|
|
514
|
-
equal(p) {
|
|
515
|
-
if (!(p instanceof DPolygon)) {
|
|
516
|
-
return false;
|
|
517
|
-
}
|
|
518
|
-
if (this.length !== p.length || this.holes.length !== p.holes.length) {
|
|
519
|
-
return false;
|
|
520
|
-
}
|
|
521
|
-
return (this.same(p) &&
|
|
522
|
-
this.holes.reduce((a, hole) => a && p.holes.some((pHoles) => pHoles.same(hole)), true));
|
|
523
|
-
}
|
|
524
|
-
same(p) {
|
|
525
|
-
const pClone = p.clone().close();
|
|
526
|
-
const thisAsString = this.clone()
|
|
527
|
-
.close()
|
|
528
|
-
.toString();
|
|
529
|
-
for (let i = 0; i < pClone.length; i++) {
|
|
530
|
-
if (thisAsString === pClone.toString() || thisAsString === pClone.clone().reverse()
|
|
531
|
-
.toString()) {
|
|
532
|
-
return true;
|
|
533
|
-
}
|
|
534
|
-
pClone.nextStart();
|
|
535
|
-
}
|
|
536
|
-
return false;
|
|
537
|
-
}
|
|
538
|
-
findIndex(a) {
|
|
539
|
-
if (a instanceof DPoint) {
|
|
540
|
-
return this.points.findIndex((t) => t.equal(a));
|
|
541
|
-
}
|
|
542
|
-
return this.points.findIndex(a);
|
|
543
|
-
}
|
|
544
|
-
approximation(e = Math.sqrt(this.perimeter) * APPROXIMATION_VALUE) {
|
|
545
|
-
return new DPolygon(this.clone().douglasPeucker(this.pPoints, e));
|
|
546
|
-
}
|
|
547
|
-
insertAfter(index, ...points) {
|
|
548
|
-
this.pPoints.splice(index + 1, 0, ...points);
|
|
549
|
-
}
|
|
550
|
-
removePart(index, count) {
|
|
551
|
-
return this.pPoints.splice(index + 1, count);
|
|
552
|
-
}
|
|
553
|
-
hasSimpleIntersection(p) {
|
|
554
|
-
const extend1 = this.extend;
|
|
555
|
-
const extend2 = p.extend;
|
|
556
|
-
const extend1points = extend1.points;
|
|
557
|
-
const extend2points = extend2.points;
|
|
558
|
-
const in1 = extend1points.some((t) => extend2.simpleInclude(t));
|
|
559
|
-
const in2 = extend2points.some((t) => extend1.simpleInclude(t));
|
|
560
|
-
return in1 || in2;
|
|
561
|
-
}
|
|
562
|
-
simpleInclude(p) {
|
|
563
|
-
return this.simpleIncludeX(p) && this.simpleIncludeY(p);
|
|
564
|
-
}
|
|
565
|
-
drawPolygonOnCanvas(canvas, fillColor, strokeColor, shadowColor, lineWidth = 1, steps = this.length - 1) {
|
|
566
|
-
if (this.length > 1) {
|
|
567
|
-
const ctx = canvas.getContext('2d');
|
|
568
|
-
if (fillColor) {
|
|
569
|
-
ctx.fillStyle = fillColor;
|
|
570
|
-
}
|
|
571
|
-
if (strokeColor) {
|
|
572
|
-
ctx.strokeStyle = strokeColor;
|
|
573
|
-
}
|
|
574
|
-
if (lineWidth) {
|
|
575
|
-
ctx.lineWidth = lineWidth;
|
|
576
|
-
}
|
|
577
|
-
if (fillColor || strokeColor) {
|
|
578
|
-
ctx.beginPath();
|
|
579
|
-
}
|
|
580
|
-
this.goByPath(ctx, steps % this.length);
|
|
581
|
-
if (shadowColor) {
|
|
582
|
-
ctx.shadowColor = shadowColor;
|
|
583
|
-
ctx.shadowBlur = 0;
|
|
584
|
-
ctx.shadowOffsetX = 1;
|
|
585
|
-
ctx.shadowOffsetY = 1;
|
|
586
|
-
}
|
|
587
|
-
if (fillColor) {
|
|
588
|
-
ctx.closePath();
|
|
589
|
-
ctx.fill();
|
|
590
|
-
}
|
|
591
|
-
if (strokeColor) {
|
|
592
|
-
ctx.stroke();
|
|
593
|
-
}
|
|
594
|
-
}
|
|
595
|
-
return this;
|
|
596
|
-
}
|
|
597
|
-
clearPolygonOnCanvas(canvas) {
|
|
598
|
-
const ctx = canvas.getContext('2d');
|
|
599
|
-
const old = ctx.globalCompositeOperation;
|
|
600
|
-
ctx.globalCompositeOperation = 'destination-out';
|
|
601
|
-
this.goByPath(ctx);
|
|
602
|
-
ctx.fill();
|
|
603
|
-
ctx.globalCompositeOperation = old;
|
|
604
|
-
}
|
|
605
|
-
contain(p, isBorderInside = false) {
|
|
606
|
-
const simpleInclude = this.simpleInclude(p);
|
|
607
|
-
if (!simpleInclude) {
|
|
608
|
-
return false;
|
|
609
|
-
}
|
|
610
|
-
const onBorder = this.onBorder(p);
|
|
611
|
-
if (onBorder) {
|
|
612
|
-
return isBorderInside;
|
|
613
|
-
}
|
|
614
|
-
const poly = this.deintersection;
|
|
615
|
-
let totalFi = 0;
|
|
616
|
-
for (const [{ x, y }, { x: a, y: b }] of poly.loopPointsGenerator()()) {
|
|
617
|
-
const line1 = new DLine(x - p.x, y - p.y, 0);
|
|
618
|
-
const line2 = new DLine(a - p.x, b - p.y, 0);
|
|
619
|
-
const fiDif = line1.findFi(line2);
|
|
620
|
-
if (line1.vectorProduct(line2).c > 0) {
|
|
621
|
-
totalFi += fiDif;
|
|
622
|
-
}
|
|
623
|
-
else {
|
|
624
|
-
totalFi -= fiDif;
|
|
625
|
-
}
|
|
626
|
-
}
|
|
627
|
-
const eps = Math.PI / 10000;
|
|
628
|
-
const absTotalFi = Math.abs(totalFi);
|
|
629
|
-
if (absTotalFi < eps) {
|
|
630
|
-
return false;
|
|
631
|
-
}
|
|
632
|
-
else if (Math.abs(2 * Math.PI - absTotalFi) < eps) {
|
|
633
|
-
return true;
|
|
634
|
-
}
|
|
635
|
-
throw new Error('contains2 faild');
|
|
636
|
-
}
|
|
637
|
-
onBorder(p) {
|
|
638
|
-
const simpleInclude = this.simpleInclude(p);
|
|
639
|
-
if (simpleInclude) {
|
|
640
|
-
const poly = this.deintersection;
|
|
641
|
-
const hasSamePoint = this.points.some((point) => point.equal(p));
|
|
642
|
-
if (hasSamePoint) {
|
|
643
|
-
return true;
|
|
644
|
-
}
|
|
645
|
-
for (const [, , polygonLine] of poly.loopPointsGenerator(true)()) {
|
|
646
|
-
const onBorder = polygonLine.x(p).equal(p) && polygonLine.inRange(p);
|
|
647
|
-
if (onBorder) {
|
|
648
|
-
return true;
|
|
649
|
-
}
|
|
650
|
-
}
|
|
651
|
-
}
|
|
652
|
-
return false;
|
|
653
|
-
}
|
|
654
|
-
nextStart() {
|
|
655
|
-
this.open();
|
|
656
|
-
this.push(this.shift());
|
|
657
|
-
this.close();
|
|
658
|
-
return this;
|
|
659
|
-
}
|
|
660
|
-
removeDuplicates() {
|
|
661
|
-
for (let i = 0; i < this.length - 1; i++) {
|
|
662
|
-
const p1 = this.at(i);
|
|
663
|
-
const p2 = this.at(i + 1);
|
|
664
|
-
if (p1.equal(p2)) {
|
|
665
|
-
this.removePart(i, 1);
|
|
666
|
-
i--;
|
|
667
|
-
}
|
|
668
|
-
}
|
|
669
|
-
return this;
|
|
670
|
-
}
|
|
671
|
-
static toGeoJSONFeatureCollection(polygons, format = DGeo.parseFormat) {
|
|
672
|
-
return {
|
|
673
|
-
type: 'FeatureCollection',
|
|
674
|
-
features: polygons.map((polygon) => polygon.toGeoJSONFeature(format))
|
|
675
|
-
};
|
|
676
|
-
}
|
|
677
|
-
static parse(a, format = DGeo.parseFormat) {
|
|
678
|
-
if (a.type) {
|
|
679
|
-
switch (a.type) {
|
|
680
|
-
case 'FeatureCollection':
|
|
681
|
-
return a.features.reduce((ak, f) => {
|
|
682
|
-
const t = DPolygon.parse(f, format);
|
|
683
|
-
if (Array.isArray(t)) {
|
|
684
|
-
ak.push(...t);
|
|
685
|
-
}
|
|
686
|
-
else {
|
|
687
|
-
ak.push(t);
|
|
688
|
-
}
|
|
689
|
-
return ak;
|
|
690
|
-
}, []);
|
|
691
|
-
case 'Feature': {
|
|
692
|
-
const t = DPolygon.parse(a.geometry, format);
|
|
693
|
-
const props = Object.assign(Object.assign({}, a.properties), (a.id ? ({
|
|
694
|
-
id: a.id
|
|
695
|
-
}) : ({})));
|
|
696
|
-
if (Array.isArray(t)) {
|
|
697
|
-
t.forEach((record) => {
|
|
698
|
-
record.properties = Object.assign({}, props);
|
|
699
|
-
});
|
|
700
|
-
}
|
|
701
|
-
else {
|
|
702
|
-
t.properties = Object.assign({}, props);
|
|
703
|
-
}
|
|
704
|
-
return t;
|
|
705
|
-
}
|
|
706
|
-
case 'LineString':
|
|
707
|
-
case 'MultiPoint':
|
|
708
|
-
return new DPolygon(a.coordinates.map((c) => DPoint.parse(c, format)));
|
|
709
|
-
case 'Polygon':
|
|
710
|
-
return a.coordinates.reduce((ak, line, index) => {
|
|
711
|
-
if (index === 0) {
|
|
712
|
-
ak.points = line.map((c) => DPoint.parse(c, format));
|
|
713
|
-
}
|
|
714
|
-
else {
|
|
715
|
-
ak.holes.push(new DPolygon(line.map((c) => DPoint.parse(c, format))));
|
|
716
|
-
}
|
|
717
|
-
return ak;
|
|
718
|
-
}, new DPolygon());
|
|
719
|
-
case 'MultiLineString':
|
|
720
|
-
return a.coordinates.reduce((ak, line) => {
|
|
721
|
-
ak.push(new DPolygon(line.map((c) => DPoint.parse(c, format))));
|
|
722
|
-
return ak;
|
|
723
|
-
}, []);
|
|
724
|
-
case 'MultiPolygon':
|
|
725
|
-
return a.coordinates.reduce((ak, coordinates) => {
|
|
726
|
-
ak.push(DPolygon.parse({
|
|
727
|
-
type: 'Polygon',
|
|
728
|
-
coordinates
|
|
729
|
-
}, format));
|
|
730
|
-
return ak;
|
|
731
|
-
}, []);
|
|
732
|
-
case 'GeometryCollection':
|
|
733
|
-
return a.geometries.reduce((ak, line) => {
|
|
734
|
-
ak.push(DPolygon.parse(line, format));
|
|
735
|
-
return ak;
|
|
736
|
-
}, []);
|
|
737
|
-
default:
|
|
738
|
-
}
|
|
739
|
-
}
|
|
740
|
-
return new DPolygon(a
|
|
741
|
-
.map((r) => DPoint.parse(r, format)));
|
|
742
|
-
}
|
|
743
|
-
toArrayOfCoords(format = DGeo.parseFormat) {
|
|
744
|
-
return this.mapArray((r) => r.toCoords(format));
|
|
745
|
-
}
|
|
746
|
-
toGeoJSONFeature(format = DGeo.parseFormat) {
|
|
747
|
-
return {
|
|
748
|
-
type: 'Feature',
|
|
749
|
-
properties: Object.assign({}, this.properties),
|
|
750
|
-
geometry: this.toGeoJSON(format)
|
|
751
|
-
};
|
|
752
|
-
}
|
|
753
|
-
toGeoJSON(format = DGeo.parseFormat) {
|
|
754
|
-
if (this.closed) {
|
|
755
|
-
return {
|
|
756
|
-
type: 'Polygon',
|
|
757
|
-
coordinates: [
|
|
758
|
-
this.toArrayOfCoords(format),
|
|
759
|
-
...this.holes.map((h) => h.toArrayOfCoords(format))
|
|
760
|
-
]
|
|
761
|
-
};
|
|
762
|
-
}
|
|
763
|
-
return {
|
|
764
|
-
type: 'LineString',
|
|
765
|
-
coordinates: this.toArrayOfCoords(format)
|
|
766
|
-
};
|
|
767
|
-
}
|
|
768
|
-
divideToPieces(piecesCount, withAltitude = false) {
|
|
769
|
-
const { fullLength } = this;
|
|
770
|
-
const pieceLength = fullLength / piecesCount;
|
|
771
|
-
let currentPieceLength = pieceLength;
|
|
772
|
-
for (const [p1, p2, , i] of this.loopPointsGenerator()()) {
|
|
773
|
-
const d = p1.distance(p2);
|
|
774
|
-
if (d === currentPieceLength) {
|
|
775
|
-
p2.properties.pieceBorder = true;
|
|
776
|
-
currentPieceLength = pieceLength;
|
|
777
|
-
}
|
|
778
|
-
else if (d - currentPieceLength > 0) {
|
|
779
|
-
const circle = new DCircle(p1, currentPieceLength);
|
|
780
|
-
const line = p1.findLine(p2);
|
|
781
|
-
const intersectionPoint = line.intersectionWithCircle(circle)
|
|
782
|
-
.filter((p) => line.inRange(p, CLOSE_TO_INTERSECTION_DISTANCE))[0];
|
|
783
|
-
intersectionPoint.properties.pieceBorder = true;
|
|
784
|
-
this.insertAfter(i, intersectionPoint);
|
|
785
|
-
if (withAltitude) {
|
|
786
|
-
const p1z = p1.z;
|
|
787
|
-
intersectionPoint.z = p1z - (p1z - p2.z) * (p1.distance(intersectionPoint) / d);
|
|
788
|
-
}
|
|
789
|
-
currentPieceLength = pieceLength;
|
|
790
|
-
}
|
|
791
|
-
else {
|
|
792
|
-
currentPieceLength -= d;
|
|
793
|
-
}
|
|
794
|
-
}
|
|
795
|
-
return this;
|
|
796
|
-
}
|
|
797
|
-
prepareToFastSearch() {
|
|
798
|
-
this.searchStore = {};
|
|
799
|
-
for (const { x, y, z } of this.points) {
|
|
800
|
-
if (!this.searchStore[x]) {
|
|
801
|
-
this.searchStore[x] = {};
|
|
802
|
-
}
|
|
803
|
-
if (!this.searchStore[x][y]) {
|
|
804
|
-
this.searchStore[x][y] = {};
|
|
805
|
-
}
|
|
806
|
-
this.searchStore[x][y][z || 'undefined'] = true;
|
|
807
|
-
}
|
|
808
|
-
}
|
|
809
|
-
fastHas({ x, y, z }) {
|
|
810
|
-
if (!this.searchStore[x]) {
|
|
811
|
-
return false;
|
|
812
|
-
}
|
|
813
|
-
if (!this.searchStore[x][y]) {
|
|
814
|
-
return false;
|
|
815
|
-
}
|
|
816
|
-
if (!this.searchStore[x][y][z || 'undefined']) {
|
|
817
|
-
return false;
|
|
818
|
-
}
|
|
819
|
-
return this.searchStore[x][y][z || 'undefined'];
|
|
820
|
-
}
|
|
821
|
-
get growingPiecesGenerator() {
|
|
822
|
-
const polygon = this;
|
|
823
|
-
return function* () {
|
|
824
|
-
const r = new DPolygon();
|
|
825
|
-
for (const p of polygon.pPoints) {
|
|
826
|
-
r.push(p);
|
|
827
|
-
if (p.properties.pieceBorder) {
|
|
828
|
-
yield r.clone();
|
|
829
|
-
}
|
|
830
|
-
}
|
|
831
|
-
return r.clone();
|
|
832
|
-
};
|
|
833
|
-
}
|
|
834
|
-
simpleUnion(p) {
|
|
835
|
-
try {
|
|
836
|
-
const res = this.simpleLogicFunction(p, true, true);
|
|
837
|
-
if (res === null) {
|
|
838
|
-
return null;
|
|
839
|
-
}
|
|
840
|
-
if (res instanceof DPolygon) {
|
|
841
|
-
return res;
|
|
842
|
-
}
|
|
843
|
-
return null;
|
|
844
|
-
}
|
|
845
|
-
catch (ex) {
|
|
846
|
-
return null;
|
|
847
|
-
}
|
|
848
|
-
}
|
|
849
|
-
simpleIntersection(p) {
|
|
850
|
-
return this.simpleLogicFunction(p, false, false);
|
|
851
|
-
}
|
|
852
|
-
simpleDifference(p) {
|
|
853
|
-
return this.simpleLogicFunction(p, true, false);
|
|
854
|
-
}
|
|
855
|
-
smartUnion(p) {
|
|
856
|
-
const res = this.clone().simpleUnion(p);
|
|
857
|
-
if (res) {
|
|
858
|
-
let allHoles = [...this.holes, ...p.holes, ...res.holes].map((h) => h.clone());
|
|
859
|
-
for (const a of allHoles) {
|
|
860
|
-
for (const b of allHoles) {
|
|
861
|
-
if (a.equal(b)) {
|
|
862
|
-
continue;
|
|
863
|
-
}
|
|
864
|
-
const r = a.simpleUnion(b);
|
|
865
|
-
if (r) {
|
|
866
|
-
allHoles = allHoles.filter((v) => !v.equal(a) && !v.equal(b));
|
|
867
|
-
allHoles.push(r);
|
|
868
|
-
}
|
|
869
|
-
}
|
|
870
|
-
}
|
|
871
|
-
res.holes = allHoles;
|
|
872
|
-
}
|
|
873
|
-
return res;
|
|
874
|
-
}
|
|
875
|
-
toTriangles() {
|
|
876
|
-
const q = this.clone().removeDuplicates()
|
|
877
|
-
.open();
|
|
878
|
-
if (q.length < 3) {
|
|
879
|
-
return [];
|
|
880
|
-
}
|
|
881
|
-
if (q.length === 3) {
|
|
882
|
-
return [q];
|
|
883
|
-
}
|
|
884
|
-
const innerAndNotIntersect = (poly, p1, p2) => {
|
|
885
|
-
const l = p1.findLine(p2);
|
|
886
|
-
const { center } = l;
|
|
887
|
-
const intersections = poly.holes.reduce((a, hole) => a && Boolean(hole.clone().close()
|
|
888
|
-
.intersection(l, true).length), Boolean(poly.clone().close()
|
|
889
|
-
.intersection(l, true).length));
|
|
890
|
-
const contain = poly.holes.reduce((a, hole) => a && !hole
|
|
891
|
-
.contain(center), poly.contain(center));
|
|
892
|
-
return !intersections && contain;
|
|
893
|
-
};
|
|
894
|
-
const getTriangle = (poly) => {
|
|
895
|
-
for (let i = 0; i < poly.length; i++) {
|
|
896
|
-
const p0 = poly.at(0);
|
|
897
|
-
const p1 = poly.at(1);
|
|
898
|
-
const p2 = poly.at(2);
|
|
899
|
-
if (innerAndNotIntersect(poly, p0, p2)) {
|
|
900
|
-
poly.removePart(0, 1);
|
|
901
|
-
return new DPolygon([
|
|
902
|
-
p0.clone(),
|
|
903
|
-
p1.clone(),
|
|
904
|
-
p2.clone()
|
|
905
|
-
]);
|
|
906
|
-
}
|
|
907
|
-
poly.push(poly.shift());
|
|
908
|
-
}
|
|
909
|
-
return undefined;
|
|
910
|
-
};
|
|
911
|
-
const p = this.clone().removeDuplicates().clockWise.open();
|
|
912
|
-
while (p.holes.length) {
|
|
913
|
-
const h = p.holes.shift()
|
|
914
|
-
.clone()
|
|
915
|
-
.clockWise
|
|
916
|
-
.reverse()
|
|
917
|
-
.close();
|
|
918
|
-
for (let i = 0; i < p.length; i++) {
|
|
919
|
-
if (innerAndNotIntersect(p, p.first, h.first)) {
|
|
920
|
-
p.insertAfter(0, ...h.points, p.first);
|
|
921
|
-
break;
|
|
922
|
-
}
|
|
923
|
-
p.push(p.shift());
|
|
924
|
-
}
|
|
925
|
-
}
|
|
926
|
-
const res = [];
|
|
927
|
-
while (p.length > 3) {
|
|
928
|
-
const triangle = getTriangle(p);
|
|
929
|
-
if (triangle) {
|
|
930
|
-
res.push(triangle);
|
|
931
|
-
}
|
|
932
|
-
}
|
|
933
|
-
res.push(p);
|
|
934
|
-
return res;
|
|
935
|
-
}
|
|
936
|
-
getTrianglesPointIndexes() {
|
|
937
|
-
const innerAndNotIntersect = (poly, p1, p2) => {
|
|
938
|
-
const l = p1.findLine(p2);
|
|
939
|
-
const { center } = l;
|
|
940
|
-
const intersections = poly.holes.reduce((a, hole) => a && Boolean(hole.clone().close()
|
|
941
|
-
.intersection(l, true).length), Boolean(poly.clone().close()
|
|
942
|
-
.intersection(l, true).length));
|
|
943
|
-
const contain = poly.holes.reduce((a, hole) => a && !hole
|
|
944
|
-
.contain(center), poly.contain(center));
|
|
945
|
-
return !intersections && contain;
|
|
946
|
-
};
|
|
947
|
-
const getTriangle = (poly) => {
|
|
948
|
-
for (let i = 0; i < poly.length; i++) {
|
|
949
|
-
const p0 = poly.at(0);
|
|
950
|
-
const p1 = poly.at(1);
|
|
951
|
-
const p2 = poly.at(2);
|
|
952
|
-
if (innerAndNotIntersect(poly, p0, p2)) {
|
|
953
|
-
poly.removePart(0, 1);
|
|
954
|
-
return [
|
|
955
|
-
p0.properties.index,
|
|
956
|
-
p1.properties.index,
|
|
957
|
-
p2.properties.index
|
|
958
|
-
];
|
|
959
|
-
}
|
|
960
|
-
poly.push(poly.shift());
|
|
961
|
-
}
|
|
962
|
-
return undefined;
|
|
963
|
-
};
|
|
964
|
-
let p = this.clone();
|
|
965
|
-
let index = 0;
|
|
966
|
-
p.points.forEach((f) => {
|
|
967
|
-
f.properties.index = index++;
|
|
968
|
-
});
|
|
969
|
-
p.holes.forEach((h) => {
|
|
970
|
-
h.pPoints.forEach((f) => {
|
|
971
|
-
f.properties.index = index++;
|
|
972
|
-
});
|
|
973
|
-
});
|
|
974
|
-
p = p.clockWise.open();
|
|
975
|
-
while (p.holes.length) {
|
|
976
|
-
const h = p.holes.shift()
|
|
977
|
-
.clone()
|
|
978
|
-
.clockWise
|
|
979
|
-
.reverse()
|
|
980
|
-
.close();
|
|
981
|
-
for (let i = 0; i < p.length; i++) {
|
|
982
|
-
if (innerAndNotIntersect(p, p.first, h.first)) {
|
|
983
|
-
p.insertAfter(0, ...h.points, p.first);
|
|
984
|
-
break;
|
|
985
|
-
}
|
|
986
|
-
p.push(p.shift());
|
|
987
|
-
}
|
|
988
|
-
}
|
|
989
|
-
const res = [];
|
|
990
|
-
while (p.length > 3) {
|
|
991
|
-
const triangle = getTriangle(p);
|
|
992
|
-
if (triangle) {
|
|
993
|
-
res.push(...triangle);
|
|
994
|
-
}
|
|
995
|
-
}
|
|
996
|
-
res.push(...p.points.map((f) => f.properties.index));
|
|
997
|
-
return res;
|
|
998
|
-
}
|
|
999
|
-
get closed() {
|
|
1000
|
-
return this.first.equal(this.last);
|
|
1001
|
-
}
|
|
1002
|
-
buffer(v, quadrantSegments = 64, type = DPolygon.CAP_ROUND) {
|
|
1003
|
-
const reader = new jstsIo.WKTReader();
|
|
1004
|
-
const { noHoles, closed } = this;
|
|
1005
|
-
const points = reader
|
|
1006
|
-
.read(noHoles.toWKT(closed ? DPolygon.WKT_POLYGON : DPolygon.WKT_LINESTRING))
|
|
1007
|
-
.buffer(v, quadrantSegments, type)
|
|
1008
|
-
.getCoordinates();
|
|
1009
|
-
return new DPolygon(points.map(({ x, y }) => new DPoint(x, y)));
|
|
1010
|
-
}
|
|
1011
|
-
sideBuffers(v, quadrantSegments = 64) {
|
|
1012
|
-
const { first, last } = this;
|
|
1013
|
-
const buffer = this.buffer(v, quadrantSegments, DPolygon.CAP_FLAT).open();
|
|
1014
|
-
const [start0, start1] = first.sortByDistance(buffer).points.map((r) => r.properties.index);
|
|
1015
|
-
const [end0, end1] = last.sortByDistance(buffer).points.map((r) => r.properties.index);
|
|
1016
|
-
const fromPoint = Math.min(Math.max(start0, start1), Math.max(end0, end1));
|
|
1017
|
-
const toPoint = Math.max(Math.min(start0, start1), Math.min(end0, end1));
|
|
1018
|
-
const linePart = new DPolygon(buffer.removePart(fromPoint - 1, toPoint - fromPoint + 1));
|
|
1019
|
-
buffer.unshift(buffer.pop());
|
|
1020
|
-
return [linePart.reverse(), buffer];
|
|
1021
|
-
}
|
|
1022
|
-
setProperties(v) {
|
|
1023
|
-
this.properties = typeof v === 'object' ? v : v(this);
|
|
1024
|
-
return this;
|
|
1025
|
-
}
|
|
1026
|
-
bezier(step = 0.1) {
|
|
1027
|
-
const res = new DPolygon();
|
|
1028
|
-
for (let i = 0; i < 1; i += step) {
|
|
1029
|
-
res.push(this.clone().getBezierPoint(i));
|
|
1030
|
-
}
|
|
1031
|
-
return res;
|
|
1032
|
-
}
|
|
1033
|
-
setGrowingHeight(from, to) {
|
|
1034
|
-
const { fullLength } = this;
|
|
1035
|
-
let { first: prevPoint } = this;
|
|
1036
|
-
const d = to - from;
|
|
1037
|
-
let currentDistance = 0;
|
|
1038
|
-
this.loop()
|
|
1039
|
-
.setZ((p) => {
|
|
1040
|
-
currentDistance += prevPoint.distance(p);
|
|
1041
|
-
prevPoint = p;
|
|
1042
|
-
return from + currentDistance / fullLength * d;
|
|
1043
|
-
})
|
|
1044
|
-
.run();
|
|
1045
|
-
return this;
|
|
1046
|
-
}
|
|
1047
|
-
loopPointsGenerator(withLine = false) {
|
|
1048
|
-
const that = this;
|
|
1049
|
-
return function* () {
|
|
1050
|
-
for (let i = 0; i < that.length - 1; i++) {
|
|
1051
|
-
const p1 = that.at(i);
|
|
1052
|
-
const p2 = that.at(i + 1);
|
|
1053
|
-
yield [p1, p2, withLine ? p1.findLine(p2) : undefined, i];
|
|
1054
|
-
}
|
|
1055
|
-
};
|
|
1056
|
-
}
|
|
1057
|
-
getBezierPoint(v) {
|
|
1058
|
-
if (this.length === 1) {
|
|
1059
|
-
return this.first;
|
|
1060
|
-
}
|
|
1061
|
-
for (const [p1, p2] of this.loopPointsGenerator()()) {
|
|
1062
|
-
p1.move(p2.clone().move(p1.clone().minus())
|
|
1063
|
-
.scale(v));
|
|
1064
|
-
}
|
|
1065
|
-
this.pop();
|
|
1066
|
-
return this.getBezierPoint(v);
|
|
1067
|
-
}
|
|
1068
|
-
simpleIncludeX(p) {
|
|
1069
|
-
const { x } = p;
|
|
1070
|
-
return this.minX <= x && this.maxX >= x;
|
|
1071
|
-
}
|
|
1072
|
-
simpleIncludeY(p) {
|
|
1073
|
-
const { y } = p;
|
|
1074
|
-
return this.minY <= y && this.maxY >= y;
|
|
1075
|
-
}
|
|
1076
|
-
douglasPeucker(points, e) {
|
|
1077
|
-
let dMax = 0;
|
|
1078
|
-
let index = 0;
|
|
1079
|
-
const end = points.length - 1;
|
|
1080
|
-
const line = points[0].findLine(points[end]);
|
|
1081
|
-
for (let i = 1; i < end; i++) {
|
|
1082
|
-
const d = line.perpendicularDistance(points[i]);
|
|
1083
|
-
if (d > dMax) {
|
|
1084
|
-
index = i;
|
|
1085
|
-
dMax = d;
|
|
1086
|
-
}
|
|
1087
|
-
}
|
|
1088
|
-
if (dMax >= e) {
|
|
1089
|
-
const recResult1 = this.douglasPeucker(points.slice(0, index + 1), e);
|
|
1090
|
-
const recResult2 = this.douglasPeucker(points.slice(index), e);
|
|
1091
|
-
recResult1.pop();
|
|
1092
|
-
return [...recResult1, ...recResult2];
|
|
1093
|
-
}
|
|
1094
|
-
return [points[0], points[end]];
|
|
1095
|
-
}
|
|
1096
|
-
goByPath(ctx, steps = this.length - 1) {
|
|
1097
|
-
const start = this.first;
|
|
1098
|
-
ctx.moveTo(start.x, start.y);
|
|
1099
|
-
for (let i = 1; i <= (steps % this.length); i++) {
|
|
1100
|
-
const { x, y } = this.at(i);
|
|
1101
|
-
ctx.lineTo(x, y);
|
|
1102
|
-
}
|
|
1103
|
-
}
|
|
1104
|
-
getJSTSGeometry(p, unionThis, unionThat) {
|
|
1105
|
-
const unionOrIntersection = unionThat === unionThis;
|
|
1106
|
-
const reader = new jstsIo.WKTReader();
|
|
1107
|
-
const a = reader.read(this.noHoles.toWKT());
|
|
1108
|
-
const b = reader.read(p.noHoles.toWKT());
|
|
1109
|
-
if (!unionOrIntersection) {
|
|
1110
|
-
return a.difference(b);
|
|
1111
|
-
}
|
|
1112
|
-
else if (unionThis) {
|
|
1113
|
-
return a.union(b);
|
|
1114
|
-
}
|
|
1115
|
-
else if (!unionThis) {
|
|
1116
|
-
return a.intersection(b);
|
|
1117
|
-
}
|
|
1118
|
-
return undefined;
|
|
1119
|
-
}
|
|
1120
|
-
simpleLogicFunction(p, unionThis, unionThat) {
|
|
1121
|
-
const c = this.getJSTSGeometry(p, unionThis, unionThat);
|
|
1122
|
-
if (c) {
|
|
1123
|
-
const coordinates = c.getCoordinates();
|
|
1124
|
-
if (coordinates.length) {
|
|
1125
|
-
let result = coordinates.reduce((ak, { x, y }, index) => {
|
|
1126
|
-
const lastIndex = ak.length - 1;
|
|
1127
|
-
const t = new DPoint(x, y);
|
|
1128
|
-
const { first } = ak[lastIndex];
|
|
1129
|
-
if (t.equal(first)) {
|
|
1130
|
-
if (coordinates[index + 1]) {
|
|
1131
|
-
const nextPoint = new DPoint(coordinates[index + 1].x, coordinates[index + 1].y);
|
|
1132
|
-
if (ak[lastIndex].length > 1) {
|
|
1133
|
-
ak.push(new DPolygon([nextPoint]));
|
|
1134
|
-
}
|
|
1135
|
-
}
|
|
1136
|
-
}
|
|
1137
|
-
else {
|
|
1138
|
-
ak[lastIndex].push(t);
|
|
1139
|
-
}
|
|
1140
|
-
return ak;
|
|
1141
|
-
}, [new DPolygon([new DPoint(coordinates[0].x, coordinates[0].y)])]);
|
|
1142
|
-
if (unionThat && unionThis && result.length > 1) {
|
|
1143
|
-
for (const q of result) {
|
|
1144
|
-
for (const r of result) {
|
|
1145
|
-
if (q.has(r.first) && !q.equal(r)) {
|
|
1146
|
-
const index = q.findIndex(r.first);
|
|
1147
|
-
q.points.splice(index, 0, ...r.points);
|
|
1148
|
-
result = result.filter((h) => !h.equal(r));
|
|
1149
|
-
continue;
|
|
1150
|
-
}
|
|
1151
|
-
if (result.length < 2) {
|
|
1152
|
-
break;
|
|
1153
|
-
}
|
|
1154
|
-
}
|
|
1155
|
-
if (result.length < 2) {
|
|
1156
|
-
break;
|
|
1157
|
-
}
|
|
1158
|
-
}
|
|
1159
|
-
}
|
|
1160
|
-
result = result.filter((h) => h.length > 2).map((h) => h.close());
|
|
1161
|
-
for (const q of result) {
|
|
1162
|
-
for (const r of result) {
|
|
1163
|
-
if (result.length < 2) {
|
|
1164
|
-
break;
|
|
1165
|
-
}
|
|
1166
|
-
if (!q.equal(r)) {
|
|
1167
|
-
if (q.contain(r.first, true)) {
|
|
1168
|
-
q.holes.push(r);
|
|
1169
|
-
result = result.filter((h) => !h.equal(r));
|
|
1170
|
-
}
|
|
1171
|
-
}
|
|
1172
|
-
}
|
|
1173
|
-
if (result.length < 2) {
|
|
1174
|
-
break;
|
|
1175
|
-
}
|
|
1176
|
-
}
|
|
1177
|
-
if (result.length === 0) {
|
|
1178
|
-
return null;
|
|
1179
|
-
}
|
|
1180
|
-
if (result.length === 1) {
|
|
1181
|
-
return result[0].close();
|
|
1182
|
-
}
|
|
1183
|
-
return result.map((g) => g.close());
|
|
1184
|
-
}
|
|
1185
|
-
}
|
|
1186
|
-
return null;
|
|
1187
|
-
}
|
|
1188
|
-
}
|
|
1189
|
-
DPolygon.CAP_ROUND = CAP_ROUND;
|
|
1190
|
-
DPolygon.CAP_FLAT = CAP_FLAT;
|
|
1191
|
-
DPolygon.CAP_SQUARE = CAP_SQUARE;
|
|
1192
|
-
DPolygon.WKT_LINESTRING = 'LINESTRING';
|
|
1193
|
-
DPolygon.WKT_POLYGON = 'POLYGON';
|