maplibre-gl 3.3.1 → 3.4.1

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.
Files changed (46) hide show
  1. package/README.md +1 -1
  2. package/dist/maplibre-gl-csp-worker.js +1 -1
  3. package/dist/maplibre-gl-csp-worker.js.map +1 -1
  4. package/dist/maplibre-gl-csp.js +1 -1
  5. package/dist/maplibre-gl-csp.js.map +1 -1
  6. package/dist/maplibre-gl-dev.js +430 -128
  7. package/dist/maplibre-gl-dev.js.map +1 -1
  8. package/dist/maplibre-gl.d.ts +18 -9
  9. package/dist/maplibre-gl.js +4 -4
  10. package/dist/maplibre-gl.js.map +1 -1
  11. package/package.json +41 -41
  12. package/src/data/dem_data.test.ts +120 -165
  13. package/src/data/dem_data.ts +38 -18
  14. package/src/render/glyph_manager.test.ts +10 -9
  15. package/src/render/glyph_manager.ts +17 -10
  16. package/src/source/image_source.test.ts +17 -24
  17. package/src/source/raster_dem_tile_source.ts +36 -11
  18. package/src/source/raster_dem_tile_worker_source.ts +9 -26
  19. package/src/source/raster_tile_source.test.ts +1 -1
  20. package/src/source/raster_tile_source.ts +1 -1
  21. package/src/source/terrain_source_cache.test.ts +1 -1
  22. package/src/source/vector_tile_source.test.ts +1 -1
  23. package/src/source/vector_tile_worker_source.test.ts +45 -1
  24. package/src/source/vector_tile_worker_source.ts +19 -6
  25. package/src/source/worker_source.ts +6 -2
  26. package/src/style/load_glyph_range.test.ts +6 -8
  27. package/src/style/load_sprite.test.ts +48 -71
  28. package/src/style/style.test.ts +19 -49
  29. package/src/style/style.ts +3 -0
  30. package/src/style/style_glyph.ts +4 -3
  31. package/src/style/style_layer/line_style_layer.test.ts +50 -0
  32. package/src/style/style_layer/line_style_layer.ts +8 -4
  33. package/src/symbol/quads.ts +4 -2
  34. package/src/ui/handler/scroll_zoom.ts +6 -0
  35. package/src/ui/handler_manager.ts +2 -1
  36. package/src/ui/map.test.ts +17 -0
  37. package/src/ui/map.ts +1 -0
  38. package/src/ui/marker.test.ts +25 -0
  39. package/src/ui/marker.ts +8 -1
  40. package/src/util/ajax.test.ts +1 -1
  41. package/src/util/image_request.test.ts +1 -1
  42. package/src/util/offscreen_canvas_distorted.test.ts +13 -0
  43. package/src/util/offscreen_canvas_distorted.ts +39 -0
  44. package/src/util/test/util.ts +12 -0
  45. package/src/util/util.test.ts +171 -1
  46. package/src/util/util.ts +150 -0
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "maplibre-gl",
3
3
  "description": "BSD licensed community fork of mapbox-gl, a WebGL interactive maps library",
4
- "version": "3.3.1",
4
+ "version": "3.4.1",
5
5
  "main": "dist/maplibre-gl.js",
6
6
  "style": "dist/maplibre-gl.css",
7
7
  "license": "BSD-3-Clause",
@@ -20,12 +20,12 @@
20
20
  "@mapbox/unitbezier": "^0.0.1",
21
21
  "@mapbox/vector-tile": "^1.3.1",
22
22
  "@mapbox/whoots-js": "^3.1.0",
23
- "@maplibre/maplibre-gl-style-spec": "^19.3.0",
24
- "@types/geojson": "^7946.0.10",
23
+ "@maplibre/maplibre-gl-style-spec": "^19.3.2",
24
+ "@types/geojson": "^7946.0.11",
25
25
  "@types/mapbox__point-geometry": "^0.1.2",
26
- "@types/mapbox__vector-tile": "^1.3.0",
27
- "@types/pbf": "^3.0.2",
28
- "@types/supercluster": "^7.1.0",
26
+ "@types/mapbox__vector-tile": "^1.3.1",
27
+ "@types/pbf": "^3.0.3",
28
+ "@types/supercluster": "^7.1.1",
29
29
  "earcut": "^2.2.4",
30
30
  "geojson-vt": "^3.2.1",
31
31
  "gl-matrix": "^3.4.3",
@@ -42,56 +42,56 @@
42
42
  "devDependencies": {
43
43
  "@mapbox/mapbox-gl-rtl-text": "^0.2.3",
44
44
  "@mapbox/mvt-fixtures": "^3.10.0",
45
- "@rollup/plugin-commonjs": "^25.0.4",
46
- "@rollup/plugin-json": "^6.0.0",
47
- "@rollup/plugin-node-resolve": "^15.2.1",
48
- "@rollup/plugin-replace": "^5.0.2",
49
- "@rollup/plugin-strip": "^3.0.2",
50
- "@rollup/plugin-terser": "^0.4.3",
51
- "@rollup/plugin-typescript": "^11.1.3",
52
- "@types/benchmark": "^2.1.2",
45
+ "@rollup/plugin-commonjs": "^25.0.5",
46
+ "@rollup/plugin-json": "^6.0.1",
47
+ "@rollup/plugin-node-resolve": "^15.2.3",
48
+ "@rollup/plugin-replace": "^5.0.3",
49
+ "@rollup/plugin-strip": "^3.0.3",
50
+ "@rollup/plugin-terser": "^0.4.4",
51
+ "@rollup/plugin-typescript": "^11.1.5",
52
+ "@types/benchmark": "^2.1.3",
53
53
  "@types/cssnano": "^5.0.0",
54
- "@types/d3": "^7.4.0",
55
- "@types/diff": "^5.0.3",
56
- "@types/earcut": "^2.1.1",
57
- "@types/eslint": "^8.44.2",
58
- "@types/gl": "^6.0.2",
54
+ "@types/d3": "^7.4.1",
55
+ "@types/diff": "^5.0.5",
56
+ "@types/earcut": "^2.1.2",
57
+ "@types/eslint": "^8.44.3",
58
+ "@types/gl": "^6.0.3",
59
59
  "@types/glob": "^8.1.0",
60
60
  "@types/jest": "^29.5.3",
61
- "@types/jsdom": "^21.1.2",
62
- "@types/minimist": "^1.2.2",
61
+ "@types/jsdom": "^21.1.3",
62
+ "@types/minimist": "^1.2.3",
63
63
  "@types/murmurhash-js": "^1.0.4",
64
- "@types/nise": "^1.4.1",
65
- "@types/node": "^20.5.7",
66
- "@types/offscreencanvas": "^2019.7.0",
64
+ "@types/nise": "^1.4.2",
65
+ "@types/node": "^20.8.3",
66
+ "@types/offscreencanvas": "^2019.7.1",
67
67
  "@types/pixelmatch": "^5.2.4",
68
- "@types/pngjs": "^6.0.1",
69
- "@types/react": "^18.2.21",
70
- "@types/react-dom": "^18.2.7",
71
- "@types/request": "^2.48.8",
68
+ "@types/pngjs": "^6.0.2",
69
+ "@types/react": "^18.2.25",
70
+ "@types/react-dom": "^18.2.11",
71
+ "@types/request": "^2.48.9",
72
72
  "@types/shuffle-seed": "^1.1.0",
73
73
  "@types/window-or-global": "^1.0.4",
74
- "@typescript-eslint/eslint-plugin": "^6.4.1",
75
- "@typescript-eslint/parser": "^6.4.1",
76
- "address": "^1.2.2",
74
+ "@typescript-eslint/eslint-plugin": "^6.7.4",
75
+ "@typescript-eslint/parser": "^6.7.4",
76
+ "address": "^2.0.1",
77
77
  "benchmark": "^2.1.4",
78
78
  "canvas": "^2.11.2",
79
79
  "cssnano": "^6.0.1",
80
80
  "d3": "^7.8.5",
81
81
  "d3-queue": "^3.0.7",
82
- "devtools-protocol": "^0.0.1188743",
82
+ "devtools-protocol": "^0.0.1206220",
83
83
  "diff": "^5.1.0",
84
84
  "dts-bundle-generator": "^8.0.1",
85
- "eslint": "^8.48.0",
85
+ "eslint": "^8.51.0",
86
86
  "eslint-config-mourner": "^3.0.0",
87
87
  "eslint-plugin-html": "^7.1.0",
88
88
  "eslint-plugin-import": "^2.28.1",
89
- "eslint-plugin-jest": "^27.2.3",
89
+ "eslint-plugin-jest": "^27.4.2",
90
90
  "eslint-plugin-react": "^7.33.2",
91
91
  "eslint-plugin-tsdoc": "0.2.17",
92
- "expect": "^29.6.4",
92
+ "expect": "^29.7.0",
93
93
  "gl": "^6.0.2",
94
- "glob": "^10.3.3",
94
+ "glob": "^10.3.10",
95
95
  "is-builtin-module": "^3.2.1",
96
96
  "jest": "^29.6.2",
97
97
  "jest-canvas-mock": "^2.5.2",
@@ -107,14 +107,14 @@
107
107
  "pdf-merger-js": "^4.3.0",
108
108
  "pixelmatch": "^5.3.0",
109
109
  "pngjs": "^7.0.0",
110
- "postcss": "^8.4.28",
110
+ "postcss": "^8.4.31",
111
111
  "postcss-cli": "^10.1.0",
112
112
  "postcss-inline-svg": "^6.0.0",
113
113
  "pretty-bytes": "^6.1.1",
114
- "puppeteer": "^21.1.0",
114
+ "puppeteer": "^21.3.8",
115
115
  "react": "^18.2.0",
116
116
  "react-dom": "^18.2.0",
117
- "rollup": "^3.28.1",
117
+ "rollup": "^4.0.2",
118
118
  "rollup-plugin-sourcemaps": "^0.6.3",
119
119
  "rw": "^1.3.3",
120
120
  "semver": "^7.5.4",
@@ -126,10 +126,10 @@
126
126
  "ts-jest": "^29.1.1",
127
127
  "ts-node": "^10.9.1",
128
128
  "tslib": "^2.6.2",
129
- "typedoc": "^0.24.8",
129
+ "typedoc": "^0.25.2",
130
130
  "typedoc-plugin-markdown": "^3.16.0",
131
131
  "typedoc-plugin-missing-exports": "^2.1.0",
132
- "typescript": "^5.1.6"
132
+ "typescript": "^5.2.2"
133
133
  },
134
134
  "overrides": {
135
135
  "postcss-inline-svg": {
@@ -47,179 +47,111 @@ describe('DEMData', () => {
47
47
  new DEMData('0', imageData0, 'otherEncoding' as any);
48
48
 
49
49
  expect(spyOnWarnConsole).toHaveBeenCalledTimes(1);
50
- expect(spyOnWarnConsole.mock.calls).toEqual([['\"otherEncoding\" is not a valid encoding type. Valid types include \"mapbox\" and \"terrarium\".']]);
50
+ expect(spyOnWarnConsole.mock.calls).toEqual([['\"otherEncoding\" is not a valid encoding type. Valid types include \"mapbox\", \"terrarium\" and \"custom\".']]);
51
51
  });
52
52
  });
53
53
  });
54
54
 
55
- describe('DEMData#backfillBorder with encoding', () => {
56
- describe('mabox encoding', () => {
57
- const dem0 = new DEMData('0', createMockImage(4, 4), 'mapbox');
58
- const dem1 = new DEMData('1', createMockImage(4, 4), 'mapbox');
59
-
60
- test('border region is initially populated with neighboring data', () => {
61
- let nonempty = true;
62
- for (let x = -1; x < 5; x++) {
63
- for (let y = -1; y < 5; y++) {
64
- if (dem0.get(x, y) === -65536) {
65
- nonempty = false;
66
- break;
67
- }
55
+ function testDEMBorderRegion(dem: DEMData) {
56
+ return () => {
57
+ let nonempty = true;
58
+ for (let x = -1; x < 5; x++) {
59
+ for (let y = -1; y < 5; y++) {
60
+ if (dem.get(x, y) === -65536) {
61
+ nonempty = false;
62
+ break;
68
63
  }
69
64
  }
70
- expect(nonempty).toBeTruthy();
71
-
72
- let verticalBorderMatch = true;
73
- for (const x of [-1, 4]) {
74
- for (let y = 0; y < 4; y++) {
75
- if (dem0.get(x, y) !== dem0.get(x < 0 ? x + 1 : x - 1, y)) {
76
- verticalBorderMatch = false;
77
- break;
78
- }
65
+ }
66
+ expect(nonempty).toBeTruthy();
67
+
68
+ let verticalBorderMatch = true;
69
+ for (const x of [-1, 4]) {
70
+ for (let y = 0; y < 4; y++) {
71
+ if (dem.get(x, y) !== dem.get(x < 0 ? x + 1 : x - 1, y)) {
72
+ verticalBorderMatch = false;
73
+ break;
79
74
  }
80
75
  }
81
- expect(verticalBorderMatch).toBeTruthy();
82
-
83
- // horizontal borders empty
84
- let horizontalBorderMatch = true;
85
- for (const y of [-1, 4]) {
86
- for (let x = 0; x < 4; x++) {
87
- if (dem0.get(x, y) !== dem0.get(x, y < 0 ? y + 1 : y - 1)) {
88
- horizontalBorderMatch = false;
89
- break;
90
- }
76
+ }
77
+ expect(verticalBorderMatch).toBeTruthy();
78
+
79
+ // horizontal borders empty
80
+ let horizontalBorderMatch = true;
81
+ for (const y of [-1, 4]) {
82
+ for (let x = 0; x < 4; x++) {
83
+ if (dem.get(x, y) !== dem.get(x, y < 0 ? y + 1 : y - 1)) {
84
+ horizontalBorderMatch = false;
85
+ break;
91
86
  }
92
87
  }
93
- expect(horizontalBorderMatch).toBeTruthy();
88
+ }
89
+ expect(horizontalBorderMatch).toBeTruthy();
94
90
 
95
- expect(dem0.get(-1, 4) === dem0.get(0, 3)).toBeTruthy();
96
- expect(dem0.get(4, 4) === dem0.get(3, 3)).toBeTruthy();
97
- expect(dem0.get(-1, -1) === dem0.get(0, 0)).toBeTruthy();
98
- expect(dem0.get(4, -1) === dem0.get(3, 0)).toBeTruthy();
99
- });
91
+ expect(dem.get(-1, 4) === dem.get(0, 3)).toBeTruthy();
92
+ expect(dem.get(4, 4) === dem.get(3, 3)).toBeTruthy();
93
+ expect(dem.get(-1, -1) === dem.get(0, 0)).toBeTruthy();
94
+ expect(dem.get(4, -1) === dem.get(3, 0)).toBeTruthy();
95
+ };
96
+ }
100
97
 
101
- test('backfillBorder correctly populates borders with neighboring data', () => {
102
- dem0.backfillBorder(dem1, -1, 0);
103
- for (let y = 0; y < 4; y++) {
98
+ function testDEMBackfill(dem0: DEMData, dem1: DEMData) {
99
+ return () => {
100
+ dem0.backfillBorder(dem1, -1, 0);
101
+ for (let y = 0; y < 4; y++) {
104
102
  // dx = -1, dy = 0, so the left edge of dem1 should equal the right edge of dem0
105
- expect(dem0.get(-1, y) === dem1.get(3, y)).toBeTruthy();
106
- }
103
+ expect(dem0.get(-1, y) === dem1.get(3, y)).toBeTruthy();
104
+ }
107
105
 
108
- dem0.backfillBorder(dem1, 0, -1);
109
- for (let x = 0; x < 4; x++) {
110
- expect(dem0.get(x, -1) === dem1.get(x, 3)).toBeTruthy();
111
- }
106
+ dem0.backfillBorder(dem1, 0, -1);
107
+ for (let x = 0; x < 4; x++) {
108
+ expect(dem0.get(x, -1) === dem1.get(x, 3)).toBeTruthy();
109
+ }
112
110
 
113
- dem0.backfillBorder(dem1, 1, 0);
114
- for (let y = 0; y < 4; y++) {
115
- expect(dem0.get(4, y) === dem1.get(0, y)).toBeTruthy();
116
- }
111
+ dem0.backfillBorder(dem1, 1, 0);
112
+ for (let y = 0; y < 4; y++) {
113
+ expect(dem0.get(4, y) === dem1.get(0, y)).toBeTruthy();
114
+ }
117
115
 
118
- dem0.backfillBorder(dem1, 0, 1);
119
- for (let x = 0; x < 4; x++) {
120
- expect(dem0.get(x, 4) === dem1.get(x, 0)).toBeTruthy();
121
- }
116
+ dem0.backfillBorder(dem1, 0, 1);
117
+ for (let x = 0; x < 4; x++) {
118
+ expect(dem0.get(x, 4) === dem1.get(x, 0)).toBeTruthy();
119
+ }
122
120
 
123
- dem0.backfillBorder(dem1, -1, 1);
124
- expect(dem0.get(-1, 4) === dem1.get(3, 0)).toBeTruthy();
121
+ dem0.backfillBorder(dem1, -1, 1);
122
+ expect(dem0.get(-1, 4) === dem1.get(3, 0)).toBeTruthy();
125
123
 
126
- dem0.backfillBorder(dem1, 1, 1);
127
- expect(dem0.get(4, 4) === dem1.get(0, 0)).toBeTruthy();
124
+ dem0.backfillBorder(dem1, 1, 1);
125
+ expect(dem0.get(4, 4) === dem1.get(0, 0)).toBeTruthy();
128
126
 
129
- dem0.backfillBorder(dem1, -1, -1);
130
- expect(dem0.get(-1, -1) === dem1.get(3, 3)).toBeTruthy();
127
+ dem0.backfillBorder(dem1, -1, -1);
128
+ expect(dem0.get(-1, -1) === dem1.get(3, 3)).toBeTruthy();
131
129
 
132
- dem0.backfillBorder(dem1, 1, -1);
133
- expect(dem0.get(4, -1) === dem1.get(0, 3)).toBeTruthy();
134
- });
130
+ dem0.backfillBorder(dem1, 1, -1);
131
+ expect(dem0.get(4, -1) === dem1.get(0, 3)).toBeTruthy();
132
+ };
133
+ }
134
+
135
+ describe('DEMData#backfillBorder with encoding', () => {
136
+ describe('mabox encoding', () => {
137
+ const dem0 = new DEMData('0', createMockImage(4, 4), 'mapbox');
138
+ const dem1 = new DEMData('1', createMockImage(4, 4), 'mapbox');
139
+
140
+ test('border region is initially populated with neighboring data', testDEMBorderRegion(dem0));
141
+ test('backfillBorder correctly populates borders with neighboring data', testDEMBackfill(dem0, dem1));
135
142
  });
136
143
 
137
144
  describe('terrarium encoding', () => {
138
145
  const dem0 = new DEMData('0', createMockImage(4, 4), 'terrarium');
139
146
  const dem1 = new DEMData('1', createMockImage(4, 4), 'terrarium');
140
147
 
141
- test('border region is initially populated with neighboring data', () => {
142
- let nonempty = true;
143
- for (let x = -1; x < 5; x++) {
144
- for (let y = -1; y < 5; y++) {
145
- if (dem0.get(x, y) === -65536) {
146
- nonempty = false;
147
- break;
148
- }
149
- }
150
- }
151
- expect(nonempty).toBeTruthy();
152
-
153
- let verticalBorderMatch = true;
154
- for (const x of [-1, 4]) {
155
- for (let y = 0; y < 4; y++) {
156
- if (dem0.get(x, y) !== dem0.get(x < 0 ? x + 1 : x - 1, y)) {
157
- verticalBorderMatch = false;
158
- break;
159
- }
160
- }
161
- }
162
- expect(verticalBorderMatch).toBeTruthy();
163
-
164
- // horizontal borders empty
165
- let horizontalBorderMatch = true;
166
- for (const y of [-1, 4]) {
167
- for (let x = 0; x < 4; x++) {
168
- if (dem0.get(x, y) !== dem0.get(x, y < 0 ? y + 1 : y - 1)) {
169
- horizontalBorderMatch = false;
170
- break;
171
- }
172
- }
173
- }
174
- expect(horizontalBorderMatch).toBeTruthy();
175
-
176
- expect(dem0.get(-1, 4) === dem0.get(0, 3)).toBeTruthy();
177
- expect(dem0.get(4, 4) === dem0.get(3, 3)).toBeTruthy();
178
- expect(dem0.get(-1, -1) === dem0.get(0, 0)).toBeTruthy();
179
- expect(dem0.get(4, -1) === dem0.get(3, 0)).toBeTruthy();
180
- });
181
-
182
- test('backfillBorder correctly populates borders with neighboring data', () => {
183
- dem0.backfillBorder(dem1, -1, 0);
184
- for (let y = 0; y < 4; y++) {
185
- // dx = -1, dy = 0, so the left edge of dem1 should equal the right edge of dem0
186
- expect(dem0.get(-1, y) === dem1.get(3, y)).toBeTruthy();
187
- }
188
-
189
- dem0.backfillBorder(dem1, 0, -1);
190
- for (let x = 0; x < 4; x++) {
191
- expect(dem0.get(x, -1) === dem1.get(x, 3)).toBeTruthy();
192
- }
193
-
194
- dem0.backfillBorder(dem1, 1, 0);
195
- for (let y = 0; y < 4; y++) {
196
- expect(dem0.get(4, y) === dem1.get(0, y)).toBeTruthy();
197
- }
198
-
199
- dem0.backfillBorder(dem1, 0, 1);
200
- for (let x = 0; x < 4; x++) {
201
- expect(dem0.get(x, 4) === dem1.get(x, 0)).toBeTruthy();
202
- }
203
-
204
- dem0.backfillBorder(dem1, -1, 1);
205
- expect(dem0.get(-1, 4) === dem1.get(3, 0)).toBeTruthy();
206
-
207
- dem0.backfillBorder(dem1, 1, 1);
208
- expect(dem0.get(4, 4) === dem1.get(0, 0)).toBeTruthy();
209
-
210
- dem0.backfillBorder(dem1, -1, -1);
211
- expect(dem0.get(-1, -1) === dem1.get(3, 3)).toBeTruthy();
212
-
213
- dem0.backfillBorder(dem1, 1, -1);
214
- expect(dem0.get(4, -1) === dem1.get(0, 3)).toBeTruthy();
215
- });
148
+ test('border region is initially populated with neighboring data', testDEMBorderRegion(dem0));
149
+ test('backfillBorder correctly populates borders with neighboring data', testDEMBackfill(dem0, dem1));
216
150
  });
217
151
  });
218
152
 
219
- describe('DEMData is correctly serialized and deserialized', () => {
220
- test('serialized', () => {
221
- const imageData0 = createMockImage(4, 4);
222
- const dem0 = new DEMData('0', imageData0, 'mapbox');
153
+ function testSerialization(dem0: DEMData, redFactor: number, greenFactor: number, blueFactor: number, baseShift: number) {
154
+ return () => {
223
155
  const serialized = serialize(dem0);
224
156
 
225
157
  // calculate min/max values
@@ -239,7 +171,10 @@ describe('DEMData is correctly serialized and deserialized', () => {
239
171
  dim: 4,
240
172
  stride: 6,
241
173
  data: dem0.data,
242
- encoding: 'mapbox',
174
+ redFactor,
175
+ greenFactor,
176
+ blueFactor,
177
+ baseShift,
243
178
  max,
244
179
  min,
245
180
  });
@@ -247,36 +182,56 @@ describe('DEMData is correctly serialized and deserialized', () => {
247
182
  const transferrables = [];
248
183
  serialize(dem0, transferrables);
249
184
  expect(new Uint32Array(transferrables[0])).toEqual(dem0.data);
250
- });
185
+ };
186
+ }
251
187
 
252
- test('deserialized', () => {
253
- const imageData0 = createMockImage(4, 4);
254
- const dem0 = new DEMData('0', imageData0, 'terrarium');
188
+ function testDeserialization(dem0: DEMData) {
189
+ return () => {
255
190
  const serialized = serialize(dem0);
256
191
 
257
192
  const deserialized = deserialize(serialized);
258
193
  expect(deserialized).toEqual(dem0);
259
- });
194
+ };
195
+ }
196
+
197
+ describe('DEMData is correctly serialized and deserialized', () => {
198
+ const mapboxDEM = new DEMData('0', createMockImage(4, 4), 'mapbox');
199
+ const terrariumDEM = new DEMData('0', createMockImage(4, 4), 'terrarium');
200
+ const customDEM = new DEMData('0', createMockImage(4, 4), 'custom', 1.0, 2.0, 3.0, 4.0);
201
+ test('serialized - mapbox', testSerialization(mapboxDEM, 6553.6, 25.6, 0.1, 10000));
202
+ test('serialized - terrarium', testSerialization(terrariumDEM, 256.0, 1.0, 1.0 / 256.0, 32768.0));
203
+ test('serialized - custom', testSerialization(customDEM, 1.0, 2.0, 3.0, 4.0));
204
+
205
+ test('deserialized - mapbox', testDeserialization(mapboxDEM));
206
+ test('deserialized - terrarium', testDeserialization(terrariumDEM));
207
+ test('deserialized - custom', testDeserialization(customDEM));
260
208
  });
261
209
 
262
210
  describe('UnpackVector is correctly returned', () => {
263
- test('terrarium and mapbox', () => {
264
- const imageData1 = createMockImage(4, 4);
265
- const dem1 = new DEMData('0', imageData1, 'terrarium');
266
-
267
- const imageData2 = createMockImage(4, 4);
268
- const dem2 = new DEMData('0', imageData2, 'mapbox');
269
-
270
- expect(dem1.getUnpackVector()).toEqual([256.0, 1.0, 1.0 / 256.0, 32768.0]);
271
- expect(dem2.getUnpackVector()).toEqual([6553.6, 25.6, 0.1, 10000.0]);
211
+ test('terrarium, mapbox and custom', () => {
212
+ const mapboxDEM = new DEMData('0', createMockImage(4, 4), 'mapbox');
213
+ const terrariumDEM = new DEMData('0', createMockImage(4, 4), 'terrarium');
214
+ const customDEM = new DEMData('0', createMockImage(4, 4), 'custom', 1.0, 2.0, 3.0, 4.0);
215
+
216
+ expect(terrariumDEM.getUnpackVector()).toEqual([256.0, 1.0, 1.0 / 256.0, 32768.0]);
217
+ expect(mapboxDEM.getUnpackVector()).toEqual([6553.6, 25.6, 0.1, 10000.0]);
218
+ expect(customDEM.getUnpackVector()).toEqual([1.0, 2.0, 3.0, 4.0]);
272
219
  });
273
220
  });
274
221
 
275
- describe('DEMData#getImage', () => {
276
- test('Image is correctly returned', () => {
277
- const imageData = createMockImage(4, 4);
278
- const dem = new DEMData('0', imageData, 'terrarium');
279
-
222
+ function testGetPixels(dem: DEMData, imageData: RGBAImage) {
223
+ return () => {
280
224
  expect(dem.getPixels()).toEqual(imageData);
281
- });
225
+ };
226
+ }
227
+
228
+ describe('DEMData#getImage', () => {
229
+ const imageData = createMockImage(4, 4);
230
+ const mapboxDEM = new DEMData('0', imageData, 'terrarium');
231
+ const terrariumDEM = new DEMData('0', imageData, 'terrarium');
232
+ const customDEM = new DEMData('0', imageData, 'terrarium');
233
+
234
+ test('Image is correctly returned - mapbox', testGetPixels(mapboxDEM, imageData));
235
+ test('Image is correctly returned - terrarium', testGetPixels(terrariumDEM, imageData));
236
+ test('Image is correctly returned - custom', testGetPixels(customDEM, imageData));
282
237
  });
@@ -13,6 +13,8 @@ import {register} from '../util/web_worker_transfer';
13
13
  // surrounding pixel values to compute the slope at that pixel, and we cannot accurately calculate the slope at pixels on a
14
14
  // tile's edge without backfilling from neighboring tiles.
15
15
 
16
+ export type DEMEncoding = 'mapbox' | 'terrarium' | 'custom'
17
+
16
18
  export class DEMData {
17
19
  uid: string;
18
20
  data: Uint32Array;
@@ -20,21 +22,48 @@ export class DEMData {
20
22
  dim: number;
21
23
  min: number;
22
24
  max: number;
23
- encoding: 'mapbox' | 'terrarium';
25
+ redFactor: number;
26
+ greenFactor: number;
27
+ blueFactor: number;
28
+ baseShift: number;
24
29
 
25
30
  // RGBAImage data has uniform 1px padding on all sides: square tile edge size defines stride
26
31
  // and dim is calculated as stride - 2.
27
- constructor(uid: string, data: RGBAImage, encoding: 'mapbox' | 'terrarium') {
32
+ constructor(uid: string, data: RGBAImage, encoding: DEMEncoding, redFactor = 1.0, greenFactor = 1.0, blueFactor = 1.0, baseShift = 0.0) {
28
33
  this.uid = uid;
29
34
  if (data.height !== data.width) throw new RangeError('DEM tiles must be square');
30
- if (encoding && encoding !== 'mapbox' && encoding !== 'terrarium') {
31
- warnOnce(`"${encoding}" is not a valid encoding type. Valid types include "mapbox" and "terrarium".`);
35
+ if (encoding && !['mapbox', 'terrarium', 'custom'].includes(encoding)) {
36
+ warnOnce(`"${encoding}" is not a valid encoding type. Valid types include "mapbox", "terrarium" and "custom".`);
32
37
  return;
33
38
  }
34
39
  this.stride = data.height;
35
40
  const dim = this.dim = data.height - 2;
36
41
  this.data = new Uint32Array(data.data.buffer);
37
- this.encoding = encoding || 'mapbox';
42
+ switch (encoding) {
43
+ case 'terrarium':
44
+ // unpacking formula for mapzen terrarium:
45
+ // https://aws.amazon.com/public-datasets/terrain/
46
+ this.redFactor = 256.0;
47
+ this.greenFactor = 1.0;
48
+ this.blueFactor = 1.0 / 256.0;
49
+ this.baseShift = 32768.0;
50
+ break;
51
+ case 'custom':
52
+ this.redFactor = redFactor;
53
+ this.greenFactor = greenFactor;
54
+ this.blueFactor = blueFactor;
55
+ this.baseShift = baseShift;
56
+ break;
57
+ case 'mapbox':
58
+ default:
59
+ // unpacking formula for mapbox.terrain-rgb:
60
+ // https://www.mapbox.com/help/access-elevation-data/#mapbox-terrain-rgb
61
+ this.redFactor = 6553.6;
62
+ this.greenFactor = 25.6;
63
+ this.blueFactor = 0.1;
64
+ this.baseShift = 10000.0;
65
+ break;
66
+ }
38
67
 
39
68
  // in order to avoid flashing seams between tiles, here we are initially populating a 1px border of pixels around the image
40
69
  // with the data of the nearest pixel from the image. this data is eventually replaced when the tile's neighboring
@@ -70,12 +99,11 @@ export class DEMData {
70
99
  get(x: number, y: number) {
71
100
  const pixels = new Uint8Array(this.data.buffer);
72
101
  const index = this._idx(x, y) * 4;
73
- const unpack = this.encoding === 'terrarium' ? this._unpackTerrarium : this._unpackMapbox;
74
- return unpack(pixels[index], pixels[index + 1], pixels[index + 2]);
102
+ return this.unpack(pixels[index], pixels[index + 1], pixels[index + 2]);
75
103
  }
76
104
 
77
105
  getUnpackVector() {
78
- return this.encoding === 'terrarium' ? [256.0, 1.0, 1.0 / 256.0, 32768.0] : [6553.6, 25.6, 0.1, 10000.0];
106
+ return [this.redFactor, this.greenFactor, this.blueFactor, this.baseShift];
79
107
  }
80
108
 
81
109
  _idx(x: number, y: number) {
@@ -83,16 +111,8 @@ export class DEMData {
83
111
  return (y + 1) * this.stride + (x + 1);
84
112
  }
85
113
 
86
- _unpackMapbox(r: number, g: number, b: number) {
87
- // unpacking formula for mapbox.terrain-rgb:
88
- // https://www.mapbox.com/help/access-elevation-data/#mapbox-terrain-rgb
89
- return ((r * 256 * 256 + g * 256.0 + b) / 10.0 - 10000.0);
90
- }
91
-
92
- _unpackTerrarium(r: number, g: number, b: number) {
93
- // unpacking formula for mapzen terrarium:
94
- // https://aws.amazon.com/public-datasets/terrain/
95
- return ((r * 256 + g + b / 256) - 32768.0);
114
+ unpack(r: number, g: number, b: number) {
115
+ return (r * this.redFactor + g * this.greenFactor + b * this.blueFactor - this.baseShift);
96
116
  }
97
117
 
98
118
  getPixels() {
@@ -95,13 +95,13 @@ describe('GlyphManager', () => {
95
95
  manager.getGlyphs({'Arial Unicode MS': [0x3005]}, (err, glyphs) => {
96
96
  expect(err).toBeFalsy();
97
97
  expect(glyphs['Arial Unicode MS'][0x3005]).not.toBeNull();
98
- //Request char from Katakana range (te)
98
+ //Request char from Katakana range (te)
99
99
  manager.getGlyphs({'Arial Unicode MS': [0x30C6]}, (err, glyphs) => {
100
100
  expect(err).toBeFalsy();
101
101
  const glyph = glyphs['Arial Unicode MS'][0x30c6];
102
102
  //Ensure that te is locally generated.
103
- expect(glyph.bitmap.height).toBe(6);
104
- expect(glyph.bitmap.width).toBe(6);
103
+ expect(glyph.bitmap.height).toBe(12);
104
+ expect(glyph.bitmap.width).toBe(12);
105
105
  done();
106
106
  });
107
107
  });
@@ -110,9 +110,10 @@ describe('GlyphManager', () => {
110
110
  test('GlyphManager generates CJK PBF locally', done => {
111
111
  const manager = createGlyphManager('sans-serif');
112
112
 
113
+ // character 平
113
114
  manager.getGlyphs({'Arial Unicode MS': [0x5e73]}, (err, glyphs) => {
114
115
  expect(err).toBeFalsy();
115
- expect(glyphs['Arial Unicode MS'][0x5e73].metrics.advance).toBe(1);
116
+ expect(glyphs['Arial Unicode MS'][0x5e73].metrics.advance).toBe(0.5);
116
117
  done();
117
118
  });
118
119
  });
@@ -120,10 +121,10 @@ describe('GlyphManager', () => {
120
121
  test('GlyphManager generates Katakana PBF locally', done => {
121
122
  const manager = createGlyphManager('sans-serif');
122
123
 
123
- // Katakana letter te
124
+ // Katakana letter te
124
125
  manager.getGlyphs({'Arial Unicode MS': [0x30c6]}, (err, glyphs) => {
125
126
  expect(err).toBeFalsy();
126
- expect(glyphs['Arial Unicode MS'][0x30c6].metrics.advance).toBe(1);
127
+ expect(glyphs['Arial Unicode MS'][0x30c6].metrics.advance).toBe(0.5);
127
128
  done();
128
129
  });
129
130
  });
@@ -131,10 +132,10 @@ describe('GlyphManager', () => {
131
132
  test('GlyphManager generates Hiragana PBF locally', done => {
132
133
  const manager = createGlyphManager('sans-serif');
133
134
 
134
- //Hiragana letter te
135
+ //Hiragana letter te
135
136
  manager.getGlyphs({'Arial Unicode MS': [0x3066]}, (err, glyphs) => {
136
137
  expect(err).toBeFalsy();
137
- expect(glyphs['Arial Unicode MS'][0x3066].metrics.advance).toBe(1);
138
+ expect(glyphs['Arial Unicode MS'][0x3066].metrics.advance).toBe(0.5);
138
139
  done();
139
140
  });
140
141
  });
@@ -143,7 +144,7 @@ describe('GlyphManager', () => {
143
144
 
144
145
  const manager = createGlyphManager('sans-serif');
145
146
  const drawSpy = GlyphManager.TinySDF.prototype.draw = jest.fn().mockImplementation(() => {
146
- return {data: new Uint8ClampedArray(900)} as any;
147
+ return {data: new Uint8ClampedArray(60 * 60)} as any;
147
148
  });
148
149
 
149
150
  // Katakana letter te