@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
package/src/convert.ts CHANGED
@@ -1,131 +1,147 @@
1
1
 
2
2
  import {simplify} from './simplify';
3
3
  import {createFeature} from './feature';
4
- import type { GeoJSONVTInternalFeature, GeoJSONVTOptions, StartEndSizeArray } from './definitions';
4
+ import type {GeoJSONVTInternalFeature, GeoJSONVTOptions, StartEndSizeArray} from './definitions';
5
5
 
6
6
  /**
7
- * converts GeoJSON feature into an intermediate projected JSON vector format with simplification data
7
+ * converts GeoJSON to internal source features (an intermediate projected JSON vector format with simplification data)
8
8
  * @param data
9
9
  * @param options
10
10
  * @returns
11
11
  */
12
- export function convert(data: GeoJSON.GeoJSON, options: GeoJSONVTOptions): GeoJSONVTInternalFeature[] {
12
+ export function convertToInternal(data: GeoJSON.GeoJSON, options: GeoJSONVTOptions): GeoJSONVTInternalFeature[] {
13
13
  const features: GeoJSONVTInternalFeature[] = [];
14
14
 
15
15
  switch (data.type) {
16
16
  case 'FeatureCollection':
17
17
  for (let i = 0; i < data.features.length; i++) {
18
- convertFeature(features, data.features[i], options, i);
18
+ featureToInternal(features, data.features[i], options, i);
19
19
  }
20
20
  break;
21
21
  case 'Feature':
22
- convertFeature(features, data, options);
22
+ featureToInternal(features, data, options);
23
23
  break;
24
24
  default:
25
- convertFeature(features, {type: "Feature" as const, geometry: data, properties: undefined}, options);
25
+ featureToInternal(features, {type: "Feature" as const, geometry: data, properties: undefined}, options);
26
26
  }
27
27
 
28
28
  return features;
29
29
  }
30
30
 
31
- function convertFeature(features: GeoJSONVTInternalFeature[], geojson: GeoJSON.Feature, options: GeoJSONVTOptions, index?: number) {
31
+ function featureToInternal(features: GeoJSONVTInternalFeature[], geojson: GeoJSON.Feature, options: GeoJSONVTOptions, index?: number) {
32
32
  if (!geojson.geometry) return;
33
33
 
34
34
  if (geojson.geometry.type === 'GeometryCollection') {
35
- for (const singleGeometry of geojson.geometry.geometries) {
36
- convertFeature(features, {
37
- id: geojson.id,
38
- type: 'Feature',
39
- geometry: singleGeometry,
40
- properties: geojson.properties
41
- }, options, index);
42
- }
35
+ convertGeometryCollection(features, geojson, geojson.geometry, options, index);
43
36
  return;
44
37
  }
45
38
 
46
39
  const coords = geojson.geometry.coordinates;
47
40
  if (!coords?.length) return;
48
41
 
42
+ const id = getFeatureId(geojson, options, index);
49
43
  const tolerance = Math.pow(options.tolerance / ((1 << options.maxZoom) * options.extent), 2);
50
- let id = geojson.id;
51
- if (options.promoteId) {
52
- id = geojson.properties?.[options.promoteId];
53
- } else if (options.generateId) {
54
- id = index || 0;
55
- }
56
44
 
57
45
  switch (geojson.geometry.type) {
58
- case 'Point': {
59
- const pointGeometry: StartEndSizeArray = [];
60
- convertPoint(geojson.geometry.coordinates, pointGeometry);
46
+ case 'Point':
47
+ convertPointFeature(features, id, geojson.geometry, geojson.properties);
48
+ return;
61
49
 
62
- features.push(createFeature(id, geojson.geometry.type, pointGeometry, geojson.properties));
50
+ case 'MultiPoint':
51
+ convertMultiPointFeature(features, id, geojson.geometry, geojson.properties);
63
52
  return;
64
- }
65
53
 
66
- case 'MultiPoint': {
67
- const multiPointGeometry: StartEndSizeArray = [];
68
- for (const p of geojson.geometry.coordinates) {
69
- convertPoint(p, multiPointGeometry);
70
- }
54
+ case 'LineString':
55
+ convertLineStringFeature(features, id, geojson.geometry, tolerance, geojson.properties);
56
+ return;
71
57
 
72
- features.push(createFeature(id, geojson.geometry.type, multiPointGeometry, geojson.properties));
58
+ case 'MultiLineString':
59
+ convertMultiLineStringFeature(features, id, geojson.geometry, tolerance, options, geojson.properties);
73
60
  return;
74
- }
75
61
 
76
- case 'LineString': {
77
- const lineGeometry: StartEndSizeArray = [];
78
- convertLine(geojson.geometry.coordinates, lineGeometry, tolerance, false);
62
+ case 'Polygon':
63
+ convertPolygonFeature(features, id, geojson.geometry, tolerance, geojson.properties);
64
+ return;
79
65
 
80
- features.push(createFeature(id, geojson.geometry.type, lineGeometry, geojson.properties));
66
+ case 'MultiPolygon':
67
+ convertMultiPolygonFeature(features, id, geojson.geometry, tolerance, geojson.properties);
81
68
  return;
82
- }
83
69
 
84
- case 'MultiLineString': {
85
- if (options.lineMetrics) {
86
- // explode into linestrings in order to track metrics
87
- for (const line of geojson.geometry.coordinates) {
88
- const lineGeometry: StartEndSizeArray = [];
89
- convertLine(line, lineGeometry, tolerance, false);
90
- features.push(createFeature(id, 'LineString', lineGeometry, geojson.properties));
91
- }
92
- return;
93
- }
70
+ default:
71
+ throw new Error('Input data is not a valid GeoJSON object.');
72
+ }
73
+ }
94
74
 
95
- const multiLineGeometry: StartEndSizeArray[] = [];
96
- convertLines(geojson.geometry.coordinates, multiLineGeometry, tolerance, false);
75
+ function getFeatureId(geojson: GeoJSON.Feature, options: GeoJSONVTOptions, index?: number): number | string | undefined {
76
+ if (options.promoteId) {
77
+ return geojson.properties?.[options.promoteId];
78
+ }
79
+ if (options.generateId) {
80
+ return index || 0;
81
+ }
82
+ return geojson.id;
83
+ }
97
84
 
98
- features.push(createFeature(id, geojson.geometry.type, multiLineGeometry, geojson.properties));
99
- return;
100
- }
85
+ function convertGeometryCollection(features: GeoJSONVTInternalFeature[], geojson: GeoJSON.Feature, geometry: GeoJSON.GeometryCollection, options: GeoJSONVTOptions, index?: number) {
86
+ for (const geom of geometry.geometries) {
87
+ featureToInternal(features, {
88
+ id: geojson.id,
89
+ type: 'Feature',
90
+ geometry: geom,
91
+ properties: geojson.properties
92
+ }, options, index);
93
+ }
94
+ }
101
95
 
102
- case 'Polygon': {
103
- const polygonGeometry: StartEndSizeArray[] = [];
104
- convertLines(geojson.geometry.coordinates, polygonGeometry, tolerance, true);
96
+ function convertPointFeature(features: GeoJSONVTInternalFeature[], id: number | string | undefined, geom: GeoJSON.Point, properties: GeoJSON.GeoJsonProperties) {
97
+ const out: number[] = [];
98
+ out.push(projectX(geom.coordinates[0]), projectY(geom.coordinates[1]), 0);
99
+ features.push(createFeature(id, 'Point', out, properties));
100
+ }
105
101
 
106
- features.push(createFeature(id, geojson.geometry.type, polygonGeometry, geojson.properties));
107
- return;
108
- }
102
+ function convertMultiPointFeature(features: GeoJSONVTInternalFeature[], id: number | string | undefined, geom: GeoJSON.MultiPoint, properties: GeoJSON.GeoJsonProperties) {
103
+ const out: number[] = [];
104
+ for (const coords of geom.coordinates) {
105
+ out.push(projectX(coords[0]), projectY(coords[1]), 0);
106
+ }
107
+ features.push(createFeature(id, 'MultiPoint', out, properties));
108
+ }
109
109
 
110
- case 'MultiPolygon': {
111
- const multiPolygonGeometry: StartEndSizeArray[][] = [];
112
- for (const polygon of geojson.geometry.coordinates) {
113
- const newPolygon: StartEndSizeArray[] = [];
114
- convertLines(polygon, newPolygon, tolerance, true);
115
- multiPolygonGeometry.push(newPolygon);
116
- }
110
+ function convertLineStringFeature(features: GeoJSONVTInternalFeature[], id: number | string | undefined, geom: GeoJSON.LineString, tolerance: number, properties: GeoJSON.GeoJsonProperties) {
111
+ const out: StartEndSizeArray = [];
112
+ convertLine(geom.coordinates, out, tolerance, false);
113
+ features.push(createFeature(id, 'LineString', out, properties));
114
+ }
117
115
 
118
- features.push(createFeature(id, geojson.geometry.type, multiPolygonGeometry, geojson.properties));
119
- return;
116
+ function convertMultiLineStringFeature(features: GeoJSONVTInternalFeature[], id: number | string | undefined, geom: GeoJSON.MultiLineString, tolerance: number, options: GeoJSONVTOptions, properties: GeoJSON.GeoJsonProperties) {
117
+ if (options.lineMetrics) {
118
+ // explode into linestrings to be able to track metrics
119
+ for (const line of geom.coordinates) {
120
+ const out: StartEndSizeArray = [];
121
+ convertLine(line, out, tolerance, false);
122
+ features.push(createFeature(id, 'LineString', out, properties));
120
123
  }
121
-
122
- default:
123
- throw new Error('Input data is not a valid GeoJSON object.');
124
+ } else {
125
+ const out: StartEndSizeArray[] = [];
126
+ convertLines(geom.coordinates, out, tolerance, false);
127
+ features.push(createFeature(id, 'MultiLineString', out, properties));
124
128
  }
125
129
  }
126
130
 
127
- function convertPoint(coords: GeoJSON.Position, out: number[]) {
128
- out.push(projectX(coords[0]), projectY(coords[1]), 0);
131
+ function convertPolygonFeature(features: GeoJSONVTInternalFeature[], id: number | string | undefined, geom: GeoJSON.Polygon, tolerance: number, properties: GeoJSON.GeoJsonProperties) {
132
+ const out: StartEndSizeArray[] = [];
133
+ convertLines(geom.coordinates, out, tolerance, true);
134
+ features.push(createFeature(id, 'Polygon', out, properties));
135
+ }
136
+
137
+ function convertMultiPolygonFeature(features: GeoJSONVTInternalFeature[], id: number | string | undefined, geom: GeoJSON.MultiPolygon, tolerance: number, properties: GeoJSON.GeoJsonProperties) {
138
+ const out: StartEndSizeArray[][] = [];
139
+ for (const polygon of geom.coordinates) {
140
+ const polygonOut: StartEndSizeArray[] = [];
141
+ convertLines(polygon, polygonOut, tolerance, true);
142
+ out.push(polygonOut);
143
+ }
144
+ features.push(createFeature(id, 'MultiPolygon', out, properties));
129
145
  }
130
146
 
131
147
  function convertLine(ring: GeoJSON.Position[], out: StartEndSizeArray, tolerance: number, isPolygon: boolean) {
@@ -167,11 +183,17 @@ function convertLines(rings: GeoJSON.Position[][], out: StartEndSizeArray[], tol
167
183
  }
168
184
  }
169
185
 
170
- function projectX(x: number) {
186
+ /**
187
+ * Convert longitude to spherical mercator in [0..1] range
188
+ */
189
+ export function projectX(x: number) {
171
190
  return x / 360 + 0.5;
172
191
  }
173
192
 
174
- function projectY(y: number) {
193
+ /**
194
+ * Convert latitude to spherical mercator in [0..1] range
195
+ */
196
+ export function projectY(y: number) {
175
197
  const sin = Math.sin(y * Math.PI / 180);
176
198
  const y2 = 0.5 - 0.25 * Math.log((1 + sin) / (1 - sin)) / Math.PI;
177
199
  return y2 < 0 ? 0 : y2 > 1 ? 1 : y2;
@@ -0,0 +1,153 @@
1
+
2
+ import {test, expect} from 'vitest';
3
+ import {featureToGeoJSON, unprojectX, unprojectY} from './deconvert';
4
+ import {projectX, projectY} from './convert';
5
+ import type {GeoJSONVTInternalFeature} from './definitions';
6
+
7
+ test('project/unproject roundtrip retains precision', () => {
8
+ const coords = [
9
+ {lng: 0, lat: 0},
10
+ {lng: 90, lat: 45.0564839289},
11
+ {lng: -90, lat: -45.0564839289},
12
+ {lng: 180, lat: 85.0511287798},
13
+ {lng: -180, lat: -85.0511287798},
14
+ {lng: 45.1234567895, lat: 23.5456789012},
15
+ {lng: -123.9876543210, lat: -67.8901234564},
16
+ {lng: 0.0000000001, lat: 0.0000000001},
17
+ {lng: 179.9999999999, lat: 85},
18
+ {lng: -179.9999999999, lat: -85}
19
+ ];
20
+
21
+ for (const {lng, lat} of coords) {
22
+ expect(unprojectX(projectX(lng))).toBeCloseTo(lng, 10);
23
+ expect(unprojectY(projectY(lat))).toBeCloseTo(lat, 10);
24
+ }
25
+ });
26
+
27
+ test('featureToGeoJSON: converts Point geometry', () => {
28
+ const feature: GeoJSONVTInternalFeature = {
29
+ type: 'Point',
30
+ id: 'point1',
31
+ geometry: [0.5, 0.5, 0],
32
+ tags: {name: 'Test Point'},
33
+ minX: 0.5, minY: 0.5, maxX: 0.5, maxY: 0.5
34
+ };
35
+
36
+ const result = featureToGeoJSON(feature);
37
+
38
+ expect(result.type).toBe('Feature');
39
+ expect(result.geometry.type).toBe('Point');
40
+ expect((result.geometry as GeoJSON.Point).coordinates).toEqual([0, 0]);
41
+ expect(result.id).toBe('point1');
42
+ expect(result.properties).toEqual({name: 'Test Point'});
43
+ });
44
+
45
+ test('featureToGeoJSON: converts MultiPoint geometry', () => {
46
+ const feature: GeoJSONVTInternalFeature = {
47
+ type: 'MultiPoint',
48
+ id: 'multipoint1',
49
+ geometry: [0.5, 0.5, 0, 0.525, 0.5, 0],
50
+ tags: {},
51
+ minX: 0.5, minY: 0.5, maxX: 0.525, maxY: 0.5
52
+ };
53
+
54
+ const result = featureToGeoJSON(feature);
55
+
56
+ expect(result.geometry.type).toBe('MultiPoint');
57
+ expect((result.geometry as GeoJSON.MultiPoint).coordinates.length).toBe(2);
58
+ });
59
+
60
+ test('featureToGeoJSON: converts LineString geometry', () => {
61
+ const feature: GeoJSONVTInternalFeature = {
62
+ type: 'LineString',
63
+ id: 'line1',
64
+ geometry: [0.5, 0.5, 0, 0.525, 0.5, 0, 0.525, 0.525, 0],
65
+ tags: {highway: 'primary'},
66
+ minX: 0.5, minY: 0.5, maxX: 0.525, maxY: 0.525
67
+ };
68
+
69
+ const result = featureToGeoJSON(feature);
70
+
71
+ expect(result.geometry.type).toBe('LineString');
72
+ expect((result.geometry as GeoJSON.LineString).coordinates.length).toBe(3);
73
+ });
74
+
75
+ test('featureToGeoJSON: converts MultiLineString geometry', () => {
76
+ const feature: GeoJSONVTInternalFeature = {
77
+ type: 'MultiLineString',
78
+ id: 'multiline1',
79
+ geometry: [
80
+ [0.5, 0.5, 0, 0.525, 0.5, 0],
81
+ [0.55, 0.55, 0, 0.575, 0.55, 0]
82
+ ],
83
+ tags: {},
84
+ minX: 0.5, minY: 0.5, maxX: 0.575, maxY: 0.55
85
+ };
86
+
87
+ const result = featureToGeoJSON(feature);
88
+
89
+ expect(result.geometry.type).toBe('MultiLineString');
90
+ expect((result.geometry as GeoJSON.MultiLineString).coordinates.length).toBe(2);
91
+ });
92
+
93
+ test('featureToGeoJSON: converts Polygon geometry', () => {
94
+ const feature: GeoJSONVTInternalFeature = {
95
+ type: 'Polygon',
96
+ id: 'polygon1',
97
+ geometry: [
98
+ [0.5, 0.5, 0, 0.6, 0.5, 0, 0.6, 0.6, 0, 0.5, 0.6, 0, 0.5, 0.5, 0],
99
+ [0.52, 0.52, 0, 0.58, 0.52, 0, 0.58, 0.58, 0, 0.52, 0.58, 0, 0.52, 0.52, 0]
100
+ ],
101
+ tags: {landuse: 'residential'},
102
+ minX: 0.5, minY: 0.5, maxX: 0.6, maxY: 0.6
103
+ };
104
+
105
+ const result = featureToGeoJSON(feature);
106
+
107
+ expect(result.geometry.type).toBe('Polygon');
108
+ expect((result.geometry as GeoJSON.Polygon).coordinates.length).toBe(2);
109
+ });
110
+
111
+ test('featureToGeoJSON: converts MultiPolygon geometry', () => {
112
+ const feature: GeoJSONVTInternalFeature = {
113
+ type: 'MultiPolygon',
114
+ id: 'multipolygon1',
115
+ geometry: [
116
+ [[0.5, 0.5, 0, 0.52, 0.5, 0, 0.52, 0.52, 0, 0.5, 0.52, 0, 0.5, 0.5, 0]],
117
+ [[0.55, 0.55, 0, 0.57, 0.55, 0, 0.57, 0.57, 0, 0.55, 0.57, 0, 0.55, 0.55, 0]]
118
+ ],
119
+ tags: {},
120
+ minX: 0.5, minY: 0.5, maxX: 0.57, maxY: 0.57
121
+ };
122
+
123
+ const result = featureToGeoJSON(feature);
124
+
125
+ expect(result.geometry.type).toBe('MultiPolygon');
126
+ expect((result.geometry as GeoJSON.MultiPolygon).coordinates.length).toBe(2);
127
+ });
128
+
129
+ test('featureToGeoJSON: handles various id types', () => {
130
+ const noId: GeoJSONVTInternalFeature = {type: 'Point', geometry: [0.5, 0.5, 0], tags: {}, minX: 0.5, minY: 0.5, maxX: 0.5, maxY: 0.5};
131
+ const stringId: GeoJSONVTInternalFeature = {type: 'Point', id: 'string-id', geometry: [0.5, 0.5, 0], tags: {}, minX: 0.5, minY: 0.5, maxX: 0.5, maxY: 0.5};
132
+ const numberId: GeoJSONVTInternalFeature = {type: 'Point', id: 42, geometry: [0.5, 0.5, 0], tags: {}, minX: 0.5, minY: 0.5, maxX: 0.5, maxY: 0.5};
133
+
134
+ expect(featureToGeoJSON(noId).id).toBeUndefined();
135
+ expect(featureToGeoJSON(stringId).id).toBe('string-id');
136
+ expect(featureToGeoJSON(numberId).id).toBe(42);
137
+ });
138
+
139
+ test('featureToGeoJSON: correctly unprojects known coordinates', () => {
140
+ const feature: GeoJSONVTInternalFeature = {
141
+ type: 'MultiPoint',
142
+ geometry: [0.5, 0.5, 0, 0, 0.5, 0, 1, 0.5, 0],
143
+ tags: {},
144
+ minX: 0, minY: 0.5, maxX: 1, maxY: 0.5
145
+ };
146
+
147
+ const result = featureToGeoJSON(feature);
148
+ const coords = (result.geometry as GeoJSON.MultiPoint).coordinates;
149
+
150
+ expect(coords[0]).toEqual([0, 0]);
151
+ expect(coords[1]).toEqual([-180, 0]);
152
+ expect(coords[2]).toEqual([180, 0]);
153
+ });
@@ -0,0 +1,92 @@
1
+ import type {GeoJSONVTInternalFeature} from './definitions';
2
+
3
+ /**
4
+ * Converts internal source features back to GeoJSON format.
5
+ */
6
+ export function convertToGeoJSON(source: GeoJSONVTInternalFeature[]): GeoJSON.GeoJSON {
7
+ const geojson: GeoJSON.GeoJSON = {
8
+ type: 'FeatureCollection',
9
+ features: source.map(feature => featureToGeoJSON(feature))
10
+ };
11
+
12
+ return geojson;
13
+ }
14
+
15
+ /**
16
+ * Converts a single internal feature to GeoJSON format.
17
+ */
18
+ export function featureToGeoJSON(feature: GeoJSONVTInternalFeature): GeoJSON.Feature {
19
+ const geojsonFeature: GeoJSON.Feature = {
20
+ type: 'Feature',
21
+ geometry: geometryToGeoJSON(feature),
22
+ properties: feature.tags
23
+ };
24
+ if (feature.id != null) {
25
+ geojsonFeature.id = feature.id;
26
+ }
27
+
28
+ return geojsonFeature;
29
+ }
30
+
31
+ /**
32
+ * Converts a single internal feature geometry to GeoJSON format.
33
+ */
34
+ function geometryToGeoJSON(feature: GeoJSONVTInternalFeature): GeoJSON.Geometry {
35
+ const {type, geometry} = feature;
36
+
37
+ switch (type) {
38
+ case 'Point':
39
+ return {
40
+ type: type,
41
+ coordinates: unprojectPoint(geometry[0], geometry[1])
42
+ };
43
+ case 'MultiPoint':
44
+ case 'LineString':
45
+ return {
46
+ type: type,
47
+ coordinates: unprojectPoints(geometry)
48
+ };
49
+ case 'MultiLineString':
50
+ case 'Polygon':
51
+ return {
52
+ type: type,
53
+ coordinates: geometry.map(ring => unprojectPoints(ring))
54
+ };
55
+ case 'MultiPolygon':
56
+ return {
57
+ type: type,
58
+ coordinates: geometry.map(polygon =>
59
+ polygon.map(ring => unprojectPoints(ring))
60
+ )
61
+ };
62
+ }
63
+ }
64
+
65
+ export function unprojectPoints(coords: number[]): GeoJSON.Position[] {
66
+ const result: GeoJSON.Position[] = [];
67
+
68
+ for (let i = 0; i < coords.length; i += 3) {
69
+ result.push(unprojectPoint(coords[i], coords[i + 1]));
70
+ }
71
+
72
+ return result;
73
+ }
74
+
75
+ function unprojectPoint(x: number, y: number): GeoJSON.Position {
76
+ return [unprojectX(x), unprojectY(y)];
77
+ }
78
+
79
+ /**
80
+ * Convert spherical mercator in [0..1] range to longitude
81
+ */
82
+ export function unprojectX(x: number): number {
83
+ return (x - 0.5) * 360;
84
+ }
85
+
86
+ /**
87
+ * Convert spherical mercator in [0..1] range to latitude
88
+ */
89
+ export function unprojectY(y: number): number {
90
+ const y2 = (180 - y * 360) * Math.PI / 180;
91
+ return 360 * Math.atan(Math.exp(y2)) / Math.PI - 90;
92
+ }