@mapwhit/tilerenderer 1.1.0 → 1.2.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/build/min/package.json +1 -1
- package/package.json +5 -5
- package/src/data/bucket/circle_bucket.js +3 -8
- package/src/data/bucket/fill_bucket.js +4 -17
- package/src/data/bucket/fill_extrusion_bucket.js +4 -15
- package/src/data/bucket/line_bucket.js +4 -17
- package/src/data/bucket/pattern_bucket_features.js +4 -4
- package/src/data/bucket/symbol_bucket.js +4 -8
- package/src/data/feature_index.js +1 -1
- package/src/source/geojson_source.js +4 -3
- package/src/source/geojson_worker_source.js +26 -10
- package/src/source/load_tilejson.js +18 -6
- package/src/source/raster_tile_source.js +4 -5
- package/src/source/source_cache.js +9 -2
- package/src/source/tile.js +3 -0
- package/src/source/vector_tile_source.js +4 -6
- package/src/source/vector_tile_worker_source.js +6 -26
- package/src/source/worker_tile.js +67 -53
- package/src/style/create_style_layer.js +2 -2
- package/src/style/evaluation_parameters.js +0 -2
- package/src/style/properties.js +24 -15
- package/src/style/style.js +25 -10
- package/src/style/style_layer/background_style_layer.js +2 -2
- package/src/style/style_layer/circle_style_layer.js +3 -3
- package/src/style/style_layer/fill_extrusion_style_layer.js +3 -3
- package/src/style/style_layer/fill_style_layer.js +3 -3
- package/src/style/style_layer/heatmap_style_layer.js +3 -3
- package/src/style/style_layer/hillshade_style_layer.js +3 -3
- package/src/style/style_layer/line_style_layer.js +3 -3
- package/src/style/style_layer/raster_style_layer.js +2 -2
- package/src/style/style_layer/symbol_style_layer.js +2 -2
- package/src/style/style_layer.js +28 -7
- package/src/style-spec/feature_filter/index.js +7 -5
- package/src/style-spec/reference/v8.json +68 -9
- package/src/style-spec/visibility.js +37 -0
- package/src/symbol/collision_index.js +2 -2
- package/src/symbol/symbol_layout.js +1 -1
- package/src/ui/map.js +11 -4
- package/src/util/classify_rings.js +1 -1
- package/src/util/group_layers.js +1 -1
- package/src/util/object.js +0 -45
- package/src/util/util.js +0 -55
- package/src/util/async.js +0 -29
- package/src/util/find_pole_of_inaccessibility.js +0 -144
- package/src/util/intersection_tests.js +0 -245
|
@@ -63,7 +63,7 @@ const filterSpec = {
|
|
|
63
63
|
* @param {Array} filter mapbox gl filter
|
|
64
64
|
* @returns {Function} filter-evaluating function
|
|
65
65
|
*/
|
|
66
|
-
export default function createFilter(filter) {
|
|
66
|
+
export default function createFilter(filter, globalState) {
|
|
67
67
|
if (filter === null || filter === undefined) {
|
|
68
68
|
return addGlobalStateRefs(() => true);
|
|
69
69
|
}
|
|
@@ -72,13 +72,15 @@ export default function createFilter(filter) {
|
|
|
72
72
|
filter = convertFilter(filter);
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
-
const compiled = createExpression(filter, filterSpec);
|
|
75
|
+
const compiled = createExpression(filter, filterSpec, globalState);
|
|
76
76
|
if (compiled.result === 'error') {
|
|
77
77
|
throw new Error(compiled.value.map(err => `${err.key}: ${err.message}`).join(', '));
|
|
78
78
|
}
|
|
79
|
-
return
|
|
80
|
-
(
|
|
81
|
-
|
|
79
|
+
return Object.assign(
|
|
80
|
+
addGlobalStateRefs(
|
|
81
|
+
(globalProperties, feature) => compiled.value.evaluate(globalProperties, feature),
|
|
82
|
+
() => findGlobalStateRefs(compiled.value.expression)
|
|
83
|
+
)
|
|
82
84
|
);
|
|
83
85
|
}
|
|
84
86
|
|
|
@@ -393,6 +393,11 @@
|
|
|
393
393
|
"type": "boolean",
|
|
394
394
|
"default": false,
|
|
395
395
|
"doc": "Whether to calculate line distance metrics. This is required for line layers that specify `line-gradient` values."
|
|
396
|
+
},
|
|
397
|
+
"generateId": {
|
|
398
|
+
"type": "boolean",
|
|
399
|
+
"default": false,
|
|
400
|
+
"doc": "Whether to generate ids for the geojson features. When enabled, the `feature.id` property will be auto assigned based on it's index in the `features` array, over-writing any previous values."
|
|
396
401
|
}
|
|
397
402
|
},
|
|
398
403
|
"source_video": {
|
|
@@ -635,7 +640,13 @@
|
|
|
635
640
|
"macos": "0.1.0"
|
|
636
641
|
}
|
|
637
642
|
},
|
|
638
|
-
"
|
|
643
|
+
"expression": {
|
|
644
|
+
"interpolated": false,
|
|
645
|
+
"parameters": [
|
|
646
|
+
"global-state"
|
|
647
|
+
]
|
|
648
|
+
},
|
|
649
|
+
"property-type": "data-constant"
|
|
639
650
|
}
|
|
640
651
|
},
|
|
641
652
|
"layout_fill": {
|
|
@@ -659,7 +670,13 @@
|
|
|
659
670
|
"macos": "0.1.0"
|
|
660
671
|
}
|
|
661
672
|
},
|
|
662
|
-
"
|
|
673
|
+
"expression": {
|
|
674
|
+
"interpolated": false,
|
|
675
|
+
"parameters": [
|
|
676
|
+
"global-state"
|
|
677
|
+
]
|
|
678
|
+
},
|
|
679
|
+
"property-type": "data-constant"
|
|
663
680
|
}
|
|
664
681
|
},
|
|
665
682
|
"layout_circle": {
|
|
@@ -683,7 +700,13 @@
|
|
|
683
700
|
"macos": "0.1.0"
|
|
684
701
|
}
|
|
685
702
|
},
|
|
686
|
-
"
|
|
703
|
+
"expression": {
|
|
704
|
+
"interpolated": false,
|
|
705
|
+
"parameters": [
|
|
706
|
+
"global-state"
|
|
707
|
+
]
|
|
708
|
+
},
|
|
709
|
+
"property-type": "data-constant"
|
|
687
710
|
}
|
|
688
711
|
},
|
|
689
712
|
"layout_heatmap": {
|
|
@@ -707,7 +730,13 @@
|
|
|
707
730
|
"macos": "0.7.0"
|
|
708
731
|
}
|
|
709
732
|
},
|
|
710
|
-
"
|
|
733
|
+
"expression": {
|
|
734
|
+
"interpolated": false,
|
|
735
|
+
"parameters": [
|
|
736
|
+
"global-state"
|
|
737
|
+
]
|
|
738
|
+
},
|
|
739
|
+
"property-type": "data-constant"
|
|
711
740
|
}
|
|
712
741
|
},
|
|
713
742
|
"layout_fill-extrusion": {
|
|
@@ -731,7 +760,13 @@
|
|
|
731
760
|
"macos": "0.5.0"
|
|
732
761
|
}
|
|
733
762
|
},
|
|
734
|
-
"
|
|
763
|
+
"expression": {
|
|
764
|
+
"interpolated": false,
|
|
765
|
+
"parameters": [
|
|
766
|
+
"global-state"
|
|
767
|
+
]
|
|
768
|
+
},
|
|
769
|
+
"property-type": "data-constant"
|
|
735
770
|
}
|
|
736
771
|
},
|
|
737
772
|
"layout_line": {
|
|
@@ -878,7 +913,13 @@
|
|
|
878
913
|
},
|
|
879
914
|
"data-driven styling": {}
|
|
880
915
|
},
|
|
881
|
-
"
|
|
916
|
+
"expression": {
|
|
917
|
+
"interpolated": false,
|
|
918
|
+
"parameters": [
|
|
919
|
+
"global-state"
|
|
920
|
+
]
|
|
921
|
+
},
|
|
922
|
+
"property-type": "data-constant"
|
|
882
923
|
}
|
|
883
924
|
},
|
|
884
925
|
"layout_symbol": {
|
|
@@ -2121,7 +2162,13 @@
|
|
|
2121
2162
|
},
|
|
2122
2163
|
"data-driven styling": {}
|
|
2123
2164
|
},
|
|
2124
|
-
"
|
|
2165
|
+
"expression": {
|
|
2166
|
+
"interpolated": false,
|
|
2167
|
+
"parameters": [
|
|
2168
|
+
"global-state"
|
|
2169
|
+
]
|
|
2170
|
+
},
|
|
2171
|
+
"property-type": "data-constant"
|
|
2125
2172
|
}
|
|
2126
2173
|
},
|
|
2127
2174
|
"layout_raster": {
|
|
@@ -2146,7 +2193,13 @@
|
|
|
2146
2193
|
},
|
|
2147
2194
|
"data-driven styling": {}
|
|
2148
2195
|
},
|
|
2149
|
-
"
|
|
2196
|
+
"expression": {
|
|
2197
|
+
"interpolated": false,
|
|
2198
|
+
"parameters": [
|
|
2199
|
+
"global-state"
|
|
2200
|
+
]
|
|
2201
|
+
},
|
|
2202
|
+
"property-type": "data-constant"
|
|
2150
2203
|
}
|
|
2151
2204
|
},
|
|
2152
2205
|
"layout_hillshade": {
|
|
@@ -2171,7 +2224,13 @@
|
|
|
2171
2224
|
},
|
|
2172
2225
|
"data-driven styling": {}
|
|
2173
2226
|
},
|
|
2174
|
-
"
|
|
2227
|
+
"expression": {
|
|
2228
|
+
"interpolated": false,
|
|
2229
|
+
"parameters": [
|
|
2230
|
+
"global-state"
|
|
2231
|
+
]
|
|
2232
|
+
},
|
|
2233
|
+
"property-type": "data-constant"
|
|
2175
2234
|
}
|
|
2176
2235
|
},
|
|
2177
2236
|
"filter": {
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { createExpression, findGlobalStateRefs } from '@mapwhit/style-expressions';
|
|
2
|
+
|
|
3
|
+
const visibilitySpec = {
|
|
4
|
+
type: 'enum',
|
|
5
|
+
values: ['visible', 'none'],
|
|
6
|
+
default: 'visible',
|
|
7
|
+
expression: {
|
|
8
|
+
parameters: ['global-state']
|
|
9
|
+
}
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export default function createVisibility(visibility, globalState) {
|
|
13
|
+
const expression = {
|
|
14
|
+
setValue
|
|
15
|
+
};
|
|
16
|
+
setValue(visibility);
|
|
17
|
+
return expression;
|
|
18
|
+
|
|
19
|
+
function setValue(visibility) {
|
|
20
|
+
if (visibility === null || visibility === undefined || visibility === 'visible' || visibility === 'none') {
|
|
21
|
+
expression.evaluate = visibility === 'none' ? () => 'none' : () => 'visible';
|
|
22
|
+
addGlobalStateRefs(expression);
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const compiled = createExpression(visibility, visibilitySpec, globalState);
|
|
27
|
+
if (compiled.result === 'error') {
|
|
28
|
+
throw new Error(compiled.value.map(err => `${err.key}: ${err.message}`).join(', '));
|
|
29
|
+
}
|
|
30
|
+
expression.evaluate = () => compiled.value.evaluate({});
|
|
31
|
+
addGlobalStateRefs(expression, () => findGlobalStateRefs(compiled.value.expression));
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function addGlobalStateRefs(visibility, getGlobalStateRefs = () => new Set()) {
|
|
36
|
+
visibility.getGlobalStateRefs = getGlobalStateRefs;
|
|
37
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import Point from '@mapbox/point-geometry';
|
|
2
|
+
import { polygonIntersectsPolygon } from '@mapwhit/geometry';
|
|
2
3
|
import * as projection from '../symbol/projection.js';
|
|
3
|
-
import * as intersectionTests from '../util/intersection_tests.js';
|
|
4
4
|
import Grid from './grid_index.js';
|
|
5
5
|
|
|
6
6
|
// When a symbol crosses the edge that causes it to be included in
|
|
@@ -293,7 +293,7 @@ class CollisionIndex {
|
|
|
293
293
|
new Point(feature.x2, feature.y2),
|
|
294
294
|
new Point(feature.x1, feature.y2)
|
|
295
295
|
];
|
|
296
|
-
if (!
|
|
296
|
+
if (!polygonIntersectsPolygon(query, bbox)) {
|
|
297
297
|
continue;
|
|
298
298
|
}
|
|
299
299
|
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
+
import { findPoleOfInaccessibility } from '@mapwhit/geometry';
|
|
1
2
|
import murmur3 from 'murmurhash-js';
|
|
2
3
|
import SymbolBucket from '../data/bucket/symbol_bucket.js';
|
|
3
4
|
import EXTENT from '../data/extent.js';
|
|
4
5
|
import EvaluationParameters from '../style/evaluation_parameters.js';
|
|
5
6
|
import classifyRings from '../util/classify_rings.js';
|
|
6
|
-
import findPoleOfInaccessibility from '../util/find_pole_of_inaccessibility.js';
|
|
7
7
|
import { allowsLetterSpacing, allowsVerticalWritingMode } from '../util/script_detection.js';
|
|
8
8
|
import warn from '../util/warn.js';
|
|
9
9
|
import Anchor from './anchor.js';
|
package/src/ui/map.js
CHANGED
|
@@ -696,7 +696,7 @@ class Map extends Camera {
|
|
|
696
696
|
if (!this.style) {
|
|
697
697
|
return warn.once('There is no style added to the map.');
|
|
698
698
|
}
|
|
699
|
-
return this.style.loaded();
|
|
699
|
+
return this.style.loaded(true);
|
|
700
700
|
}
|
|
701
701
|
|
|
702
702
|
/**
|
|
@@ -1069,11 +1069,17 @@ class Map extends Camera {
|
|
|
1069
1069
|
*
|
|
1070
1070
|
* @param {Object} [feature] Feature identifier. Feature objects returned from
|
|
1071
1071
|
* {@link Map#queryRenderedFeatures} or event handlers can be used as feature identifiers.
|
|
1072
|
+
* @param {string} [feature.id] Unique id of the feature.
|
|
1072
1073
|
* @param {string} [feature.source] The Id of the vector source or GeoJSON source for the feature.
|
|
1073
1074
|
* @param {string} [feature.sourceLayer] (optional) *For vector tile sources, the sourceLayer is
|
|
1074
1075
|
* required.*
|
|
1075
|
-
* @param {string} [feature.id] Unique id of the feature.
|
|
1076
1076
|
* @param {Object} state A set of key-value pairs. The values should be valid JSON types.
|
|
1077
|
+
*
|
|
1078
|
+
* This method requires the `feature.id` attribute on data sets. For GeoJSON sources without
|
|
1079
|
+
* feature ids, set the `generateIds` option in the `GeoJSONSourceSpecification` to auto-assign them. This
|
|
1080
|
+
* option assigns ids based on a feature's index in the source data. Changing the feature data using
|
|
1081
|
+
* `map.getSource('some id').setData(..)` resets the cache of feature states and requires the
|
|
1082
|
+
* caller re-apply the state as needed with the updated `id` values.
|
|
1077
1083
|
*/
|
|
1078
1084
|
setFeatureState(feature, state) {
|
|
1079
1085
|
this.style.setFeatureState(feature, state);
|
|
@@ -1320,8 +1326,7 @@ class Map extends Camera {
|
|
|
1320
1326
|
now,
|
|
1321
1327
|
fadeDuration: this._fadeDuration,
|
|
1322
1328
|
zoomHistory: this.style.zoomHistory,
|
|
1323
|
-
transition: this.style.getTransition()
|
|
1324
|
-
globalState: this.style.getGlobalState()
|
|
1329
|
+
transition: this.style.getTransition()
|
|
1325
1330
|
});
|
|
1326
1331
|
|
|
1327
1332
|
const factor = parameters.crossFadingFactor();
|
|
@@ -1383,6 +1388,8 @@ class Map extends Camera {
|
|
|
1383
1388
|
// Style#_updateSources could have caused them to be set again.
|
|
1384
1389
|
if (this._sourcesDirty || this._repaint || this._styleDirty || this._placementDirty) {
|
|
1385
1390
|
this._rerender();
|
|
1391
|
+
} else if (!this.isMoving() && this.loaded()) {
|
|
1392
|
+
this.fire(new Event('idle'));
|
|
1386
1393
|
}
|
|
1387
1394
|
|
|
1388
1395
|
return this;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
+
import { calculateSignedArea } from '@mapwhit/geometry';
|
|
1
2
|
import quickselect from 'quickselect';
|
|
2
|
-
import { calculateSignedArea } from './util.js';
|
|
3
3
|
|
|
4
4
|
// classifies an array of rings into polygons with outer rings and holes
|
|
5
5
|
export default function classifyRings(rings, maxRings) {
|
package/src/util/group_layers.js
CHANGED
package/src/util/object.js
CHANGED
|
@@ -1,22 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Given an object and a number of properties as strings, return version
|
|
3
|
-
* of that object with only those properties.
|
|
4
|
-
*
|
|
5
|
-
* @param src the object
|
|
6
|
-
* @param properties an array of property names chosen
|
|
7
|
-
* to appear on the resulting object.
|
|
8
|
-
* @returns object with limited properties.
|
|
9
|
-
* @example
|
|
10
|
-
* var foo = { name: 'Charlie', age: 10 };
|
|
11
|
-
* var justName = pick(foo, ['name']);
|
|
12
|
-
* // justName = { name: 'Charlie' }
|
|
13
|
-
* @private
|
|
14
|
-
*/
|
|
15
|
-
export function pick(src, properties) {
|
|
16
|
-
const entries = properties.filter(p => p in src).map(p => [p, src[p]]);
|
|
17
|
-
return Object.fromEntries(entries);
|
|
18
|
-
}
|
|
19
|
-
|
|
20
1
|
/**
|
|
21
2
|
* Given an array of member function names as strings, replace all of them
|
|
22
3
|
* with bound versions that will always refer to `context` as `this`. This
|
|
@@ -59,17 +40,6 @@ export function mapObject(input, iterator, context) {
|
|
|
59
40
|
return Object.fromEntries(entries);
|
|
60
41
|
}
|
|
61
42
|
|
|
62
|
-
/**
|
|
63
|
-
* Create an object by filtering out values of an existing object.
|
|
64
|
-
*
|
|
65
|
-
* @private
|
|
66
|
-
*/
|
|
67
|
-
export function filterObject(input, iterator, context) {
|
|
68
|
-
context ??= this;
|
|
69
|
-
const entries = Object.entries(input).filter(([k, v]) => iterator.call(context, v, k, input));
|
|
70
|
-
return Object.fromEntries(entries);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
43
|
/**
|
|
74
44
|
* Deeply compares two object literals.
|
|
75
45
|
*
|
|
@@ -105,21 +75,6 @@ export function deepEqual(a, b) {
|
|
|
105
75
|
return a === b;
|
|
106
76
|
}
|
|
107
77
|
|
|
108
|
-
/**
|
|
109
|
-
* Deeply clones two objects.
|
|
110
|
-
*
|
|
111
|
-
* @private
|
|
112
|
-
*/
|
|
113
|
-
export function clone(input) {
|
|
114
|
-
if (Array.isArray(input)) {
|
|
115
|
-
return input.map(clone);
|
|
116
|
-
}
|
|
117
|
-
if (typeof input === 'object' && input) {
|
|
118
|
-
return mapObject(input, clone);
|
|
119
|
-
}
|
|
120
|
-
return input;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
78
|
/**
|
|
124
79
|
* Check if two arrays have at least one common element.
|
|
125
80
|
*
|
package/src/util/util.js
CHANGED
|
@@ -104,61 +104,6 @@ export function getCoordinatesCenter(coords) {
|
|
|
104
104
|
return new Coordinate((minX + maxX) / 2, (minY + maxY) / 2, 0).zoomTo(zoom);
|
|
105
105
|
}
|
|
106
106
|
|
|
107
|
-
/**
|
|
108
|
-
* Indicates if the provided Points are in a counter clockwise (true) or clockwise (false) order
|
|
109
|
-
*
|
|
110
|
-
* @private
|
|
111
|
-
* @returns true for a counter clockwise set of points
|
|
112
|
-
*/
|
|
113
|
-
// http://bryceboe.com/2006/10/23/line-segment-intersection-algorithm/
|
|
114
|
-
export function isCounterClockwise(a, b, c) {
|
|
115
|
-
return (c.y - a.y) * (b.x - a.x) > (b.y - a.y) * (c.x - a.x);
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
/**
|
|
119
|
-
* Returns the signed area for the polygon ring. Postive areas are exterior rings and
|
|
120
|
-
* have a clockwise winding. Negative areas are interior rings and have a counter clockwise
|
|
121
|
-
* ordering.
|
|
122
|
-
*
|
|
123
|
-
* @private
|
|
124
|
-
* @param ring Exterior or interior ring
|
|
125
|
-
*/
|
|
126
|
-
export function calculateSignedArea(ring) {
|
|
127
|
-
let sum = 0;
|
|
128
|
-
const len = ring.length;
|
|
129
|
-
let p2 = ring[len - 1]; // last point
|
|
130
|
-
for (const p1 of ring) {
|
|
131
|
-
sum += (p2.x - p1.x) * (p1.y + p2.y);
|
|
132
|
-
p2 = p1;
|
|
133
|
-
}
|
|
134
|
-
return sum;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
/**
|
|
138
|
-
* Detects closed polygons, first + last point are equal
|
|
139
|
-
*
|
|
140
|
-
* @private
|
|
141
|
-
* @param points array of points
|
|
142
|
-
* @return true if the points are a closed polygon
|
|
143
|
-
*/
|
|
144
|
-
export function isClosedPolygon(points) {
|
|
145
|
-
// If it is 2 points that are the same then it is a point
|
|
146
|
-
// If it is 3 points with start and end the same then it is a line
|
|
147
|
-
if (points.length < 4) {
|
|
148
|
-
return false;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
const p1 = points[0];
|
|
152
|
-
const p2 = points[points.length - 1];
|
|
153
|
-
|
|
154
|
-
if (Math.abs(p1.x - p2.x) > 0 || Math.abs(p1.y - p2.y) > 0) {
|
|
155
|
-
return false;
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
// polygon simplification can produce polygons with zero area and more than 3 points
|
|
159
|
-
return Math.abs(calculateSignedArea(points)) > 0.01;
|
|
160
|
-
}
|
|
161
|
-
|
|
162
107
|
/**
|
|
163
108
|
* Converts spherical coordinates to cartesian coordinates.
|
|
164
109
|
*
|
package/src/util/async.js
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* Call an asynchronous function on an array of arguments,
|
|
3
|
-
* calling `callback` with the completed results of all calls.
|
|
4
|
-
*
|
|
5
|
-
* @param array input to each call of the async function.
|
|
6
|
-
* @param fn an async function with signature (data, callback)
|
|
7
|
-
* @param callback a callback run after all async work is done.
|
|
8
|
-
* called with an array, containing the results of each async call.
|
|
9
|
-
* @private
|
|
10
|
-
*/
|
|
11
|
-
export function all(array, fn, callback) {
|
|
12
|
-
if (!array.length) {
|
|
13
|
-
return callback(null, []);
|
|
14
|
-
}
|
|
15
|
-
let remaining = array.length;
|
|
16
|
-
const results = new Array(array.length);
|
|
17
|
-
let error = null;
|
|
18
|
-
array.forEach((item, i) => {
|
|
19
|
-
fn(item, (err, result) => {
|
|
20
|
-
if (err) {
|
|
21
|
-
error = err;
|
|
22
|
-
}
|
|
23
|
-
results[i] = result;
|
|
24
|
-
if (--remaining === 0) {
|
|
25
|
-
callback(error, results);
|
|
26
|
-
}
|
|
27
|
-
});
|
|
28
|
-
});
|
|
29
|
-
}
|
|
@@ -1,144 +0,0 @@
|
|
|
1
|
-
import Point from '@mapbox/point-geometry';
|
|
2
|
-
import Queue from 'tinyqueue';
|
|
3
|
-
import { distToSegmentSquared } from './intersection_tests.js';
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Finds an approximation of a polygon's Pole Of Inaccessibiliy https://en.wikipedia.org/wiki/Pole_of_inaccessibility
|
|
7
|
-
* This is a copy of http://github.com/mapbox/polylabel adapted to use Points
|
|
8
|
-
*
|
|
9
|
-
* @param polygonRings first item in array is the outer ring followed optionally by the list of holes, should be an element of the result of util/classify_rings
|
|
10
|
-
* @param precision Specified in input coordinate units. If 0 returns after first run, if > 0 repeatedly narrows the search space until the radius of the area searched for the best pole is less than precision
|
|
11
|
-
* @param debug Print some statistics to the console during execution
|
|
12
|
-
* @returns Pole of Inaccessibiliy.
|
|
13
|
-
* @private
|
|
14
|
-
*/
|
|
15
|
-
export default function (polygonRings, precision = 1, debug = false) {
|
|
16
|
-
// find the bounding box of the outer ring
|
|
17
|
-
let minX = Number.POSITIVE_INFINITY;
|
|
18
|
-
let minY = Number.POSITIVE_INFINITY;
|
|
19
|
-
let maxX = Number.NEGATIVE_INFINITY;
|
|
20
|
-
let maxY = Number.NEGATIVE_INFINITY;
|
|
21
|
-
const outerRing = polygonRings[0];
|
|
22
|
-
for (let i = 0; i < outerRing.length; i++) {
|
|
23
|
-
const p = outerRing[i];
|
|
24
|
-
if (!i || p.x < minX) {
|
|
25
|
-
minX = p.x;
|
|
26
|
-
}
|
|
27
|
-
if (!i || p.y < minY) {
|
|
28
|
-
minY = p.y;
|
|
29
|
-
}
|
|
30
|
-
if (!i || p.x > maxX) {
|
|
31
|
-
maxX = p.x;
|
|
32
|
-
}
|
|
33
|
-
if (!i || p.y > maxY) {
|
|
34
|
-
maxY = p.y;
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
const width = maxX - minX;
|
|
39
|
-
const height = maxY - minY;
|
|
40
|
-
const cellSize = Math.min(width, height);
|
|
41
|
-
let h = cellSize / 2;
|
|
42
|
-
|
|
43
|
-
// a priority queue of cells in order of their "potential" (max distance to polygon)
|
|
44
|
-
const cellQueue = new Queue(undefined, compareMax);
|
|
45
|
-
|
|
46
|
-
if (cellSize === 0) {
|
|
47
|
-
return new Point(minX, minY);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
// cover polygon with initial cells
|
|
51
|
-
for (let x = minX; x < maxX; x += cellSize) {
|
|
52
|
-
for (let y = minY; y < maxY; y += cellSize) {
|
|
53
|
-
cellQueue.push(new Cell(x + h, y + h, h, polygonRings));
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
// take centroid as the first best guess
|
|
58
|
-
let bestCell = getCentroidCell(polygonRings);
|
|
59
|
-
let numProbes = cellQueue.length;
|
|
60
|
-
|
|
61
|
-
while (cellQueue.length) {
|
|
62
|
-
// pick the most promising cell from the queue
|
|
63
|
-
const cell = cellQueue.pop();
|
|
64
|
-
|
|
65
|
-
// update the best cell if we found a better one
|
|
66
|
-
if (cell.d > bestCell.d || !bestCell.d) {
|
|
67
|
-
bestCell = cell;
|
|
68
|
-
if (debug) {
|
|
69
|
-
console.log('found best %d after %d probes', Math.round(1e4 * cell.d) / 1e4, numProbes);
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
// do not drill down further if there's no chance of a better solution
|
|
74
|
-
if (cell.max - bestCell.d <= precision) {
|
|
75
|
-
continue;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
// split the cell into four cells
|
|
79
|
-
h = cell.h / 2;
|
|
80
|
-
cellQueue.push(new Cell(cell.p.x - h, cell.p.y - h, h, polygonRings));
|
|
81
|
-
cellQueue.push(new Cell(cell.p.x + h, cell.p.y - h, h, polygonRings));
|
|
82
|
-
cellQueue.push(new Cell(cell.p.x - h, cell.p.y + h, h, polygonRings));
|
|
83
|
-
cellQueue.push(new Cell(cell.p.x + h, cell.p.y + h, h, polygonRings));
|
|
84
|
-
numProbes += 4;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
if (debug) {
|
|
88
|
-
console.log(`num probes: ${numProbes}`);
|
|
89
|
-
console.log(`best distance: ${bestCell.d}`);
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
return bestCell.p;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
function compareMax(a, b) {
|
|
96
|
-
return b.max - a.max;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
function Cell(x, y, h, polygon) {
|
|
100
|
-
this.p = new Point(x, y);
|
|
101
|
-
this.h = h; // half the cell size
|
|
102
|
-
this.d = pointToPolygonDist(this.p, polygon); // distance from cell center to polygon
|
|
103
|
-
this.max = this.d + this.h * Math.SQRT2; // max distance to polygon within a cell
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
// signed distance from point to polygon outline (negative if point is outside)
|
|
107
|
-
function pointToPolygonDist(p, polygon) {
|
|
108
|
-
let inside = false;
|
|
109
|
-
let minDistSq = Number.POSITIVE_INFINITY;
|
|
110
|
-
|
|
111
|
-
for (let k = 0; k < polygon.length; k++) {
|
|
112
|
-
const ring = polygon[k];
|
|
113
|
-
|
|
114
|
-
for (let i = 0, len = ring.length, j = len - 1; i < len; j = i++) {
|
|
115
|
-
const a = ring[i];
|
|
116
|
-
const b = ring[j];
|
|
117
|
-
|
|
118
|
-
if (a.y > p.y !== b.y > p.y && p.x < ((b.x - a.x) * (p.y - a.y)) / (b.y - a.y) + a.x) {
|
|
119
|
-
inside = !inside;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
minDistSq = Math.min(minDistSq, distToSegmentSquared(p, a, b));
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
return (inside ? 1 : -1) * Math.sqrt(minDistSq);
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
// get polygon centroid
|
|
130
|
-
function getCentroidCell(polygon) {
|
|
131
|
-
let area = 0;
|
|
132
|
-
let x = 0;
|
|
133
|
-
let y = 0;
|
|
134
|
-
const points = polygon[0];
|
|
135
|
-
for (let i = 0, len = points.length, j = len - 1; i < len; j = i++) {
|
|
136
|
-
const a = points[i];
|
|
137
|
-
const b = points[j];
|
|
138
|
-
const f = a.x * b.y - b.x * a.y;
|
|
139
|
-
x += (a.x + b.x) * f;
|
|
140
|
-
y += (a.y + b.y) * f;
|
|
141
|
-
area += f * 3;
|
|
142
|
-
}
|
|
143
|
-
return new Cell(x / area, y / area, 0, polygon);
|
|
144
|
-
}
|