maplibre-gl 2.1.8 → 2.2.0-pre.2
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/generate-style-spec.ts +2 -0
- package/dist/maplibre-gl-csp-worker.js +1 -1
- package/dist/maplibre-gl-csp-worker.js.map +1 -1
- package/dist/maplibre-gl-csp.js +1 -1
- package/dist/maplibre-gl-csp.js.map +1 -1
- package/dist/maplibre-gl-dev.js +1517 -290
- package/dist/maplibre-gl.css +1 -1
- package/dist/maplibre-gl.d.ts +3476 -3112
- package/dist/maplibre-gl.js +4 -4
- package/dist/maplibre-gl.js.map +1 -1
- package/dist/package.json +1 -1
- package/package.json +15 -15
- package/src/css/maplibre-gl.css +20 -0
- package/src/css/svg/maplibregl-ctrl-terrain.svg +7 -0
- package/src/data/bucket/fill_extrusion_attributes.ts +4 -0
- package/src/data/bucket/fill_extrusion_bucket.ts +28 -4
- package/src/data/dem_data.test.ts +14 -1
- package/src/data/dem_data.ts +13 -0
- package/src/geo/transform.test.ts +56 -1
- package/src/geo/transform.ts +199 -47
- package/src/index.ts +2 -0
- package/src/render/draw_background.ts +6 -6
- package/src/render/draw_circle.ts +6 -2
- package/src/render/draw_collision_debug.ts +5 -1
- package/src/render/draw_debug.ts +5 -5
- package/src/render/draw_fill.ts +5 -2
- package/src/render/draw_fill_extrusion.ts +3 -2
- package/src/render/draw_heatmap.ts +2 -3
- package/src/render/draw_hillshade.ts +8 -7
- package/src/render/draw_line.ts +7 -5
- package/src/render/draw_raster.ts +8 -6
- package/src/render/draw_symbol.test.ts +34 -10
- package/src/render/draw_symbol.ts +23 -12
- package/src/render/draw_terrain.ts +123 -0
- package/src/render/painter.ts +52 -14
- package/src/render/program/hillshade_program.ts +7 -2
- package/src/render/program/line_program.ts +24 -10
- package/src/render/program/program_uniforms.ts +5 -1
- package/src/render/program/terrain_program.ts +83 -0
- package/src/render/program.ts +29 -5
- package/src/render/render_to_texture.test.ts +41 -0
- package/src/render/render_to_texture.ts +154 -0
- package/src/render/terrain.test.ts +53 -0
- package/src/render/terrain.ts +369 -0
- package/src/render/vertex_array_object.ts +21 -4
- package/src/shaders/_prelude.vertex.glsl +76 -0
- package/src/shaders/_prelude.vertex.glsl.g.ts +1 -1
- package/src/shaders/circle.fragment.glsl +2 -1
- package/src/shaders/circle.fragment.glsl.g.ts +1 -1
- package/src/shaders/circle.vertex.glsl +6 -2
- package/src/shaders/circle.vertex.glsl.g.ts +1 -1
- package/src/shaders/collision_box.vertex.glsl +1 -1
- package/src/shaders/collision_box.vertex.glsl.g.ts +1 -1
- package/src/shaders/debug.vertex.glsl +1 -1
- package/src/shaders/debug.vertex.glsl.g.ts +1 -1
- package/src/shaders/fill_extrusion.vertex.glsl +16 -2
- package/src/shaders/fill_extrusion.vertex.glsl.g.ts +1 -1
- package/src/shaders/fill_extrusion_pattern.vertex.glsl +15 -2
- package/src/shaders/fill_extrusion_pattern.vertex.glsl.g.ts +1 -1
- package/src/shaders/line.vertex.glsl +7 -3
- package/src/shaders/line.vertex.glsl.g.ts +1 -1
- package/src/shaders/line_gradient.vertex.glsl +7 -3
- package/src/shaders/line_gradient.vertex.glsl.g.ts +1 -1
- package/src/shaders/line_pattern.vertex.glsl +7 -3
- package/src/shaders/line_pattern.vertex.glsl.g.ts +1 -1
- package/src/shaders/line_sdf.vertex.glsl +7 -4
- package/src/shaders/line_sdf.vertex.glsl.g.ts +1 -1
- package/src/shaders/shaders.ts +11 -1
- package/src/shaders/symbol_icon.vertex.glsl +8 -8
- package/src/shaders/symbol_icon.vertex.glsl.g.ts +1 -1
- package/src/shaders/symbol_sdf.vertex.glsl +8 -5
- package/src/shaders/symbol_sdf.vertex.glsl.g.ts +1 -1
- package/src/shaders/symbol_text_and_icon.vertex.glsl +8 -5
- package/src/shaders/symbol_text_and_icon.vertex.glsl.g.ts +1 -1
- package/src/shaders/terrain.fragment.glsl +7 -0
- package/src/shaders/terrain.fragment.glsl.g.ts +2 -0
- package/src/shaders/terrain.vertex.glsl +12 -0
- package/src/shaders/terrain.vertex.glsl.g.ts +2 -0
- package/src/shaders/terrain_coords.fragment.glsl +11 -0
- package/src/shaders/terrain_coords.fragment.glsl.g.ts +2 -0
- package/src/shaders/terrain_depth.fragment.glsl +15 -0
- package/src/shaders/terrain_depth.fragment.glsl.g.ts +2 -0
- package/src/source/canvas_source.test.ts +1 -1
- package/src/source/geojson_source.test.ts +25 -0
- package/src/source/geojson_source.ts +1 -8
- package/src/source/geojson_worker_source.test.ts +19 -23
- package/src/source/geojson_worker_source.ts +19 -70
- package/src/source/raster_dem_tile_source.ts +4 -3
- package/src/source/raster_dem_tile_worker_source.ts +0 -1
- package/src/source/source_cache.test.ts +83 -0
- package/src/source/source_cache.ts +72 -11
- package/src/source/terrain_source_cache.test.ts +89 -0
- package/src/source/terrain_source_cache.ts +201 -0
- package/src/source/tile.ts +15 -0
- package/src/source/tile_id.ts +9 -0
- package/src/style/pauseable_placement.ts +3 -1
- package/src/style/style.test.ts +16 -0
- package/src/style/style.ts +57 -3
- package/src/style/validate_style.ts +2 -0
- package/src/style-spec/CHANGELOG.md +6 -0
- package/src/style-spec/error/validation_error.ts +1 -1
- package/src/style-spec/package.json +2 -2
- package/src/style-spec/reference/v8.json +42 -0
- package/src/style-spec/types.g.ts +7 -0
- package/src/style-spec/validate/validate.ts +2 -0
- package/src/style-spec/validate/validate_terrain.test.ts +46 -0
- package/src/style-spec/validate/validate_terrain.ts +41 -0
- package/src/style-spec/validate_style.min.ts +2 -0
- package/src/style-spec/validate_style.ts +1 -0
- package/src/symbol/collision_index.ts +28 -12
- package/src/symbol/placement.ts +24 -9
- package/src/symbol/projection.ts +42 -27
- package/src/ui/camera.ts +2 -0
- package/src/ui/control/terrain_control.ts +77 -0
- package/src/ui/default_locale.ts +3 -2
- package/src/ui/events.ts +18 -3
- package/src/ui/handler_manager.ts +33 -3
- package/src/ui/map.ts +36 -6
- package/src/ui/marker.test.ts +21 -0
- package/src/ui/marker.ts +14 -0
- package/src/util/primitives.ts +14 -11
|
@@ -54,6 +54,7 @@ void main() {
|
|
|
54
54
|
float a_size_min = floor(a_size[0] * 0.5);
|
|
55
55
|
float is_sdf = a_size[0] - 2.0 * a_size_min;
|
|
56
56
|
|
|
57
|
+
float ele = get_elevation(a_pos);
|
|
57
58
|
highp float segment_angle = -a_projected_pos[2];
|
|
58
59
|
float size;
|
|
59
60
|
|
|
@@ -65,7 +66,7 @@ void main() {
|
|
|
65
66
|
size = u_size;
|
|
66
67
|
}
|
|
67
68
|
|
|
68
|
-
vec4 projectedPoint = u_matrix * vec4(a_pos,
|
|
69
|
+
vec4 projectedPoint = u_matrix * vec4(a_pos, ele, 1);
|
|
69
70
|
highp float camera_to_anchor_distance = projectedPoint.w;
|
|
70
71
|
// If the label is pitched with the map, layout is done in pitched space,
|
|
71
72
|
// which makes labels in the distance smaller relative to viewport space.
|
|
@@ -90,7 +91,7 @@ void main() {
|
|
|
90
91
|
// Point labels with 'rotation-alignment: map' are horizontal with respect to tile units
|
|
91
92
|
// To figure out that angle in projected space, we draw a short horizontal line in tile
|
|
92
93
|
// space, project it, and measure its angle in projected space.
|
|
93
|
-
vec4 offsetProjectedPoint = u_matrix * vec4(a_pos + vec2(1, 0),
|
|
94
|
+
vec4 offsetProjectedPoint = u_matrix * vec4(a_pos + vec2(1, 0), ele, 1);
|
|
94
95
|
|
|
95
96
|
vec2 a = projectedPoint.xy / projectedPoint.w;
|
|
96
97
|
vec2 b = offsetProjectedPoint.xy / offsetProjectedPoint.w;
|
|
@@ -102,13 +103,15 @@ void main() {
|
|
|
102
103
|
highp float angle_cos = cos(segment_angle + symbol_rotation);
|
|
103
104
|
mat2 rotation_matrix = mat2(angle_cos, -1.0 * angle_sin, angle_sin, angle_cos);
|
|
104
105
|
|
|
105
|
-
vec4 projected_pos = u_label_plane_matrix * vec4(a_projected_pos.xy,
|
|
106
|
-
|
|
106
|
+
vec4 projected_pos = u_label_plane_matrix * vec4(a_projected_pos.xy, ele, 1.0);
|
|
107
|
+
float z = float(u_pitch_with_map) * projected_pos.z / projected_pos.w;
|
|
108
|
+
gl_Position = u_coord_matrix * vec4(projected_pos.xy / projected_pos.w + rotation_matrix * (a_offset / 32.0 * fontScale), z, 1.0);
|
|
107
109
|
float gamma_scale = gl_Position.w;
|
|
108
110
|
|
|
109
111
|
vec2 fade_opacity = unpack_opacity(a_fade_opacity);
|
|
112
|
+
float visibility = calculate_visibility(projectedPoint);
|
|
110
113
|
float fade_change = fade_opacity[1] > 0.5 ? u_fade_change : -u_fade_change;
|
|
111
|
-
float interpolated_fade_opacity = max(0.0, min(
|
|
114
|
+
float interpolated_fade_opacity = max(0.0, min(visibility, fade_opacity[0] + fade_change));
|
|
112
115
|
|
|
113
116
|
v_data0.xy = a_tex / u_texsize;
|
|
114
117
|
v_data0.zw = a_tex / u_texsize_icon;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// This file is generated. Edit build/generate-shaders.ts, then run `npm run codegen`.
|
|
2
|
-
export default 'const float PI=3.141592653589793;attribute vec4 a_pos_offset;attribute vec4 a_data;attribute vec3 a_projected_pos;attribute float a_fade_opacity;uniform bool u_is_size_zoom_constant;uniform bool u_is_size_feature_constant;uniform highp float u_size_t;uniform highp float u_size;uniform mat4 u_matrix;uniform mat4 u_label_plane_matrix;uniform mat4 u_coord_matrix;uniform bool u_is_text;uniform bool u_pitch_with_map;uniform highp float u_pitch;uniform bool u_rotate_symbol;uniform highp float u_aspect_ratio;uniform highp float u_camera_to_center_distance;uniform float u_fade_change;uniform vec2 u_texsize;uniform vec2 u_texsize_icon;varying vec4 v_data0;varying vec4 v_data1;\n#pragma mapbox: define highp vec4 fill_color\n#pragma mapbox: define highp vec4 halo_color\n#pragma mapbox: define lowp float opacity\n#pragma mapbox: define lowp float halo_width\n#pragma mapbox: define lowp float halo_blur\nvoid main() {\n#pragma mapbox: initialize highp vec4 fill_color\n#pragma mapbox: initialize highp vec4 halo_color\n#pragma mapbox: initialize lowp float opacity\n#pragma mapbox: initialize lowp float halo_width\n#pragma mapbox: initialize lowp float halo_blur\nvec2 a_pos=a_pos_offset.xy;vec2 a_offset=a_pos_offset.zw;vec2 a_tex=a_data.xy;vec2 a_size=a_data.zw;float a_size_min=floor(a_size[0]*0.5);float is_sdf=a_size[0]-2.0*a_size_min;highp float segment_angle=-a_projected_pos[2];float size;if (!u_is_size_zoom_constant && !u_is_size_feature_constant) {size=mix(a_size_min,a_size[1],u_size_t)/128.0;} else if (u_is_size_zoom_constant && !u_is_size_feature_constant) {size=a_size_min/128.0;} else {size=u_size;}vec4 projectedPoint=u_matrix*vec4(a_pos,
|
|
2
|
+
export default 'const float PI=3.141592653589793;attribute vec4 a_pos_offset;attribute vec4 a_data;attribute vec3 a_projected_pos;attribute float a_fade_opacity;uniform bool u_is_size_zoom_constant;uniform bool u_is_size_feature_constant;uniform highp float u_size_t;uniform highp float u_size;uniform mat4 u_matrix;uniform mat4 u_label_plane_matrix;uniform mat4 u_coord_matrix;uniform bool u_is_text;uniform bool u_pitch_with_map;uniform highp float u_pitch;uniform bool u_rotate_symbol;uniform highp float u_aspect_ratio;uniform highp float u_camera_to_center_distance;uniform float u_fade_change;uniform vec2 u_texsize;uniform vec2 u_texsize_icon;varying vec4 v_data0;varying vec4 v_data1;\n#pragma mapbox: define highp vec4 fill_color\n#pragma mapbox: define highp vec4 halo_color\n#pragma mapbox: define lowp float opacity\n#pragma mapbox: define lowp float halo_width\n#pragma mapbox: define lowp float halo_blur\nvoid main() {\n#pragma mapbox: initialize highp vec4 fill_color\n#pragma mapbox: initialize highp vec4 halo_color\n#pragma mapbox: initialize lowp float opacity\n#pragma mapbox: initialize lowp float halo_width\n#pragma mapbox: initialize lowp float halo_blur\nvec2 a_pos=a_pos_offset.xy;vec2 a_offset=a_pos_offset.zw;vec2 a_tex=a_data.xy;vec2 a_size=a_data.zw;float a_size_min=floor(a_size[0]*0.5);float is_sdf=a_size[0]-2.0*a_size_min;float ele=get_elevation(a_pos);highp float segment_angle=-a_projected_pos[2];float size;if (!u_is_size_zoom_constant && !u_is_size_feature_constant) {size=mix(a_size_min,a_size[1],u_size_t)/128.0;} else if (u_is_size_zoom_constant && !u_is_size_feature_constant) {size=a_size_min/128.0;} else {size=u_size;}vec4 projectedPoint=u_matrix*vec4(a_pos,ele,1);highp float camera_to_anchor_distance=projectedPoint.w;highp float distance_ratio=u_pitch_with_map ?\ncamera_to_anchor_distance/u_camera_to_center_distance :\nu_camera_to_center_distance/camera_to_anchor_distance;highp float perspective_ratio=clamp(0.5+0.5*distance_ratio,0.0,4.0);size*=perspective_ratio;float fontScale=size/24.0;highp float symbol_rotation=0.0;if (u_rotate_symbol) {vec4 offsetProjectedPoint=u_matrix*vec4(a_pos+vec2(1,0),ele,1);vec2 a=projectedPoint.xy/projectedPoint.w;vec2 b=offsetProjectedPoint.xy/offsetProjectedPoint.w;symbol_rotation=atan((b.y-a.y)/u_aspect_ratio,b.x-a.x);}highp float angle_sin=sin(segment_angle+symbol_rotation);highp float angle_cos=cos(segment_angle+symbol_rotation);mat2 rotation_matrix=mat2(angle_cos,-1.0*angle_sin,angle_sin,angle_cos);vec4 projected_pos=u_label_plane_matrix*vec4(a_projected_pos.xy,ele,1.0);float z=float(u_pitch_with_map)*projected_pos.z/projected_pos.w;gl_Position=u_coord_matrix*vec4(projected_pos.xy/projected_pos.w+rotation_matrix*(a_offset/32.0*fontScale),z,1.0);float gamma_scale=gl_Position.w;vec2 fade_opacity=unpack_opacity(a_fade_opacity);float visibility=calculate_visibility(projectedPoint);float fade_change=fade_opacity[1] > 0.5 ? u_fade_change :-u_fade_change;float interpolated_fade_opacity=max(0.0,min(visibility,fade_opacity[0]+fade_change));v_data0.xy=a_tex/u_texsize;v_data0.zw=a_tex/u_texsize_icon;v_data1=vec4(gamma_scale,size,interpolated_fade_opacity,is_sdf);}';
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
attribute vec2 a_pos;
|
|
2
|
+
|
|
3
|
+
uniform mat4 u_matrix;
|
|
4
|
+
|
|
5
|
+
varying vec2 v_texture_pos;
|
|
6
|
+
varying float v_depth;
|
|
7
|
+
|
|
8
|
+
void main() {
|
|
9
|
+
v_texture_pos = a_pos / 8192.0; // 8192.0 is the hardcoded vector-tiles coordinates resolution
|
|
10
|
+
gl_Position = u_matrix * vec4(a_pos, get_elevation(a_pos), 1.0);
|
|
11
|
+
v_depth = gl_Position.z / gl_Position.w;
|
|
12
|
+
}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
// This file is generated. Edit build/generate-shaders.ts, then run `npm run codegen`.
|
|
2
|
+
export default 'attribute vec2 a_pos;uniform mat4 u_matrix;varying vec2 v_texture_pos;varying float v_depth;void main() {v_texture_pos=a_pos/8192.0;gl_Position=u_matrix*vec4(a_pos,get_elevation(a_pos),1.0);v_depth=gl_Position.z/gl_Position.w;}';
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
precision mediump float;
|
|
2
|
+
|
|
3
|
+
uniform sampler2D u_texture;
|
|
4
|
+
uniform float u_terrain_coords_id;
|
|
5
|
+
|
|
6
|
+
varying vec2 v_texture_pos;
|
|
7
|
+
|
|
8
|
+
void main() {
|
|
9
|
+
vec4 rgba = texture2D(u_texture, v_texture_pos);
|
|
10
|
+
gl_FragColor = vec4(rgba.r, rgba.g, rgba.b, u_terrain_coords_id);
|
|
11
|
+
}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
// This file is generated. Edit build/generate-shaders.ts, then run `npm run codegen`.
|
|
2
|
+
export default 'precision mediump float;uniform sampler2D u_texture;uniform float u_terrain_coords_id;varying vec2 v_texture_pos;void main() {vec4 rgba=texture2D(u_texture,v_texture_pos);gl_FragColor=vec4(rgba.r,rgba.g,rgba.b,u_terrain_coords_id);}';
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
varying float v_depth;
|
|
2
|
+
|
|
3
|
+
// methods for pack/unpack depth value to texture rgba
|
|
4
|
+
// https://stackoverflow.com/questions/34963366/encode-floating-point-data-in-a-rgba-texture
|
|
5
|
+
const highp vec4 bitSh = vec4(256. * 256. * 256., 256. * 256., 256., 1.);
|
|
6
|
+
const highp vec4 bitMsk = vec4(0.,vec3(1./256.0));
|
|
7
|
+
highp vec4 pack(highp float value) {
|
|
8
|
+
highp vec4 comp = fract(value * bitSh);
|
|
9
|
+
comp -= comp.xxyz * bitMsk;
|
|
10
|
+
return comp;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
void main() {
|
|
14
|
+
gl_FragColor = pack(v_depth);
|
|
15
|
+
}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
// This file is generated. Edit build/generate-shaders.ts, then run `npm run codegen`.
|
|
2
|
+
export default 'varying float v_depth;const highp vec4 bitSh=vec4(256.*256.*256.,256.*256.,256.,1.);const highp vec4 bitMsk=vec4(0.,vec3(1./256.0));highp vec4 pack(highp float value) {highp vec4 comp=fract(value*bitSh);comp-=comp.xxyz*bitMsk;return comp;}void main() {gl_FragColor=pack(v_depth);}';
|
|
@@ -61,7 +61,7 @@ describe('CanvasSource', () => {
|
|
|
61
61
|
});
|
|
62
62
|
|
|
63
63
|
test('self-validates', () => {
|
|
64
|
-
const stub = jest.spyOn(console, 'error');
|
|
64
|
+
const stub = jest.spyOn(console, 'error').mockImplementation(() => {});
|
|
65
65
|
createSource({coordinates: []});
|
|
66
66
|
expect(stub).toHaveBeenCalled();
|
|
67
67
|
stub.mockReset();
|
|
@@ -90,6 +90,18 @@ describe('GeoJSONSource#setData', () => {
|
|
|
90
90
|
source.load();
|
|
91
91
|
});
|
|
92
92
|
|
|
93
|
+
test('fires "dataabort" event', done => {
|
|
94
|
+
const source = new GeoJSONSource('id', {} as any, wrapDispatcher({
|
|
95
|
+
send(type, data, callback) {
|
|
96
|
+
setTimeout(() => callback(null, {abandoned: true}));
|
|
97
|
+
}
|
|
98
|
+
}), undefined);
|
|
99
|
+
source.on('dataabort', () => {
|
|
100
|
+
done();
|
|
101
|
+
});
|
|
102
|
+
source.load();
|
|
103
|
+
});
|
|
104
|
+
|
|
93
105
|
test('respects collectResourceTiming parameter on source', done => {
|
|
94
106
|
const source = createSource({collectResourceTiming: true});
|
|
95
107
|
source.map = {
|
|
@@ -137,6 +149,19 @@ describe('GeoJSONSource#setData', () => {
|
|
|
137
149
|
});
|
|
138
150
|
source.setData({} as GeoJSON.GeoJSON);
|
|
139
151
|
});
|
|
152
|
+
|
|
153
|
+
test('marks source as loaded before firing "dataabort" event', done => {
|
|
154
|
+
const source = new GeoJSONSource('id', {} as any, wrapDispatcher({
|
|
155
|
+
send(type, data, callback) {
|
|
156
|
+
setTimeout(() => callback(null, {abandoned: true}));
|
|
157
|
+
}
|
|
158
|
+
}), undefined);
|
|
159
|
+
source.on('dataabort', () => {
|
|
160
|
+
expect(source.loaded()).toBeTruthy();
|
|
161
|
+
done();
|
|
162
|
+
});
|
|
163
|
+
source.setData({} as GeoJSON.GeoJSON);
|
|
164
|
+
});
|
|
140
165
|
});
|
|
141
166
|
|
|
142
167
|
describe('GeoJSONSource#onRemove', () => {
|
|
@@ -256,20 +256,13 @@ class GeoJSONSource extends Evented implements Source {
|
|
|
256
256
|
this._pendingLoads--;
|
|
257
257
|
|
|
258
258
|
if (this._removed || (result && result.abandoned)) {
|
|
259
|
+
this.fire(new Event('dataabort', {dataType: 'source', sourceDataType}));
|
|
259
260
|
return;
|
|
260
261
|
}
|
|
261
262
|
|
|
262
263
|
let resourceTiming = null;
|
|
263
264
|
if (result && result.resourceTiming && result.resourceTiming[this.id])
|
|
264
265
|
resourceTiming = result.resourceTiming[this.id].slice(0);
|
|
265
|
-
// Any `loadData` calls that piled up while we were processing
|
|
266
|
-
// this one will get coalesced into a single call when this
|
|
267
|
-
// 'coalesce' message is processed.
|
|
268
|
-
// We would self-send from the worker if we had access to its
|
|
269
|
-
// message queue. Waiting instead for the 'coalesce' to round-trip
|
|
270
|
-
// through the foreground just means we're throttling the worker
|
|
271
|
-
// to run at a little less than full-throttle.
|
|
272
|
-
this.actor.send(`${this.type}.coalesce`, {source: options.source}, null);
|
|
273
266
|
|
|
274
267
|
if (err) {
|
|
275
268
|
this.fire(new ErrorEvent(err));
|
|
@@ -46,7 +46,6 @@ describe('reloadTile', () => {
|
|
|
46
46
|
|
|
47
47
|
function addData(callback) {
|
|
48
48
|
source.loadData({source: 'sourceId', data: JSON.stringify(geoJson)} as LoadGeoJSONParameters, (err) => {
|
|
49
|
-
source.coalesce();
|
|
50
49
|
expect(err).toBeNull();
|
|
51
50
|
callback();
|
|
52
51
|
});
|
|
@@ -134,7 +133,10 @@ describe('resourceTiming', () => {
|
|
|
134
133
|
window.performance.getEntriesByName = jest.fn().mockReturnValue([ exampleResourceTiming ]);
|
|
135
134
|
|
|
136
135
|
const layerIndex = new StyleLayerIndex(layers);
|
|
137
|
-
const source = new GeoJSONWorkerSource(actor, layerIndex, [], (params, callback) => {
|
|
136
|
+
const source = new GeoJSONWorkerSource(actor, layerIndex, [], (params, callback) => {
|
|
137
|
+
callback(null, geoJson);
|
|
138
|
+
return {cancel: () => {}};
|
|
139
|
+
});
|
|
138
140
|
|
|
139
141
|
source.loadData({source: 'testSource', request: {url: 'http://localhost/nonexistent', collectResourceTiming: true}} as LoadGeoJSONParameters, (err, result) => {
|
|
140
142
|
expect(err).toBeNull();
|
|
@@ -166,7 +168,10 @@ describe('resourceTiming', () => {
|
|
|
166
168
|
jest.spyOn(perf, 'clearMeasures').mockImplementation(() => { return null; });
|
|
167
169
|
|
|
168
170
|
const layerIndex = new StyleLayerIndex(layers);
|
|
169
|
-
const source = new GeoJSONWorkerSource(actor, layerIndex, [], (params, callback) => {
|
|
171
|
+
const source = new GeoJSONWorkerSource(actor, layerIndex, [], (params, callback) => {
|
|
172
|
+
callback(null, geoJson);
|
|
173
|
+
return {cancel: () => {}};
|
|
174
|
+
});
|
|
170
175
|
|
|
171
176
|
source.loadData({source: 'testSource', request: {url: 'http://localhost/nonexistent', collectResourceTiming: true}} as LoadGeoJSONParameters, (err, result) => {
|
|
172
177
|
expect(err).toBeNull();
|
|
@@ -221,55 +226,46 @@ describe('loadData', () => {
|
|
|
221
226
|
// (regardless of timing)
|
|
222
227
|
const originalLoadGeoJSON = worker.loadGeoJSON;
|
|
223
228
|
worker.loadGeoJSON = function(params, callback) {
|
|
224
|
-
setTimeout(() => {
|
|
229
|
+
const timeout = setTimeout(() => {
|
|
225
230
|
originalLoadGeoJSON(params, callback);
|
|
226
231
|
}, 0);
|
|
232
|
+
|
|
233
|
+
return {cancel: () => clearTimeout(timeout)};
|
|
227
234
|
};
|
|
228
235
|
return worker;
|
|
229
236
|
}
|
|
230
237
|
|
|
231
|
-
test('abandons
|
|
232
|
-
// Expect first call to run, second to be abandoned,
|
|
233
|
-
// and third to run in response to coalesce
|
|
238
|
+
test('abandons previous callbacks', done => {
|
|
234
239
|
const worker = createWorker();
|
|
235
|
-
|
|
236
|
-
expect(err).toBeNull();
|
|
237
|
-
expect(result && result.abandoned).toBeFalsy();
|
|
238
|
-
worker.coalesce();
|
|
239
|
-
});
|
|
240
|
+
let firstCallbackHasRun = false;
|
|
240
241
|
|
|
241
242
|
worker.loadData({source: 'source1', data: JSON.stringify(geoJson)} as LoadGeoJSONParameters, (err, result) => {
|
|
242
243
|
expect(err).toBeNull();
|
|
243
244
|
expect(result && result.abandoned).toBeTruthy();
|
|
245
|
+
firstCallbackHasRun = true;
|
|
244
246
|
});
|
|
245
247
|
|
|
246
248
|
worker.loadData({source: 'source1', data: JSON.stringify(geoJson)} as LoadGeoJSONParameters, (err, result) => {
|
|
247
249
|
expect(err).toBeNull();
|
|
248
250
|
expect(result && result.abandoned).toBeFalsy();
|
|
251
|
+
expect(firstCallbackHasRun).toBeTruthy();
|
|
249
252
|
done();
|
|
250
253
|
});
|
|
251
254
|
});
|
|
252
255
|
|
|
253
256
|
test('removeSource aborts callbacks', done => {
|
|
254
|
-
// Expect:
|
|
255
|
-
// First loadData starts running before removeSource arrives
|
|
256
|
-
// Second loadData is pending when removeSource arrives, gets cancelled
|
|
257
|
-
// removeSource is executed immediately
|
|
258
|
-
// First loadData finishes running, sends results back to foreground
|
|
259
257
|
const worker = createWorker();
|
|
260
|
-
|
|
261
|
-
expect(err).toBeNull();
|
|
262
|
-
expect(result && result.abandoned).toBeFalsy();
|
|
263
|
-
done();
|
|
264
|
-
});
|
|
265
|
-
|
|
258
|
+
let loadDataCallbackHasRun = false;
|
|
266
259
|
worker.loadData({source: 'source1', data: JSON.stringify(geoJson)} as LoadGeoJSONParameters, (err, result) => {
|
|
267
260
|
expect(err).toBeNull();
|
|
268
261
|
expect(result && result.abandoned).toBeTruthy();
|
|
262
|
+
loadDataCallbackHasRun = true;
|
|
269
263
|
});
|
|
270
264
|
|
|
271
265
|
worker.removeSource({source: 'source1'}, (err) => {
|
|
272
266
|
expect(err).toBeFalsy();
|
|
267
|
+
expect(loadDataCallbackHasRun).toBeTruthy();
|
|
268
|
+
done();
|
|
273
269
|
});
|
|
274
270
|
|
|
275
271
|
});
|
|
@@ -21,6 +21,7 @@ import type StyleLayerIndex from '../style/style_layer_index';
|
|
|
21
21
|
import type {LoadVectorDataCallback} from './vector_tile_worker_source';
|
|
22
22
|
import type {RequestParameters, ResponseCallback} from '../util/ajax';
|
|
23
23
|
import type {Callback} from '../types/callback';
|
|
24
|
+
import type {Cancelable} from '../types/cancelable';
|
|
24
25
|
|
|
25
26
|
export type LoadGeoJSONParameters = {
|
|
26
27
|
request?: RequestParameters;
|
|
@@ -33,7 +34,7 @@ export type LoadGeoJSONParameters = {
|
|
|
33
34
|
filter?: Array<unknown>;
|
|
34
35
|
};
|
|
35
36
|
|
|
36
|
-
export type LoadGeoJSON = (params: LoadGeoJSONParameters, callback: ResponseCallback<any>) =>
|
|
37
|
+
export type LoadGeoJSON = (params: LoadGeoJSONParameters, callback: ResponseCallback<any>) => Cancelable;
|
|
37
38
|
|
|
38
39
|
export interface GeoJSONIndex {
|
|
39
40
|
getTile(z: number, x: number, y: number): any;
|
|
@@ -72,10 +73,6 @@ function loadGeoJSONTile(params: WorkerTileParameters, callback: LoadVectorDataC
|
|
|
72
73
|
});
|
|
73
74
|
}
|
|
74
75
|
|
|
75
|
-
export type SourceState = // Source empty or data loaded
|
|
76
|
-
'Idle' | // Data finished loading, but discard 'loadData' messages until receiving 'coalesced'
|
|
77
|
-
'Coalescing' | 'NeedsLoadData'; // 'loadData' received while coalescing, trigger one more 'loadData' on receiving 'coalesced'
|
|
78
|
-
|
|
79
76
|
/**
|
|
80
77
|
* The {@link WorkerSource} implementation that supports {@link GeoJSONSource}.
|
|
81
78
|
* This class is designed to be easily reused to support custom source types
|
|
@@ -87,12 +84,11 @@ export type SourceState = // Source empty or data loaded
|
|
|
87
84
|
* @private
|
|
88
85
|
*/
|
|
89
86
|
class GeoJSONWorkerSource extends VectorTileWorkerSource {
|
|
90
|
-
_state: SourceState;
|
|
91
87
|
_pendingCallback: Callback<{
|
|
92
88
|
resourceTiming?: {[_: string]: Array<PerformanceResourceTiming>};
|
|
93
89
|
abandoned?: boolean;
|
|
94
90
|
}>;
|
|
95
|
-
|
|
91
|
+
_pendingRequest: Cancelable;
|
|
96
92
|
_geoJSONIndex: GeoJSONIndex;
|
|
97
93
|
|
|
98
94
|
/**
|
|
@@ -117,9 +113,8 @@ class GeoJSONWorkerSource extends VectorTileWorkerSource {
|
|
|
117
113
|
* expecting `callback(error, data)` to be called with either an error or a
|
|
118
114
|
* parsed GeoJSON object.
|
|
119
115
|
*
|
|
120
|
-
* When `loadData`
|
|
121
|
-
*
|
|
122
|
-
* See {@link GeoJSONWorkerSource#coalesce}
|
|
116
|
+
* When a `loadData` request comes in while a previous one is being processed,
|
|
117
|
+
* the previous one is aborted.
|
|
123
118
|
*
|
|
124
119
|
* @param params
|
|
125
120
|
* @param callback
|
|
@@ -129,40 +124,20 @@ class GeoJSONWorkerSource extends VectorTileWorkerSource {
|
|
|
129
124
|
resourceTiming?: {[_: string]: Array<PerformanceResourceTiming>};
|
|
130
125
|
abandoned?: boolean;
|
|
131
126
|
}>) {
|
|
127
|
+
this._pendingRequest?.cancel();
|
|
132
128
|
if (this._pendingCallback) {
|
|
133
129
|
// Tell the foreground the previous call has been abandoned
|
|
134
130
|
this._pendingCallback(null, {abandoned: true});
|
|
135
131
|
}
|
|
136
|
-
this._pendingCallback = callback;
|
|
137
|
-
this._pendingLoadDataParams = params;
|
|
138
|
-
|
|
139
|
-
if (this._state &&
|
|
140
|
-
this._state !== 'Idle') {
|
|
141
|
-
this._state = 'NeedsLoadData';
|
|
142
|
-
} else {
|
|
143
|
-
this._state = 'Coalescing';
|
|
144
|
-
this._loadData();
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
/**
|
|
149
|
-
* Internal implementation: called directly by `loadData`
|
|
150
|
-
* or by `coalesce` using stored parameters.
|
|
151
|
-
*/
|
|
152
|
-
_loadData() {
|
|
153
|
-
if (!this._pendingCallback || !this._pendingLoadDataParams) {
|
|
154
|
-
assert(false);
|
|
155
|
-
return;
|
|
156
|
-
}
|
|
157
|
-
const callback = this._pendingCallback;
|
|
158
|
-
const params = this._pendingLoadDataParams;
|
|
159
|
-
delete this._pendingCallback;
|
|
160
|
-
delete this._pendingLoadDataParams;
|
|
161
132
|
|
|
162
133
|
const perf = (params && params.request && params.request.collectResourceTiming) ?
|
|
163
134
|
new RequestPerformance(params.request) : false;
|
|
164
135
|
|
|
165
|
-
this.
|
|
136
|
+
this._pendingCallback = callback;
|
|
137
|
+
this._pendingRequest = this.loadGeoJSON(params, (err?: Error | null, data?: any | null) => {
|
|
138
|
+
delete this._pendingCallback;
|
|
139
|
+
delete this._pendingRequest;
|
|
140
|
+
|
|
166
141
|
if (err || !data) {
|
|
167
142
|
return callback(err);
|
|
168
143
|
} else if (typeof data !== 'object') {
|
|
@@ -204,35 +179,6 @@ class GeoJSONWorkerSource extends VectorTileWorkerSource {
|
|
|
204
179
|
});
|
|
205
180
|
}
|
|
206
181
|
|
|
207
|
-
/**
|
|
208
|
-
* While processing `loadData`, we coalesce all further
|
|
209
|
-
* `loadData` messages into a single call to _loadData
|
|
210
|
-
* that will happen once we've finished processing the
|
|
211
|
-
* first message. {@link GeoJSONSource#_updateWorkerData}
|
|
212
|
-
* is responsible for sending us the `coalesce` message
|
|
213
|
-
* at the time it receives a response from `loadData`
|
|
214
|
-
*
|
|
215
|
-
* State: Idle
|
|
216
|
-
* ↑ |
|
|
217
|
-
* 'coalesce' 'loadData'
|
|
218
|
-
* | (triggers load)
|
|
219
|
-
* | ↓
|
|
220
|
-
* State: Coalescing
|
|
221
|
-
* ↑ |
|
|
222
|
-
* (triggers load) |
|
|
223
|
-
* 'coalesce' 'loadData'
|
|
224
|
-
* | ↓
|
|
225
|
-
* State: NeedsLoadData
|
|
226
|
-
*/
|
|
227
|
-
coalesce() {
|
|
228
|
-
if (this._state === 'Coalescing') {
|
|
229
|
-
this._state = 'Idle';
|
|
230
|
-
} else if (this._state === 'NeedsLoadData') {
|
|
231
|
-
this._state = 'Coalescing';
|
|
232
|
-
this._loadData();
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
|
|
236
182
|
/**
|
|
237
183
|
* Implements {@link WorkerSource#reloadTile}.
|
|
238
184
|
*
|
|
@@ -264,24 +210,27 @@ class GeoJSONWorkerSource extends VectorTileWorkerSource {
|
|
|
264
210
|
* @param params
|
|
265
211
|
* @param [params.url] A URL to the remote GeoJSON data.
|
|
266
212
|
* @param [params.data] Literal GeoJSON data. Must be provided if `params.url` is not.
|
|
213
|
+
* @returns {Cancelable} A Cancelable object.
|
|
267
214
|
* @private
|
|
268
215
|
*/
|
|
269
|
-
loadGeoJSON(params: LoadGeoJSONParameters, callback: ResponseCallback<any>) {
|
|
216
|
+
loadGeoJSON(params: LoadGeoJSONParameters, callback: ResponseCallback<any>): Cancelable {
|
|
270
217
|
// Because of same origin issues, urls must either include an explicit
|
|
271
218
|
// origin or absolute path.
|
|
272
219
|
// ie: /foo/bar.json or http://example.com/bar.json
|
|
273
220
|
// but not ../foo/bar.json
|
|
274
221
|
if (params.request) {
|
|
275
|
-
getJSON(params.request, callback);
|
|
222
|
+
return getJSON(params.request, callback);
|
|
276
223
|
} else if (typeof params.data === 'string') {
|
|
277
224
|
try {
|
|
278
|
-
|
|
225
|
+
callback(null, JSON.parse(params.data));
|
|
279
226
|
} catch (e) {
|
|
280
|
-
|
|
227
|
+
callback(new Error(`Input data given to '${params.source}' is not a valid GeoJSON object.`));
|
|
281
228
|
}
|
|
282
229
|
} else {
|
|
283
|
-
|
|
230
|
+
callback(new Error(`Input data given to '${params.source}' is not a valid GeoJSON object.`));
|
|
284
231
|
}
|
|
232
|
+
|
|
233
|
+
return {cancel: () => {}};
|
|
285
234
|
}
|
|
286
235
|
|
|
287
236
|
removeSource(params: {
|
|
@@ -70,15 +70,16 @@ class RasterDEMTileSource extends RasterTileSource implements Source {
|
|
|
70
70
|
}
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
-
function done(err,
|
|
73
|
+
function done(err, data) {
|
|
74
74
|
if (err) {
|
|
75
75
|
tile.state = 'errored';
|
|
76
76
|
callback(err);
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
-
if (
|
|
80
|
-
tile.dem =
|
|
79
|
+
if (data) {
|
|
80
|
+
tile.dem = data;
|
|
81
81
|
tile.needsHillshadePrepare = true;
|
|
82
|
+
tile.needsTerrainPrepare = true;
|
|
82
83
|
tile.state = 'loaded';
|
|
83
84
|
callback(null);
|
|
84
85
|
}
|
|
@@ -1690,3 +1690,86 @@ describe('SourceCache#onRemove', () => {
|
|
|
1690
1690
|
expect(sourceOnRemove).toHaveBeenCalled();
|
|
1691
1691
|
});
|
|
1692
1692
|
});
|
|
1693
|
+
|
|
1694
|
+
describe('SourceCache#usedForTerrain', () => {
|
|
1695
|
+
test('loads covering tiles with usedForTerrain with source zoom 0-14', done => {
|
|
1696
|
+
const transform = new Transform();
|
|
1697
|
+
transform.resize(511, 511);
|
|
1698
|
+
transform.zoom = 10;
|
|
1699
|
+
|
|
1700
|
+
const sourceCache = createSourceCache({});
|
|
1701
|
+
sourceCache.usedForTerrain = true;
|
|
1702
|
+
sourceCache.tileSize = 1024;
|
|
1703
|
+
expect(sourceCache.usedForTerrain).toBeTruthy();
|
|
1704
|
+
sourceCache.on('data', (e) => {
|
|
1705
|
+
if (e.sourceDataType === 'metadata') {
|
|
1706
|
+
sourceCache.update(transform);
|
|
1707
|
+
expect(Object.values(sourceCache._tiles).map(t => t.tileID.key)).toEqual(
|
|
1708
|
+
['2tc099', '2tbz99', '2sxs99', '2sxr99', 'pds88', 'eo55', 'pdr88', 'en55', 'p6o88', 'ds55', 'p6n88', 'dr55']
|
|
1709
|
+
);
|
|
1710
|
+
done();
|
|
1711
|
+
}
|
|
1712
|
+
});
|
|
1713
|
+
sourceCache.onAdd(undefined);
|
|
1714
|
+
});
|
|
1715
|
+
|
|
1716
|
+
test('loads covering tiles with usedForTerrain with source zoom 8-14', done => {
|
|
1717
|
+
const transform = new Transform();
|
|
1718
|
+
transform.resize(511, 511);
|
|
1719
|
+
transform.zoom = 10;
|
|
1720
|
+
|
|
1721
|
+
const sourceCache = createSourceCache({minzoom: 8, maxzoom: 14});
|
|
1722
|
+
sourceCache.usedForTerrain = true;
|
|
1723
|
+
sourceCache.tileSize = 1024;
|
|
1724
|
+
sourceCache.on('data', (e) => {
|
|
1725
|
+
if (e.sourceDataType === 'metadata') {
|
|
1726
|
+
sourceCache.update(transform);
|
|
1727
|
+
expect(Object.values(sourceCache._tiles).map(t => t.tileID.key)).toEqual(
|
|
1728
|
+
['2tc099', '2tbz99', '2sxs99', '2sxr99', 'pds88', 'pdr88', 'p6o88', 'p6n88']
|
|
1729
|
+
);
|
|
1730
|
+
done();
|
|
1731
|
+
}
|
|
1732
|
+
});
|
|
1733
|
+
sourceCache.onAdd(undefined);
|
|
1734
|
+
});
|
|
1735
|
+
|
|
1736
|
+
test('loads covering tiles with usedForTerrain with source zoom 0-4', done => {
|
|
1737
|
+
const transform = new Transform();
|
|
1738
|
+
transform.resize(511, 511);
|
|
1739
|
+
transform.zoom = 10;
|
|
1740
|
+
|
|
1741
|
+
const sourceCache = createSourceCache({minzoom: 0, maxzoom: 4});
|
|
1742
|
+
sourceCache.usedForTerrain = true;
|
|
1743
|
+
sourceCache.tileSize = 1024;
|
|
1744
|
+
sourceCache.on('data', (e) => {
|
|
1745
|
+
if (e.sourceDataType === 'metadata') {
|
|
1746
|
+
sourceCache.update(transform);
|
|
1747
|
+
expect(Object.values(sourceCache._tiles).map(t => t.tileID.key)).toEqual(
|
|
1748
|
+
['1033', '3s44', '3r44', '3c44', '3b44', 'z33', 's33', 'r33']
|
|
1749
|
+
);
|
|
1750
|
+
done();
|
|
1751
|
+
}
|
|
1752
|
+
});
|
|
1753
|
+
sourceCache.onAdd(undefined);
|
|
1754
|
+
});
|
|
1755
|
+
|
|
1756
|
+
test('loads covering tiles with usedForTerrain with source zoom 4-4', done => {
|
|
1757
|
+
const transform = new Transform();
|
|
1758
|
+
transform.resize(511, 511);
|
|
1759
|
+
transform.zoom = 10;
|
|
1760
|
+
|
|
1761
|
+
const sourceCache = createSourceCache({minzoom: 4, maxzoom: 4});
|
|
1762
|
+
sourceCache.usedForTerrain = true;
|
|
1763
|
+
sourceCache.tileSize = 1024;
|
|
1764
|
+
sourceCache.on('data', (e) => {
|
|
1765
|
+
if (e.sourceDataType === 'metadata') {
|
|
1766
|
+
sourceCache.update(transform);
|
|
1767
|
+
expect(Object.values(sourceCache._tiles).map(t => t.tileID.key)).toEqual(
|
|
1768
|
+
['3s44', '3r44', '3c44', '3b44']
|
|
1769
|
+
);
|
|
1770
|
+
done();
|
|
1771
|
+
}
|
|
1772
|
+
});
|
|
1773
|
+
sourceCache.onAdd(undefined);
|
|
1774
|
+
});
|
|
1775
|
+
});
|