@deck.gl/carto 9.3.2 → 9.3.4
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.
- package/dist/cartoPropertiesTile-worker.js +1 -1
- package/dist/cartoRasterTile-worker.js +1 -1
- package/dist/cartoSpatialTile-worker.js +1 -1
- package/dist/cartoVectorTile-worker.js +1 -1
- package/dist/dist.dev.js +89 -40
- package/dist/index.cjs +81 -35
- package/dist/index.cjs.map +2 -2
- package/dist/layers/label-utils.d.ts +3 -1
- package/dist/layers/label-utils.d.ts.map +1 -1
- package/dist/layers/label-utils.js +85 -35
- package/dist/layers/label-utils.js.map +1 -1
- package/dist/layers/schema/carto-properties-tile-loader.js +1 -1
- package/dist/layers/schema/carto-raster-tile-loader.js +1 -1
- package/dist/layers/schema/carto-spatial-tile-loader.js +1 -1
- package/dist/layers/schema/carto-vector-tile-loader.js +1 -1
- package/dist/layers/vector-tile-layer.d.ts.map +1 -1
- package/dist/layers/vector-tile-layer.js +1 -1
- package/dist/layers/vector-tile-layer.js.map +1 -1
- package/dist.min.js +10 -10
- package/package.json +2 -2
- package/src/layers/label-utils.ts +107 -46
- package/src/layers/vector-tile-layer.ts +2 -1
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"description": "CARTO official integration with Deck.gl. Build geospatial applications using CARTO and Deck.gl.",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
|
-
"version": "9.3.
|
|
6
|
+
"version": "9.3.4",
|
|
7
7
|
"publishConfig": {
|
|
8
8
|
"access": "public"
|
|
9
9
|
},
|
|
@@ -75,5 +75,5 @@
|
|
|
75
75
|
"@loaders.gl/core": "^4.4.1",
|
|
76
76
|
"@luma.gl/core": "~9.3.3"
|
|
77
77
|
},
|
|
78
|
-
"gitHead": "
|
|
78
|
+
"gitHead": "c489a387f9c314a3d381111176a5d65c9fd74e87"
|
|
79
79
|
}
|
|
@@ -8,6 +8,38 @@ type TileBBox = GeoBoundingBox;
|
|
|
8
8
|
type Properties = BinaryPointFeature['properties'];
|
|
9
9
|
type LineInfo = {index: number; length: number};
|
|
10
10
|
|
|
11
|
+
/** Property name for server-provided feature bounding box (comma-separated west,south,east,north in world coordinates) */
|
|
12
|
+
export const FEATURE_BBOX_PROP = '_carto_bbox';
|
|
13
|
+
|
|
14
|
+
function parseFeatureBbox(value: unknown): [number, number, number, number] | null {
|
|
15
|
+
if (typeof value !== 'string') return null;
|
|
16
|
+
const parts = value.split(',');
|
|
17
|
+
if (parts.length !== 4) return null;
|
|
18
|
+
return parts.map(Number) as [number, number, number, number];
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function latToMercatorY(lat: number): number {
|
|
22
|
+
return Math.log(Math.tan(Math.PI / 4 + (lat * Math.PI) / 360));
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** Convert a WGS84 point to MVT tile-space, where y=0 is north, y=1 is south, linear in Mercator */
|
|
26
|
+
function worldToTile(
|
|
27
|
+
point: [number, number],
|
|
28
|
+
geoBbox: TileBBox,
|
|
29
|
+
tileBbox: TileBBox
|
|
30
|
+
): [number, number] {
|
|
31
|
+
const xFrac = (point[0] - geoBbox.west) / (geoBbox.east - geoBbox.west);
|
|
32
|
+
const x = tileBbox.west + xFrac * (tileBbox.east - tileBbox.west);
|
|
33
|
+
|
|
34
|
+
const mercY = latToMercatorY(point[1]);
|
|
35
|
+
const mercNorth = latToMercatorY(geoBbox.north);
|
|
36
|
+
const mercSouth = latToMercatorY(geoBbox.south);
|
|
37
|
+
const yFrac = (mercNorth - mercY) / (mercNorth - mercSouth);
|
|
38
|
+
const y = tileBbox.south + yFrac * (tileBbox.north - tileBbox.south);
|
|
39
|
+
|
|
40
|
+
return [x, y];
|
|
41
|
+
}
|
|
42
|
+
|
|
11
43
|
export function createPointsFromLines(
|
|
12
44
|
lines: BinaryLineFeature,
|
|
13
45
|
uniqueIdProperty?: string
|
|
@@ -70,9 +102,16 @@ export function createPointsFromLines(
|
|
|
70
102
|
export function createPointsFromPolygons(
|
|
71
103
|
polygons: Required<BinaryPolygonFeature>,
|
|
72
104
|
tileBbox: TileBBox,
|
|
73
|
-
props: any
|
|
105
|
+
props: any,
|
|
106
|
+
geoBbox?: TileBBox
|
|
74
107
|
): BinaryPointFeature {
|
|
75
|
-
|
|
108
|
+
// When feature bounding boxes are provided, use geoBbox for area filtering
|
|
109
|
+
// as the bbox values are in world coordinates
|
|
110
|
+
const useBbox = Boolean(
|
|
111
|
+
geoBbox && polygons.properties.length > 0 && FEATURE_BBOX_PROP in polygons.properties[0]
|
|
112
|
+
);
|
|
113
|
+
const boundsBbox = useBbox ? geoBbox! : tileBbox;
|
|
114
|
+
const {west, south, east, north} = boundsBbox;
|
|
76
115
|
const tileArea = (east - west) * (north - south);
|
|
77
116
|
const minPolygonArea = tileArea * 0.0001; // 0.01% threshold
|
|
78
117
|
|
|
@@ -85,6 +124,9 @@ export function createPointsFromPolygons(
|
|
|
85
124
|
polygons.numericProps
|
|
86
125
|
);
|
|
87
126
|
|
|
127
|
+
// MVT: tile space is Mercator-projected, need worldToTile conversion
|
|
128
|
+
const isMVT = useBbox && geoBbox !== tileBbox;
|
|
129
|
+
|
|
88
130
|
// Process each polygon
|
|
89
131
|
let pointIndex = 0;
|
|
90
132
|
let triangleIndex = 0;
|
|
@@ -93,61 +135,80 @@ export function createPointsFromPolygons(
|
|
|
93
135
|
const startIndex = polygons.polygonIndices.value[i];
|
|
94
136
|
const endIndex = polygons.polygonIndices.value[i + 1];
|
|
95
137
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
138
|
+
let labelPoint: [number, number] | null = null;
|
|
139
|
+
|
|
140
|
+
// Determine preferred label position and area filter
|
|
141
|
+
let preferredPoint: [number, number] | null = null;
|
|
142
|
+
if (useBbox) {
|
|
143
|
+
const featureId = polygons.featureIds.value[startIndex];
|
|
144
|
+
const bbox = parseFeatureBbox(polygons.properties[featureId][FEATURE_BBOX_PROP]);
|
|
145
|
+
if (bbox) {
|
|
146
|
+
const [bboxWest, bboxSouth, bboxEast, bboxNorth] = bbox;
|
|
147
|
+
const bboxArea = (bboxEast - bboxWest) * (bboxNorth - bboxSouth);
|
|
148
|
+
if (bboxArea >= minPolygonArea) {
|
|
149
|
+
const center: [number, number] = [(bboxWest + bboxEast) / 2, (bboxSouth + bboxNorth) / 2];
|
|
150
|
+
if (isPointInBounds(center, geoBbox!)) {
|
|
151
|
+
preferredPoint = isMVT ? worldToTile(center, geoBbox!, tileBbox) : center;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
} else {
|
|
156
|
+
if (getPolygonArea(polygons, i) < minPolygonArea) {
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
preferredPoint = getPolygonCentroid(polygons, i);
|
|
99
160
|
}
|
|
100
161
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
162
|
+
if (preferredPoint) {
|
|
163
|
+
// Check if preferred point is inside the polygon, tracking largest triangle as fallback
|
|
164
|
+
let maxArea = -1;
|
|
165
|
+
let largestTriangleCenter: [number, number] = [0, 0];
|
|
166
|
+
let isInside = false;
|
|
105
167
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
168
|
+
while (triangleIndex < polygons.triangles.value.length) {
|
|
169
|
+
const i1 = polygons.triangles.value[triangleIndex];
|
|
170
|
+
if (i1 >= endIndex) break;
|
|
109
171
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
172
|
+
if (isInside) {
|
|
173
|
+
triangleIndex += 3;
|
|
174
|
+
continue;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
const i2 = polygons.triangles.value[triangleIndex + 1];
|
|
178
|
+
const i3 = polygons.triangles.value[triangleIndex + 2];
|
|
179
|
+
const v1 = polygons.positions.value.subarray(
|
|
180
|
+
i1 * polygons.positions.size,
|
|
181
|
+
i1 * polygons.positions.size + polygons.positions.size
|
|
182
|
+
);
|
|
183
|
+
const v2 = polygons.positions.value.subarray(
|
|
184
|
+
i2 * polygons.positions.size,
|
|
185
|
+
i2 * polygons.positions.size + polygons.positions.size
|
|
186
|
+
);
|
|
187
|
+
const v3 = polygons.positions.value.subarray(
|
|
188
|
+
i3 * polygons.positions.size,
|
|
189
|
+
i3 * polygons.positions.size + polygons.positions.size
|
|
190
|
+
);
|
|
191
|
+
|
|
192
|
+
if (isPointInTriangle(preferredPoint, v1, v2, v3)) {
|
|
193
|
+
isInside = true;
|
|
194
|
+
} else {
|
|
195
|
+
const area = getTriangleArea(v1, v2, v3);
|
|
196
|
+
if (area > maxArea) {
|
|
197
|
+
maxArea = area;
|
|
198
|
+
largestTriangleCenter = [(v1[0] + v2[0] + v3[0]) / 3, (v1[1] + v2[1] + v3[1]) / 3];
|
|
199
|
+
}
|
|
200
|
+
}
|
|
114
201
|
|
|
115
|
-
// If we've already found a triangle containing the centroid, skip the rest
|
|
116
|
-
if (centroidIsInside) {
|
|
117
202
|
triangleIndex += 3;
|
|
118
|
-
continue;
|
|
119
203
|
}
|
|
120
204
|
|
|
121
|
-
const
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
i1 * polygons.positions.size,
|
|
125
|
-
i1 * polygons.positions.size + polygons.positions.size
|
|
126
|
-
);
|
|
127
|
-
const v2 = polygons.positions.value.subarray(
|
|
128
|
-
i2 * polygons.positions.size,
|
|
129
|
-
i2 * polygons.positions.size + polygons.positions.size
|
|
130
|
-
);
|
|
131
|
-
const v3 = polygons.positions.value.subarray(
|
|
132
|
-
i3 * polygons.positions.size,
|
|
133
|
-
i3 * polygons.positions.size + polygons.positions.size
|
|
134
|
-
);
|
|
135
|
-
|
|
136
|
-
if (isPointInTriangle(centroid, v1, v2, v3)) {
|
|
137
|
-
centroidIsInside = true;
|
|
138
|
-
} else {
|
|
139
|
-
const area = getTriangleArea(v1, v2, v3);
|
|
140
|
-
if (area > maxArea) {
|
|
141
|
-
maxArea = area;
|
|
142
|
-
largestTriangleCenter = [(v1[0] + v2[0] + v3[0]) / 3, (v1[1] + v2[1] + v3[1]) / 3];
|
|
143
|
-
}
|
|
205
|
+
const candidate = isInside ? preferredPoint : largestTriangleCenter;
|
|
206
|
+
if (isPointInBounds(candidate, tileBbox)) {
|
|
207
|
+
labelPoint = candidate;
|
|
144
208
|
}
|
|
145
|
-
|
|
146
|
-
triangleIndex += 3;
|
|
147
209
|
}
|
|
148
210
|
|
|
149
|
-
|
|
150
|
-
if (isPointInBounds(labelPoint, tileBbox)) {
|
|
211
|
+
if (labelPoint) {
|
|
151
212
|
positions.push(...labelPoint);
|
|
152
213
|
const featureId = polygons.featureIds.value[startIndex];
|
|
153
214
|
if (extruded) {
|