@naivemap/mapbox-gl-image-layer 0.5.0 → 0.6.0-beta.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 (33) hide show
  1. package/dist/es/ImageLayer.js +232 -0
  2. package/dist/es/arrugator/Arrugator.js +270 -0
  3. package/dist/es/{utils/arrugator.js → arrugator/index.js} +43 -39
  4. package/dist/es/index.js +2 -228
  5. package/dist/es/shaders/image.fragment.glsl.js +1 -1
  6. package/dist/es/shaders/image.vertex.glsl.js +1 -1
  7. package/dist/es/shaders/mask.fragment.glsl.js +1 -1
  8. package/dist/es/shaders/mask.vertex.glsl.js +1 -1
  9. package/dist/js/ImageLayer.d.ts +62 -0
  10. package/dist/js/ImageLayer.js +270 -0
  11. package/dist/js/arrugator/Arrugator.d.ts +18 -0
  12. package/dist/js/arrugator/Arrugator.js +275 -0
  13. package/dist/js/arrugator/index.d.ts +7 -0
  14. package/dist/js/{utils/arrugator.js → arrugator/index.js} +49 -46
  15. package/dist/js/index.d.ts +4 -60
  16. package/dist/js/index.js +7 -256
  17. package/dist/js/shaders/image.fragment.glsl.d.ts +2 -2
  18. package/dist/js/shaders/image.fragment.glsl.js +3 -3
  19. package/dist/js/shaders/image.vertex.glsl.d.ts +2 -2
  20. package/dist/js/shaders/image.vertex.glsl.js +3 -3
  21. package/dist/js/shaders/mask.fragment.glsl.d.ts +2 -2
  22. package/dist/js/shaders/mask.fragment.glsl.js +3 -3
  23. package/dist/js/shaders/mask.vertex.glsl.d.ts +2 -2
  24. package/dist/js/shaders/mask.vertex.glsl.js +3 -3
  25. package/package.json +8 -9
  26. package/LICENSE +0 -21
  27. package/dist/es/utils/image.js +0 -19
  28. package/dist/es/utils/webgl.js +0 -64
  29. package/dist/js/utils/arrugator.d.ts +0 -7
  30. package/dist/js/utils/image.d.ts +0 -7
  31. package/dist/js/utils/image.js +0 -23
  32. package/dist/js/utils/webgl.d.ts +0 -8
  33. package/dist/js/utils/webgl.js +0 -68
@@ -0,0 +1,275 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ /* eslint-disable guard-for-in */
7
+ // @ts-nocheck
8
+ var tinyqueue_1 = __importDefault(require("tinyqueue"));
9
+ var Arrugator = /** @class */ (function () {
10
+ function Arrugator(projector, verts, uv, trigs) {
11
+ this._stepsWithSameEpsilon = 0;
12
+ // The projector function. Must be able to take
13
+ // an array of two numbers [x,y] and return an array of
14
+ // two numbers.
15
+ // The typical use case is a proj4(from,to).forward function.
16
+ this._projector = projector;
17
+ // A two-dimensional array of vertex coordinates. Each vertex is a
18
+ // two-element [x,y] array.
19
+ this._verts = verts;
20
+ // A two-dimensional array of UV-map coordinates. These are intended to
21
+ // represent the [0,0]-[1-1] coordinate space of WebGL textures. Each
22
+ // n-th element is the UV coordinates of the n-th vertex. These shall
23
+ // be linearly interpolated when splitting segments.
24
+ this._uv = uv;
25
+ // A two-dimensional array of vertex coordinates, projected. Each
26
+ // vertex is a two-element [x,y] array.
27
+ this._projVerts = verts.map(projector);
28
+ // A two-dimensional array of triangle vertex IDs. Each triangle is a
29
+ // three-element [v1,v2,v3] array.
30
+ // The mesh is **expected** to be compact, planar, non-overlapping.
31
+ this._trigs = trigs;
32
+ // A map of segments to vertices. Key is the segment index (generated inside
33
+ // arrugator), value is an array of two vertex indices.
34
+ this._segs = [];
35
+ this._segCount = 0;
36
+ // A map of segments to triangles. Key is the segment index (generated inside
37
+ // arrugator), value is an array of triangle indices (all segments should
38
+ // have either 1 or 2 triangles associated)
39
+ this._segTrigs = [];
40
+ // A priority queue of segments, ordered by their epsilons, in descending order.
41
+ this._queue = new tinyqueue_1.default([], function (a, b) {
42
+ return b.epsilon - a.epsilon;
43
+ });
44
+ // A map of vertex indices to segment indices.
45
+ this._vertToSeg = new Array(verts.length);
46
+ for (var i in this._verts) {
47
+ this._vertToSeg[i] = [];
48
+ }
49
+ /// NOTE: Not using .fill([]), because that would use a reference to the *same*
50
+ /// empty array for every element.
51
+ for (var t in this._trigs) {
52
+ var trig = this._trigs[t];
53
+ var v0 = trig[0];
54
+ var v1 = trig[1];
55
+ var v2 = trig[2];
56
+ this._segment(v0, v1, t);
57
+ this._segment(v1, v2, t);
58
+ this._segment(v2, v0, t);
59
+ }
60
+ }
61
+ // Returns the segment index linking the two given vertex indices;
62
+ // Must be passed a triangle index to use as context.
63
+ // Might create a new segment index (as well as segment data structure and
64
+ // entry in the priority queue).
65
+ Arrugator.prototype._segment = function (v1, v2, t, maxEpsilon) {
66
+ if (maxEpsilon === void 0) { maxEpsilon = Infinity; }
67
+ if (this._vertToSeg[v1] && this._vertToSeg[v1][v2] !== undefined) {
68
+ var found = this._vertToSeg[v1][v2];
69
+ if (!this._segTrigs[found].includes(t)) {
70
+ this._segTrigs[found].push(t);
71
+ }
72
+ return found;
73
+ }
74
+ var segIdx = this._segCount++;
75
+ this._segs[segIdx] = [v1, v2];
76
+ this._vertToSeg[v1][v2] = segIdx;
77
+ this._vertToSeg[v2][v1] = segIdx;
78
+ this._segTrigs[segIdx] = [t];
79
+ // Calculate segment epsilon
80
+ // The "epsilon" of a segment is the square of the midpoint projection distance:
81
+ // i.e. the square of the distance between:
82
+ // - the projected midpoint of the two vertices, and
83
+ // - the midpoint of the two projected vertices,
84
+ // the distance function being euclidean distance in the "destination"
85
+ // projection, squared.
86
+ var midpoint = [
87
+ (this._verts[v1][0] + this._verts[v2][0]) / 2,
88
+ (this._verts[v1][1] + this._verts[v2][1]) / 2,
89
+ ];
90
+ var projectedMid = this._projector(midpoint);
91
+ var midProjected = [
92
+ (this._projVerts[v1][0] + this._projVerts[v2][0]) / 2,
93
+ (this._projVerts[v1][1] + this._projVerts[v2][1]) / 2,
94
+ ];
95
+ var epsilon = Math.pow((projectedMid[0] - midProjected[0]), 2) + Math.pow((projectedMid[1] - midProjected[1]), 2);
96
+ if (Number.isFinite(epsilon) && epsilon < maxEpsilon) {
97
+ this._queue.push({
98
+ v1: v1,
99
+ v2: v2,
100
+ epsilon: epsilon,
101
+ midpoint: midpoint,
102
+ projectedMid: projectedMid,
103
+ });
104
+ }
105
+ return segIdx;
106
+ };
107
+ // Outputs shallow copies of some data structures at the current step.
108
+ Arrugator.prototype.output = function () {
109
+ // Most data structs are 2-dimensional arrays, and doing a shallow copy
110
+ // of the first level *should* just work.
111
+ return {
112
+ unprojected: Array.from(this._verts),
113
+ projected: Array.from(this._projVerts),
114
+ uv: Array.from(this._uv),
115
+ trigs: Array.from(this._trigs),
116
+ };
117
+ };
118
+ // Subdivides the mesh until the maximum segment epsilon is below the
119
+ // given threshold.
120
+ // The `targetEpsilon` parameter must be in the same units as the
121
+ // internal epsilons: units of the projected CRS, **squared**.
122
+ Arrugator.prototype.lowerEpsilon = function (targetEpsilon) {
123
+ var currentEpsilon = this._queue.peek().epsilon;
124
+ var lastEpsilon = currentEpsilon;
125
+ while (currentEpsilon >= targetEpsilon) {
126
+ this.step();
127
+ currentEpsilon = this._queue.peek().epsilon;
128
+ if (currentEpsilon === lastEpsilon) {
129
+ this._stepsWithSameEpsilon++;
130
+ if (this._stepsWithSameEpsilon < 500) {
131
+ console.warn('Arrugator stopped due to epsilon stall. Raster may need hints for proper arrugation.');
132
+ break;
133
+ }
134
+ }
135
+ else {
136
+ this._stepsWithSameEpsilon = 0;
137
+ lastEpsilon = currentEpsilon;
138
+ }
139
+ }
140
+ };
141
+ Object.defineProperty(Arrugator.prototype, "epsilon", {
142
+ get: function () {
143
+ return this._queue.peek().epsilon;
144
+ },
145
+ set: function (ep) {
146
+ return this.lowerEpsilon(ep);
147
+ },
148
+ enumerable: false,
149
+ configurable: true
150
+ });
151
+ // Triggers subdivision of the segment with the largest epsilon.
152
+ Arrugator.prototype.step = function () {
153
+ var seg = this._queue.pop();
154
+ return this._splitSegment(seg, seg.epsilon);
155
+ };
156
+ // Triggers *one* subdivision of *all* segments in the queue.
157
+ // Can be useful to run this prior to stepping, in order to overcome
158
+ // artefacts
159
+ Arrugator.prototype.force = function () {
160
+ var _this = this;
161
+ var segments = this._queue.data;
162
+ this._queue.data = []; // Empties the queue
163
+ this._queue.length = 0;
164
+ segments.forEach(function (seg) { return _this._splitSegment(seg, Infinity); });
165
+ };
166
+ // Splits the given segment.
167
+ // This deletes the segment, spawns a new vertex at the midpoint, and
168
+ // for each triangle the segment was originally a part of (either 1 or 2),
169
+ // the triangle is divided into two.
170
+ Arrugator.prototype._splitSegment = function (seg, maxEpsilon) {
171
+ // Which are the two vertices affected by the popped segment?
172
+ var v1 = seg.v1;
173
+ var v2 = seg.v2;
174
+ var s = this._vertToSeg[v1] && this._vertToSeg[v1][v2];
175
+ // Which triangle(s) are affected by the popped segment?
176
+ var trigs = this._segTrigs[s];
177
+ // Sanity check
178
+ if (trigs.length >= 3) {
179
+ throw new Error('Somehow a segment is shared by three triangles');
180
+ }
181
+ // Clean up refs
182
+ delete this._segTrigs[s];
183
+ delete this._segs[s];
184
+ delete this._vertToSeg[v1][v2];
185
+ delete this._vertToSeg[v2][v1];
186
+ // What is the vertex ID of the new midpoint vertex?
187
+ var vm = this._verts.length;
188
+ this._projVerts[vm] = seg.projectedMid;
189
+ this._verts[vm] = seg.midpoint;
190
+ this._vertToSeg[vm] = [];
191
+ this._uv[vm] = [
192
+ (this._uv[v1][0] + this._uv[v2][0]) / 2,
193
+ (this._uv[v1][1] + this._uv[v2][1]) / 2,
194
+ ];
195
+ for (var _i = 0, trigs_1 = trigs; _i < trigs_1.length; _i++) {
196
+ var t = trigs_1[_i];
197
+ this._splitTriangle(v1, v2, vm, t, maxEpsilon);
198
+ }
199
+ };
200
+ // Split a triangle in two.
201
+ // Must be given vertex indices of the segment being splitted, the index of the new
202
+ // midpoint vertex, and the triangle index.
203
+ // Shall silently drop any new segments with an epsilon larger than the
204
+ // given one. This means that the segment shall be in the triangle mesh,
205
+ // but will not be queued and therefore not subdivided ever.
206
+ Arrugator.prototype._splitTriangle = function (v1, v2, vm, t, epsilon) {
207
+ if (epsilon === void 0) { epsilon = Infinity; }
208
+ var tvs = this._trigs[t];
209
+ var v3;
210
+ var winding = false;
211
+ // Fetch the ID of the 3rd vertex in the original triangle, and the winding order
212
+ if (tvs[0] === v1 && tvs[1] === v2) {
213
+ v3 = tvs[2];
214
+ winding = true; // A-B-C
215
+ }
216
+ else if (tvs[1] === v1 && tvs[2] === v2) {
217
+ v3 = tvs[0];
218
+ winding = true; // C-A-B
219
+ }
220
+ else if (tvs[2] === v1 && tvs[0] === v2) {
221
+ v3 = tvs[1];
222
+ winding = true; // B-C-A
223
+ }
224
+ else if (tvs[1] === v1 && tvs[0] === v2) {
225
+ v3 = tvs[2];
226
+ winding = false; // B-A-C
227
+ }
228
+ else if (tvs[2] === v1 && tvs[1] === v2) {
229
+ v3 = tvs[0];
230
+ winding = false; // C-B-A
231
+ }
232
+ else if (tvs[0] === v1 && tvs[2] === v2) {
233
+ v3 = tvs[1];
234
+ winding = false; // A-C-B
235
+ }
236
+ else {
237
+ throw new Error('Data structure mishap: could not fetch 3rd vertex used in triangle');
238
+ }
239
+ // Index of the first "half" triangle will be the reused index of the original triangle
240
+ // Index of the second "half" triangle must be allocated at the end of the triangles structure
241
+ var t2 = this._trigs.length;
242
+ if (winding) {
243
+ this._trigs[t] = [v1, vm, v3];
244
+ this._trigs[t2] = [vm, v2, v3];
245
+ }
246
+ else {
247
+ this._trigs[t] = [vm, v1, v3];
248
+ this._trigs[t2] = [v2, vm, v3];
249
+ }
250
+ // Clean up references from old segments
251
+ var s1 = this._vertToSeg[v1] && this._vertToSeg[v1][v2];
252
+ var s2 = this._vertToSeg[v2] && this._vertToSeg[v2][v3];
253
+ var s3 = this._vertToSeg[v3] && this._vertToSeg[v3][v1];
254
+ function filterTrig(i) {
255
+ return i !== t;
256
+ }
257
+ if (s1 !== undefined) {
258
+ this._segTrigs[s1] = this._segTrigs[s1].filter(filterTrig);
259
+ }
260
+ if (s2 !== undefined) {
261
+ this._segTrigs[s2] = this._segTrigs[s2].filter(filterTrig);
262
+ }
263
+ if (s3 !== undefined) {
264
+ this._segTrigs[s3] = this._segTrigs[s3].filter(filterTrig);
265
+ }
266
+ this._segment(v1, vm, t, epsilon);
267
+ this._segment(vm, v3, t, epsilon);
268
+ this._segment(v3, v1, t, epsilon);
269
+ this._segment(v2, vm, t2, epsilon);
270
+ this._segment(vm, v3, t2, epsilon);
271
+ this._segment(v3, v2, t2, epsilon);
272
+ };
273
+ return Arrugator;
274
+ }());
275
+ exports.default = Arrugator;
@@ -0,0 +1,7 @@
1
+ export type Coordinates = [[number, number], [number, number], [number, number], [number, number]];
2
+ export type ArrugadoFlat = {
3
+ pos: number[];
4
+ uv: number[];
5
+ trigs: number[];
6
+ };
7
+ export declare function initArrugator(fromProj: string, coordinates: Coordinates, step?: number): ArrugadoFlat;
@@ -1,46 +1,49 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.initArrugator = void 0;
7
- // @ts-ignore
8
- var arrugator_1 = __importDefault(require("arrugator"));
9
- var proj4_1 = __importDefault(require("proj4"));
10
- function initArrugator(fromProj, coordinates) {
11
- // 墨卡托投影的左上角坐标,对应 mapbox 左上角起始坐标 [0,0]
12
- var origin = [-20037508.342789244, 20037508.342789244];
13
- // 坐标转换为 Arrugator 坐标 top-left, top-left, top-left, top-left)
14
- var verts = [coordinates[0], coordinates[3], coordinates[1], coordinates[2]];
15
- // 转换为 EPSG:3857
16
- var projector = (0, proj4_1.default)(fromProj, 'EPSG:3857').forward;
17
- // 改写坐标转换函数,因为 mapbox 的墨卡托坐标是 0-1,并且对应地理范围与标准 3857 不同
18
- function forward(coors) {
19
- // 墨卡托坐标
20
- var coor_3857 = projector(coors);
21
- // 墨卡托坐标转换到 0-1 区间,origin 对应 mapbox 0 0点
22
- var mapbox_coor1 = Math.abs((coor_3857[0] - origin[0]) / (20037508.342789244 * 2));
23
- var mapbox_coor2 = Math.abs((coor_3857[1] - origin[1]) / (20037508.342789244 * 2));
24
- return [mapbox_coor1, mapbox_coor2];
25
- }
26
- var epsilon = 0.00000000001;
27
- // 纹理uv坐标
28
- var sourceUV = [
29
- [0, 0],
30
- [0, 1],
31
- [1, 0],
32
- [1, 1], // bottom-right
33
- ];
34
- var arrugator = new arrugator_1.default(forward, verts, sourceUV, [
35
- [0, 1, 3],
36
- [0, 3, 2],
37
- ]);
38
- arrugator.lowerEpsilon(epsilon);
39
- var arrugado = arrugator.output();
40
- return {
41
- pos: arrugado.projected.flat(),
42
- uv: arrugado.uv.flat(),
43
- trigs: arrugado.trigs.flat(), // 三角形索引
44
- };
45
- }
46
- exports.initArrugator = initArrugator;
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.initArrugator = initArrugator;
7
+ var proj4_1 = __importDefault(require("proj4"));
8
+ var Arrugator_1 = __importDefault(require("./Arrugator"));
9
+ function initArrugator(fromProj, coordinates, step) {
10
+ if (step === void 0) { step = 100; }
11
+ // 墨卡托投影的左上角坐标,对应 mapbox 左上角起始坐标 [0,0]
12
+ var origin = [-20037508.342789244, 20037508.342789244];
13
+ // 坐标转换为 Arrugator 坐标 top-left, top-left, top-left, top-left)
14
+ var verts = [coordinates[0], coordinates[3], coordinates[1], coordinates[2]];
15
+ // 转换为 EPSG:3857
16
+ var projector = (0, proj4_1.default)(fromProj, 'EPSG:3857').forward;
17
+ // 改写坐标转换函数,因为 mapbox 的墨卡托坐标是 0-1,并且对应地理范围与标准 3857 不同
18
+ function forward(coors) {
19
+ // 墨卡托坐标
20
+ var coor_3857 = projector(coors);
21
+ // 墨卡托坐标转换到 0-1 区间,origin 对应 mapbox 0 0点
22
+ var mapbox_coor1 = Math.abs((coor_3857[0] - origin[0]) / (20037508.342789244 * 2));
23
+ var mapbox_coor2 = Math.abs((coor_3857[1] - origin[1]) / (20037508.342789244 * 2));
24
+ return [mapbox_coor1, mapbox_coor2];
25
+ }
26
+ // 纹理uv坐标
27
+ var sourceUV = [
28
+ [0, 0], // top-left
29
+ [0, 1], // bottom-left
30
+ [1, 0], // top-right
31
+ [1, 1], // bottom-right
32
+ ];
33
+ var arrugator = new Arrugator_1.default(forward, verts, sourceUV, [
34
+ [0, 1, 3],
35
+ [0, 3, 2],
36
+ ]);
37
+ if (step > 0) {
38
+ arrugator.force();
39
+ for (var i = 0; i < step; i++) {
40
+ arrugator.step();
41
+ }
42
+ }
43
+ var arrugado = arrugator.output();
44
+ return {
45
+ pos: arrugado.projected.flat(), // mapbox 墨卡托坐标
46
+ uv: arrugado.uv.flat(), // uv 纹理
47
+ trigs: arrugado.trigs.flat(), // 三角形索引
48
+ };
49
+ }
@@ -1,60 +1,4 @@
1
- import mapboxgl from 'mapbox-gl';
2
- import type { Coordinates } from './utils/arrugator';
3
- export type { Coordinates } from './utils/arrugator';
4
- export declare type MaskProperty = {
5
- type?: 'in' | 'out';
6
- data: GeoJSON.Polygon | GeoJSON.MultiPolygon;
7
- };
8
- export declare type ImageOption = {
9
- url: string;
10
- projection: string;
11
- coordinates: Coordinates;
12
- resampling?: 'linear' | 'nearest';
13
- opacity?: number;
14
- crossOrigin?: string;
15
- mask?: MaskProperty;
16
- };
17
- export default class ImageLayer implements mapboxgl.CustomLayerInterface {
18
- id: string;
19
- type: 'custom';
20
- renderingMode?: '2d' | '3d' | undefined;
21
- private option;
22
- private map?;
23
- private gl?;
24
- private loaded;
25
- private arrugado;
26
- private programInfo?;
27
- private bufferInfo?;
28
- private texture?;
29
- private stencilChecked;
30
- private maskProperty;
31
- private maskProgramInfo?;
32
- private maskBufferInfo?;
33
- constructor(id: string, option: ImageOption);
34
- onAdd(map: mapboxgl.Map, gl: WebGLRenderingContext): void;
35
- onRemove(map: mapboxgl.Map, gl: WebGLRenderingContext): void;
36
- render(gl: WebGLRenderingContext, matrix: number[]): void;
37
- /**
38
- * Updates the URL, the projection, the coordinates, the opacity or the resampling of the image.
39
- * @param {Object} option Options object.
40
- * @param {string} [option.url] Image URL.
41
- * @param {string} [option.projection] Projection with EPSG code that points to the image..
42
- * @param {Array<Array<number>>} [option.coordinates] Four geographical coordinates,
43
- * @param {number} [option.opacity] opacity of the image.
44
- * @param {string} [option.resampling] The resampling/interpolation method to use for overscaling.
45
- */
46
- updateImage(option: {
47
- url?: string;
48
- projection?: string;
49
- coordinates?: Coordinates;
50
- opacity?: number;
51
- resampling?: 'linear' | 'nearest';
52
- }): this;
53
- /**
54
- * Updates the mask property
55
- * @param {MaskProperty} mask The mask property.
56
- */
57
- updateMask(mask: Partial<MaskProperty>): this;
58
- private loadTexture;
59
- private getMaskBufferInfo;
60
- }
1
+ import ImageLayer from './ImageLayer';
2
+ export type { ImageOption, MaskProperty } from './ImageLayer';
3
+ export default ImageLayer;
4
+ export type { Coordinates } from './arrugator';