maplibre-gl 3.3.0 → 3.4.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.
Files changed (50) hide show
  1. package/LICENSE.txt +1 -1
  2. package/README.md +3 -2
  3. package/build/generate-dist-package.js +7 -2
  4. package/build/generate-typings.ts +1 -1
  5. package/dist/LICENSE.txt +116 -0
  6. package/dist/maplibre-gl-csp-worker.js +1 -1
  7. package/dist/maplibre-gl-csp-worker.js.map +1 -1
  8. package/dist/maplibre-gl-csp.js +1 -1
  9. package/dist/maplibre-gl-csp.js.map +1 -1
  10. package/dist/maplibre-gl-dev.js +243 -100
  11. package/dist/maplibre-gl-dev.js.map +1 -1
  12. package/dist/maplibre-gl.d.ts +28 -15
  13. package/dist/maplibre-gl.js +4 -4
  14. package/dist/maplibre-gl.js.map +1 -1
  15. package/dist/package.json +1 -1
  16. package/package.json +48 -47
  17. package/src/data/dem_data.test.ts +120 -165
  18. package/src/data/dem_data.ts +38 -18
  19. package/src/render/program.ts +15 -0
  20. package/src/source/image_source.test.ts +17 -24
  21. package/src/source/raster_dem_tile_source.ts +15 -2
  22. package/src/source/raster_dem_tile_worker_source.ts +2 -2
  23. package/src/source/raster_tile_source.test.ts +1 -1
  24. package/src/source/raster_tile_source.ts +1 -1
  25. package/src/source/terrain_source_cache.test.ts +1 -1
  26. package/src/source/vector_tile_source.test.ts +1 -1
  27. package/src/source/vector_tile_source.ts +0 -1
  28. package/src/source/vector_tile_worker_source.test.ts +45 -1
  29. package/src/source/vector_tile_worker_source.ts +19 -6
  30. package/src/source/video_source.ts +4 -0
  31. package/src/source/worker_source.ts +6 -2
  32. package/src/style/load_glyph_range.test.ts +6 -8
  33. package/src/style/load_sprite.test.ts +48 -71
  34. package/src/style/style.test.ts +19 -49
  35. package/src/style/style.ts +14 -8
  36. package/src/style/style_layer/line_style_layer.test.ts +50 -0
  37. package/src/style/style_layer/line_style_layer.ts +8 -4
  38. package/src/style/style_layer/variable_text_anchor.ts +1 -1
  39. package/src/ui/control/navigation_control.ts +0 -1
  40. package/src/ui/handler/scroll_zoom.ts +6 -0
  41. package/src/ui/handler_manager.ts +2 -1
  42. package/src/ui/map.test.ts +37 -8
  43. package/src/ui/map.ts +15 -13
  44. package/src/ui/marker.test.ts +25 -0
  45. package/src/ui/marker.ts +9 -2
  46. package/src/ui/popup.ts +1 -1
  47. package/src/util/ajax.test.ts +1 -1
  48. package/src/util/image_request.test.ts +1 -1
  49. package/src/util/test/util.ts +12 -0
  50. package/src/util/throttle.ts +7 -3
package/dist/package.json CHANGED
@@ -1 +1 @@
1
- {"name":"@maplibre/distfiles","type":"commonjs","deprecated":"Please install maplibre-gl from parent directory instead"}
1
+ {"name":"maplibre-gl","type":"commonjs","deprecated":"Please install maplibre-gl from parent directory instead"}
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.0",
4
+ "version": "3.4.0",
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.3",
46
- "@rollup/plugin-json": "^6.0.0",
47
- "@rollup/plugin-node-resolve": "^15.1.0",
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.2",
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.2",
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.1",
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.4.8",
66
- "@types/offscreencanvas": "^2019.7.0",
64
+ "@types/nise": "^1.4.2",
65
+ "@types/node": "^20.8.2",
66
+ "@types/offscreencanvas": "^2019.7.1",
67
67
  "@types/pixelmatch": "^5.2.4",
68
- "@types/pngjs": "^6.0.1",
69
- "@types/react": "^18.2.18",
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.10",
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.2.1",
75
- "@typescript-eslint/parser": "^6.2.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.1179426",
82
+ "devtools-protocol": "^0.0.1205644",
83
83
  "diff": "^5.1.0",
84
84
  "dts-bundle-generator": "^8.0.1",
85
- "eslint": "^8.46.0",
85
+ "eslint": "^8.50.0",
86
86
  "eslint-config-mourner": "^3.0.0",
87
87
  "eslint-plugin-html": "^7.1.0",
88
- "eslint-plugin-import": "^2.28.0",
89
- "eslint-plugin-jest": "^27.2.3",
90
- "eslint-plugin-react": "^7.33.1",
88
+ "eslint-plugin-import": "^2.28.1",
89
+ "eslint-plugin-jest": "^27.4.2",
90
+ "eslint-plugin-react": "^7.33.2",
91
91
  "eslint-plugin-tsdoc": "0.2.17",
92
- "expect": "^29.6.2",
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,29 +107,29 @@
107
107
  "pdf-merger-js": "^4.3.0",
108
108
  "pixelmatch": "^5.3.0",
109
109
  "pngjs": "^7.0.0",
110
- "postcss": "^8.4.27",
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.0.1",
114
+ "puppeteer": "^21.3.6",
115
115
  "react": "^18.2.0",
116
116
  "react-dom": "^18.2.0",
117
- "rollup": "^3.27.2",
117
+ "rollup": "^3.29.4",
118
118
  "rollup-plugin-sourcemaps": "^0.6.3",
119
119
  "rw": "^1.3.3",
120
120
  "semver": "^7.5.4",
121
121
  "shuffle-seed": "^1.1.6",
122
122
  "source-map-explorer": "^2.5.3",
123
123
  "st": "^3.0.0",
124
- "stylelint": "^15.10.2",
124
+ "stylelint": "^15.10.3",
125
125
  "stylelint-config-standard": "^34.0.0",
126
126
  "ts-jest": "^29.1.1",
127
127
  "ts-node": "^10.9.1",
128
- "tslib": "^2.6.1",
129
- "typedoc": "^0.24.8",
130
- "typedoc-plugin-markdown": "^3.15.4",
131
- "typedoc-plugin-missing-exports": "^2.0.1",
132
- "typescript": "^5.1.6"
128
+ "tslib": "^2.6.2",
129
+ "typedoc": "^0.25.1",
130
+ "typedoc-plugin-markdown": "^3.16.0",
131
+ "typedoc-plugin-missing-exports": "^2.1.0",
132
+ "typescript": "^5.2.2"
133
133
  },
134
134
  "overrides": {
135
135
  "postcss-inline-svg": {
@@ -159,6 +159,7 @@
159
159
  "build-benchmarks": "npm run build-dev && rollup --configPlugin @rollup/plugin-typescript -c test/bench/rollup_config_benchmarks.ts",
160
160
  "watch-benchmarks": "rollup --configPlugin @rollup/plugin-typescript -c test/bench/rollup_config_benchmarks.ts --watch",
161
161
  "start-server": "st --no-cache -H 0.0.0.0 --port 9966 .",
162
+ "start-docs": "docker run --rm -it -p 8000:8000 -v ${PWD}:/docs squidfunk/mkdocs-material",
162
163
  "start": "run-p watch-css watch-dev start-server",
163
164
  "start-bench": "run-p watch-css watch-benchmarks start-server",
164
165
  "lint": "eslint --cache --ext .ts,.tsx,.js,.html --ignore-path .gitignore .",
@@ -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() {
@@ -89,6 +89,11 @@ export class Program<Us extends UniformBindings> {
89
89
  }
90
90
  gl.shaderSource(fragmentShader, fragmentSource);
91
91
  gl.compileShader(fragmentShader);
92
+
93
+ if (!gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS)) {
94
+ throw new Error(`Could not compile fragment shader: ${gl.getShaderInfoLog(fragmentShader)}`);
95
+ }
96
+
92
97
  gl.attachShader(this.program, fragmentShader);
93
98
 
94
99
  const vertexShader = gl.createShader(gl.VERTEX_SHADER);
@@ -98,6 +103,11 @@ export class Program<Us extends UniformBindings> {
98
103
  }
99
104
  gl.shaderSource(vertexShader, vertexSource);
100
105
  gl.compileShader(vertexShader);
106
+
107
+ if (!gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS)) {
108
+ throw new Error(`Could not compile vertex shader: ${gl.getShaderInfoLog(vertexShader)}`);
109
+ }
110
+
101
111
  gl.attachShader(this.program, vertexShader);
102
112
 
103
113
  this.attributes = {};
@@ -113,6 +123,11 @@ export class Program<Us extends UniformBindings> {
113
123
  }
114
124
 
115
125
  gl.linkProgram(this.program);
126
+
127
+ if (!gl.getProgramParameter(this.program, gl.LINK_STATUS)) {
128
+ throw new Error(`Program failed to link: ${gl.getProgramInfoLog(this.program)}`);
129
+ }
130
+
116
131
  gl.deleteShader(vertexShader);
117
132
  gl.deleteShader(fragmentShader);
118
133