zenit-sdk 0.0.1
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/README.md +279 -0
- package/dist/chunk-RGS6AZWV.mjs +3057 -0
- package/dist/chunk-RGS6AZWV.mjs.map +1 -0
- package/dist/index-C_2fk0Fk.d.mts +690 -0
- package/dist/index-C_2fk0Fk.d.ts +690 -0
- package/dist/index.d.mts +105 -0
- package/dist/index.d.ts +105 -0
- package/dist/index.js +4165 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +1095 -0
- package/dist/index.mjs.map +1 -0
- package/dist/react/index.d.mts +4 -0
- package/dist/react/index.d.ts +4 -0
- package/dist/react/index.js +3088 -0
- package/dist/react/index.js.map +1 -0
- package/dist/react/index.mjs +53 -0
- package/dist/react/index.mjs.map +1 -0
- package/package.json +65 -0
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,1095 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ChevronLeft,
|
|
3
|
+
ChevronRight,
|
|
4
|
+
Eye,
|
|
5
|
+
EyeOff,
|
|
6
|
+
FloatingChatBox,
|
|
7
|
+
Layers,
|
|
8
|
+
Upload,
|
|
9
|
+
X,
|
|
10
|
+
ZenitFeatureFilterPanel,
|
|
11
|
+
ZenitLayerManager,
|
|
12
|
+
ZenitMap,
|
|
13
|
+
ZoomIn,
|
|
14
|
+
applyFilteredGeoJSONStrategy,
|
|
15
|
+
applyLayerOverrides,
|
|
16
|
+
centroidFromBBox,
|
|
17
|
+
clampNumber,
|
|
18
|
+
clampOpacity,
|
|
19
|
+
computeBBoxFromFeature,
|
|
20
|
+
createChatService,
|
|
21
|
+
extractMapDto,
|
|
22
|
+
getAccentByLayerId,
|
|
23
|
+
getEffectiveLayerOpacity,
|
|
24
|
+
getLayerColor,
|
|
25
|
+
getLayerZoomOpacityFactor,
|
|
26
|
+
getStyleByLayerId,
|
|
27
|
+
getZoomOpacityFactor,
|
|
28
|
+
initLayerStates,
|
|
29
|
+
isPolygonLayer,
|
|
30
|
+
normalizeMapCenter,
|
|
31
|
+
normalizeMapLayers,
|
|
32
|
+
resetOverrides,
|
|
33
|
+
resolveLayerAccent,
|
|
34
|
+
sendMessage,
|
|
35
|
+
sendMessageStream,
|
|
36
|
+
useSendMessage,
|
|
37
|
+
useSendMessageStream
|
|
38
|
+
} from "./chunk-RGS6AZWV.mjs";
|
|
39
|
+
|
|
40
|
+
// src/http/HttpClient.ts
|
|
41
|
+
var HttpClient = class {
|
|
42
|
+
constructor(options) {
|
|
43
|
+
this.baseUrl = options.baseUrl.replace(/\/$/, "");
|
|
44
|
+
this.resolveTokens = options.resolveTokens;
|
|
45
|
+
this.resolveAuthorizationHeader = options.resolveAuthorizationHeader ?? (() => ({}));
|
|
46
|
+
}
|
|
47
|
+
async get(path, options = {}) {
|
|
48
|
+
const headers = {
|
|
49
|
+
...options.headers,
|
|
50
|
+
...this.resolveAuthorizationHeader()
|
|
51
|
+
};
|
|
52
|
+
return this.request(path, { ...options, method: "GET", headers });
|
|
53
|
+
}
|
|
54
|
+
async post(path, body, options = {}) {
|
|
55
|
+
const headers = {
|
|
56
|
+
"Content-Type": "application/json",
|
|
57
|
+
...options.headers,
|
|
58
|
+
...this.resolveAuthorizationHeader()
|
|
59
|
+
};
|
|
60
|
+
return this.request(path, {
|
|
61
|
+
...options,
|
|
62
|
+
method: "POST",
|
|
63
|
+
headers,
|
|
64
|
+
body: body !== void 0 ? JSON.stringify(body) : void 0
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
async put(path, body, options = {}) {
|
|
68
|
+
const headers = {
|
|
69
|
+
"Content-Type": "application/json",
|
|
70
|
+
...options.headers,
|
|
71
|
+
...this.resolveAuthorizationHeader()
|
|
72
|
+
};
|
|
73
|
+
return this.request(path, {
|
|
74
|
+
...options,
|
|
75
|
+
method: "PUT",
|
|
76
|
+
headers,
|
|
77
|
+
body: body !== void 0 ? JSON.stringify(body) : void 0
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
async patch(path, body, options = {}) {
|
|
81
|
+
const headers = {
|
|
82
|
+
"Content-Type": "application/json",
|
|
83
|
+
...options.headers,
|
|
84
|
+
...this.resolveAuthorizationHeader()
|
|
85
|
+
};
|
|
86
|
+
return this.request(path, {
|
|
87
|
+
...options,
|
|
88
|
+
method: "PATCH",
|
|
89
|
+
headers,
|
|
90
|
+
body: body !== void 0 ? JSON.stringify(body) : void 0
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
async delete(path, options = {}) {
|
|
94
|
+
const headers = {
|
|
95
|
+
...options.headers,
|
|
96
|
+
...this.resolveAuthorizationHeader()
|
|
97
|
+
};
|
|
98
|
+
return this.request(path, { ...options, method: "DELETE", headers });
|
|
99
|
+
}
|
|
100
|
+
async request(path, options) {
|
|
101
|
+
const url = `${this.baseUrl}${path}`;
|
|
102
|
+
const tokenState = this.resolveTokens?.();
|
|
103
|
+
const headers = {
|
|
104
|
+
...options.headers
|
|
105
|
+
};
|
|
106
|
+
if (!headers["Authorization"] && tokenState?.accessToken) {
|
|
107
|
+
headers["Authorization"] = `Bearer ${tokenState.accessToken}`;
|
|
108
|
+
}
|
|
109
|
+
if (tokenState?.sdkToken) {
|
|
110
|
+
headers["X-SDK-Token"] = tokenState.sdkToken;
|
|
111
|
+
}
|
|
112
|
+
const response = await fetch(url, {
|
|
113
|
+
...options,
|
|
114
|
+
headers
|
|
115
|
+
});
|
|
116
|
+
const contentType = response.headers.get("content-type");
|
|
117
|
+
const isJson = contentType?.includes("application/json");
|
|
118
|
+
const payload = isJson ? await response.json() : await response.text();
|
|
119
|
+
if (!response.ok) {
|
|
120
|
+
const normalizedError = {
|
|
121
|
+
success: false,
|
|
122
|
+
statusCode: response.status,
|
|
123
|
+
message: typeof payload === "string" ? payload : payload?.message || "Unknown error",
|
|
124
|
+
timestamp: typeof payload === "object" ? payload.timestamp : void 0,
|
|
125
|
+
path: typeof payload === "object" ? payload.path : void 0,
|
|
126
|
+
method: typeof payload === "object" ? payload.method : void 0,
|
|
127
|
+
error: typeof payload === "object" ? payload.error : void 0
|
|
128
|
+
};
|
|
129
|
+
throw normalizedError;
|
|
130
|
+
}
|
|
131
|
+
return payload;
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
// src/auth/ZenitAuthClient.ts
|
|
136
|
+
var ZenitAuthClient = class {
|
|
137
|
+
constructor(http, updateTokens, config) {
|
|
138
|
+
this.http = http;
|
|
139
|
+
this.updateTokens = updateTokens;
|
|
140
|
+
this.config = config;
|
|
141
|
+
}
|
|
142
|
+
async login(credentials) {
|
|
143
|
+
try {
|
|
144
|
+
const response = await this.http.post("/auth/login", credentials);
|
|
145
|
+
const accessToken = response?.accessToken ?? response?.data?.accessToken;
|
|
146
|
+
const refreshToken = response?.refreshToken ?? response?.data?.refreshToken;
|
|
147
|
+
this.updateTokens({ accessToken, refreshToken });
|
|
148
|
+
return response;
|
|
149
|
+
} catch (error) {
|
|
150
|
+
this.config.onAuthError?.(error);
|
|
151
|
+
throw error;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
async refresh(refreshToken) {
|
|
155
|
+
const tokenToUse = refreshToken || this.config.refreshToken;
|
|
156
|
+
const headers = tokenToUse ? { Authorization: `Bearer ${tokenToUse}` } : void 0;
|
|
157
|
+
const response = await this.http.post("/auth/refresh", void 0, { headers });
|
|
158
|
+
const accessToken = response?.accessToken ?? response?.data?.accessToken;
|
|
159
|
+
const nextRefreshToken = response?.refreshToken ?? response?.data?.refreshToken;
|
|
160
|
+
this.updateTokens({ accessToken, refreshToken: nextRefreshToken });
|
|
161
|
+
return response;
|
|
162
|
+
}
|
|
163
|
+
async me() {
|
|
164
|
+
return this.http.get("/auth/me");
|
|
165
|
+
}
|
|
166
|
+
async validate() {
|
|
167
|
+
return this.http.get("/auth/validate");
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
// src/sdkAuth/ZenitSdkAuthClient.ts
|
|
172
|
+
var ZenitSdkAuthClient = class {
|
|
173
|
+
constructor(http, updateAccessToken, config) {
|
|
174
|
+
this.http = http;
|
|
175
|
+
this.updateAccessToken = updateAccessToken;
|
|
176
|
+
this.config = config;
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Validate an SDK token. If no token argument is provided, it falls back to config.sdkToken.
|
|
180
|
+
*/
|
|
181
|
+
async validateSdkToken(token) {
|
|
182
|
+
const sdkToken = token || this.config.sdkToken;
|
|
183
|
+
return this.http.post("/sdk-auth/validate", { token: sdkToken });
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Exchange an SDK token for an access token. Token can come from method args or config.sdkToken.
|
|
187
|
+
*/
|
|
188
|
+
async exchangeSdkToken(token) {
|
|
189
|
+
const sdkToken = token || this.config.sdkToken;
|
|
190
|
+
const response = await this.http.post("/sdk-auth/exchange", { token: sdkToken });
|
|
191
|
+
const accessToken = response?.accessToken ?? response?.data?.accessToken;
|
|
192
|
+
this.updateAccessToken(accessToken);
|
|
193
|
+
return response;
|
|
194
|
+
}
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
// src/maps/ZenitMapsClient.ts
|
|
198
|
+
var ZenitMapsClient = class {
|
|
199
|
+
constructor(httpClient) {
|
|
200
|
+
this.httpClient = httpClient;
|
|
201
|
+
}
|
|
202
|
+
async getMap(mapId, includeLayers = true) {
|
|
203
|
+
const include = includeLayers ? "?includeLayers=true" : "";
|
|
204
|
+
return this.httpClient.get(`/maps/${mapId}${include}`);
|
|
205
|
+
}
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
// src/layers/catalogSupport.ts
|
|
209
|
+
function isPolygonLike(value) {
|
|
210
|
+
if (!value) {
|
|
211
|
+
return false;
|
|
212
|
+
}
|
|
213
|
+
const normalized = value.toLowerCase();
|
|
214
|
+
return normalized === "polygon" || normalized === "multipolygon";
|
|
215
|
+
}
|
|
216
|
+
function getCatalogSupport(args) {
|
|
217
|
+
const { layerType, geometryType } = args;
|
|
218
|
+
if (layerType !== void 0) {
|
|
219
|
+
const supported = isPolygonLike(layerType);
|
|
220
|
+
return {
|
|
221
|
+
supported,
|
|
222
|
+
reason: supported ? "supported" : "unsupported-geometry",
|
|
223
|
+
layerType,
|
|
224
|
+
geometryType
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
if (geometryType !== void 0) {
|
|
228
|
+
const supported = isPolygonLike(geometryType);
|
|
229
|
+
return {
|
|
230
|
+
supported,
|
|
231
|
+
reason: supported ? "supported" : "unsupported-geometry",
|
|
232
|
+
geometryType
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
return {
|
|
236
|
+
supported: false,
|
|
237
|
+
reason: "unknown-geometry"
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
var ZenitCatalogNotSupportedError = class extends Error {
|
|
241
|
+
constructor(support, message) {
|
|
242
|
+
super(message ?? "Catalog is not supported for this layer geometry");
|
|
243
|
+
this.name = "ZenitCatalogNotSupportedError";
|
|
244
|
+
this.support = support;
|
|
245
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
246
|
+
}
|
|
247
|
+
};
|
|
248
|
+
|
|
249
|
+
// src/engine/PrefilterEngine.ts
|
|
250
|
+
var normalizeString = (value) => value.trim().toLowerCase();
|
|
251
|
+
var isPrefilterValue = (value) => typeof value === "string" || typeof value === "number" || typeof value === "boolean";
|
|
252
|
+
var matchesPrefilterValue = (candidate, expected) => {
|
|
253
|
+
if (typeof expected === "string") {
|
|
254
|
+
if (candidate === null || candidate === void 0) return false;
|
|
255
|
+
return normalizeString(String(candidate)) === normalizeString(expected);
|
|
256
|
+
}
|
|
257
|
+
return candidate === expected;
|
|
258
|
+
};
|
|
259
|
+
var shouldApplyPrefilters = (prefilters) => !!prefilters && Object.keys(prefilters).length > 0;
|
|
260
|
+
var featureMatchesPrefilters = (feature, prefilters) => {
|
|
261
|
+
const properties = feature.properties ?? {};
|
|
262
|
+
return Object.entries(prefilters).every(([key, expected]) => {
|
|
263
|
+
if (!isPrefilterValue(expected)) return true;
|
|
264
|
+
return matchesPrefilterValue(properties[key], expected);
|
|
265
|
+
});
|
|
266
|
+
};
|
|
267
|
+
var applyPrefiltersToFeatureCollection = (featureCollection, prefilters) => {
|
|
268
|
+
if (!shouldApplyPrefilters(prefilters)) return featureCollection;
|
|
269
|
+
const nextFeatures = featureCollection.features.filter(
|
|
270
|
+
(feature) => featureMatchesPrefilters(feature, prefilters)
|
|
271
|
+
);
|
|
272
|
+
return {
|
|
273
|
+
...featureCollection,
|
|
274
|
+
features: nextFeatures
|
|
275
|
+
};
|
|
276
|
+
};
|
|
277
|
+
var decorateFeaturesWithLayerId = (featureCollection, layerId) => {
|
|
278
|
+
let shouldClone = false;
|
|
279
|
+
featureCollection.features.forEach((feature) => {
|
|
280
|
+
const existing = feature.properties?.__zenit_layerId;
|
|
281
|
+
if (existing !== layerId) {
|
|
282
|
+
shouldClone = true;
|
|
283
|
+
}
|
|
284
|
+
});
|
|
285
|
+
if (!shouldClone) {
|
|
286
|
+
return featureCollection;
|
|
287
|
+
}
|
|
288
|
+
return {
|
|
289
|
+
...featureCollection,
|
|
290
|
+
features: featureCollection.features.map((feature) => ({
|
|
291
|
+
...feature,
|
|
292
|
+
properties: {
|
|
293
|
+
...feature.properties ?? {},
|
|
294
|
+
__zenit_layerId: layerId
|
|
295
|
+
}
|
|
296
|
+
}))
|
|
297
|
+
};
|
|
298
|
+
};
|
|
299
|
+
|
|
300
|
+
// src/layers/ZenitLayersClient.ts
|
|
301
|
+
function parseQueryParams(options = {}) {
|
|
302
|
+
const params = new URLSearchParams();
|
|
303
|
+
if (options.maxFeatures !== void 0) {
|
|
304
|
+
params.set("maxFeatures", String(options.maxFeatures));
|
|
305
|
+
}
|
|
306
|
+
if (options.simplify !== void 0) {
|
|
307
|
+
params.set("simplify", String(options.simplify));
|
|
308
|
+
}
|
|
309
|
+
return params;
|
|
310
|
+
}
|
|
311
|
+
function serializeFilterValue(value) {
|
|
312
|
+
if (Array.isArray(value)) {
|
|
313
|
+
const serialized2 = value.map(String).filter((item) => item.trim().length > 0).join(",");
|
|
314
|
+
return serialized2.length > 0 ? serialized2 : void 0;
|
|
315
|
+
}
|
|
316
|
+
if (value === void 0 || value === null) {
|
|
317
|
+
return void 0;
|
|
318
|
+
}
|
|
319
|
+
const serialized = String(value);
|
|
320
|
+
return serialized.trim().length > 0 ? serialized : void 0;
|
|
321
|
+
}
|
|
322
|
+
function buildLayerFilters(filters) {
|
|
323
|
+
const query = new URLSearchParams();
|
|
324
|
+
if (!filters) {
|
|
325
|
+
return query;
|
|
326
|
+
}
|
|
327
|
+
Object.entries(filters).forEach(([key, value]) => {
|
|
328
|
+
const serialized = serializeFilterValue(value);
|
|
329
|
+
if (serialized !== void 0) {
|
|
330
|
+
query.set(key, serialized);
|
|
331
|
+
}
|
|
332
|
+
});
|
|
333
|
+
return query;
|
|
334
|
+
}
|
|
335
|
+
function buildFilterMultipleParams(params) {
|
|
336
|
+
const query = buildLayerFilters(params.filters);
|
|
337
|
+
if (!Array.isArray(params.layerIds) || params.layerIds.length === 0) {
|
|
338
|
+
throw new Error("layerIds is required and cannot be empty");
|
|
339
|
+
}
|
|
340
|
+
query.set("layerIds", params.layerIds.map((id) => String(id)).join(","));
|
|
341
|
+
return query;
|
|
342
|
+
}
|
|
343
|
+
function asOptionalNumber(value) {
|
|
344
|
+
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
345
|
+
}
|
|
346
|
+
function createEmptyFeatureCollection() {
|
|
347
|
+
return { type: "FeatureCollection", features: [] };
|
|
348
|
+
}
|
|
349
|
+
function hasFilters(filters) {
|
|
350
|
+
return buildLayerFilters(filters).size > 0;
|
|
351
|
+
}
|
|
352
|
+
function mergeLayerFilters(base, extra) {
|
|
353
|
+
if (!base && !extra) return void 0;
|
|
354
|
+
const merged = { ...base ?? {}, ...extra ?? {} };
|
|
355
|
+
return hasFilters(merged) ? merged : void 0;
|
|
356
|
+
}
|
|
357
|
+
var DEFAULT_MAX_FEATURES_FULL_GEOJSON = 5e4;
|
|
358
|
+
function normalizeLayerType(layerType) {
|
|
359
|
+
return typeof layerType === "string" ? layerType.toLowerCase() : void 0;
|
|
360
|
+
}
|
|
361
|
+
function shouldUseFilterMultiple(layerType) {
|
|
362
|
+
return normalizeLayerType(layerType) === "multipolygon";
|
|
363
|
+
}
|
|
364
|
+
function shouldSkipGeojsonDownload(layerType, totalFeatures, thresholds = {
|
|
365
|
+
maxFeatures: DEFAULT_MAX_FEATURES_FULL_GEOJSON
|
|
366
|
+
}) {
|
|
367
|
+
if (thresholds.allowLargeGeojson) {
|
|
368
|
+
return false;
|
|
369
|
+
}
|
|
370
|
+
const normalizedType = normalizeLayerType(layerType);
|
|
371
|
+
if (normalizedType === "mixed") {
|
|
372
|
+
return true;
|
|
373
|
+
}
|
|
374
|
+
if (typeof totalFeatures === "number" && totalFeatures > thresholds.maxFeatures) {
|
|
375
|
+
return true;
|
|
376
|
+
}
|
|
377
|
+
return false;
|
|
378
|
+
}
|
|
379
|
+
function parseBbox(value) {
|
|
380
|
+
if (!value || typeof value !== "object") {
|
|
381
|
+
return void 0;
|
|
382
|
+
}
|
|
383
|
+
const candidate = value;
|
|
384
|
+
if (typeof candidate.minLon === "number" && typeof candidate.minLat === "number" && typeof candidate.maxLon === "number" && typeof candidate.maxLat === "number") {
|
|
385
|
+
return {
|
|
386
|
+
minLon: candidate.minLon,
|
|
387
|
+
minLat: candidate.minLat,
|
|
388
|
+
maxLon: candidate.maxLon,
|
|
389
|
+
maxLat: candidate.maxLat
|
|
390
|
+
};
|
|
391
|
+
}
|
|
392
|
+
if (Array.isArray(value) && value.length === 4) {
|
|
393
|
+
const [minLon, minLat, maxLon, maxLat] = value;
|
|
394
|
+
if (typeof minLon === "number" && typeof minLat === "number" && typeof maxLon === "number" && typeof maxLat === "number") {
|
|
395
|
+
return { minLon, minLat, maxLon, maxLat };
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
return void 0;
|
|
399
|
+
}
|
|
400
|
+
function buildBboxFromFeatureCollection(featureCollection) {
|
|
401
|
+
const coords = [];
|
|
402
|
+
const collect = (candidate) => {
|
|
403
|
+
if (!Array.isArray(candidate)) return;
|
|
404
|
+
if (candidate.length === 2 && typeof candidate[0] === "number" && typeof candidate[1] === "number" && Number.isFinite(candidate[0]) && Number.isFinite(candidate[1])) {
|
|
405
|
+
coords.push([candidate[0], candidate[1]]);
|
|
406
|
+
return;
|
|
407
|
+
}
|
|
408
|
+
candidate.forEach((item) => collect(item));
|
|
409
|
+
};
|
|
410
|
+
featureCollection.features.forEach((feature) => {
|
|
411
|
+
collect(feature.geometry?.coordinates);
|
|
412
|
+
});
|
|
413
|
+
if (coords.length === 0) return void 0;
|
|
414
|
+
const [firstLon, firstLat] = coords[0];
|
|
415
|
+
const bbox = { minLon: firstLon, minLat: firstLat, maxLon: firstLon, maxLat: firstLat };
|
|
416
|
+
coords.forEach(([lon, lat]) => {
|
|
417
|
+
bbox.minLon = Math.min(bbox.minLon, lon);
|
|
418
|
+
bbox.minLat = Math.min(bbox.minLat, lat);
|
|
419
|
+
bbox.maxLon = Math.max(bbox.maxLon, lon);
|
|
420
|
+
bbox.maxLat = Math.max(bbox.maxLat, lat);
|
|
421
|
+
});
|
|
422
|
+
return bbox;
|
|
423
|
+
}
|
|
424
|
+
function buildGeometryCollectionFromFeatureCollection(featureCollection) {
|
|
425
|
+
return {
|
|
426
|
+
type: "GeometryCollection",
|
|
427
|
+
geometries: featureCollection.features.map((feature) => feature.geometry).filter((geometry) => geometry !== void 0 && geometry !== null)
|
|
428
|
+
};
|
|
429
|
+
}
|
|
430
|
+
function buildAoiFromFeatureCollection(featureCollection, metadata) {
|
|
431
|
+
const metadataBbox = parseBbox(metadata?.bbox);
|
|
432
|
+
const derivedBbox = metadataBbox ?? buildBboxFromFeatureCollection(featureCollection);
|
|
433
|
+
const hasFeatures = featureCollection.features.length > 0;
|
|
434
|
+
const geometry = hasFeatures ? buildGeometryCollectionFromFeatureCollection(featureCollection) : void 0;
|
|
435
|
+
if (!derivedBbox && !geometry) {
|
|
436
|
+
return null;
|
|
437
|
+
}
|
|
438
|
+
return { bbox: derivedBbox, geometry };
|
|
439
|
+
}
|
|
440
|
+
function splitFeatureCollectionByLayerId(featureCollection, layerIds) {
|
|
441
|
+
const grouped = {};
|
|
442
|
+
layerIds.forEach((layerId) => {
|
|
443
|
+
grouped[String(layerId)] = createEmptyFeatureCollection();
|
|
444
|
+
});
|
|
445
|
+
featureCollection.features.forEach((feature) => {
|
|
446
|
+
const properties = feature.properties;
|
|
447
|
+
const layerId = properties?.layerId ?? properties?.layer_id ?? properties?.__zenit_layerId;
|
|
448
|
+
if (layerId === void 0 || layerId === null) {
|
|
449
|
+
return;
|
|
450
|
+
}
|
|
451
|
+
const key = String(layerId);
|
|
452
|
+
if (!grouped[key]) {
|
|
453
|
+
grouped[key] = createEmptyFeatureCollection();
|
|
454
|
+
}
|
|
455
|
+
grouped[key] = {
|
|
456
|
+
...grouped[key],
|
|
457
|
+
features: [...grouped[key].features, feature]
|
|
458
|
+
};
|
|
459
|
+
});
|
|
460
|
+
return grouped;
|
|
461
|
+
}
|
|
462
|
+
function resolveLayerStrategy(params) {
|
|
463
|
+
const {
|
|
464
|
+
layerDetail,
|
|
465
|
+
bbox,
|
|
466
|
+
filterBBox,
|
|
467
|
+
filteredGeoJSON,
|
|
468
|
+
isVisible = true,
|
|
469
|
+
maxFeaturesFullGeojson,
|
|
470
|
+
allowLargeGeojson
|
|
471
|
+
} = params;
|
|
472
|
+
if (!isVisible) {
|
|
473
|
+
return { strategy: "geojson", effectiveBBox: bbox };
|
|
474
|
+
}
|
|
475
|
+
if (filteredGeoJSON && layerDetail.layerType !== "multipolygon") {
|
|
476
|
+
return {
|
|
477
|
+
strategy: "intersect",
|
|
478
|
+
intersectionGeometry: buildGeometryCollectionFromFeatureCollection(filteredGeoJSON)
|
|
479
|
+
};
|
|
480
|
+
}
|
|
481
|
+
const shouldSkipGeojson = shouldSkipGeojsonDownload(layerDetail.layerType, layerDetail.totalFeatures, {
|
|
482
|
+
maxFeatures: maxFeaturesFullGeojson ?? DEFAULT_MAX_FEATURES_FULL_GEOJSON,
|
|
483
|
+
allowLargeGeojson
|
|
484
|
+
});
|
|
485
|
+
if (shouldSkipGeojson && (filterBBox ?? bbox)) {
|
|
486
|
+
return { strategy: "bbox", effectiveBBox: filterBBox ?? bbox };
|
|
487
|
+
}
|
|
488
|
+
if ((layerDetail.totalFeatures ?? 0) > 5e3 && (filterBBox ?? bbox)) {
|
|
489
|
+
return { strategy: "bbox", effectiveBBox: filterBBox ?? bbox };
|
|
490
|
+
}
|
|
491
|
+
return { strategy: "geojson", effectiveBBox: bbox };
|
|
492
|
+
}
|
|
493
|
+
function buildLimitedMessage(metadata) {
|
|
494
|
+
if (!metadata) {
|
|
495
|
+
return void 0;
|
|
496
|
+
}
|
|
497
|
+
const explicit = metadata.limitedMessage;
|
|
498
|
+
if (typeof explicit === "string" && explicit.trim().length > 0) {
|
|
499
|
+
return explicit;
|
|
500
|
+
}
|
|
501
|
+
const limit = typeof metadata.limit === "number" ? metadata.limit : void 0;
|
|
502
|
+
const total = typeof metadata.totalFeatures === "number" ? metadata.totalFeatures : void 0;
|
|
503
|
+
if (limit !== void 0 && total !== void 0 && total > limit) {
|
|
504
|
+
return `Mostrando ${limit.toLocaleString()} de ${total.toLocaleString()} elementos por l\xEDmite de capa.`;
|
|
505
|
+
}
|
|
506
|
+
if (metadata.limited && limit !== void 0) {
|
|
507
|
+
return `Mostrando un m\xE1ximo de ${limit.toLocaleString()} elementos por l\xEDmite de capa.`;
|
|
508
|
+
}
|
|
509
|
+
return void 0;
|
|
510
|
+
}
|
|
511
|
+
var ZenitLayersClient = class {
|
|
512
|
+
constructor(httpClient) {
|
|
513
|
+
this.httpClient = httpClient;
|
|
514
|
+
}
|
|
515
|
+
async getLayer(layerId) {
|
|
516
|
+
const response = await this.httpClient.get(
|
|
517
|
+
`/layers/${layerId}`
|
|
518
|
+
);
|
|
519
|
+
return this.unwrapResponse(response);
|
|
520
|
+
}
|
|
521
|
+
async getLayerGeoJson(layerId, options = {}) {
|
|
522
|
+
const params = parseQueryParams(options);
|
|
523
|
+
const suffix = params.size > 0 ? `?${params.toString()}` : "";
|
|
524
|
+
const response = await this.httpClient.get(
|
|
525
|
+
`/layers/${layerId}/geojson${suffix}`
|
|
526
|
+
);
|
|
527
|
+
return this.unwrapResponse(response);
|
|
528
|
+
}
|
|
529
|
+
async getLayerFeaturesCatalog(layerId, options) {
|
|
530
|
+
if (options?.strict) {
|
|
531
|
+
const support = getCatalogSupport({
|
|
532
|
+
layerType: options.layerType,
|
|
533
|
+
geometryType: options.geometryType
|
|
534
|
+
});
|
|
535
|
+
if (!support.supported) {
|
|
536
|
+
throw new ZenitCatalogNotSupportedError(
|
|
537
|
+
support,
|
|
538
|
+
support.reason === "unknown-geometry" ? "Catalog support cannot be determined without a geometry type" : "Catalog is only supported for polygon or multipolygon geometries"
|
|
539
|
+
);
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
const response = await this.httpClient.get(
|
|
543
|
+
`/layers/${layerId}/features/catalog`
|
|
544
|
+
);
|
|
545
|
+
return this.unwrapResponse(response);
|
|
546
|
+
}
|
|
547
|
+
async getLayerFeatures(layerId, featureIds) {
|
|
548
|
+
if (!Array.isArray(featureIds) || featureIds.length === 0) {
|
|
549
|
+
throw new Error("featureIds is required and cannot be empty");
|
|
550
|
+
}
|
|
551
|
+
const query = featureIds.map(String).join(",");
|
|
552
|
+
const response = await this.httpClient.get(
|
|
553
|
+
`/layers/${layerId}/features?featureIds=${encodeURIComponent(query)}`
|
|
554
|
+
);
|
|
555
|
+
return this.unwrapResponse(response);
|
|
556
|
+
}
|
|
557
|
+
async filterLayerFeatures(layerId, filters) {
|
|
558
|
+
const query = buildLayerFilters(filters);
|
|
559
|
+
const suffix = query.toString();
|
|
560
|
+
const response = await this.httpClient.get(
|
|
561
|
+
`/layers/${layerId}/features/filter${suffix ? `?${suffix}` : ""}`
|
|
562
|
+
);
|
|
563
|
+
return this.unwrapResponse(response);
|
|
564
|
+
}
|
|
565
|
+
async filterMultipleLayersFeatures(params) {
|
|
566
|
+
const query = buildFilterMultipleParams(params);
|
|
567
|
+
const suffix = query.toString();
|
|
568
|
+
const response = await this.httpClient.get(`/layers/features/filter-multiple${suffix ? `?${suffix}` : ""}`);
|
|
569
|
+
return this.unwrapResponse(response);
|
|
570
|
+
}
|
|
571
|
+
async filterMultiple(params) {
|
|
572
|
+
return this.filterMultipleLayersFeatures(params);
|
|
573
|
+
}
|
|
574
|
+
/**
|
|
575
|
+
* Resilient filter-multiple helper:
|
|
576
|
+
* - Usa filter-multiple SOLO en capas multipolygon.
|
|
577
|
+
* - Para otras capas aplica filterLayerFeatures o getLayerGeoJson según haya filtros.
|
|
578
|
+
* - Nunca falla toda la operación por una capa inválida.
|
|
579
|
+
*
|
|
580
|
+
* Para mejor detección de layerType, pasa layerMetas con el layerType de cada capa.
|
|
581
|
+
*/
|
|
582
|
+
async filterMultipleWithFallback(params) {
|
|
583
|
+
const layerIds = params.layerIds ?? [];
|
|
584
|
+
const perLayer = {};
|
|
585
|
+
const warnings = [];
|
|
586
|
+
const hasLayerMetas = (params.layerMetas ?? []).length > 0;
|
|
587
|
+
const maxFeaturesFullGeojson = params.maxFeaturesFullGeojson ?? DEFAULT_MAX_FEATURES_FULL_GEOJSON;
|
|
588
|
+
const allowLargeGeojson = params.allowLargeGeojson ?? false;
|
|
589
|
+
layerIds.forEach((layerId) => {
|
|
590
|
+
perLayer[String(layerId)] = createEmptyFeatureCollection();
|
|
591
|
+
});
|
|
592
|
+
if (layerIds.length === 0) {
|
|
593
|
+
return { perLayer };
|
|
594
|
+
}
|
|
595
|
+
const globalFilters = hasFilters(params.filters) ? params.filters : void 0;
|
|
596
|
+
const metaIndex = /* @__PURE__ */ new Map();
|
|
597
|
+
(params.layerMetas ?? []).forEach((meta) => {
|
|
598
|
+
metaIndex.set(String(meta.layerId), meta);
|
|
599
|
+
});
|
|
600
|
+
let multipolygonIds = [];
|
|
601
|
+
let otherIds = [];
|
|
602
|
+
const layerIdsWithData = /* @__PURE__ */ new Set();
|
|
603
|
+
let aoi = null;
|
|
604
|
+
const pushWarning = (message) => {
|
|
605
|
+
warnings.push(message);
|
|
606
|
+
console.warn(message);
|
|
607
|
+
};
|
|
608
|
+
const buildSkipWarning = (layerId, layerType, totalFeatures, reason) => {
|
|
609
|
+
const typeLabel = layerType ?? "unknown";
|
|
610
|
+
const totalLabel = typeof totalFeatures === "number" ? ` totalFeatures=${totalFeatures}` : "";
|
|
611
|
+
const reasonLabel = reason ? ` ${reason}` : "";
|
|
612
|
+
return `Layer ${layerId} skipped: layerType=${typeLabel}${totalLabel}${reasonLabel}`;
|
|
613
|
+
};
|
|
614
|
+
if (hasLayerMetas) {
|
|
615
|
+
layerIds.forEach((layerId) => {
|
|
616
|
+
const meta = metaIndex.get(String(layerId));
|
|
617
|
+
if (shouldUseFilterMultiple(meta?.layerType)) {
|
|
618
|
+
multipolygonIds.push(layerId);
|
|
619
|
+
} else {
|
|
620
|
+
otherIds.push(layerId);
|
|
621
|
+
}
|
|
622
|
+
});
|
|
623
|
+
} else {
|
|
624
|
+
multipolygonIds = [...layerIds];
|
|
625
|
+
}
|
|
626
|
+
let metadata;
|
|
627
|
+
if (multipolygonIds.length > 0) {
|
|
628
|
+
try {
|
|
629
|
+
const response = await this.filterMultipleLayersFeatures({
|
|
630
|
+
layerIds: multipolygonIds,
|
|
631
|
+
filters: globalFilters
|
|
632
|
+
});
|
|
633
|
+
metadata = response.metadata;
|
|
634
|
+
const geojson = response.data ?? createEmptyFeatureCollection();
|
|
635
|
+
if (globalFilters) {
|
|
636
|
+
aoi = buildAoiFromFeatureCollection(geojson, metadata);
|
|
637
|
+
}
|
|
638
|
+
const perLayerFromMultiple = splitFeatureCollectionByLayerId(geojson, layerIds);
|
|
639
|
+
Object.entries(perLayerFromMultiple).forEach(([layerKey, collection]) => {
|
|
640
|
+
if (collection.features.length === 0) {
|
|
641
|
+
return;
|
|
642
|
+
}
|
|
643
|
+
perLayer[layerKey] = decorateFeaturesWithLayerId(collection, layerKey);
|
|
644
|
+
layerIdsWithData.add(layerKey);
|
|
645
|
+
});
|
|
646
|
+
} catch {
|
|
647
|
+
if (hasLayerMetas) {
|
|
648
|
+
otherIds = [.../* @__PURE__ */ new Set([...otherIds, ...multipolygonIds])];
|
|
649
|
+
} else {
|
|
650
|
+
otherIds = [...layerIds];
|
|
651
|
+
}
|
|
652
|
+
multipolygonIds = [];
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
if (otherIds.length > 0) {
|
|
656
|
+
const pendingIds = otherIds.filter(
|
|
657
|
+
(layerId) => !layerIdsWithData.has(String(layerId))
|
|
658
|
+
);
|
|
659
|
+
const resolved = await Promise.all(
|
|
660
|
+
pendingIds.map(async (layerId) => {
|
|
661
|
+
const meta = metaIndex.get(String(layerId));
|
|
662
|
+
const layerType = meta?.layerType;
|
|
663
|
+
const totalFeatures = meta?.totalFeatures;
|
|
664
|
+
const layerFilters = mergeLayerFilters(globalFilters, meta?.prefilters);
|
|
665
|
+
const canUseIntersect = !!globalFilters && !shouldUseFilterMultiple(layerType) && !!aoi?.geometry;
|
|
666
|
+
if (canUseIntersect) {
|
|
667
|
+
const intersectionGeometry = aoi?.geometry;
|
|
668
|
+
if (!intersectionGeometry) {
|
|
669
|
+
pushWarning(
|
|
670
|
+
buildSkipWarning(
|
|
671
|
+
layerId,
|
|
672
|
+
layerType,
|
|
673
|
+
totalFeatures,
|
|
674
|
+
"no AOI geometry available"
|
|
675
|
+
)
|
|
676
|
+
);
|
|
677
|
+
return { layerId, geojson: createEmptyFeatureCollection() };
|
|
678
|
+
}
|
|
679
|
+
try {
|
|
680
|
+
const response = await this.getLayerGeoJsonIntersect({
|
|
681
|
+
id: layerId,
|
|
682
|
+
geometry: intersectionGeometry
|
|
683
|
+
});
|
|
684
|
+
if (aoi?.bbox) {
|
|
685
|
+
console.debug(
|
|
686
|
+
`Layer ${layerId} fetched by intersect using AOI bbox=${JSON.stringify(
|
|
687
|
+
aoi.bbox
|
|
688
|
+
)}`
|
|
689
|
+
);
|
|
690
|
+
} else {
|
|
691
|
+
console.debug(`Layer ${layerId} fetched by intersect using AOI geometry`);
|
|
692
|
+
}
|
|
693
|
+
const geojson = response.data ?? createEmptyFeatureCollection();
|
|
694
|
+
return {
|
|
695
|
+
layerId,
|
|
696
|
+
geojson: decorateFeaturesWithLayerId(geojson, layerId)
|
|
697
|
+
};
|
|
698
|
+
} catch (error) {
|
|
699
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
700
|
+
pushWarning(
|
|
701
|
+
buildSkipWarning(
|
|
702
|
+
layerId,
|
|
703
|
+
layerType,
|
|
704
|
+
totalFeatures,
|
|
705
|
+
`intersect failed: ${message}`
|
|
706
|
+
)
|
|
707
|
+
);
|
|
708
|
+
return { layerId, geojson: createEmptyFeatureCollection() };
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
if (layerFilters && (hasLayerMetas ? shouldUseFilterMultiple(layerType) : true)) {
|
|
712
|
+
try {
|
|
713
|
+
const response = await this.filterLayerFeatures(layerId, layerFilters);
|
|
714
|
+
const geojson = response.data ?? createEmptyFeatureCollection();
|
|
715
|
+
return {
|
|
716
|
+
layerId,
|
|
717
|
+
geojson: decorateFeaturesWithLayerId(geojson, layerId)
|
|
718
|
+
};
|
|
719
|
+
} catch (error) {
|
|
720
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
721
|
+
pushWarning(
|
|
722
|
+
`Layer ${layerId} filter failed: ${message}`
|
|
723
|
+
);
|
|
724
|
+
return { layerId, geojson: createEmptyFeatureCollection() };
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
if (shouldSkipGeojsonDownload(layerType, totalFeatures, {
|
|
728
|
+
maxFeatures: maxFeaturesFullGeojson,
|
|
729
|
+
allowLargeGeojson
|
|
730
|
+
})) {
|
|
731
|
+
pushWarning(
|
|
732
|
+
buildSkipWarning(
|
|
733
|
+
layerId,
|
|
734
|
+
layerType,
|
|
735
|
+
totalFeatures,
|
|
736
|
+
"not supported by filter endpoint and too large for /geojson; use intersect with AOI"
|
|
737
|
+
)
|
|
738
|
+
);
|
|
739
|
+
return { layerId, geojson: createEmptyFeatureCollection() };
|
|
740
|
+
}
|
|
741
|
+
try {
|
|
742
|
+
const response = await this.getLayerGeoJson(layerId);
|
|
743
|
+
const geojson = response.data ?? createEmptyFeatureCollection();
|
|
744
|
+
return {
|
|
745
|
+
layerId,
|
|
746
|
+
geojson: decorateFeaturesWithLayerId(geojson, layerId)
|
|
747
|
+
};
|
|
748
|
+
} catch (error) {
|
|
749
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
750
|
+
pushWarning(
|
|
751
|
+
buildSkipWarning(layerId, layerType, totalFeatures, `error=${message}`)
|
|
752
|
+
);
|
|
753
|
+
return { layerId, geojson: createEmptyFeatureCollection() };
|
|
754
|
+
}
|
|
755
|
+
})
|
|
756
|
+
);
|
|
757
|
+
resolved.forEach(({ layerId, geojson }) => {
|
|
758
|
+
perLayer[String(layerId)] = geojson;
|
|
759
|
+
});
|
|
760
|
+
}
|
|
761
|
+
const mergedMetadata = warnings.length > 0 ? {
|
|
762
|
+
...metadata ?? {},
|
|
763
|
+
warnings: [...metadata?.warnings ?? [], ...warnings]
|
|
764
|
+
} : metadata;
|
|
765
|
+
return { perLayer, metadata: mergedMetadata, aoi };
|
|
766
|
+
}
|
|
767
|
+
async getLayerGeoJsonBBox(request) {
|
|
768
|
+
const params = new URLSearchParams({
|
|
769
|
+
minLon: String(request.bbox.minLon),
|
|
770
|
+
minLat: String(request.bbox.minLat),
|
|
771
|
+
maxLon: String(request.bbox.maxLon),
|
|
772
|
+
maxLat: String(request.bbox.maxLat)
|
|
773
|
+
});
|
|
774
|
+
parseQueryParams(request).forEach((value, key) => params.set(key, value));
|
|
775
|
+
const response = await this.httpClient.get(
|
|
776
|
+
`/layers/${request.id}/geojson/bbox?${params.toString()}`
|
|
777
|
+
);
|
|
778
|
+
return this.unwrapResponse(response);
|
|
779
|
+
}
|
|
780
|
+
async getLayerGeoJsonIntersect(request) {
|
|
781
|
+
const params = parseQueryParams(request);
|
|
782
|
+
const suffix = params.size > 0 ? `?${params.toString()}` : "";
|
|
783
|
+
const response = await this.httpClient.post(
|
|
784
|
+
`/layers/${request.id}/geojson/intersects${suffix}`,
|
|
785
|
+
{ geometry: request.geometry }
|
|
786
|
+
);
|
|
787
|
+
return this.unwrapResponse(response);
|
|
788
|
+
}
|
|
789
|
+
async getLayerTileTemplate(layerId) {
|
|
790
|
+
const response = await this.httpClient.get(
|
|
791
|
+
`/layers/${layerId}/tiles/template`
|
|
792
|
+
);
|
|
793
|
+
const unwrapped = this.unwrapResponse(response);
|
|
794
|
+
if (typeof unwrapped.data === "string") {
|
|
795
|
+
return unwrapped.data;
|
|
796
|
+
}
|
|
797
|
+
if (unwrapped.data && typeof unwrapped.data.template === "string") {
|
|
798
|
+
return unwrapped.data.template;
|
|
799
|
+
}
|
|
800
|
+
throw new Error("Invalid tile template response");
|
|
801
|
+
}
|
|
802
|
+
async loadLayerData(params) {
|
|
803
|
+
const { id, layerDetail, bbox, filterBBox, filteredGeoJSON, isVisible = true, ...options } = params;
|
|
804
|
+
const resolution = resolveLayerStrategy({
|
|
805
|
+
layerDetail,
|
|
806
|
+
bbox,
|
|
807
|
+
filterBBox,
|
|
808
|
+
filteredGeoJSON,
|
|
809
|
+
isVisible,
|
|
810
|
+
maxFeaturesFullGeojson: params.maxFeaturesFullGeojson,
|
|
811
|
+
allowLargeGeojson: params.allowLargeGeojson
|
|
812
|
+
});
|
|
813
|
+
const response = await this.fetchByStrategy(id, resolution, options);
|
|
814
|
+
const totalFeatures = asOptionalNumber(response.totalFeatures) ?? asOptionalNumber(response.metadata?.totalFeatures) ?? asOptionalNumber(layerDetail.totalFeatures);
|
|
815
|
+
const limitedMessage = response.limitedMessage ?? buildLimitedMessage(response.metadata);
|
|
816
|
+
return {
|
|
817
|
+
geojson: response.data,
|
|
818
|
+
strategy: resolution.strategy,
|
|
819
|
+
limitedMessage,
|
|
820
|
+
totalFeatures,
|
|
821
|
+
metadata: response.metadata
|
|
822
|
+
};
|
|
823
|
+
}
|
|
824
|
+
async loadFilteredFeatures(params) {
|
|
825
|
+
const response = await this.filterMultipleLayersFeatures(params);
|
|
826
|
+
return {
|
|
827
|
+
geojson: response.data,
|
|
828
|
+
metadata: response.metadata,
|
|
829
|
+
totalFeatures: response.metadata?.totalFeatures ?? response.totalFeatures
|
|
830
|
+
};
|
|
831
|
+
}
|
|
832
|
+
async fetchByStrategy(layerId, resolution, options) {
|
|
833
|
+
switch (resolution.strategy) {
|
|
834
|
+
case "bbox": {
|
|
835
|
+
if (!resolution.effectiveBBox) {
|
|
836
|
+
throw new Error("Bounding box is required for bbox strategy");
|
|
837
|
+
}
|
|
838
|
+
return this.getLayerGeoJsonBBox({ id: layerId, bbox: resolution.effectiveBBox, ...options });
|
|
839
|
+
}
|
|
840
|
+
case "intersect": {
|
|
841
|
+
if (!resolution.intersectionGeometry) {
|
|
842
|
+
throw new Error("Geometry is required for intersect strategy");
|
|
843
|
+
}
|
|
844
|
+
return this.getLayerGeoJsonIntersect({
|
|
845
|
+
id: layerId,
|
|
846
|
+
geometry: resolution.intersectionGeometry,
|
|
847
|
+
...options
|
|
848
|
+
});
|
|
849
|
+
}
|
|
850
|
+
case "geojson":
|
|
851
|
+
default:
|
|
852
|
+
return this.getLayerGeoJson(layerId, options);
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
unwrapResponse(response) {
|
|
856
|
+
if (!response || typeof response !== "object" || !("data" in response)) {
|
|
857
|
+
throw new Error("Invalid API response: missing data field");
|
|
858
|
+
}
|
|
859
|
+
if ("success" in response && response.success === false) {
|
|
860
|
+
throw new Error(response.message ?? "Request was not successful");
|
|
861
|
+
}
|
|
862
|
+
return response;
|
|
863
|
+
}
|
|
864
|
+
};
|
|
865
|
+
|
|
866
|
+
// src/config/ZenitSdkConfig.ts
|
|
867
|
+
var ZenitClient = class {
|
|
868
|
+
constructor(config) {
|
|
869
|
+
this.config = config;
|
|
870
|
+
this.accessToken = config.accessToken;
|
|
871
|
+
this.refreshToken = config.refreshToken;
|
|
872
|
+
this.sdkToken = config.sdkToken;
|
|
873
|
+
this.httpClient = new HttpClient({
|
|
874
|
+
baseUrl: config.baseUrl,
|
|
875
|
+
resolveTokens: () => ({ accessToken: this.accessToken, sdkToken: this.sdkToken }),
|
|
876
|
+
resolveAuthorizationHeader: this.getAuthorizationHeader.bind(this)
|
|
877
|
+
});
|
|
878
|
+
this.auth = new ZenitAuthClient(this.httpClient, this.updateTokens.bind(this), config);
|
|
879
|
+
this.sdkAuth = new ZenitSdkAuthClient(
|
|
880
|
+
this.httpClient,
|
|
881
|
+
this.updateAccessTokenFromSdkExchange.bind(this),
|
|
882
|
+
config
|
|
883
|
+
);
|
|
884
|
+
this.maps = new ZenitMapsClient(this.httpClient);
|
|
885
|
+
this.layers = new ZenitLayersClient(this.httpClient);
|
|
886
|
+
}
|
|
887
|
+
/**
|
|
888
|
+
* Update tokens in memory and propagate to config callbacks.
|
|
889
|
+
*/
|
|
890
|
+
updateTokens(tokens) {
|
|
891
|
+
if (tokens.accessToken) {
|
|
892
|
+
this.setAccessToken(tokens.accessToken);
|
|
893
|
+
}
|
|
894
|
+
if (tokens.refreshToken) {
|
|
895
|
+
this.setRefreshToken(tokens.refreshToken);
|
|
896
|
+
}
|
|
897
|
+
this.config.onTokenRefreshed?.({
|
|
898
|
+
accessToken: this.accessToken || "",
|
|
899
|
+
refreshToken: this.refreshToken
|
|
900
|
+
});
|
|
901
|
+
}
|
|
902
|
+
// Se usa cuando /sdk-auth/exchange devuelve un accessToken; pasa a ser el accessToken principal del SDK.
|
|
903
|
+
updateAccessTokenFromSdkExchange(token) {
|
|
904
|
+
if (token) {
|
|
905
|
+
this.setAccessToken(token);
|
|
906
|
+
}
|
|
907
|
+
}
|
|
908
|
+
setAccessToken(token) {
|
|
909
|
+
this.accessToken = token;
|
|
910
|
+
this.config.accessToken = token;
|
|
911
|
+
}
|
|
912
|
+
setRefreshToken(token) {
|
|
913
|
+
this.refreshToken = token;
|
|
914
|
+
this.config.refreshToken = token;
|
|
915
|
+
}
|
|
916
|
+
getAuthorizationHeader() {
|
|
917
|
+
const header = {};
|
|
918
|
+
if (this.accessToken) {
|
|
919
|
+
header.Authorization = `Bearer ${this.accessToken}`;
|
|
920
|
+
}
|
|
921
|
+
return header;
|
|
922
|
+
}
|
|
923
|
+
getHttpClient() {
|
|
924
|
+
return this.httpClient;
|
|
925
|
+
}
|
|
926
|
+
};
|
|
927
|
+
|
|
928
|
+
// src/engine/FilterEngine.ts
|
|
929
|
+
var FilterEngine = class {
|
|
930
|
+
decide(params) {
|
|
931
|
+
if (params.request) {
|
|
932
|
+
return { nextRequest: params.request };
|
|
933
|
+
}
|
|
934
|
+
if (params.filters && params.layerIds && params.layerIds.length > 1) {
|
|
935
|
+
return {
|
|
936
|
+
nextRequest: {
|
|
937
|
+
type: "filter-multiple",
|
|
938
|
+
layerIds: params.layerIds,
|
|
939
|
+
filters: params.filters
|
|
940
|
+
},
|
|
941
|
+
reason: "filters+layerIds"
|
|
942
|
+
};
|
|
943
|
+
}
|
|
944
|
+
if (params.filters && params.layerId !== void 0) {
|
|
945
|
+
return {
|
|
946
|
+
nextRequest: {
|
|
947
|
+
type: "filter-layer",
|
|
948
|
+
layerId: params.layerId,
|
|
949
|
+
filters: params.filters
|
|
950
|
+
},
|
|
951
|
+
reason: "filters+layerId"
|
|
952
|
+
};
|
|
953
|
+
}
|
|
954
|
+
if (params.bbox && params.layerId !== void 0) {
|
|
955
|
+
return {
|
|
956
|
+
nextRequest: {
|
|
957
|
+
type: "bbox",
|
|
958
|
+
layerId: params.layerId,
|
|
959
|
+
bbox: params.bbox,
|
|
960
|
+
options: params.options
|
|
961
|
+
},
|
|
962
|
+
reason: "bbox+layerId"
|
|
963
|
+
};
|
|
964
|
+
}
|
|
965
|
+
if (params.geometry && params.layerId !== void 0) {
|
|
966
|
+
return {
|
|
967
|
+
nextRequest: {
|
|
968
|
+
type: "intersect",
|
|
969
|
+
layerId: params.layerId,
|
|
970
|
+
geometry: params.geometry,
|
|
971
|
+
options: params.options
|
|
972
|
+
},
|
|
973
|
+
reason: "geometry+layerId"
|
|
974
|
+
};
|
|
975
|
+
}
|
|
976
|
+
if (params.layerId !== void 0) {
|
|
977
|
+
return {
|
|
978
|
+
nextRequest: {
|
|
979
|
+
type: "geojson",
|
|
980
|
+
layerId: params.layerId,
|
|
981
|
+
options: params.options
|
|
982
|
+
},
|
|
983
|
+
reason: "layerId"
|
|
984
|
+
};
|
|
985
|
+
}
|
|
986
|
+
throw new Error("FilterEngine could not resolve a next request");
|
|
987
|
+
}
|
|
988
|
+
};
|
|
989
|
+
var filterEngine = new FilterEngine();
|
|
990
|
+
|
|
991
|
+
// src/ai/normalize.ts
|
|
992
|
+
function normalizeBbox(input) {
|
|
993
|
+
if (!input) return null;
|
|
994
|
+
if (Array.isArray(input)) {
|
|
995
|
+
if (input.length !== 4) {
|
|
996
|
+
console.warn("[normalizeBbox] Array must have exactly 4 elements, got:", input.length);
|
|
997
|
+
return null;
|
|
998
|
+
}
|
|
999
|
+
const [minLon, minLat, maxLon, maxLat] = input;
|
|
1000
|
+
if (typeof minLon !== "number" || typeof minLat !== "number" || typeof maxLon !== "number" || typeof maxLat !== "number" || !Number.isFinite(minLon) || !Number.isFinite(minLat) || !Number.isFinite(maxLon) || !Number.isFinite(maxLat)) {
|
|
1001
|
+
console.warn("[normalizeBbox] Array contains non-finite numbers:", input);
|
|
1002
|
+
return null;
|
|
1003
|
+
}
|
|
1004
|
+
if (minLon >= maxLon || minLat >= maxLat) {
|
|
1005
|
+
console.warn("[normalizeBbox] Invalid bbox: min must be < max", {
|
|
1006
|
+
minLon,
|
|
1007
|
+
maxLon,
|
|
1008
|
+
minLat,
|
|
1009
|
+
maxLat
|
|
1010
|
+
});
|
|
1011
|
+
return null;
|
|
1012
|
+
}
|
|
1013
|
+
return { minLon, minLat, maxLon, maxLat };
|
|
1014
|
+
}
|
|
1015
|
+
if (typeof input === "object") {
|
|
1016
|
+
const candidate = input;
|
|
1017
|
+
const { minLon, minLat, maxLon, maxLat } = candidate;
|
|
1018
|
+
if (typeof minLon !== "number" || typeof minLat !== "number" || typeof maxLon !== "number" || typeof maxLat !== "number" || !Number.isFinite(minLon) || !Number.isFinite(minLat) || !Number.isFinite(maxLon) || !Number.isFinite(maxLat)) {
|
|
1019
|
+
console.warn("[normalizeBbox] Object missing valid bbox keys or contains non-finite numbers:", input);
|
|
1020
|
+
return null;
|
|
1021
|
+
}
|
|
1022
|
+
if (minLon >= maxLon || minLat >= maxLat) {
|
|
1023
|
+
console.warn("[normalizeBbox] Invalid bbox: min must be < max", {
|
|
1024
|
+
minLon,
|
|
1025
|
+
maxLon,
|
|
1026
|
+
minLat,
|
|
1027
|
+
maxLat
|
|
1028
|
+
});
|
|
1029
|
+
return null;
|
|
1030
|
+
}
|
|
1031
|
+
return { minLon, minLat, maxLon, maxLat };
|
|
1032
|
+
}
|
|
1033
|
+
console.warn("[normalizeBbox] Unsupported bbox format:", typeof input);
|
|
1034
|
+
return null;
|
|
1035
|
+
}
|
|
1036
|
+
export {
|
|
1037
|
+
ChevronLeft,
|
|
1038
|
+
ChevronRight,
|
|
1039
|
+
DEFAULT_MAX_FEATURES_FULL_GEOJSON,
|
|
1040
|
+
Eye,
|
|
1041
|
+
EyeOff,
|
|
1042
|
+
FilterEngine,
|
|
1043
|
+
FloatingChatBox,
|
|
1044
|
+
HttpClient,
|
|
1045
|
+
Layers,
|
|
1046
|
+
Upload,
|
|
1047
|
+
X,
|
|
1048
|
+
ZenitAuthClient,
|
|
1049
|
+
ZenitCatalogNotSupportedError,
|
|
1050
|
+
ZenitClient,
|
|
1051
|
+
ZenitFeatureFilterPanel,
|
|
1052
|
+
ZenitLayerManager,
|
|
1053
|
+
ZenitLayersClient,
|
|
1054
|
+
ZenitMap,
|
|
1055
|
+
ZenitMapsClient,
|
|
1056
|
+
ZenitSdkAuthClient,
|
|
1057
|
+
ZoomIn,
|
|
1058
|
+
applyFilteredGeoJSONStrategy,
|
|
1059
|
+
applyLayerOverrides,
|
|
1060
|
+
applyPrefiltersToFeatureCollection,
|
|
1061
|
+
buildAoiFromFeatureCollection,
|
|
1062
|
+
buildFilterMultipleParams,
|
|
1063
|
+
buildLayerFilters,
|
|
1064
|
+
buildLimitedMessage,
|
|
1065
|
+
centroidFromBBox,
|
|
1066
|
+
clampNumber,
|
|
1067
|
+
clampOpacity,
|
|
1068
|
+
computeBBoxFromFeature,
|
|
1069
|
+
createChatService,
|
|
1070
|
+
decorateFeaturesWithLayerId,
|
|
1071
|
+
extractMapDto,
|
|
1072
|
+
filterEngine,
|
|
1073
|
+
getAccentByLayerId,
|
|
1074
|
+
getCatalogSupport,
|
|
1075
|
+
getEffectiveLayerOpacity,
|
|
1076
|
+
getLayerColor,
|
|
1077
|
+
getLayerZoomOpacityFactor,
|
|
1078
|
+
getStyleByLayerId,
|
|
1079
|
+
getZoomOpacityFactor,
|
|
1080
|
+
initLayerStates,
|
|
1081
|
+
isPolygonLayer,
|
|
1082
|
+
normalizeBbox,
|
|
1083
|
+
normalizeMapCenter,
|
|
1084
|
+
normalizeMapLayers,
|
|
1085
|
+
resetOverrides,
|
|
1086
|
+
resolveLayerAccent,
|
|
1087
|
+
resolveLayerStrategy,
|
|
1088
|
+
sendMessage,
|
|
1089
|
+
sendMessageStream,
|
|
1090
|
+
shouldSkipGeojsonDownload,
|
|
1091
|
+
shouldUseFilterMultiple,
|
|
1092
|
+
useSendMessage,
|
|
1093
|
+
useSendMessageStream
|
|
1094
|
+
};
|
|
1095
|
+
//# sourceMappingURL=index.mjs.map
|