@loaders.gl/mvt 4.3.0-alpha.2 → 4.3.0-alpha.3

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 (74) hide show
  1. package/dist/dist.dev.js +777 -688
  2. package/dist/dist.min.js +1 -1
  3. package/dist/index.cjs +746 -659
  4. package/dist/index.cjs.map +4 -4
  5. package/dist/index.d.ts +6 -5
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.js +5 -1
  8. package/dist/lib/parse-tilejson.js +1 -1
  9. package/dist/lib/utils/geometry-utils.js +1 -1
  10. package/dist/lib/vector-tiler/{clip.d.ts → features/clip-features.d.ts} +4 -4
  11. package/dist/lib/vector-tiler/features/clip-features.d.ts.map +1 -0
  12. package/dist/lib/vector-tiler/{clip.js → features/clip-features.js} +4 -4
  13. package/dist/lib/vector-tiler/{convert.d.ts → features/convert-feature.d.ts} +7 -7
  14. package/dist/lib/vector-tiler/features/convert-feature.d.ts.map +1 -0
  15. package/dist/lib/vector-tiler/features/convert-feature.js +140 -0
  16. package/dist/lib/vector-tiler/features/proto-feature.d.ts +30 -0
  17. package/dist/lib/vector-tiler/features/proto-feature.d.ts.map +1 -0
  18. package/dist/lib/vector-tiler/features/proto-feature.js +52 -0
  19. package/dist/lib/vector-tiler/{simplify.d.ts → features/simplify-path.d.ts} +2 -2
  20. package/dist/lib/vector-tiler/features/simplify-path.d.ts.map +1 -0
  21. package/dist/lib/vector-tiler/{simplify.js → features/simplify-path.js} +3 -3
  22. package/dist/lib/vector-tiler/{wrap.d.ts → features/wrap-features.d.ts} +5 -5
  23. package/dist/lib/vector-tiler/features/wrap-features.d.ts.map +1 -0
  24. package/dist/lib/vector-tiler/{wrap.js → features/wrap-features.js} +33 -26
  25. package/dist/lib/vector-tiler/proto-tile.d.ts +40 -0
  26. package/dist/lib/vector-tiler/proto-tile.d.ts.map +1 -0
  27. package/dist/lib/vector-tiler/proto-tile.js +138 -0
  28. package/dist/lib/vector-tiler/tile-to-geojson.d.ts +12 -0
  29. package/dist/lib/vector-tiler/tile-to-geojson.d.ts.map +1 -0
  30. package/dist/lib/vector-tiler/tile-to-geojson.js +81 -0
  31. package/dist/lib/vector-tiler/transform-tile.d.ts +7 -0
  32. package/dist/lib/vector-tiler/transform-tile.d.ts.map +1 -0
  33. package/dist/lib/vector-tiler/transform-tile.js +41 -0
  34. package/dist/mvt-loader.js +1 -1
  35. package/dist/mvt-source.d.ts +31 -14
  36. package/dist/mvt-source.d.ts.map +1 -1
  37. package/dist/mvt-source.js +26 -6
  38. package/dist/mvt-worker.js +4 -4
  39. package/dist/table-tile-source.d.ts +66 -36
  40. package/dist/table-tile-source.d.ts.map +1 -1
  41. package/dist/table-tile-source.js +167 -117
  42. package/dist/tilejson-loader.js +1 -1
  43. package/package.json +6 -6
  44. package/src/index.ts +13 -6
  45. package/src/lib/parse-tilejson.ts +1 -1
  46. package/src/lib/utils/geometry-utils.ts +1 -1
  47. package/src/lib/vector-tiler/{clip.ts → features/clip-features.ts} +8 -8
  48. package/src/lib/vector-tiler/{convert.ts → features/convert-feature.ts} +91 -70
  49. package/src/lib/vector-tiler/features/proto-feature.ts +104 -0
  50. package/src/lib/vector-tiler/{simplify.ts → features/simplify-path.ts} +8 -3
  51. package/src/lib/vector-tiler/{wrap.ts → features/wrap-features.ts} +44 -29
  52. package/src/lib/vector-tiler/proto-tile.ts +217 -0
  53. package/src/lib/vector-tiler/tile-to-geojson.ts +105 -0
  54. package/src/lib/vector-tiler/transform-tile.ts +57 -0
  55. package/src/mvt-source.ts +42 -18
  56. package/src/table-tile-source.ts +130 -85
  57. package/src/tilejson-loader.ts +2 -2
  58. package/dist/lib/vector-tiler/clip.d.ts.map +0 -1
  59. package/dist/lib/vector-tiler/convert.d.ts.map +0 -1
  60. package/dist/lib/vector-tiler/convert.js +0 -139
  61. package/dist/lib/vector-tiler/feature.d.ts +0 -3
  62. package/dist/lib/vector-tiler/feature.d.ts.map +0 -1
  63. package/dist/lib/vector-tiler/feature.js +0 -44
  64. package/dist/lib/vector-tiler/simplify.d.ts.map +0 -1
  65. package/dist/lib/vector-tiler/tile.d.ts +0 -38
  66. package/dist/lib/vector-tiler/tile.d.ts.map +0 -1
  67. package/dist/lib/vector-tiler/tile.js +0 -123
  68. package/dist/lib/vector-tiler/transform.d.ts +0 -7
  69. package/dist/lib/vector-tiler/transform.d.ts.map +0 -1
  70. package/dist/lib/vector-tiler/transform.js +0 -41
  71. package/dist/lib/vector-tiler/wrap.d.ts.map +0 -1
  72. package/src/lib/vector-tiler/feature.ts +0 -47
  73. package/src/lib/vector-tiler/tile.ts +0 -187
  74. package/src/lib/vector-tiler/transform.ts +0 -57
@@ -7,30 +7,10 @@
7
7
  // @ts-nocheck
8
8
 
9
9
  import type {Feature, FeatureCollection} from '@loaders.gl/schema';
10
- import type {TableTileFeature} from './tile';
10
+ import type {ProtoFeature} from './proto-feature';
11
11
 
12
- import {simplify} from './simplify';
13
- import {createFeature} from './feature';
14
-
15
- /**
16
- * converts a GeoJSON feature into an intermediate projected JSON vector format
17
- * with simplification data
18
- */
19
- export function convert(data: Feature | FeatureCollection, options): TableTileFeature[] {
20
- const features = [];
21
- if (data.type === 'FeatureCollection') {
22
- for (let i = 0; i < data.features.length; i++) {
23
- convertFeature(features, data.features[i], options, i);
24
- }
25
- } else if (data.type === 'Feature') {
26
- convertFeature(features, data, options);
27
- } else {
28
- // single geometry or a geometry collection
29
- convertFeature(features, {geometry: data}, options);
30
- }
31
-
32
- return features;
33
- }
12
+ import {createProtoFeature} from './proto-feature';
13
+ import {simplifyPath} from './simplify-path';
34
14
 
35
15
  export type ConvertFeatureOptions = {
36
16
  /** max zoom to preserve detail on */
@@ -43,16 +23,43 @@ export type ConvertFeatureOptions = {
43
23
  lineMetrics?: boolean;
44
24
  };
45
25
 
26
+ /**
27
+ * converts a GeoJSON feature into an intermediate projected JSON vector format
28
+ * with simplification data
29
+ */
30
+ export function convertFeaturesToProtoFeature(
31
+ data: Feature | FeatureCollection,
32
+ options: ConvertFeatureOptions
33
+ ): ProtoFeature[] {
34
+ const protoFeatures = [];
35
+ switch (data.type) {
36
+ case 'FeatureCollection':
37
+ let i = 0;
38
+ for (const feature of data.features) {
39
+ protoFeatures.push(convertFeature(feature, options, i++));
40
+ }
41
+ break;
42
+ case 'Feature':
43
+ protoFeatures.push(convertFeature(data, options));
44
+ break;
45
+ default:
46
+ // single geometry or a geometry collection
47
+ protoFeatures.push(convertFeature({geometry: data}, options));
48
+ }
49
+
50
+ return protoFeatures;
51
+ }
52
+
46
53
  /**
47
54
  * converts a GeoJSON feature into an intermediate projected JSON vector format
48
55
  * with simplification data
49
56
  */
50
57
  function convertFeature(
51
- features: TableTileFeature[],
52
58
  geojson: Feature,
53
59
  options: ConvertFeatureOptions,
54
60
  index: number
55
- ): void {
61
+ ): ProtoFeature {
62
+ // GeoJSON geometries can be null, but no vector tile will include them.
56
63
  if (!geojson.geometry) {
57
64
  return;
58
65
  }
@@ -67,53 +74,67 @@ function convertFeature(
67
74
  } else if (options.generateId) {
68
75
  id = index || 0;
69
76
  }
70
- if (type === 'Point') {
71
- convertPoint(coords, geometry);
72
- } else if (type === 'MultiPoint') {
73
- for (const p of coords) {
74
- convertPoint(p, geometry);
75
- }
76
- } else if (type === 'LineString') {
77
- convertLine(coords, geometry, tolerance, false);
78
- } else if (type === 'MultiLineString') {
79
- if (options.lineMetrics) {
80
- // explode into linestrings to be able to track metrics
81
- for (const line of coords) {
82
- geometry = [];
83
- convertLine(line, geometry, tolerance, false);
84
- features.push(createFeature(id, 'LineString', geometry, geojson.properties));
77
+
78
+ switch (type) {
79
+ case 'Point':
80
+ convertPoint(coords, geometry);
81
+ break;
82
+
83
+ case 'MultiPoint':
84
+ for (const p of coords) {
85
+ convertPoint(p, geometry);
85
86
  }
86
- return;
87
- } else {
88
- convertLines(coords, geometry, tolerance, false);
89
- }
90
- } else if (type === 'Polygon') {
91
- convertLines(coords, geometry, tolerance, true);
92
- } else if (type === 'MultiPolygon') {
93
- for (const polygon of coords) {
94
- const newPolygon = [];
95
- convertLines(polygon, newPolygon, tolerance, true);
96
- geometry.push(newPolygon);
97
- }
98
- } else if (type === 'GeometryCollection') {
99
- for (const singleGeometry of geojson.geometry.geometries) {
100
- convertFeature(
101
- features,
102
- {
103
- id,
104
- geometry: singleGeometry,
105
- properties: geojson.properties
106
- },
107
- options,
108
- index
109
- );
110
- }
111
- return;
112
- } else {
113
- throw new Error('Input data is not a valid GeoJSON object.');
87
+ break;
88
+
89
+ case 'LineString':
90
+ convertLine(coords, geometry, tolerance, false);
91
+ break;
92
+
93
+ case 'MultiLineString':
94
+ if (options.lineMetrics) {
95
+ // explode into linestrings to be able to track metrics
96
+ for (const line of coords) {
97
+ geometry = [];
98
+ convertLine(line, geometry, tolerance, false);
99
+ features.push(createProtoFeature(id, 'LineString', geometry, geojson.properties));
100
+ }
101
+ return;
102
+ convertLines(coords, geometry, tolerance, false);
103
+ }
104
+ break;
105
+
106
+ case 'Polygon':
107
+ convertLines(coords, geometry, tolerance, true);
108
+ break;
109
+
110
+ case 'MultiPolygon':
111
+ for (const polygon of coords) {
112
+ const newPolygon = [];
113
+ convertLines(polygon, newPolygon, tolerance, true);
114
+ geometry.push(newPolygon);
115
+ }
116
+ break;
117
+
118
+ case 'GeometryCollection':
119
+ for (const singleGeometry of geojson.geometry.geometries) {
120
+ convertFeature(
121
+ features,
122
+ {
123
+ id,
124
+ geometry: singleGeometry,
125
+ properties: geojson.properties
126
+ },
127
+ options,
128
+ index
129
+ );
130
+ }
131
+ break;
132
+
133
+ default:
134
+ throw new Error('Input data is not a valid GeoJSON object.');
114
135
  }
115
136
 
116
- features.push(createFeature(id, type, geometry, geojson.properties));
137
+ return createProtoFeature(id, type, geometry, geojson.properties);
117
138
  }
118
139
 
119
140
  function convertPoint(coords, out): void {
@@ -143,7 +164,7 @@ function convertLine(ring: number[], out, tolerance: number, isPolygon: boolean)
143
164
 
144
165
  const last = out.length - 3;
145
166
  out[2] = 1;
146
- simplify(out, 0, last, tolerance);
167
+ simplifyPath(out, 0, last, tolerance);
147
168
  out[last + 2] = 1;
148
169
 
149
170
  out.size = Math.abs(size);
@@ -0,0 +1,104 @@
1
+ // loaders.gl
2
+ // SPDX-License-Identifier: MIT
3
+ // Copyright (c) vis.gl contributors
4
+ // Forked from https://github.com/mapbox/geojson-vt under compatible ISC license
5
+
6
+ export type ProtoFeature = {
7
+ type: 'Point' | 'MultiPoint' | 'LineString' | 'MultiLineString' | 'Polygon' | 'MultiPolygon';
8
+ simplifiedType: 1 | 2 | 3;
9
+ geometry: any[];
10
+
11
+ // book keeping
12
+ id?: string;
13
+ tags?: Record<string, unknown>;
14
+
15
+ /** spatial extents */
16
+ minX: number;
17
+ /** spatial extents */
18
+ maxX: number;
19
+ /** spatial extents */
20
+ minY: number;
21
+ /** spatial extents */
22
+ maxY: number;
23
+ };
24
+
25
+ export type GeoJSONTileGeometry =
26
+ | GeoJSONTilePointGeometry
27
+ | GeoJSONTileLineGeometry
28
+ | GeoJSONTilePolygonGeometry;
29
+
30
+ export type GeoJSONTilePointGeometry = {
31
+ simplifiedType: 1;
32
+ geometry: number[];
33
+ };
34
+
35
+ export type GeoJSONTileLineGeometry = {
36
+ simplifiedType: 1;
37
+ geometry: number[][];
38
+ };
39
+
40
+ export type GeoJSONTilePolygonGeometry = {
41
+ simplifiedType: 1;
42
+ geometry: number[][][];
43
+ };
44
+
45
+ export function createProtoFeature(
46
+ id: any,
47
+ type: 'Point' | 'MultiPoint' | 'LineString' | 'MultiLineString' | 'Polygon' | 'MultiPolygon',
48
+ geometry: any[],
49
+ tags
50
+ ): ProtoFeature {
51
+ const feature: ProtoFeature = {
52
+ // eslint-disable-next-line
53
+ id: id == null ? null : id,
54
+ type,
55
+ simplifiedType: undefined!, // TODO
56
+ geometry,
57
+ tags,
58
+ minX: Infinity,
59
+ minY: Infinity,
60
+ maxX: -Infinity,
61
+ maxY: -Infinity
62
+ };
63
+
64
+ // TODO break out into separate function
65
+ switch (type) {
66
+ case 'Point':
67
+ case 'MultiPoint':
68
+ case 'LineString':
69
+ calcLineBBox(feature, geometry);
70
+ break;
71
+
72
+ case 'MultiLineString':
73
+ for (const line of geometry) {
74
+ calcLineBBox(feature, line);
75
+ }
76
+ break;
77
+
78
+ case 'Polygon':
79
+ // the outer ring (ie [0]) contains all inner rings
80
+ calcLineBBox(feature, geometry[0]);
81
+ break;
82
+
83
+ case 'MultiPolygon':
84
+ for (const polygon of geometry) {
85
+ // the outer ring (ie [0]) contains all inner rings
86
+ calcLineBBox(feature, polygon[0]);
87
+ }
88
+ break;
89
+
90
+ default:
91
+ throw new Error(String(type));
92
+ }
93
+
94
+ return feature;
95
+ }
96
+
97
+ function calcLineBBox(feature, geometry) {
98
+ for (let i = 0; i < geometry.length; i += 3) {
99
+ feature.minX = Math.min(feature.minX, geometry[i]);
100
+ feature.minY = Math.min(feature.minY, geometry[i + 1]);
101
+ feature.maxX = Math.max(feature.maxX, geometry[i]);
102
+ feature.maxY = Math.max(feature.maxY, geometry[i + 1]);
103
+ }
104
+ }
@@ -11,7 +11,12 @@
11
11
  * @param last last coord to simplify
12
12
  * @param sqTolerance tolerance (square distance)
13
13
  */
14
- export function simplify(coords: number[], first: number, last: number, sqTolerance: number): void {
14
+ export function simplifyPath(
15
+ coords: number[],
16
+ first: number,
17
+ last: number,
18
+ sqTolerance: number
19
+ ): void {
15
20
  let maxSqDist = sqTolerance;
16
21
  const mid = (last - first) >> 1;
17
22
  let minPosToMid = last - first;
@@ -41,9 +46,9 @@ export function simplify(coords: number[], first: number, last: number, sqTolera
41
46
  }
42
47
 
43
48
  if (maxSqDist > sqTolerance) {
44
- if (index - first > 3) simplify(coords, first, index, sqTolerance);
49
+ if (index - first > 3) simplifyPath(coords, first, index, sqTolerance);
45
50
  coords[index + 2] = maxSqDist;
46
- if (last - index > 3) simplify(coords, index, last, sqTolerance);
51
+ if (last - index > 3) simplifyPath(coords, index, last, sqTolerance);
47
52
  }
48
53
  }
49
54
 
@@ -3,14 +3,14 @@
3
3
  // Copyright (c) vis.gl contributors
4
4
  // Forked from https://github.com/mapbox/geojson-vt under compatible ISC license
5
5
 
6
- import type {TableTileFeature} from './tile';
7
- import {clip} from './clip';
8
- import {createFeature} from './feature';
6
+ import type {ProtoFeature} from './proto-feature';
7
+ import {createProtoFeature} from './proto-feature';
8
+ import {clipFeatures} from './clip-features';
9
9
 
10
10
  /**
11
11
  * Options for wrap()
12
12
  */
13
- export type WrapOptions = {
13
+ export type WrapFeaturesOptions = {
14
14
  buffer: number /** number of pixels of buffer for the tile */;
15
15
  extent: number /** extent of each tile */;
16
16
  lineMetrics: boolean;
@@ -18,18 +18,21 @@ export type WrapOptions = {
18
18
 
19
19
  /**
20
20
  * Wrap across antemeridian, by clipping into two tiles, shifting the overflowing x coordinates
21
- * @param features list of features to be wrapped
21
+ * @param list of features to be wrapped
22
22
  * @param options buffer and extent
23
23
  * @returns
24
24
  */
25
- export function wrap(features: TableTileFeature[], options: WrapOptions) {
25
+ export function wrapFeatures(
26
+ features: ProtoFeature[],
27
+ options: WrapFeaturesOptions
28
+ ): ProtoFeature[] {
26
29
  const buffer = options.buffer / options.extent;
27
- let merged: TableTileFeature[] = features;
28
- const left = clip(features, 1, -1 - buffer, buffer, 0, -1, 2, options); // left world copy
29
- const right = clip(features, 1, 1 - buffer, 2 + buffer, 0, -1, 2, options); // right world copy
30
+ let merged: ProtoFeature[] = features;
31
+ const left = clipFeatures(features, 1, -1 - buffer, buffer, 0, -1, 2, options); // left world copy
32
+ const right = clipFeatures(features, 1, 1 - buffer, 2 + buffer, 0, -1, 2, options); // right world copy
30
33
 
31
34
  if (left || right) {
32
- merged = clip(features, 1, -buffer, 1 + buffer, 0, -1, 2, options) || []; // center world copy
35
+ merged = clipFeatures(features, 1, -buffer, 1 + buffer, 0, -1, 2, options) || []; // center world copy
33
36
 
34
37
  if (left) {
35
38
  merged = shiftFeatureCoords(left, 1).concat(merged); // merge left into center
@@ -48,8 +51,8 @@ export function wrap(features: TableTileFeature[], options: WrapOptions) {
48
51
  * @param offset
49
52
  * @returns
50
53
  */
51
- function shiftFeatureCoords(features: TableTileFeature[], offset: number): TableTileFeature[] {
52
- const newFeatures: TableTileFeature[] = [];
54
+ function shiftFeatureCoords(features: ProtoFeature[], offset: number): ProtoFeature[] {
55
+ const newFeatures: ProtoFeature[] = [];
53
56
 
54
57
  for (let i = 0; i < features.length; i++) {
55
58
  const feature = features[i];
@@ -57,26 +60,38 @@ function shiftFeatureCoords(features: TableTileFeature[], offset: number): Table
57
60
 
58
61
  let newGeometry;
59
62
 
60
- if (type === 'Point' || type === 'MultiPoint' || type === 'LineString') {
61
- newGeometry = shiftCoords(feature.geometry, offset);
62
- } else if (type === 'MultiLineString' || type === 'Polygon') {
63
- newGeometry = [];
64
- for (const line of feature.geometry) {
65
- newGeometry.push(shiftCoords(line, offset));
66
- }
67
- } else if (type === 'MultiPolygon') {
68
- newGeometry = [];
69
- for (const polygon of feature.geometry) {
70
- const newPolygon: Points = [];
71
- for (const line of polygon) {
72
- // @ts-expect-error TODO
73
- newPolygon.push(shiftCoords(line, offset));
63
+ switch (type) {
64
+ case 'Point':
65
+ case 'MultiPoint':
66
+ case 'LineString':
67
+ newGeometry = shiftCoords(feature.geometry, offset);
68
+ break;
69
+
70
+ case 'MultiLineString':
71
+ case 'Polygon':
72
+ newGeometry = [];
73
+ for (const line of feature.geometry) {
74
+ newGeometry.push(shiftCoords(line, offset));
74
75
  }
75
- newGeometry.push(newPolygon);
76
- }
76
+ break;
77
+
78
+ case 'MultiPolygon':
79
+ newGeometry = [];
80
+ for (const polygon of feature.geometry) {
81
+ const newPolygon: Points = [];
82
+ for (const line of polygon) {
83
+ // @ts-expect-error TODO
84
+ newPolygon.push(shiftCoords(line, offset));
85
+ }
86
+ newGeometry.push(newPolygon);
87
+ }
88
+ break;
89
+
90
+ default:
91
+ throw new Error(String(type));
77
92
  }
78
93
 
79
- newFeatures.push(createFeature(feature.id, type, newGeometry, feature.tags));
94
+ newFeatures.push(createProtoFeature(feature.id, type, newGeometry, feature.tags));
80
95
  }
81
96
 
82
97
  return newFeatures;
@@ -0,0 +1,217 @@
1
+ // loaders.gl
2
+ // SPDX-License-Identifier: MIT
3
+ // Copyright (c) vis.gl contributors
4
+ // Forked from https://github.com/mapbox/geojson-vt under compatible ISC license
5
+
6
+ import type {ProtoFeature} from './features/proto-feature';
7
+
8
+ export type ProtoTile = {
9
+ /** Processed features */
10
+ protoFeatures: ProtoFeature[];
11
+ /** if we slice further down, no need to keep source geometry */
12
+ sourceFeatures: ProtoFeature[] | null;
13
+
14
+ /** Properties */
15
+ tags?: Record<string, string>;
16
+
17
+ /** tile x coordinate */
18
+ x: number;
19
+ /** tile y coordinate */
20
+ y: number;
21
+ /** tile z coordinate */
22
+ z: number;
23
+
24
+ /** spatial extent */
25
+ minX: number;
26
+ /** spatial extent */
27
+ maxX: number;
28
+ /** spatial extent */
29
+ minY: number;
30
+ /** spatial extent */
31
+ maxY: number;
32
+
33
+ /** Whether this tile has been transformed */
34
+ transformed: boolean;
35
+ numPoints: number;
36
+ numSimplified: number;
37
+ /** Number of features in this tile */
38
+ numFeatures: number;
39
+ };
40
+
41
+ export type CreateTileOptions = {
42
+ maxZoom?: number;
43
+ tolerance: number;
44
+ extent: number;
45
+ lineMetrics: boolean;
46
+ };
47
+
48
+ /**
49
+ * Create a tile from features and tile index
50
+ */
51
+ export function createProtoTile(
52
+ features: ProtoFeature[],
53
+ z,
54
+ tx,
55
+ ty,
56
+ options: CreateTileOptions
57
+ ): ProtoTile {
58
+ const tolerance = z === options.maxZoom ? 0 : options.tolerance / ((1 << z) * options.extent);
59
+ const tile: ProtoTile = {
60
+ protoFeatures: [],
61
+ sourceFeatures: null,
62
+ numPoints: 0,
63
+ numSimplified: 0,
64
+ numFeatures: features.length,
65
+ x: tx,
66
+ y: ty,
67
+ z,
68
+ transformed: false,
69
+ minX: 2,
70
+ minY: 1,
71
+ maxX: -1,
72
+ maxY: 0
73
+ };
74
+ for (const feature of features) {
75
+ addProtoFeature(tile, feature, tolerance, options);
76
+ }
77
+ return tile;
78
+ }
79
+
80
+ // eslint-disable-next-line complexity, max-statements
81
+ function addProtoFeature(
82
+ tile: ProtoTile,
83
+ feature: ProtoFeature,
84
+ tolerance: number,
85
+ options: CreateTileOptions
86
+ ) {
87
+ const geometry = feature.geometry;
88
+ const type = feature.type;
89
+ const simplifiedGeometry: number[] = [];
90
+
91
+ tile.minX = Math.min(tile.minX, feature.minX);
92
+ tile.minY = Math.min(tile.minY, feature.minY);
93
+ tile.maxX = Math.max(tile.maxX, feature.maxX);
94
+ tile.maxY = Math.max(tile.maxY, feature.maxY);
95
+
96
+ let simplifiedType: 1 | 2 | 3;
97
+ switch (type) {
98
+ case 'Point':
99
+ case 'MultiPoint':
100
+ simplifiedType = 1;
101
+ for (let i = 0; i < geometry.length; i += 3) {
102
+ simplifiedGeometry.push(geometry[i], geometry[i + 1]);
103
+ tile.numPoints++;
104
+ tile.numSimplified++;
105
+ }
106
+ break;
107
+
108
+ case 'LineString':
109
+ simplifiedType = 2;
110
+ addProtoLine(simplifiedGeometry, geometry, tile, tolerance, false, false);
111
+ break;
112
+
113
+ case 'MultiLineString':
114
+ simplifiedType = 2;
115
+ for (let i = 0; i < geometry.length; i++) {
116
+ addProtoLine(simplifiedGeometry, geometry[i], tile, tolerance, false, i === 0);
117
+ }
118
+ break;
119
+
120
+ case 'Polygon':
121
+ simplifiedType = 3;
122
+ for (let i = 0; i < geometry.length; i++) {
123
+ addProtoLine(simplifiedGeometry, geometry[i], tile, tolerance, true, i === 0);
124
+ }
125
+ break;
126
+
127
+ case 'MultiPolygon':
128
+ simplifiedType = 3;
129
+ for (let k = 0; k < geometry.length; k++) {
130
+ const polygon = geometry[k];
131
+ for (let i = 0; i < polygon.length; i++) {
132
+ addProtoLine(simplifiedGeometry, polygon[i], tile, tolerance, true, i === 0);
133
+ }
134
+ }
135
+ break;
136
+
137
+ default:
138
+ throw new Error(`Unknown geometry type: ${type}`);
139
+ }
140
+
141
+ if (simplifiedGeometry.length) {
142
+ let tags: Record<string, unknown> | null = feature.tags || null;
143
+
144
+ if (type === 'LineString' && options.lineMetrics) {
145
+ tags = {};
146
+ for (const key in feature.tags) {
147
+ tags[key] = feature.tags[key];
148
+ }
149
+ // @ts-expect-error adding fields to arrays
150
+ // eslint-disable-next-line camelcase
151
+ tags.mapbox_clip_start = geometry.start / geometry.size;
152
+ // @ts-expect-error adding fields to arrays
153
+ // eslint-disable-next-line camelcase
154
+ tags.mapbox_clip_end = geometry.end / geometry.size;
155
+ }
156
+
157
+ const tileFeature: ProtoFeature = {
158
+ geometry: simplifiedGeometry,
159
+ simplifiedType,
160
+ // @ts-expect-error
161
+ tags
162
+ };
163
+ if (feature.id !== null) {
164
+ tileFeature.id = feature.id;
165
+ }
166
+
167
+ tile.protoFeatures.push(tileFeature);
168
+ }
169
+ }
170
+
171
+ // eslint-disable-next-line max-params, max-statements
172
+ function addProtoLine(
173
+ result: any[],
174
+ geometry: any,
175
+ tile: ProtoTile,
176
+ tolerance: number,
177
+ isPolygon: boolean,
178
+ isOuter: boolean
179
+ ): void {
180
+ const sqTolerance = tolerance * tolerance;
181
+
182
+ if (tolerance > 0 && geometry.size < (isPolygon ? sqTolerance : tolerance)) {
183
+ tile.numPoints += geometry.length / 3;
184
+ return;
185
+ }
186
+
187
+ const ring: number[] = [];
188
+
189
+ for (let i = 0; i < geometry.length; i += 3) {
190
+ if (tolerance === 0 || geometry[i + 2] > sqTolerance) {
191
+ tile.numSimplified++;
192
+ ring.push(geometry[i], geometry[i + 1]);
193
+ }
194
+ tile.numPoints++;
195
+ }
196
+
197
+ if (isPolygon) rewind(ring, isOuter);
198
+
199
+ result.push(ring);
200
+ }
201
+
202
+ function rewind(ring: number[], clockwise?: boolean): void {
203
+ let area = 0;
204
+ for (let i = 0, j = ring.length - 2; i < ring.length; j = i, i += 2) {
205
+ area += (ring[i] - ring[j]) * (ring[i + 1] + ring[j + 1]);
206
+ }
207
+ if (area > 0 === clockwise) {
208
+ for (let i = 0, len = ring.length; i < len / 2; i += 2) {
209
+ const x = ring[i];
210
+ const y = ring[i + 1];
211
+ ring[i] = ring[len - 2 - i];
212
+ ring[i + 1] = ring[len - 1 - i];
213
+ ring[len - 2 - i] = x;
214
+ ring[len - 1 - i] = y;
215
+ }
216
+ }
217
+ }