geojs 1.8.8 → 1.9.0
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/.github/workflows/main.yml +1 -1
- package/CHANGELOG.md +20 -1
- package/geo.js +3123 -212
- package/geo.lean.js +3123 -212
- package/geo.lean.min.js +10 -4
- package/geo.min.js +11 -5
- package/package.json +5 -4
- package/src/annotation/annotation.js +19 -2
- package/src/annotation/circleAnnotation.js +8 -1
- package/src/annotation/ellipseAnnotation.js +51 -1
- package/src/annotation/lineAnnotation.js +29 -20
- package/src/annotation/pointAnnotation.js +28 -23
- package/src/annotation/polygonAnnotation.js +52 -22
- package/src/annotation/rectangleAnnotation.js +42 -23
- package/src/annotation/squareAnnotation.js +8 -1
- package/src/annotationLayer.js +188 -2
- package/src/css/cursor-crosshair-difference.svg +4 -0
- package/src/css/cursor-crosshair-intersect.svg +4 -0
- package/src/css/cursor-crosshair-union.svg +4 -0
- package/src/css/cursor-crosshair-xor.svg +4 -0
- package/src/event.js +14 -0
- package/src/geojsonReader.js +2 -0
- package/src/lineFeature.js +29 -11
- package/src/main.styl +8 -0
- package/src/polygonFeature.js +52 -1
- package/src/typedef.js +11 -0
- package/src/util/index.js +2 -1
- package/src/util/polyops.js +449 -0
- package/src/webgl/lineFeature.js +23 -9
- package/src/webgl/polygonFeature.js +12 -14
|
@@ -0,0 +1,449 @@
|
|
|
1
|
+
var PolyBool = require('polybooljs');
|
|
2
|
+
var geo_map = require('../map');
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* A list of flat polygon lists.
|
|
6
|
+
*
|
|
7
|
+
* @typedef {Array.<Array.<Array.<geo.geoPositionFlat>>>} geo.polygonList
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* A polygon in any of a variety of formats.
|
|
12
|
+
*
|
|
13
|
+
* This can be any object with a ``toPolygonList`` and ``fromPolygonList``
|
|
14
|
+
* method.
|
|
15
|
+
*
|
|
16
|
+
* @typedef {geo.polygonFlat|Array.<geo.polygonFlat>|Array.<Array.<geo.polygonFlat>>|geo.polygonObject|Array.<geo.polygonObject>|Array.<geo.geoPositionFlat>|Array.<Array.<geo.geoPositionFlat>>|geo.polygonList|object} geo.polygonAny
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Object specification for polygon operation options.
|
|
21
|
+
*
|
|
22
|
+
* @typedef {object} geo.util.polyop.spec
|
|
23
|
+
* @property {geo.polygonAny} [poly1] The first polygon set to operate on.
|
|
24
|
+
* @property {geo.polygonAny} [poly2] The second polygon set to operate on.
|
|
25
|
+
* @property {number} [epsilon1] A precision value to use when computing the
|
|
26
|
+
* operation. If not specified, this is computed from the range of values in
|
|
27
|
+
* ``poly1``. It is used for processing ``poly1`` and the general operation.
|
|
28
|
+
* @property {number} [epsilon1] A precision value to use when processing
|
|
29
|
+
* ``poly2``. If not specified, this is computed from the range of values in
|
|
30
|
+
* ``poly2``.
|
|
31
|
+
* @property {string|object} [style] If specified, the preferred output style.
|
|
32
|
+
* This can be (flat|object)[-list[list[-outer[-list]]]]. If an object,
|
|
33
|
+
* the object must have a method ``fromPolygonList``.
|
|
34
|
+
* @property {string|geo.transform} [ingcs] The default coordinate
|
|
35
|
+
* system of the source polygon coordinates. If not specified , this is
|
|
36
|
+
* taken from the feature first or the map second if either is available.
|
|
37
|
+
* @property {string|geo.transform} [gcs] The default coordinate system to use
|
|
38
|
+
* for the actual operations. This is where the epsilon and rdp values are
|
|
39
|
+
* applied. If not specified , this is taken from the map second if
|
|
40
|
+
* available.
|
|
41
|
+
* @property {geo.map} [map] Used for ``ingcs`` and ``gcs`` if needed.
|
|
42
|
+
* @property {string} [innerOperation="union"] one of union, intersect, xor.
|
|
43
|
+
* Used to combine individual polygons in each of ``poly1`` and ``poly2``
|
|
44
|
+
* before the main operation is carried out.
|
|
45
|
+
* @property {object} [correspond] If present, information about the
|
|
46
|
+
* correspondence of the input and output polygons is added to this object.
|
|
47
|
+
* This is of the form {poly1: [idx...], poly2: [idx...], exact1: [idx...],
|
|
48
|
+
* exact2: [idx...]}, where the [idx...] array is the length of the input
|
|
49
|
+
* polygon array and each entry is either undefined or contains a list of
|
|
50
|
+
* indices of corresponding output polygons. Exact means that all of the
|
|
51
|
+
* points in the input polygon are in the output polygon. Multiple inputs
|
|
52
|
+
* can refer to the same output.
|
|
53
|
+
* @property {number} [tolerance] A tolerance value to pass to features,
|
|
54
|
+
* annotations, or other object-based polygon generators to specify how
|
|
55
|
+
* closely non-polygons are converted to polygons. This is in the feature's
|
|
56
|
+
* gcs coordinate system.
|
|
57
|
+
* @property {number} [pixelTolerance] A tolerance value to pass to features,
|
|
58
|
+
* annotations, or other object-based polygon generators to specify how
|
|
59
|
+
* closely non-polygons are converted to polygons. This is interpreted in
|
|
60
|
+
* the feature's map's display coordinates.
|
|
61
|
+
*/
|
|
62
|
+
|
|
63
|
+
const AlternateOpNames = {
|
|
64
|
+
'+': 'union',
|
|
65
|
+
'|': 'union',
|
|
66
|
+
add: 'union',
|
|
67
|
+
'-': 'difference',
|
|
68
|
+
sub: 'difference',
|
|
69
|
+
subtract: 'difference',
|
|
70
|
+
minus: 'difference',
|
|
71
|
+
'*': 'intersect',
|
|
72
|
+
'&': 'intersect',
|
|
73
|
+
mul: 'intersect',
|
|
74
|
+
multiply: 'intersect',
|
|
75
|
+
x: 'xor',
|
|
76
|
+
'^': 'xor'
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Convert a segment list to a polygon list.
|
|
81
|
+
*
|
|
82
|
+
* @param {object[]} seglist A PolyBool segment list.
|
|
83
|
+
* @returns {geo.polygonList} A polygon list.
|
|
84
|
+
*/
|
|
85
|
+
function seglistToPolygonList(seglist) {
|
|
86
|
+
// This single line doesn't arrange holes correctly
|
|
87
|
+
// return seglist.map((s) => PolyBool.polygon(s).regions);
|
|
88
|
+
const polys = [];
|
|
89
|
+
seglist.forEach((s) => {
|
|
90
|
+
const geojson = PolyBool.polygonToGeoJSON(PolyBool.polygon(s));
|
|
91
|
+
if (geojson.type === 'MultiPolygon') {
|
|
92
|
+
geojson.coordinates.forEach((p) => {
|
|
93
|
+
polys.push(p.map((h) => h.slice(0, h.length - 1)));
|
|
94
|
+
});
|
|
95
|
+
} else if (geojson.type === 'Polygon') {
|
|
96
|
+
polys.push(geojson.coordinates.map((h) => h.slice(0, h.length - 1)));
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
return polys;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Perform an boolean operation on a seglist from polygons.
|
|
104
|
+
*
|
|
105
|
+
* @param {string} op One of 'union', 'intersect', or other value PolyBool
|
|
106
|
+
* supports.
|
|
107
|
+
* @param {number} epsilon Precision for calculations. In degrees, 1e-9 is
|
|
108
|
+
* around 0.11 mm in ground distance.
|
|
109
|
+
* @param {object} seglist A seglist array as used by the polybool library.
|
|
110
|
+
* @returns {object} A seglist array.
|
|
111
|
+
*/
|
|
112
|
+
function polygonOperationSeglist(op, epsilon, seglist) {
|
|
113
|
+
op = 'select' + op.charAt(0).toUpperCase() + op.slice(1);
|
|
114
|
+
PolyBool.epsilon(epsilon);
|
|
115
|
+
|
|
116
|
+
while (seglist.length > 1) {
|
|
117
|
+
const newlist = [];
|
|
118
|
+
const half = Math.ceil(seglist.length / 2);
|
|
119
|
+
for (let i = 0; i < half; i += 1) {
|
|
120
|
+
let segments = seglist[i];
|
|
121
|
+
if (i + half < seglist.length) {
|
|
122
|
+
let nextseg = seglist[i + half];
|
|
123
|
+
try {
|
|
124
|
+
segments = PolyBool.combine(segments, nextseg);
|
|
125
|
+
} catch (err) {
|
|
126
|
+
segments = PolyBool.segments(PolyBool.polygon(segments));
|
|
127
|
+
nextseg = PolyBool.segments(PolyBool.polygon(nextseg));
|
|
128
|
+
for (let j = 20; j >= 6; j -= 1) {
|
|
129
|
+
PolyBool.epsilon(Math.pow(0.1, j));
|
|
130
|
+
try {
|
|
131
|
+
segments = PolyBool.combine(segments, nextseg);
|
|
132
|
+
break;
|
|
133
|
+
} catch (err) {}
|
|
134
|
+
}
|
|
135
|
+
PolyBool.epsilon(epsilon);
|
|
136
|
+
}
|
|
137
|
+
if (segments.combined) {
|
|
138
|
+
segments.combined = segments.combined.filter(s => Math.abs(s.start[0] - s.end[0]) > epsilon || Math.abs(s.start[1] - s.end[1]) > epsilon);
|
|
139
|
+
segments = PolyBool[op](segments);
|
|
140
|
+
} else {
|
|
141
|
+
console.warn('Failed in polygon functions.');
|
|
142
|
+
}
|
|
143
|
+
segments.segments = segments.segments.filter(s => Math.abs(s.start[0] - s.end[0]) > epsilon || Math.abs(s.start[1] - s.end[1]) > epsilon);
|
|
144
|
+
}
|
|
145
|
+
newlist.push(segments);
|
|
146
|
+
}
|
|
147
|
+
seglist = newlist;
|
|
148
|
+
}
|
|
149
|
+
return seglist;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Convert a polygon in any of several formats to a polygon list. Each polygon
|
|
154
|
+
* in the list is a list of polylines. Each polyline is a list of points.
|
|
155
|
+
* Each point is a list of two coordinates. The input can be a variety of
|
|
156
|
+
* formats:
|
|
157
|
+
* - object, flat: a list of {x, y} or [x, y]
|
|
158
|
+
* - object-list, flat-list: a list of lists of {x, y} or [x, y]
|
|
159
|
+
* - object-listlist, flat-listlist: a list if lists of lists of {x, y} or
|
|
160
|
+
* [x, y]
|
|
161
|
+
* - object-listlist-outer, flat-listlist-outer: {outer: {x, y}, inner:
|
|
162
|
+
* [{x, y}]} or {outer: [x, y], inner: [[x, y]]}
|
|
163
|
+
* - object-listlist-outer-list, flat-listlist-outer-list: a list of objects
|
|
164
|
+
* in outer/inner object or outer/inner list form
|
|
165
|
+
* The actual format is stored in the mode object.
|
|
166
|
+
*
|
|
167
|
+
* @param {geo.polygonAny} poly A polygon in one of the above formats to
|
|
168
|
+
* convert.
|
|
169
|
+
* @param {object} mode An object that is modified with information about
|
|
170
|
+
* the conversion. This includes style: the input polygon format; min: a
|
|
171
|
+
* 2-item list with minimum values in x, y; max: a 2-item list with
|
|
172
|
+
* maximum values in x, y; epsilon: a recommended value for epsilon in other
|
|
173
|
+
* functions.
|
|
174
|
+
* @param {geo.util.polyop.spec} [opts] Options for the operation. Only used
|
|
175
|
+
* if poly is an object with a toPolygonList method.
|
|
176
|
+
* @returns {geo.polygonList} A list of polygons.
|
|
177
|
+
*/
|
|
178
|
+
function toPolygonList(poly, mode, opts) {
|
|
179
|
+
mode = mode || {};
|
|
180
|
+
|
|
181
|
+
if (poly.toPolygonList) {
|
|
182
|
+
mode.style = poly;
|
|
183
|
+
poly = poly.toPolygonList(opts);
|
|
184
|
+
if (!poly.length) {
|
|
185
|
+
mode.min = mode.max = [0, 0];
|
|
186
|
+
mode.epsilon = 1e-10;
|
|
187
|
+
return poly;
|
|
188
|
+
}
|
|
189
|
+
} else {
|
|
190
|
+
mode.style = '';
|
|
191
|
+
if (poly.outer) {
|
|
192
|
+
mode.style = '-outer';
|
|
193
|
+
poly = [[poly.outer, ...(poly.inner || [])]];
|
|
194
|
+
} else if (poly.length && poly[0].outer) {
|
|
195
|
+
mode.style = '-outer-list';
|
|
196
|
+
poly = poly.map((p) => [p.outer, ...(p.inner || [])]);
|
|
197
|
+
}
|
|
198
|
+
if (!poly.length) {
|
|
199
|
+
mode.style = 'flat-listlist' + mode.style;
|
|
200
|
+
mode.min = mode.max = [0, 0];
|
|
201
|
+
mode.epsilon = 1e-10;
|
|
202
|
+
return poly;
|
|
203
|
+
}
|
|
204
|
+
if (poly[0].x !== undefined) {
|
|
205
|
+
mode.style = 'object';
|
|
206
|
+
poly = [[poly.map((pt) => [pt.x, pt.y])]];
|
|
207
|
+
} else if (poly[0][0].x !== undefined) {
|
|
208
|
+
mode.style = 'object-list' + mode.style;
|
|
209
|
+
poly = [poly.map((p) => p.map((pt) => [pt.x, pt.y]))];
|
|
210
|
+
} else if (poly[0][0][0] === undefined) {
|
|
211
|
+
mode.style = 'flat';
|
|
212
|
+
poly = [[poly]];
|
|
213
|
+
} else if (poly[0][0][0].x !== undefined) {
|
|
214
|
+
mode.style = 'object-listlist' + mode.style;
|
|
215
|
+
poly = poly.map((p) => p.map((l) => l.map((pt) => [pt.x, pt.y])));
|
|
216
|
+
} else if (poly[0][0][0][0] === undefined) {
|
|
217
|
+
mode.style = 'flat-list';
|
|
218
|
+
poly = [poly];
|
|
219
|
+
} else {
|
|
220
|
+
mode.style = 'flat-listlist' + mode.style;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
mode.min = [poly[0][0][0][0], poly[0][0][0][1]];
|
|
224
|
+
mode.max = [poly[0][0][0][0], poly[0][0][0][1]];
|
|
225
|
+
poly.forEach((p) => p.forEach((l) => l.forEach((pt) => {
|
|
226
|
+
if (pt[0] < mode.min[0]) { mode.min[0] = pt[0]; }
|
|
227
|
+
if (pt[0] > mode.max[0]) { mode.max[0] = pt[0]; }
|
|
228
|
+
if (pt[1] < mode.min[1]) { mode.min[1] = pt[1]; }
|
|
229
|
+
if (pt[1] > mode.max[1]) { mode.max[1] = pt[1]; }
|
|
230
|
+
})));
|
|
231
|
+
mode.epsilon = Math.max(Math.max(mode.max[0], mode.max[1]), Math.max(Math.abs(mode.min[0]), Math.abs(mode.min[1]))) * 1e-10;
|
|
232
|
+
return poly;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Convert a polygon list to another format.
|
|
237
|
+
*
|
|
238
|
+
* @param {geo.polygonList} poly A list of polygons.
|
|
239
|
+
* @param {object} mode An object that is modified with information about the
|
|
240
|
+
* conversion. This includes style: the input polygon format; min: a 2-item
|
|
241
|
+
* list with minimum values in x, y; max: a 2-item list with maximum values
|
|
242
|
+
* in x, y; epsilon: a recommended value for epsilon in other functions.
|
|
243
|
+
* @param {geo.util.polyop.spec} [opts] Options for the operation. Only used
|
|
244
|
+
* if ``mode.style`` is an object with a fromPolygonList method.
|
|
245
|
+
* @returns {geo.polygonAny} A polygon in one of several formats.
|
|
246
|
+
*/
|
|
247
|
+
function fromPolygonList(poly, mode, opts) {
|
|
248
|
+
if (mode.style.fromPolygonList) {
|
|
249
|
+
return mode.style.fromPolygonList(poly, opts);
|
|
250
|
+
}
|
|
251
|
+
if (mode.style.includes('object')) {
|
|
252
|
+
poly = poly.map((p) => p.map((h) => h.map((pt) => ({x: pt[0], y: pt[1]}))));
|
|
253
|
+
}
|
|
254
|
+
if (mode.style.includes('outer')) {
|
|
255
|
+
poly = [poly.map((p) => ({outer: p[0], inner: p.slice(1)}))];
|
|
256
|
+
}
|
|
257
|
+
if (!mode.style.endsWith('listlist')) {
|
|
258
|
+
if (mode.style.endsWith('list')) {
|
|
259
|
+
poly = poly[0];
|
|
260
|
+
} else {
|
|
261
|
+
poly = poly[0][0];
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
return poly;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Use a minimum style for output to include all of the results.
|
|
269
|
+
*
|
|
270
|
+
* @param {geo.polygonList} polylist A list of polygons.
|
|
271
|
+
* @param {string} style the proposed style.
|
|
272
|
+
* @returns {string} The preferred style.
|
|
273
|
+
*/
|
|
274
|
+
function minimumPolygonStyle(polylist, style) {
|
|
275
|
+
if (style.fromPolygonList) {
|
|
276
|
+
return style;
|
|
277
|
+
}
|
|
278
|
+
if (polylist.length > 1) {
|
|
279
|
+
if (style.includes('outer')) {
|
|
280
|
+
return style + (style.endsWith('list') ? '' : '-list');
|
|
281
|
+
}
|
|
282
|
+
if (style.endsWith('listlist')) {
|
|
283
|
+
return style;
|
|
284
|
+
}
|
|
285
|
+
return style + (style.endsWith('list') ? 'list' : '-listlist');
|
|
286
|
+
}
|
|
287
|
+
if (polylist.length === 1 && polylist[0].length > 1) {
|
|
288
|
+
if (style.includes('outer') || style.endsWith('list')) {
|
|
289
|
+
return style;
|
|
290
|
+
}
|
|
291
|
+
return style + '-list';
|
|
292
|
+
}
|
|
293
|
+
return style;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* Generate the correspondence between the source polygons and the output
|
|
298
|
+
* polygons. A polygon corresponds if it has at least two points in common.
|
|
299
|
+
*
|
|
300
|
+
* @param {geo.polygonList} poly1 First set of source polygons.
|
|
301
|
+
* @param {geo.polygonList} poly2 Second set of source polygons.
|
|
302
|
+
* @param {geo.polygonList} newpoly Output polygons.
|
|
303
|
+
* @param {object} results An object to add results to.
|
|
304
|
+
*/
|
|
305
|
+
function generateCorrespondence(poly1, poly2, newpoly, results) {
|
|
306
|
+
const pts = {};
|
|
307
|
+
const counts = [];
|
|
308
|
+
|
|
309
|
+
newpoly.forEach((p, idx) => p.forEach((h) => h.forEach((pt) => {
|
|
310
|
+
const key = '_' + pt[0] + '_' + pt[1];
|
|
311
|
+
if (pts[key]) {
|
|
312
|
+
if (pts[key].indexOf(idx) === -1) {
|
|
313
|
+
pts[key].push(idx);
|
|
314
|
+
}
|
|
315
|
+
} else {
|
|
316
|
+
pts[key] = [idx];
|
|
317
|
+
}
|
|
318
|
+
if (counts.length <= idx) {
|
|
319
|
+
counts.push(0);
|
|
320
|
+
}
|
|
321
|
+
counts[idx] += 1;
|
|
322
|
+
})));
|
|
323
|
+
['poly1', 'poly2'].forEach((pkey) => {
|
|
324
|
+
const poly = pkey === 'poly1' ? poly1 : poly2;
|
|
325
|
+
const ekey = pkey === 'poly1' ? 'exact1' : 'exact2';
|
|
326
|
+
results[pkey] = Array(poly.length);
|
|
327
|
+
results[ekey] = Array(poly.length);
|
|
328
|
+
poly.forEach((p, idx) => {
|
|
329
|
+
const found = {};
|
|
330
|
+
p.forEach((h) => h.forEach((pt) => {
|
|
331
|
+
const key = '_' + pt[0] + '_' + pt[1];
|
|
332
|
+
if (pts[key]) {
|
|
333
|
+
pts[key].forEach((val) => {
|
|
334
|
+
found[val] = (found[val] || 0) + 1;
|
|
335
|
+
});
|
|
336
|
+
}
|
|
337
|
+
}));
|
|
338
|
+
Object.keys(found).forEach((nidx) => {
|
|
339
|
+
if (found[nidx] === counts[+nidx]) {
|
|
340
|
+
if (!results[ekey][idx]) {
|
|
341
|
+
results[ekey][idx] = [];
|
|
342
|
+
}
|
|
343
|
+
results[ekey][idx].push(+nidx);
|
|
344
|
+
}
|
|
345
|
+
if (found[nidx] >= 2) {
|
|
346
|
+
if (!results[pkey][idx]) {
|
|
347
|
+
results[pkey][idx] = [];
|
|
348
|
+
}
|
|
349
|
+
results[pkey][idx].push(+nidx);
|
|
350
|
+
}
|
|
351
|
+
});
|
|
352
|
+
});
|
|
353
|
+
});
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/**
|
|
357
|
+
* Perform a general operation of a set of polygons.
|
|
358
|
+
*
|
|
359
|
+
* @param {string} op The operation to handle. One of union, intersect,
|
|
360
|
+
* difference, or xor.
|
|
361
|
+
* @param {geo.polygonAny|geo.util.polyop.spec} poly1 Either the first polygon
|
|
362
|
+
* set or an object containing all parameters for the function.
|
|
363
|
+
* @param {geo.polygonAny} [poly2] If the poly1 parameter is not a complete
|
|
364
|
+
* options object, the second polygon set for the operation.
|
|
365
|
+
* @param {geo.util.polyop.spec} [opts] If the poly1 parameter is not a
|
|
366
|
+
* complete options object, the options for the operation.
|
|
367
|
+
* @returns {geo.polygonAny} A polygon set in the same style as poly1.
|
|
368
|
+
*/
|
|
369
|
+
function generalOperationProcess(op, poly1, poly2, opts) {
|
|
370
|
+
var transform = require('../transform');
|
|
371
|
+
|
|
372
|
+
op = AlternateOpNames[op] || op;
|
|
373
|
+
if (poly1.poly1) {
|
|
374
|
+
opts = poly1;
|
|
375
|
+
poly1 = opts.poly1;
|
|
376
|
+
poly2 = opts.poly2;
|
|
377
|
+
}
|
|
378
|
+
opts = opts || {};
|
|
379
|
+
const ingcs1 = opts.ingcs || (
|
|
380
|
+
opts.map ? opts.map.ingcs() : (
|
|
381
|
+
poly1.gcs ? poly1.gcs() : (
|
|
382
|
+
poly1.layer && poly1.layer().gcs ? poly1.layer().gcs() : (
|
|
383
|
+
poly1.layer ? poly1.layer().map().ingcs() : (
|
|
384
|
+
poly1.map instanceof geo_map ? poly1.map().ingcs() : undefined)))));
|
|
385
|
+
const ingcs2 = opts.ingcs || (
|
|
386
|
+
opts.map ? opts.map.ingcs() : (
|
|
387
|
+
poly2.gcs ? poly2.gcs() : (
|
|
388
|
+
poly2.layer && poly2.layer().gcs ? poly2.layer().gcs() : (
|
|
389
|
+
poly2.layer ? poly2.layer().map().ingcs() : (
|
|
390
|
+
poly2.map instanceof geo_map ? poly2.map().ingcs() : ingcs1)))));
|
|
391
|
+
const gcs = opts.gcs || (
|
|
392
|
+
opts.map ? opts.map.gcs() : (
|
|
393
|
+
poly1.layer ? poly1.layer().map().gcs() : (
|
|
394
|
+
poly1.map instanceof geo_map ? poly1.map().gcs() : undefined)));
|
|
395
|
+
const mode1 = {};
|
|
396
|
+
const mode2 = {};
|
|
397
|
+
poly1 = toPolygonList(poly1, mode1, opts);
|
|
398
|
+
poly2 = toPolygonList(poly2, mode2, opts);
|
|
399
|
+
mode1.epsilon = opts.epsilon1 || mode1.epsilon;
|
|
400
|
+
mode2.epsilon = opts.epsilon2 || mode1.epsilon;
|
|
401
|
+
if (ingcs1 && gcs && ingcs1 !== gcs) {
|
|
402
|
+
poly1 = poly1.map((p) => p.map((h) => transform.transformCoordinates(ingcs1, gcs, h)));
|
|
403
|
+
}
|
|
404
|
+
if (ingcs2 && gcs && ingcs2 !== gcs) {
|
|
405
|
+
poly2 = poly2.map((p) => p.map((h) => transform.transformCoordinates(ingcs2, gcs, h)));
|
|
406
|
+
}
|
|
407
|
+
let seglist1 = poly1.map(p => PolyBool.segments({regions: p}));
|
|
408
|
+
let seglist2 = poly2.map(p => PolyBool.segments({regions: p}));
|
|
409
|
+
seglist1 = polygonOperationSeglist(opts.innerOperation || 'union', mode1.epsilon, seglist1);
|
|
410
|
+
seglist2 = polygonOperationSeglist(opts.innerOperation || 'union', mode2.epsilon, seglist2);
|
|
411
|
+
let seglist = seglist1;
|
|
412
|
+
if (seglist1[0] && seglist2[0]) {
|
|
413
|
+
/* We need to do the main operation with the same inversion flags */
|
|
414
|
+
seglist1[0].inverted = false;
|
|
415
|
+
seglist2[0].inverted = false;
|
|
416
|
+
seglist = polygonOperationSeglist(op, Math.max(mode1.epsilon, mode2.epsilon), [seglist1[0], seglist2[0]]);
|
|
417
|
+
} else if (seglist2[0]) {
|
|
418
|
+
seglist = seglist2;
|
|
419
|
+
}
|
|
420
|
+
let newpoly = seglistToPolygonList(seglist);
|
|
421
|
+
if (opts.correspond) {
|
|
422
|
+
generateCorrespondence(poly1, poly2, newpoly, opts.correspond);
|
|
423
|
+
}
|
|
424
|
+
if (ingcs1 && gcs && ingcs1 !== gcs) {
|
|
425
|
+
newpoly = newpoly.map((p) => p.map((h) => transform.transformCoordinates(gcs, ingcs1, h)));
|
|
426
|
+
}
|
|
427
|
+
const mode = {style: opts.style || minimumPolygonStyle(newpoly, mode1.style)};
|
|
428
|
+
return fromPolygonList(newpoly, mode, opts);
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
/**
|
|
432
|
+
* Generate a polygon function for a specific operation.
|
|
433
|
+
*
|
|
434
|
+
* @param {string} op The operation to handle. One of union, intersect,
|
|
435
|
+
* difference, or xor.
|
|
436
|
+
* @returns {function} a function for the polygons.
|
|
437
|
+
*/
|
|
438
|
+
function generalOperation(op) {
|
|
439
|
+
return (poly1, poly2, opts) => generalOperationProcess(op, poly1, poly2, opts);
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
module.exports = {
|
|
443
|
+
union: generalOperation('union'),
|
|
444
|
+
intersect: generalOperation('intersect'),
|
|
445
|
+
difference: generalOperation('difference'),
|
|
446
|
+
xor: generalOperation('xor'),
|
|
447
|
+
toPolygonList,
|
|
448
|
+
fromPolygonList
|
|
449
|
+
};
|
package/src/webgl/lineFeature.js
CHANGED
|
@@ -136,7 +136,6 @@ var webgl_lineFeature = function (arg) {
|
|
|
136
136
|
v = vert[1],
|
|
137
137
|
target_gcs = m_this.gcs(),
|
|
138
138
|
map_gcs = m_this.layer().map().gcs(),
|
|
139
|
-
simpleInverse = transform.onlyInvertedY(target_gcs, map_gcs),
|
|
140
139
|
pos, posIdx3, firstpos, firstPosIdx3,
|
|
141
140
|
lineFunc = m_this.line(),
|
|
142
141
|
strokeWidthFunc = m_this.style.get('strokeWidth'), strokeWidthVal,
|
|
@@ -147,6 +146,7 @@ var webgl_lineFeature = function (arg) {
|
|
|
147
146
|
strokeOffsetFunc = m_this.style.get('strokeOffset'), strokeOffsetVal,
|
|
148
147
|
miterLimit = m_this.style.get('miterLimit')(data),
|
|
149
148
|
antialiasing = m_this.style.get('antialiasing')(data) || 0,
|
|
149
|
+
uniformFunc = m_this.style.get('uniformLine'), uniformVal, uniform,
|
|
150
150
|
order = m_this.featureVertices(), orderk0, prevkey, nextkey, offkey,
|
|
151
151
|
orderLen = order.length,
|
|
152
152
|
// webgl buffers; see _init for details
|
|
@@ -165,6 +165,7 @@ var webgl_lineFeature = function (arg) {
|
|
|
165
165
|
strokeOffsetVal = util.isFunction(m_this.style('strokeOffset')) ? undefined : (strokeOffsetFunc() || 0);
|
|
166
166
|
strokeOpacityVal = util.isFunction(m_this.style('strokeOpacity')) ? undefined : strokeOpacityFunc();
|
|
167
167
|
strokeWidthVal = util.isFunction(m_this.style('strokeWidth')) ? undefined : strokeWidthFunc();
|
|
168
|
+
uniformVal = util.isFunction(m_this.style('uniformLine')) ? undefined : uniformFunc();
|
|
168
169
|
|
|
169
170
|
if (miterLimit !== undefined) {
|
|
170
171
|
/* We impose a limit no matter what, since otherwise the growth is
|
|
@@ -192,7 +193,7 @@ var webgl_lineFeature = function (arg) {
|
|
|
192
193
|
for (j = 0; j < lineItem.length; j += 1) {
|
|
193
194
|
pos = posFunc ? posFunc(lineItem[j], j, d, i) : lineItem[j];
|
|
194
195
|
position.push(pos.x);
|
|
195
|
-
position.push(
|
|
196
|
+
position.push(pos.y);
|
|
196
197
|
position.push(pos.z || 0.0);
|
|
197
198
|
if (!j) {
|
|
198
199
|
firstpos = pos;
|
|
@@ -212,9 +213,7 @@ var webgl_lineFeature = function (arg) {
|
|
|
212
213
|
}
|
|
213
214
|
}
|
|
214
215
|
|
|
215
|
-
|
|
216
|
-
position = transform.transformCoordinates(target_gcs, map_gcs, position, 3);
|
|
217
|
-
}
|
|
216
|
+
position = transform.transformCoordinates(target_gcs, map_gcs, position, 3);
|
|
218
217
|
m_origin = new Float32Array(m_this.style.get('origin')(position));
|
|
219
218
|
if (m_origin[0] || m_origin[1] || m_origin[2]) {
|
|
220
219
|
for (i = 0; i < position.length; i += 3) {
|
|
@@ -265,6 +264,7 @@ var webgl_lineFeature = function (arg) {
|
|
|
265
264
|
if (lineItem.length < 2) {
|
|
266
265
|
continue;
|
|
267
266
|
}
|
|
267
|
+
uniform = uniformVal === undefined ? uniformFunc(lineItem, i) : uniformVal;
|
|
268
268
|
d = data[i];
|
|
269
269
|
closedVal = closed[i];
|
|
270
270
|
firstPosIdx3 = posIdx3;
|
|
@@ -291,12 +291,26 @@ var webgl_lineFeature = function (arg) {
|
|
|
291
291
|
(j !== lidx ? firstPosIdx3 + 3 : firstPosIdx3 + 6 - closedVal * 3) :
|
|
292
292
|
posIdx3);
|
|
293
293
|
}
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
294
|
+
if (uniform && j > 0) {
|
|
295
|
+
if (j === 1) {
|
|
296
|
+
v.strokeWidth = vert[0].strokeWidth;
|
|
297
|
+
v.strokeColor = vert[0].strokeColor;
|
|
298
|
+
v.strokeOpacity = vert[0].strokeOpacity;
|
|
299
|
+
}
|
|
300
|
+
} else {
|
|
301
|
+
v.strokeWidth = strokeWidthVal === undefined ? strokeWidthFunc(lineItemData, lidx, d, i) : strokeWidthVal;
|
|
302
|
+
v.strokeColor = strokeColorVal === undefined ? strokeColorFunc(lineItemData, lidx, d, i) : strokeColorVal;
|
|
303
|
+
v.strokeOpacity = strokeOpacityVal === undefined ? strokeOpacityFunc(lineItemData, lidx, d, i) : strokeOpacityVal;
|
|
304
|
+
}
|
|
297
305
|
if (updateFlags) {
|
|
298
306
|
if (strokeOffsetVal !== 0) {
|
|
299
|
-
|
|
307
|
+
if (uniform && j > 0) {
|
|
308
|
+
if (j === 1) {
|
|
309
|
+
v.strokeOffset = vert[0].strokeOffset;
|
|
310
|
+
}
|
|
311
|
+
} else {
|
|
312
|
+
v.strokeOffset = (strokeOffsetVal === undefined ? strokeOffsetFunc(lineItemData, lidx, d, i) : strokeOffsetVal) || 0;
|
|
313
|
+
}
|
|
300
314
|
if (v.strokeOffset) {
|
|
301
315
|
/* we use 11 bits to store the offset, and we want to store values
|
|
302
316
|
* from -1 to 1, so multiply our values by 1023, and use some bit
|
|
@@ -77,12 +77,11 @@ var webgl_polygonFeature = function (arg) {
|
|
|
77
77
|
fillColor, fillColorFunc, fillColorVal,
|
|
78
78
|
fillOpacity, fillOpacityFunc, fillOpacityVal,
|
|
79
79
|
fillFunc, fillVal,
|
|
80
|
-
|
|
80
|
+
uniformFunc, uniformVal, uniform,
|
|
81
81
|
indices,
|
|
82
82
|
items = [],
|
|
83
83
|
target_gcs = m_this.gcs(),
|
|
84
84
|
map_gcs = m_this.layer().map().gcs(),
|
|
85
|
-
simpleInverse = transform.onlyInvertedY(target_gcs, map_gcs),
|
|
86
85
|
numPts = 0,
|
|
87
86
|
geom = m_mapper.geometryData(),
|
|
88
87
|
color, opacity, fill, d, d3, vertices, i, j, k, n,
|
|
@@ -94,7 +93,8 @@ var webgl_polygonFeature = function (arg) {
|
|
|
94
93
|
fillOpacityVal = util.isFunction(m_this.style('fillOpacity')) ? undefined : fillOpacityFunc();
|
|
95
94
|
fillFunc = m_this.style.get('fill');
|
|
96
95
|
fillVal = util.isFunction(m_this.style('fill')) ? undefined : fillFunc();
|
|
97
|
-
|
|
96
|
+
uniformFunc = m_this.style.get('uniformPolygon');
|
|
97
|
+
uniformVal = util.isFunction(m_this.style('uniformPolygon')) ? undefined : uniformFunc();
|
|
98
98
|
|
|
99
99
|
if (!onlyStyle) {
|
|
100
100
|
posFunc = m_this.style.get('position');
|
|
@@ -120,7 +120,7 @@ var webgl_polygonFeature = function (arg) {
|
|
|
120
120
|
for (i = d3 = 0; i < outer.length; i += 1, d3 += 3) {
|
|
121
121
|
c = posFunc ? posFunc(outer[i], i, item, itemIndex) : outer[i];
|
|
122
122
|
geometry[d3] = c.x;
|
|
123
|
-
geometry[d3 + 1] =
|
|
123
|
+
geometry[d3 + 1] = c.y;
|
|
124
124
|
// ignore the z values until we support them
|
|
125
125
|
geometry[d3 + 2] = 0; // c.z || 0;
|
|
126
126
|
}
|
|
@@ -137,7 +137,7 @@ var webgl_polygonFeature = function (arg) {
|
|
|
137
137
|
for (i = 0; i < hole.length; i += 1, d3 += 3) {
|
|
138
138
|
c = posFunc ? posFunc(hole[i], i, item, itemIndex) : hole[i];
|
|
139
139
|
geometry.vertices[d3] = c.x;
|
|
140
|
-
geometry.vertices[d3 + 1] =
|
|
140
|
+
geometry.vertices[d3 + 1] = c.y;
|
|
141
141
|
// ignore the z values until we support them
|
|
142
142
|
geometry.vertices[d3 + 2] = 0; // c.z || 0;
|
|
143
143
|
}
|
|
@@ -145,14 +145,12 @@ var webgl_polygonFeature = function (arg) {
|
|
|
145
145
|
}
|
|
146
146
|
|
|
147
147
|
// transform to map gcs
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
);
|
|
155
|
-
}
|
|
148
|
+
geometry.vertices = transform.transformCoordinates(
|
|
149
|
+
target_gcs,
|
|
150
|
+
map_gcs,
|
|
151
|
+
geometry.vertices,
|
|
152
|
+
geometry.dimensions
|
|
153
|
+
);
|
|
156
154
|
|
|
157
155
|
record = {
|
|
158
156
|
// triangulate
|
|
@@ -191,7 +189,7 @@ var webgl_polygonFeature = function (arg) {
|
|
|
191
189
|
item = items[k].item;
|
|
192
190
|
itemIndex = items[k].itemIndex;
|
|
193
191
|
original = items[k].original;
|
|
194
|
-
uniform =
|
|
192
|
+
uniform = uniformVal === undefined ? uniformFunc(item, itemIndex) : uniformVal;
|
|
195
193
|
opacity = fillOpacityVal;
|
|
196
194
|
if (uniform) {
|
|
197
195
|
if (fillColorVal === undefined) {
|