@mapwhit/tilerenderer 1.3.0 → 1.5.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 +1 -1
- package/src/data/array_types.js +115 -64
- package/src/data/bucket/circle_bucket.js +42 -5
- package/src/data/bucket/fill_bucket.js +31 -13
- package/src/data/bucket/fill_extrusion_bucket.js +8 -6
- package/src/data/bucket/line_bucket.js +38 -14
- package/src/data/bucket/symbol_attributes.js +13 -5
- package/src/data/bucket/symbol_bucket.js +112 -40
- package/src/data/bucket/symbol_collision_buffers.js +1 -1
- package/src/data/bucket.js +3 -1
- package/src/data/feature_index.js +24 -11
- package/src/data/segment.js +15 -7
- package/src/index.js +23 -23
- package/src/render/draw_circle.js +45 -4
- package/src/render/draw_symbol.js +190 -22
- package/src/render/painter.js +1 -1
- package/src/source/geojson_source.js +118 -21
- package/src/source/geojson_source_diff.js +148 -0
- package/src/source/geojson_tiler.js +89 -0
- package/src/source/rtl_text_plugin.js +133 -66
- package/src/source/source.js +16 -5
- package/src/source/source_cache.js +6 -6
- package/src/source/source_state.js +4 -2
- package/src/source/tile.js +5 -3
- package/src/source/vector_tile_source.js +2 -0
- package/src/source/worker_tile.js +6 -2
- package/src/style/evaluation_parameters.js +2 -3
- package/src/style/pauseable_placement.js +39 -7
- package/src/style/style.js +34 -23
- package/src/style/style_layer/circle_style_layer_properties.js +8 -1
- package/src/style/style_layer/fill_style_layer_properties.js +8 -1
- package/src/style/style_layer/line_style_layer_properties.js +4 -0
- package/src/style/style_layer/symbol_style_layer_properties.js +17 -2
- package/src/style-spec/reference/v8.json +161 -4
- package/src/symbol/one_em.js +4 -0
- package/src/symbol/placement.js +406 -173
- package/src/symbol/projection.js +3 -3
- package/src/symbol/quads.js +1 -6
- package/src/symbol/shaping.js +18 -29
- package/src/symbol/symbol_layout.js +243 -81
- package/src/symbol/transform_text.js +3 -4
- package/src/util/config.js +1 -9
- package/src/util/script_detection.js +19 -7
- package/src/util/vectortile_to_geojson.js +3 -4
- package/src/source/geojson_worker_source.js +0 -97
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import rewind from '@mapwhit/geojson-rewind';
|
|
2
|
+
import geojsonvt from 'geojson-vt';
|
|
3
|
+
import Supercluster from 'supercluster';
|
|
4
|
+
import FeatureIndex from '../data/feature_index.js';
|
|
5
|
+
import GeoJSONWrapper from './geojson_wrapper.js';
|
|
6
|
+
import { makeSingleSourceLayerWorkerTile as makeWorkerTile } from './worker_tile.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Creates tiles from GeoJSON data.
|
|
10
|
+
*/
|
|
11
|
+
export default function makeTiler(resources, layerIndex) {
|
|
12
|
+
let geoJSONIndex;
|
|
13
|
+
let createGeoJSONIndex;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Fetches (if appropriate), parses, and index geojson data into tiles. This
|
|
17
|
+
* preparatory method must be called before {@link GeoJSONWorkerSource#loadTile}
|
|
18
|
+
* can correctly serve up tiles.
|
|
19
|
+
*
|
|
20
|
+
* Defers to {@link GeoJSONWorkerSource#loadGeoJSON} for the fetching/parsing,
|
|
21
|
+
* expecting `callback(error, data)` to be called with either an error or a
|
|
22
|
+
* parsed GeoJSON object.
|
|
23
|
+
*
|
|
24
|
+
* @param params
|
|
25
|
+
*/
|
|
26
|
+
function loadData(params) {
|
|
27
|
+
const { cluster, data, geojsonVtOptions, superclusterOptions } = params;
|
|
28
|
+
geoJSONIndex = undefined;
|
|
29
|
+
createGeoJSONIndex = cluster
|
|
30
|
+
? () => {
|
|
31
|
+
rewind(data, true);
|
|
32
|
+
return new Supercluster(superclusterOptions).load(data.features);
|
|
33
|
+
}
|
|
34
|
+
: () => {
|
|
35
|
+
rewind(data, true);
|
|
36
|
+
return geojsonvt(data, geojsonVtOptions);
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function getTile(tileID) {
|
|
41
|
+
if (!geoJSONIndex) {
|
|
42
|
+
if (!createGeoJSONIndex) {
|
|
43
|
+
return; // we couldn't load the file
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
try {
|
|
47
|
+
geoJSONIndex = createGeoJSONIndex();
|
|
48
|
+
} finally {
|
|
49
|
+
createGeoJSONIndex = undefined;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
const { z, x, y } = tileID.canonical;
|
|
53
|
+
return geoJSONIndex.getTile(z, x, y);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Returns a single tile.
|
|
58
|
+
*/
|
|
59
|
+
async function loadTile(params) {
|
|
60
|
+
const { tileID, source, promoteId } = params;
|
|
61
|
+
const geoJSONTile = getTile(tileID);
|
|
62
|
+
if (!geoJSONTile) {
|
|
63
|
+
return; // nothing in the given tile
|
|
64
|
+
}
|
|
65
|
+
const sourceLayer = new GeoJSONWrapper(geoJSONTile.features);
|
|
66
|
+
const layerFamilies = layerIndex.familiesBySource.get(source);
|
|
67
|
+
if (!layerFamilies) {
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
params.featureIndex = new FeatureIndex(tileID, promoteId);
|
|
71
|
+
const { name } = sourceLayer;
|
|
72
|
+
const features = new Array(sourceLayer.length);
|
|
73
|
+
for (let index = 0; index < sourceLayer.length; index++) {
|
|
74
|
+
const feature = sourceLayer.feature(index);
|
|
75
|
+
const id = params.featureIndex.getId(feature, name);
|
|
76
|
+
features[index] = { feature, id, index, sourceLayerIndex: 0 };
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const result = await makeWorkerTile(params, features, layerFamilies.get(name), resources);
|
|
80
|
+
|
|
81
|
+
result.vectorTile = sourceLayer;
|
|
82
|
+
return result;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return {
|
|
86
|
+
loadData,
|
|
87
|
+
loadTile
|
|
88
|
+
};
|
|
89
|
+
}
|
|
@@ -1,82 +1,145 @@
|
|
|
1
|
+
import { Event, Evented } from '@mapwhit/events';
|
|
1
2
|
import dynload from 'dynload';
|
|
2
3
|
import browser from '../util/browser.js';
|
|
3
4
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
/**
|
|
6
|
+
* The possible option of the plugin's status
|
|
7
|
+
*
|
|
8
|
+
* `unavailable`: Not loaded.
|
|
9
|
+
*
|
|
10
|
+
* `deferred`: The plugin URL has been specified, but loading has been deferred.
|
|
11
|
+
*
|
|
12
|
+
* `loading`: request in-flight.
|
|
13
|
+
*
|
|
14
|
+
* `loaded`: The plugin is now loaded
|
|
15
|
+
*
|
|
16
|
+
* `error`: The plugin failed to load
|
|
17
|
+
*/
|
|
7
18
|
|
|
8
|
-
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
clearRTLTextPlugin, // exported for testing
|
|
13
|
-
loadScript, // exported for testing
|
|
14
|
-
registerForPluginAvailability,
|
|
15
|
-
setRTLTextPlugin
|
|
16
|
-
};
|
|
19
|
+
function RTLPlugin() {
|
|
20
|
+
const self = {
|
|
21
|
+
isRTLSupported
|
|
22
|
+
};
|
|
17
23
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
24
|
+
/**
|
|
25
|
+
* Checks whether the RTL language support is available.
|
|
26
|
+
* If `canChangeShortly` is false, it will only return true if the RTL language
|
|
27
|
+
* is properly supported.
|
|
28
|
+
* If `canChangeShortly` is true, it will also return true if the RTL language
|
|
29
|
+
* is not supported unless it can obtain the RTL text plugin.
|
|
30
|
+
* @param {boolean} canChangeShortly
|
|
31
|
+
* @returns
|
|
32
|
+
*/
|
|
33
|
+
function isRTLSupported(canChangeShortly) {
|
|
34
|
+
if (rtlPluginLoader.getRTLTextPluginStatus() === 'loaded') {
|
|
35
|
+
return true;
|
|
36
|
+
}
|
|
37
|
+
if (!canChangeShortly) {
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
rtlPluginLoader.lazyLoad();
|
|
41
|
+
// any transitive state other than 'loading' means we can consider RTL supported as best as possible for now
|
|
42
|
+
return rtlPluginLoader.getRTLTextPluginStatus() !== 'loading';
|
|
22
43
|
}
|
|
23
|
-
_loadedCallbacks.push(callback);
|
|
24
|
-
loadRTLTextPlugin();
|
|
25
|
-
return () => _loadedCallbacks.splice(_loadedCallbacks.indexOf(callback), 1);
|
|
26
|
-
}
|
|
27
44
|
|
|
28
|
-
|
|
29
|
-
_loadedCallbacks.length = 0;
|
|
30
|
-
pluginRequested = false;
|
|
31
|
-
pluginURL = undefined;
|
|
45
|
+
return self;
|
|
32
46
|
}
|
|
33
47
|
|
|
34
|
-
export
|
|
35
|
-
|
|
36
|
-
|
|
48
|
+
export const rtlPlugin = RTLPlugin();
|
|
49
|
+
|
|
50
|
+
function RTLPluginLoader() {
|
|
51
|
+
let status = 'unavailable';
|
|
52
|
+
let url;
|
|
53
|
+
|
|
54
|
+
const self = {
|
|
55
|
+
getRTLTextPluginStatus,
|
|
56
|
+
setRTLTextPlugin,
|
|
57
|
+
lazyLoad,
|
|
58
|
+
_clearRTLTextPlugin,
|
|
59
|
+
_registerRTLTextPlugin
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
/** This one is exposed to outside */
|
|
63
|
+
function getRTLTextPluginStatus() {
|
|
64
|
+
return status;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// public for testing
|
|
68
|
+
function _clearRTLTextPlugin() {
|
|
69
|
+
url = undefined;
|
|
70
|
+
status = 'unavailable';
|
|
71
|
+
_setMethods();
|
|
37
72
|
}
|
|
38
|
-
|
|
39
|
-
pluginURL =
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
73
|
+
|
|
74
|
+
function setRTLTextPlugin(pluginURL, deferred = false) {
|
|
75
|
+
if (url) {
|
|
76
|
+
// error
|
|
77
|
+
return Promise.reject(new Error('setRTLTextPlugin cannot be called multiple times.'));
|
|
78
|
+
}
|
|
79
|
+
url = browser.resolveURL(pluginURL);
|
|
80
|
+
if (!url) {
|
|
81
|
+
return Promise.reject(new Error(`requested url ${pluginURL} is invalid`));
|
|
82
|
+
}
|
|
83
|
+
if (status === 'requested') {
|
|
84
|
+
return _downloadRTLTextPlugin();
|
|
85
|
+
}
|
|
86
|
+
if (status === 'unavailable') {
|
|
87
|
+
// from initial state:
|
|
88
|
+
if (!deferred) {
|
|
89
|
+
return _downloadRTLTextPlugin();
|
|
47
90
|
}
|
|
91
|
+
status = 'deferred';
|
|
48
92
|
}
|
|
49
|
-
|
|
50
|
-
_completionCallback = undefined;
|
|
51
|
-
};
|
|
52
|
-
loadRTLTextPlugin();
|
|
53
|
-
}
|
|
93
|
+
}
|
|
54
94
|
|
|
55
|
-
function
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
95
|
+
async function _downloadRTLTextPlugin() {
|
|
96
|
+
status = 'loading';
|
|
97
|
+
try {
|
|
98
|
+
await rtlPluginLoader._loadScript({ url });
|
|
99
|
+
} catch {
|
|
100
|
+
status = 'error';
|
|
101
|
+
}
|
|
102
|
+
rtlPluginLoader.fire(new Event('RTLPluginLoaded'));
|
|
59
103
|
}
|
|
60
|
-
}
|
|
61
104
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
105
|
+
/** Start a lazy loading process of RTL plugin */
|
|
106
|
+
function lazyLoad() {
|
|
107
|
+
if (status === 'unavailable') {
|
|
108
|
+
status = 'requested';
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
if (status === 'deferred') {
|
|
112
|
+
return _downloadRTLTextPlugin();
|
|
113
|
+
}
|
|
65
114
|
}
|
|
66
|
-
plugin['applyArabicShaping'] = loadedPlugin.applyArabicShaping;
|
|
67
|
-
plugin['processBidirectionalText'] = loadedPlugin.processBidirectionalText;
|
|
68
|
-
plugin['processStyledBidirectionalText'] = loadedPlugin.processStyledBidirectionalText;
|
|
69
115
|
|
|
70
|
-
|
|
71
|
-
|
|
116
|
+
function _setMethods(rtlTextPlugin) {
|
|
117
|
+
if (!rtlTextPlugin) {
|
|
118
|
+
// for testing only
|
|
119
|
+
rtlPlugin.processStyledBidirectionalText = null;
|
|
120
|
+
rtlPlugin.processBidirectionalText = null;
|
|
121
|
+
rtlPlugin.applyArabicShaping = null;
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
rtlPlugin.applyArabicShaping = rtlTextPlugin.applyArabicShaping;
|
|
125
|
+
rtlPlugin.processBidirectionalText = rtlTextPlugin.processBidirectionalText;
|
|
126
|
+
rtlPlugin.processStyledBidirectionalText = rtlTextPlugin.processStyledBidirectionalText;
|
|
72
127
|
}
|
|
73
|
-
_loadedCallbacks.forEach(callback => callback());
|
|
74
|
-
_loadedCallbacks.length = 0;
|
|
75
|
-
}
|
|
76
128
|
|
|
77
|
-
|
|
129
|
+
// This is invoked by the RTL text plugin when the download has finished, and the code has been parsed.
|
|
130
|
+
function _registerRTLTextPlugin(rtlTextPlugin) {
|
|
131
|
+
if (rtlPlugin.isRTLSupported()) {
|
|
132
|
+
throw new Error('RTL text plugin already registered.');
|
|
133
|
+
}
|
|
134
|
+
status = 'loaded';
|
|
135
|
+
_setMethods(rtlTextPlugin);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return self;
|
|
139
|
+
}
|
|
78
140
|
|
|
79
|
-
|
|
141
|
+
// public for testing
|
|
142
|
+
function _loadScript({ url }) {
|
|
80
143
|
const { promise, resolve, reject } = Promise.withResolvers();
|
|
81
144
|
const s = dynload(url);
|
|
82
145
|
s.onload = () => resolve();
|
|
@@ -84,11 +147,15 @@ function loadScript(url) {
|
|
|
84
147
|
return promise;
|
|
85
148
|
}
|
|
86
149
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
isLoaded: () => plugin.applyArabicShaping != null
|
|
92
|
-
});
|
|
150
|
+
const { getRTLTextPluginStatus, setRTLTextPlugin, lazyLoad, _clearRTLTextPlugin, _registerRTLTextPlugin } =
|
|
151
|
+
RTLPluginLoader();
|
|
152
|
+
|
|
153
|
+
globalThis.registerRTLTextPlugin ??= _registerRTLTextPlugin;
|
|
93
154
|
|
|
94
|
-
export
|
|
155
|
+
export const rtlPluginLoader = Object.assign(new Evented(), {
|
|
156
|
+
getRTLTextPluginStatus,
|
|
157
|
+
setRTLTextPlugin,
|
|
158
|
+
lazyLoad,
|
|
159
|
+
_clearRTLTextPlugin,
|
|
160
|
+
_loadScript
|
|
161
|
+
});
|
package/src/source/source.js
CHANGED
|
@@ -25,6 +25,7 @@ import { bindAll } from '../util/object.js';
|
|
|
25
25
|
*/
|
|
26
26
|
|
|
27
27
|
import geojson from './geojson_source.js';
|
|
28
|
+
import geojsonTiler from './geojson_tiler.js';
|
|
28
29
|
import image from './image_source.js';
|
|
29
30
|
import rasterDem from './raster_dem_tile_source.js';
|
|
30
31
|
import raster from './raster_tile_source.js';
|
|
@@ -38,6 +39,9 @@ const sourceTypes = {
|
|
|
38
39
|
image
|
|
39
40
|
};
|
|
40
41
|
|
|
42
|
+
const tilerTypes = {
|
|
43
|
+
geojson: geojsonTiler
|
|
44
|
+
};
|
|
41
45
|
/*
|
|
42
46
|
* Creates a tiled data source instance given an options object.
|
|
43
47
|
*
|
|
@@ -48,11 +52,18 @@ const sourceTypes = {
|
|
|
48
52
|
* @returns {Source}
|
|
49
53
|
*/
|
|
50
54
|
export function create(id, specification, eventedParent, { resources, layerIndex, showTileBoundaries }) {
|
|
51
|
-
const
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
55
|
+
const tiler = tilerTypes[specification.type]?.(resources, layerIndex);
|
|
56
|
+
|
|
57
|
+
const source = new sourceTypes[specification.type](
|
|
58
|
+
id,
|
|
59
|
+
specification,
|
|
60
|
+
eventedParent,
|
|
61
|
+
tiler ?? {
|
|
62
|
+
resources,
|
|
63
|
+
layerIndex,
|
|
64
|
+
showTileBoundaries
|
|
65
|
+
}
|
|
66
|
+
);
|
|
56
67
|
|
|
57
68
|
bindAll(['load', 'abort', 'unload', 'serialize', 'prepare'], source);
|
|
58
69
|
return source;
|
|
@@ -799,27 +799,27 @@ class SourceCache extends Evented {
|
|
|
799
799
|
* Set the value of a particular state for a feature
|
|
800
800
|
* @private
|
|
801
801
|
*/
|
|
802
|
-
setFeatureState(sourceLayer,
|
|
802
|
+
setFeatureState(sourceLayer, featureId, state) {
|
|
803
803
|
sourceLayer = sourceLayer || '_geojsonTileLayer';
|
|
804
|
-
this._state.updateState(sourceLayer,
|
|
804
|
+
this._state.updateState(sourceLayer, featureId, state);
|
|
805
805
|
}
|
|
806
806
|
|
|
807
807
|
/**
|
|
808
808
|
* Resets the value of a particular state key for a feature
|
|
809
809
|
* @private
|
|
810
810
|
*/
|
|
811
|
-
removeFeatureState(sourceLayer,
|
|
811
|
+
removeFeatureState(sourceLayer, featureId, key) {
|
|
812
812
|
sourceLayer = sourceLayer || '_geojsonTileLayer';
|
|
813
|
-
this._state.removeFeatureState(sourceLayer,
|
|
813
|
+
this._state.removeFeatureState(sourceLayer, featureId, key);
|
|
814
814
|
}
|
|
815
815
|
|
|
816
816
|
/**
|
|
817
817
|
* Get the entire state object for a feature
|
|
818
818
|
* @private
|
|
819
819
|
*/
|
|
820
|
-
getFeatureState(sourceLayer,
|
|
820
|
+
getFeatureState(sourceLayer, featureId) {
|
|
821
821
|
sourceLayer = sourceLayer || '_geojsonTileLayer';
|
|
822
|
-
return this._state.getState(sourceLayer,
|
|
822
|
+
return this._state.getState(sourceLayer, featureId);
|
|
823
823
|
}
|
|
824
824
|
}
|
|
825
825
|
|
|
@@ -13,7 +13,8 @@ class SourceFeatureState {
|
|
|
13
13
|
#stateChanges = {};
|
|
14
14
|
#deletedStates = {};
|
|
15
15
|
|
|
16
|
-
updateState(sourceLayer,
|
|
16
|
+
updateState(sourceLayer, featureId, newState) {
|
|
17
|
+
const feature = String(featureId);
|
|
17
18
|
const changes = (this.#stateChanges[sourceLayer] ??= {});
|
|
18
19
|
const featureState = (changes[feature] ??= {});
|
|
19
20
|
Object.assign(featureState, newState);
|
|
@@ -76,7 +77,8 @@ class SourceFeatureState {
|
|
|
76
77
|
}
|
|
77
78
|
}
|
|
78
79
|
|
|
79
|
-
getState(sourceLayer,
|
|
80
|
+
getState(sourceLayer, featureId) {
|
|
81
|
+
const feature = String(featureId);
|
|
80
82
|
const base = this.#state[sourceLayer];
|
|
81
83
|
const changes = this.#stateChanges[sourceLayer];
|
|
82
84
|
const reconciledState = Object.assign({}, base?.[feature], changes?.[feature]);
|
package/src/source/tile.js
CHANGED
|
@@ -191,11 +191,12 @@ class Tile {
|
|
|
191
191
|
}
|
|
192
192
|
|
|
193
193
|
querySourceFeatures(result, params) {
|
|
194
|
-
|
|
194
|
+
const featureIndex = this.latestFeatureIndex;
|
|
195
|
+
if (!featureIndex?.vectorTile) {
|
|
195
196
|
return;
|
|
196
197
|
}
|
|
197
198
|
|
|
198
|
-
const vtLayers =
|
|
199
|
+
const vtLayers = featureIndex.loadVTLayers();
|
|
199
200
|
|
|
200
201
|
const sourceLayer = params ? params.sourceLayer : '';
|
|
201
202
|
const layer = vtLayers._geojsonTileLayer || vtLayers[sourceLayer];
|
|
@@ -211,7 +212,8 @@ class Tile {
|
|
|
211
212
|
for (let i = 0; i < layer.length; i++) {
|
|
212
213
|
const feature = layer.feature(i);
|
|
213
214
|
if (filter(new EvaluationParameters(this.tileID.overscaledZ), feature)) {
|
|
214
|
-
const
|
|
215
|
+
const id = featureIndex.getId(feature, sourceLayer);
|
|
216
|
+
const geojsonFeature = new GeoJSONFeature(feature, z, x, y, id);
|
|
215
217
|
geojsonFeature.tile = coord;
|
|
216
218
|
result.push(geojsonFeature);
|
|
217
219
|
}
|
|
@@ -22,6 +22,7 @@ class VectorTileSource extends Evented {
|
|
|
22
22
|
this.url = options.url;
|
|
23
23
|
this.scheme = options.scheme ?? 'xyz';
|
|
24
24
|
this.tileSize = options.tileSize ?? 512;
|
|
25
|
+
this.promoteId = options.promoteId;
|
|
25
26
|
|
|
26
27
|
if (this.tileSize !== 512) {
|
|
27
28
|
throw new Error('vector tile sources must have a tileSize of 512');
|
|
@@ -93,6 +94,7 @@ class VectorTileSource extends Evented {
|
|
|
93
94
|
source: this.id,
|
|
94
95
|
pixelRatio: browser.devicePixelRatio,
|
|
95
96
|
showCollisionBoxes: this.map.showCollisionBoxes,
|
|
97
|
+
promoteId: this.promoteId,
|
|
96
98
|
painter: this.map.painter
|
|
97
99
|
};
|
|
98
100
|
tile.workerID ??= true;
|
|
@@ -27,7 +27,9 @@ async function makeWorkerTile(params, { layers }, layerIndex, resources) {
|
|
|
27
27
|
const sourceLayerIndex = sourceLayerCoder.encode(sourceLayerId);
|
|
28
28
|
const features = new Array(sourceLayer.length);
|
|
29
29
|
for (let index = 0; index < sourceLayer.length; index++) {
|
|
30
|
-
|
|
30
|
+
const feature = sourceLayer.feature(index);
|
|
31
|
+
const id = options.featureIndex.getId(feature, sourceLayerId);
|
|
32
|
+
features[index] = { feature, id, index, sourceLayerIndex };
|
|
31
33
|
}
|
|
32
34
|
|
|
33
35
|
makeBucketsForSourceLayer(sourceLayerFamilies, sourceLayerIndex, features, params, options);
|
|
@@ -47,7 +49,7 @@ export async function makeSingleSourceLayerWorkerTile(params, features, sourceLa
|
|
|
47
49
|
function initializeBucketsOptions(params) {
|
|
48
50
|
const tileID = createTileID(params);
|
|
49
51
|
|
|
50
|
-
const featureIndex = new FeatureIndex(tileID);
|
|
52
|
+
const featureIndex = params.featureIndex ?? new FeatureIndex(tileID, params.promoteId);
|
|
51
53
|
featureIndex.bucketLayerIDs = [];
|
|
52
54
|
|
|
53
55
|
return {
|
|
@@ -146,6 +148,8 @@ async function finalizeBuckets(params, options, resources) {
|
|
|
146
148
|
}
|
|
147
149
|
|
|
148
150
|
async function makeAtlasses({ glyphDependencies, patternDependencies, iconDependencies }, resources) {
|
|
151
|
+
// options.glyphDependencies looks like: {"SomeFontName":{"10":true,"32":true}}
|
|
152
|
+
// this line makes an object like: {"SomeFontName":[10,32]}
|
|
149
153
|
const stacks = mapObject(glyphDependencies, glyphs => Object.keys(glyphs).map(Number));
|
|
150
154
|
const icons = Object.keys(iconDependencies);
|
|
151
155
|
const patterns = Object.keys(patternDependencies);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { rtlPlugin } from '../source/rtl_text_plugin.js';
|
|
2
2
|
import { isStringInSupportedScript } from '../util/script_detection.js';
|
|
3
3
|
import ZoomHistory from './zoom_history.js';
|
|
4
4
|
|
|
@@ -20,7 +20,6 @@ export default class EvaluationParameters {
|
|
|
20
20
|
this.transition = {};
|
|
21
21
|
}
|
|
22
22
|
}
|
|
23
|
-
|
|
24
23
|
crossFadingFactor() {
|
|
25
24
|
if (this.fadeDuration === 0) {
|
|
26
25
|
return 1;
|
|
@@ -40,5 +39,5 @@ export default class EvaluationParameters {
|
|
|
40
39
|
}
|
|
41
40
|
|
|
42
41
|
function isSupportedScript(str) {
|
|
43
|
-
return isStringInSupportedScript(str,
|
|
42
|
+
return isStringInSupportedScript(str, rtlPlugin.isRTLSupported());
|
|
44
43
|
}
|
|
@@ -2,27 +2,59 @@ import { Placement } from '../symbol/placement.js';
|
|
|
2
2
|
import browser from '../util/browser.js';
|
|
3
3
|
|
|
4
4
|
class LayerPlacement {
|
|
5
|
-
constructor() {
|
|
5
|
+
constructor(styleLayer) {
|
|
6
|
+
this._sortAcrossTiles =
|
|
7
|
+
styleLayer._layout.get('symbol-z-order') !== 'viewport-y' &&
|
|
8
|
+
styleLayer._layout.get('symbol-sort-key').constantOr(1) !== undefined;
|
|
9
|
+
|
|
6
10
|
this._currentTileIndex = 0;
|
|
11
|
+
this._currentPartIndex = 0;
|
|
7
12
|
this._seenCrossTileIDs = {};
|
|
13
|
+
this._bucketParts = [];
|
|
8
14
|
}
|
|
9
15
|
|
|
10
16
|
continuePlacement(tiles, placement, showCollisionBoxes, styleLayer, shouldPausePlacement) {
|
|
17
|
+
const bucketParts = this._bucketParts;
|
|
18
|
+
|
|
11
19
|
while (this._currentTileIndex < tiles.length) {
|
|
12
20
|
const tile = tiles[this._currentTileIndex];
|
|
13
|
-
placement.
|
|
21
|
+
placement.getBucketParts(bucketParts, styleLayer, tile, this._sortAcrossTiles);
|
|
14
22
|
|
|
15
23
|
this._currentTileIndex++;
|
|
16
24
|
if (shouldPausePlacement()) {
|
|
17
25
|
return true;
|
|
18
26
|
}
|
|
19
27
|
}
|
|
28
|
+
|
|
29
|
+
if (this._sortAcrossTiles) {
|
|
30
|
+
this._sortAcrossTiles = false;
|
|
31
|
+
bucketParts.sort((a, b) => a.sortKey - b.sortKey);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
while (this._currentPartIndex < bucketParts.length) {
|
|
35
|
+
const bucketPart = bucketParts[this._currentPartIndex];
|
|
36
|
+
placement.placeLayerBucketPart(bucketPart, this._seenCrossTileIDs, showCollisionBoxes);
|
|
37
|
+
|
|
38
|
+
this._currentPartIndex++;
|
|
39
|
+
if (shouldPausePlacement()) {
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return false;
|
|
20
44
|
}
|
|
21
45
|
}
|
|
22
46
|
|
|
23
47
|
class PauseablePlacement {
|
|
24
|
-
constructor(
|
|
25
|
-
|
|
48
|
+
constructor(
|
|
49
|
+
transform,
|
|
50
|
+
maxIndex,
|
|
51
|
+
forceFullPlacement,
|
|
52
|
+
showCollisionBoxes,
|
|
53
|
+
fadeDuration,
|
|
54
|
+
crossSourceCollisions,
|
|
55
|
+
prevPlacement
|
|
56
|
+
) {
|
|
57
|
+
this.placement = new Placement(transform, fadeDuration, crossSourceCollisions, prevPlacement);
|
|
26
58
|
this._currentPlacementIndex = maxIndex;
|
|
27
59
|
this._forceFullPlacement = forceFullPlacement;
|
|
28
60
|
this._showCollisionBoxes = showCollisionBoxes;
|
|
@@ -50,7 +82,7 @@ class PauseablePlacement {
|
|
|
50
82
|
(!layer.maxzoom || layer.maxzoom > placementZoom)
|
|
51
83
|
) {
|
|
52
84
|
if (!this._inProgressLayer) {
|
|
53
|
-
this._inProgressLayer = new LayerPlacement();
|
|
85
|
+
this._inProgressLayer = new LayerPlacement(layer);
|
|
54
86
|
}
|
|
55
87
|
|
|
56
88
|
const pausePlacement = this._inProgressLayer.continuePlacement(
|
|
@@ -77,8 +109,8 @@ class PauseablePlacement {
|
|
|
77
109
|
this._done = true;
|
|
78
110
|
}
|
|
79
111
|
|
|
80
|
-
commit(
|
|
81
|
-
this.placement.commit(
|
|
112
|
+
commit(now) {
|
|
113
|
+
this.placement.commit(now);
|
|
82
114
|
return this.placement;
|
|
83
115
|
}
|
|
84
116
|
}
|