@maplibre/geojson-vt 5.0.3 → 6.0.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 (65) hide show
  1. package/README.md +3 -13
  2. package/dist/clip.d.ts +22 -0
  3. package/dist/clip.d.ts.map +1 -0
  4. package/dist/clip.test.d.ts +2 -0
  5. package/dist/clip.test.d.ts.map +1 -0
  6. package/dist/cluster-tile-index.d.ts +76 -0
  7. package/dist/cluster-tile-index.d.ts.map +1 -0
  8. package/dist/cluster-tile-index.test.d.ts +2 -0
  9. package/dist/cluster-tile-index.test.d.ts.map +1 -0
  10. package/dist/convert.d.ts +17 -0
  11. package/dist/convert.d.ts.map +1 -0
  12. package/dist/deconvert.d.ts +19 -0
  13. package/dist/deconvert.d.ts.map +1 -0
  14. package/dist/deconvert.test.d.ts +2 -0
  15. package/dist/deconvert.test.d.ts.map +1 -0
  16. package/dist/definitions.d.ts +241 -0
  17. package/dist/definitions.d.ts.map +1 -0
  18. package/dist/difference.d.ts +67 -0
  19. package/dist/difference.d.ts.map +1 -0
  20. package/dist/difference.test.d.ts +2 -0
  21. package/dist/difference.test.d.ts.map +1 -0
  22. package/dist/feature.d.ts +20 -0
  23. package/dist/feature.d.ts.map +1 -0
  24. package/dist/geojson-to-tile.d.ts +35 -0
  25. package/dist/geojson-to-tile.d.ts.map +1 -0
  26. package/dist/geojson-vt-dev.js +1582 -478
  27. package/dist/geojson-vt.js +1 -1
  28. package/dist/geojson-vt.mjs +1250 -473
  29. package/dist/geojson-vt.mjs.map +1 -1
  30. package/dist/geojsonvt.d.ts +76 -0
  31. package/dist/geojsonvt.d.ts.map +1 -0
  32. package/dist/geojsonvt.test.d.ts +2 -0
  33. package/dist/geojsonvt.test.d.ts.map +1 -0
  34. package/dist/index.d.ts +9 -0
  35. package/dist/index.d.ts.map +1 -0
  36. package/dist/simplify.d.ts +9 -0
  37. package/dist/simplify.d.ts.map +1 -0
  38. package/dist/simplify.test.d.ts +2 -0
  39. package/dist/simplify.test.d.ts.map +1 -0
  40. package/dist/tile-index.d.ts +51 -0
  41. package/dist/tile-index.d.ts.map +1 -0
  42. package/dist/tile.d.ts +12 -0
  43. package/dist/tile.d.ts.map +1 -0
  44. package/dist/transform.d.ts +10 -0
  45. package/dist/transform.d.ts.map +1 -0
  46. package/dist/wrap.d.ts +3 -0
  47. package/dist/wrap.d.ts.map +1 -0
  48. package/package.json +26 -12
  49. package/src/clip.ts +119 -81
  50. package/src/cluster-tile-index.test.ts +205 -0
  51. package/src/cluster-tile-index.ts +513 -0
  52. package/src/convert.ts +97 -75
  53. package/src/deconvert.test.ts +153 -0
  54. package/src/deconvert.ts +92 -0
  55. package/src/definitions.ts +196 -18
  56. package/src/difference.ts +3 -3
  57. package/src/feature.ts +11 -4
  58. package/src/geojson-to-tile.ts +58 -0
  59. package/src/geojsonvt.test.ts +39 -0
  60. package/src/geojsonvt.ts +209 -0
  61. package/src/index.ts +27 -378
  62. package/src/tile-index.ts +310 -0
  63. package/src/tile.ts +92 -103
  64. package/src/transform.ts +41 -39
  65. package/src/wrap.ts +4 -4
@@ -0,0 +1,205 @@
1
+ import {test, expect} from 'vitest';
2
+ import {readFileSync} from 'fs';
3
+ import {ClusterTileIndex} from './cluster-tile-index';
4
+ import type {ClusterProperties, GeoJSONVTTile} from './definitions';
5
+
6
+ const places = JSON.parse(readFileSync(new URL('../test/fixtures/places.json', import.meta.url), 'utf-8')) as GeoJSON.FeatureCollection<GeoJSON.Point>;
7
+ const placesTile = JSON.parse(readFileSync(new URL('../test/fixtures/places-z0-0-0.json', import.meta.url), 'utf-8')) as GeoJSONVTTile;
8
+ const placesTileMin5 = JSON.parse(readFileSync(new URL('../test/fixtures/places-z0-0-0-min5.json', import.meta.url), 'utf-8')) as GeoJSONVTTile;
9
+
10
+ test('generates clusters properly', () => {
11
+ const index = new ClusterTileIndex();
12
+ index.load(places.features);
13
+ const tile = index.getTile(0, 0, 0);
14
+ expect(tile?.features).toEqual(placesTile.features);
15
+ });
16
+
17
+ test('supports minPoints option', () => {
18
+ const index = new ClusterTileIndex({minPoints: 5});
19
+ index.load(places.features);
20
+ const tile = index.getTile(0, 0, 0);
21
+ expect(tile?.features).toEqual(placesTileMin5.features);
22
+ });
23
+
24
+ test('returns children of a cluster', () => {
25
+ const index = new ClusterTileIndex();
26
+ index.load(places.features);
27
+ const childCounts = index.getChildren(163).map(p => (p.properties as ClusterProperties)?.point_count || 1);
28
+ expect(childCounts).toEqual([6, 7, 2, 1]);
29
+ });
30
+
31
+ test('returns leaves of a cluster', () => {
32
+ const index = new ClusterTileIndex();
33
+ index.load(places.features);
34
+ const leafNames = index.getLeaves(163, 10, 5).map(p => (p.properties as {name: string} | null)?.name);
35
+ expect(leafNames).toEqual([
36
+ 'Niagara Falls',
37
+ 'Cape San Blas',
38
+ 'Cape Sable',
39
+ 'Cape Canaveral',
40
+ 'San Salvador',
41
+ 'Cabo Gracias a Dios',
42
+ 'I. de Cozumel',
43
+ 'Grand Cayman',
44
+ 'Miquelon',
45
+ 'Cape Bauld'
46
+ ]);
47
+ });
48
+
49
+ test('generates unique ids with generateId option', () => {
50
+ const index = new ClusterTileIndex({generateId: true});
51
+ index.load(places.features);
52
+ const tile = index.getTile(0, 0, 0)!;
53
+ const ids = tile.features.filter(f => !(f.tags as ClusterProperties)?.cluster).map(f => f.id);
54
+ expect(ids).toEqual([12, 20, 21, 22, 24, 28, 30, 62, 81, 118, 119, 125, 81, 118]);
55
+ });
56
+
57
+ test('getLeaves handles null-property features', () => {
58
+ const index = new ClusterTileIndex();
59
+ index.load(places.features.concat([{
60
+ type: 'Feature',
61
+ properties: null,
62
+ geometry: {
63
+ type: 'Point',
64
+ coordinates: [-79.04411780507252, 43.08771393436908]
65
+ }
66
+ }]));
67
+ const leaves = index.getLeaves(164, 1, 6);
68
+ expect(leaves[0].properties).toBe(null);
69
+ });
70
+
71
+ test('returns cluster expansion zoom', () => {
72
+ const index = new ClusterTileIndex();
73
+ index.load(places.features);
74
+ expect(index.getClusterExpansionZoom(163)).toBe(1);
75
+ expect(index.getClusterExpansionZoom(195)).toBe(1);
76
+ expect(index.getClusterExpansionZoom(580)).toBe(2);
77
+ expect(index.getClusterExpansionZoom(1156)).toBe(2);
78
+ expect(index.getClusterExpansionZoom(4133)).toBe(3);
79
+ });
80
+
81
+ test('returns cluster expansion zoom for maxZoom', () => {
82
+ const index = new ClusterTileIndex({
83
+ radius: 60,
84
+ extent: 256,
85
+ maxZoom: 4,
86
+ });
87
+ index.load(places.features);
88
+
89
+ expect(index.getClusterExpansionZoom(2503)).toBe(5);
90
+ });
91
+
92
+ test('aggregates cluster properties with reduce', () => {
93
+ const index = new ClusterTileIndex({
94
+ map: (props) => ({sum: (props as {scalerank: number})?.scalerank}),
95
+ reduce: (a, b) => { (a as {sum: number}).sum += (b as {sum: number}).sum; },
96
+ radius: 100
97
+ });
98
+ index.load(places.features);
99
+
100
+ expect(index.getTile(1, 0, 0)!.features.map(f => (f.tags as {sum?: number})?.sum).filter(Boolean)).toEqual(
101
+ [146, 84, 63, 23, 34, 12, 19, 29, 8, 8, 80, 35]);
102
+ expect(index.getTile(0, 0, 0)!.features.map(f => (f.tags as {sum?: number})?.sum).filter(Boolean)).toEqual(
103
+ [298, 122, 12, 36, 98, 7, 24, 8, 125, 98, 125, 12, 36, 8]);
104
+ });
105
+
106
+ test('uses default map function with reduce', () => {
107
+ const index = new ClusterTileIndex({
108
+ reduce: () => {},
109
+ radius: 100
110
+ });
111
+ index.load(places.features);
112
+
113
+ expect(index.getTile(0, 0, 0)).toBeTruthy();
114
+ });
115
+
116
+ test('returns clusters when query crosses international dateline', () => {
117
+ const index = new ClusterTileIndex();
118
+ index.load([
119
+ {
120
+ type: 'Feature',
121
+ properties: null,
122
+ geometry: {
123
+ type: 'Point',
124
+ coordinates: [-178.989, 0]
125
+ }
126
+ }, {
127
+ type: 'Feature',
128
+ properties: null,
129
+ geometry: {
130
+ type: 'Point',
131
+ coordinates: [-178.990, 0]
132
+ }
133
+ }, {
134
+ type: 'Feature',
135
+ properties: null,
136
+ geometry: {
137
+ type: 'Point',
138
+ coordinates: [-178.991, 0]
139
+ }
140
+ }, {
141
+ type: 'Feature',
142
+ properties: null,
143
+ geometry: {
144
+ type: 'Point',
145
+ coordinates: [-178.992, 0]
146
+ }
147
+ }
148
+ ]);
149
+
150
+ const nonCrossing = index.getClusters([-179, -10, -177, 10], 1);
151
+ const crossing = index.getClusters([179, -10, -177, 10], 1);
152
+
153
+ expect(nonCrossing.length).toBeGreaterThan(0);
154
+ expect(crossing.length).toBeGreaterThan(0);
155
+ expect(nonCrossing.length).toBe(crossing.length);
156
+ });
157
+
158
+ test('does not crash on weird bbox values', () => {
159
+ const index = new ClusterTileIndex();
160
+ index.load(places.features);
161
+ expect(index.getClusters([129.426390, -103.720017, -445.930843, 114.518236], 1).length).toBe(26);
162
+ expect(index.getClusters([112.207836, -84.578666, -463.149397, 120.169159], 1).length).toBe(27);
163
+ expect(index.getClusters([129.886277, -82.332680, -445.470956, 120.390930], 1).length).toBe(26);
164
+ expect(index.getClusters([458.220043, -84.239039, -117.137190, 120.206585], 1).length).toBe(25);
165
+ expect(index.getClusters([456.713058, -80.354196, -118.644175, 120.539148], 1).length).toBe(25);
166
+ expect(index.getClusters([453.105328, -75.857422, -122.251904, 120.732760], 1).length).toBe(25);
167
+ expect(index.getClusters([-180, -90, 180, 90], 1).length).toBe(61);
168
+ });
169
+
170
+ test('does not crash on non-integer zoom values', () => {
171
+ const index = new ClusterTileIndex();
172
+ index.load(places.features);
173
+ expect(index.getClusters([179, -10, -177, 10], 1.25)).toBeTruthy();
174
+ });
175
+
176
+ test('makes sure same-location points are clustered', () => {
177
+ const index = new ClusterTileIndex({
178
+ maxZoom: 20,
179
+ extent: 8192,
180
+ radius: 16
181
+ });
182
+ index.load([
183
+ {type: 'Feature', properties: null, geometry: {type: 'Point', coordinates: [-1.426798, 53.943034]}},
184
+ {type: 'Feature', properties: null, geometry: {type: 'Point', coordinates: [-1.426798, 53.943034]}}
185
+ ]);
186
+
187
+ expect(index.trees[20].ids.length).toBe(1);
188
+ });
189
+
190
+ test('makes sure unclustered point coords are not rounded', () => {
191
+ const index = new ClusterTileIndex({maxZoom: 19});
192
+ index.load([
193
+ {type: 'Feature', properties: null, geometry: {type: 'Point', coordinates: [173.19150559062456, -41.340357424709275]}}
194
+ ]);
195
+
196
+ expect(index.getTile(20, 1028744, 656754)!.features[0].geometry[0]).toEqual([421, 281]);
197
+ });
198
+
199
+ test('does not throw on zero items', () => {
200
+ expect(() => {
201
+ const index = new ClusterTileIndex();
202
+ index.load([]);
203
+ expect(index.getClusters([-180, -85, 180, 85], 0)).toEqual([]);
204
+ }).not.toThrow();
205
+ });