passerelle-geo-components 0.2.0-alpha.4 → 0.2.0-alpha.5
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/geo-components.js
CHANGED
|
@@ -231,6 +231,9 @@ function _v031ToLegacyShim(params, hostContext) {
|
|
|
231
231
|
const catalog = [];
|
|
232
232
|
const overrides = [];
|
|
233
233
|
for (const l of params.layers) {
|
|
234
|
+
// V0.3.2 alpha.5 : propager source complete pour permettre fetch async
|
|
235
|
+
// universel (geojson_path, geojson_url, wfs, boundary_admin, grist_table, x_*).
|
|
236
|
+
// Legacy V1.13 catalog_layers.geojson reste pour compat (source.type='geojson' inline).
|
|
234
237
|
const geojson = l.source && l.source.type === "geojson" ? l.source.data : null;
|
|
235
238
|
const style = l.style || {};
|
|
236
239
|
const interactions = l.interactions || {};
|
|
@@ -240,6 +243,7 @@ function _v031ToLegacyShim(params, hostContext) {
|
|
|
240
243
|
name: l.name,
|
|
241
244
|
geometry_type: l.geometry_type,
|
|
242
245
|
geojson,
|
|
246
|
+
source_v031: l.source,
|
|
243
247
|
n_features: l.n_features,
|
|
244
248
|
bbox: l.bbox,
|
|
245
249
|
classification: classifShim,
|
|
@@ -278,6 +282,108 @@ function _v031ToLegacyShim(params, hostContext) {
|
|
|
278
282
|
return { params: shimParams, hostContext: shimHost };
|
|
279
283
|
}
|
|
280
284
|
|
|
285
|
+
// V0.3.2 alpha.5 (Sprint V0.2 session finale, 2026-07-11) : resolveur
|
|
286
|
+
// universel des 7 types de source du contract V0.3.2. Retourne une Promise
|
|
287
|
+
// qui resout vers un GeoJSON FeatureCollection utilisable par MapLibre.
|
|
288
|
+
// Pattern async : le layer est ajoute immediatement avec source vide, puis
|
|
289
|
+
// setData appele quand la promise resout. Consequence : la carte s'affiche
|
|
290
|
+
// instantanement (fond + controls), features apparaissent async.
|
|
291
|
+
//
|
|
292
|
+
// Types supportes :
|
|
293
|
+
// - geojson → source.data (immediat)
|
|
294
|
+
// - geojson_path → fetch(path, {credentials:'include'}) same-origin
|
|
295
|
+
// - geojson_url → fetch(url) public sans credentials
|
|
296
|
+
// - wfs → construct WFS GetFeature URL + fetch
|
|
297
|
+
// - boundary_admin → hostContext.boundary_resolver({catalog_id, filter})
|
|
298
|
+
// - grist_table → hostContext.grist_resolver({table, id_field, sync})
|
|
299
|
+
// - x_<prefix> → hostContext.custom_resolvers[prefix]
|
|
300
|
+
// - vector/pmtiles → PAS gere ici (utiliser addSource type=vector direct)
|
|
301
|
+
async function _fetchSourceData(source, hostContext) {
|
|
302
|
+
if (!source || !source.type) return { type: "FeatureCollection", features: [] };
|
|
303
|
+
const t = source.type;
|
|
304
|
+
|
|
305
|
+
if (t === "geojson") {
|
|
306
|
+
if (typeof source.data === "string") {
|
|
307
|
+
// legacy : data peut etre une URL string
|
|
308
|
+
const resp = await fetch(source.data);
|
|
309
|
+
if (!resp.ok) throw new Error(`geojson data fetch failed: ${resp.status}`);
|
|
310
|
+
return resp.json();
|
|
311
|
+
}
|
|
312
|
+
return source.data || { type: "FeatureCollection", features: [] };
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
if (t === "geojson_path") {
|
|
316
|
+
// Path serveur (PVC hub). Fetch same-origin avec cookies OIDC.
|
|
317
|
+
const url = source.fetch_url || source.path;
|
|
318
|
+
const resp = await fetch(url, { credentials: "include" });
|
|
319
|
+
if (!resp.ok) throw new Error(`geojson_path fetch failed: ${resp.status}`);
|
|
320
|
+
return resp.json();
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
if (t === "geojson_url") {
|
|
324
|
+
// URL publique. Fetch sans credentials.
|
|
325
|
+
const resp = await fetch(source.url);
|
|
326
|
+
if (!resp.ok) throw new Error(`geojson_url fetch failed: ${resp.status}`);
|
|
327
|
+
return resp.json();
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
if (t === "wfs") {
|
|
331
|
+
// Construct GetFeature URL avec parametres par defaut V0.3.2.
|
|
332
|
+
const p = new URLSearchParams({
|
|
333
|
+
service: "WFS",
|
|
334
|
+
version: "2.0.0",
|
|
335
|
+
request: "GetFeature",
|
|
336
|
+
typeName: source.type_name,
|
|
337
|
+
outputFormat: "application/json",
|
|
338
|
+
srsName: "EPSG:4326",
|
|
339
|
+
});
|
|
340
|
+
if (source.max_features) p.set("count", String(source.max_features));
|
|
341
|
+
if (source.filter_cql) p.set("cql_filter", source.filter_cql);
|
|
342
|
+
const sep = source.url.includes("?") ? "&" : "?";
|
|
343
|
+
const resp = await fetch(source.url + sep + p.toString());
|
|
344
|
+
if (!resp.ok) throw new Error(`wfs fetch failed: ${resp.status}`);
|
|
345
|
+
return resp.json();
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
if (t === "boundary_admin") {
|
|
349
|
+
// Delegue au hostContext du consommateur.
|
|
350
|
+
const resolver = hostContext && hostContext.boundary_resolver;
|
|
351
|
+
if (typeof resolver !== "function") {
|
|
352
|
+
throw new Error(
|
|
353
|
+
"boundary_admin requires hostContext.boundary_resolver(source)"
|
|
354
|
+
);
|
|
355
|
+
}
|
|
356
|
+
return await resolver(source);
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
if (t === "grist_table") {
|
|
360
|
+
// Delegue au widget Grist parent.
|
|
361
|
+
const resolver = hostContext && hostContext.grist_resolver;
|
|
362
|
+
if (typeof resolver !== "function") {
|
|
363
|
+
throw new Error(
|
|
364
|
+
"grist_table requires hostContext.grist_resolver(source)"
|
|
365
|
+
);
|
|
366
|
+
}
|
|
367
|
+
return await resolver(source);
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
if (typeof t === "string" && t.startsWith("x_")) {
|
|
371
|
+
// Extension par-projet.
|
|
372
|
+
const resolvers = (hostContext && hostContext.custom_resolvers) || {};
|
|
373
|
+
const resolver = resolvers[t];
|
|
374
|
+
if (typeof resolver !== "function") {
|
|
375
|
+
throw new Error(
|
|
376
|
+
`custom source type '${t}' requires hostContext.custom_resolvers['${t}']`
|
|
377
|
+
);
|
|
378
|
+
}
|
|
379
|
+
return await resolver(source);
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
// Types vector/pmtiles/... : geres directement par MapLibre addSource,
|
|
383
|
+
// pas via ce resolveur.
|
|
384
|
+
throw new Error(`_fetchSourceData: type '${t}' non supporte comme geojson`);
|
|
385
|
+
}
|
|
386
|
+
|
|
281
387
|
// V0.3.1 classification : {color: {mode, field, method, breaks, palette,
|
|
282
388
|
// _compiled_expression}, size, label} -> {paint_expression, method, field, ...}
|
|
283
389
|
// attendus par _paintForClassification (V1.13).
|
|
@@ -1174,6 +1280,40 @@ export class GeoMap extends HTMLElement {
|
|
|
1174
1280
|
data: sceneLayer.geojson || { type: "FeatureCollection", features: [] },
|
|
1175
1281
|
});
|
|
1176
1282
|
|
|
1283
|
+
// V0.3.2 alpha.5 : fetch async des sources non-inline
|
|
1284
|
+
// (geojson_path, geojson_url, wfs, boundary_admin, grist_table, x_*).
|
|
1285
|
+
// La carte s'affiche instantanement, features arrivent async.
|
|
1286
|
+
if (sceneLayer.source_v031
|
|
1287
|
+
&& sceneLayer.source_v031.type !== "geojson"
|
|
1288
|
+
&& !sceneLayer.geojson) {
|
|
1289
|
+
const hostCtx = this._hostContext || {};
|
|
1290
|
+
_fetchSourceData(sceneLayer.source_v031, hostCtx)
|
|
1291
|
+
.then((geojson) => {
|
|
1292
|
+
const src = this.map.getSource(sourceId);
|
|
1293
|
+
if (src && typeof src.setData === "function") {
|
|
1294
|
+
src.setData(geojson);
|
|
1295
|
+
}
|
|
1296
|
+
this.dispatchEvent(new CustomEvent("geo:source-loaded", {
|
|
1297
|
+
detail: {
|
|
1298
|
+
layerId: sceneLayer.id,
|
|
1299
|
+
features: (geojson.features || []).length,
|
|
1300
|
+
},
|
|
1301
|
+
}));
|
|
1302
|
+
})
|
|
1303
|
+
.catch((e) => {
|
|
1304
|
+
console.warn(
|
|
1305
|
+
"[geo-components] fetch source failed for",
|
|
1306
|
+
sceneLayer.id, "->", sceneLayer.source_v031, e
|
|
1307
|
+
);
|
|
1308
|
+
this.dispatchEvent(new CustomEvent("geo:source-error", {
|
|
1309
|
+
detail: {
|
|
1310
|
+
layerId: sceneLayer.id,
|
|
1311
|
+
error: String(e).slice(0, 200),
|
|
1312
|
+
},
|
|
1313
|
+
}));
|
|
1314
|
+
});
|
|
1315
|
+
}
|
|
1316
|
+
|
|
1177
1317
|
// V0.2.0 Chantier 4 : détection type + auto-split polygon+point
|
|
1178
1318
|
// Analyse la geojson pour détecter FC hétérogène et éviter le bug
|
|
1179
1319
|
// POINT invisibles (remontée cerema-livrables).
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "passerelle-geo-components",
|
|
3
|
-
"version": "0.2.0-alpha.
|
|
3
|
+
"version": "0.2.0-alpha.5",
|
|
4
4
|
"description": "Web Components pour cartes MapLibre + composants controllers pilotes - brique commune multi-projet avec contract SceneManifest V0.3.1. Zero dependance runtime, ES module, encapsulation Shadow DOM.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "geo-components.js",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# generated by datamodel-codegen:
|
|
2
2
|
# filename: scene_manifest.schema.json
|
|
3
|
-
# timestamp: 2026-07-
|
|
3
|
+
# timestamp: 2026-07-11T12:39:43+00:00
|
|
4
4
|
|
|
5
5
|
from __future__ import annotations
|
|
6
6
|
|
|
@@ -337,6 +337,18 @@ class LayerSource2(BaseModel):
|
|
|
337
337
|
|
|
338
338
|
|
|
339
339
|
class LayerSource3(BaseModel):
|
|
340
|
+
model_config = ConfigDict(
|
|
341
|
+
extra="allow",
|
|
342
|
+
)
|
|
343
|
+
type: Literal["geojson_url"]
|
|
344
|
+
url: AnyUrl = Field(
|
|
345
|
+
...,
|
|
346
|
+
description="URL publique du GeoJSON (S3 public, CDN, endpoint sans auth). Fetche cote client sans credentials.",
|
|
347
|
+
)
|
|
348
|
+
crs: Literal["EPSG:4326"] = "EPSG:4326"
|
|
349
|
+
|
|
350
|
+
|
|
351
|
+
class LayerSource4(BaseModel):
|
|
340
352
|
model_config = ConfigDict(
|
|
341
353
|
extra="allow",
|
|
342
354
|
)
|
|
@@ -350,7 +362,7 @@ class LayerSource3(BaseModel):
|
|
|
350
362
|
promote_id: str | None = None
|
|
351
363
|
|
|
352
364
|
|
|
353
|
-
class
|
|
365
|
+
class LayerSource5(BaseModel):
|
|
354
366
|
model_config = ConfigDict(
|
|
355
367
|
extra="allow",
|
|
356
368
|
)
|
|
@@ -369,7 +381,7 @@ class Sync(Enum):
|
|
|
369
381
|
save = "save"
|
|
370
382
|
|
|
371
383
|
|
|
372
|
-
class
|
|
384
|
+
class LayerSource6(BaseModel):
|
|
373
385
|
model_config = ConfigDict(
|
|
374
386
|
extra="allow",
|
|
375
387
|
)
|
|
@@ -382,7 +394,7 @@ class LayerSource5(BaseModel):
|
|
|
382
394
|
)
|
|
383
395
|
|
|
384
396
|
|
|
385
|
-
class
|
|
397
|
+
class LayerSource7(BaseModel):
|
|
386
398
|
model_config = ConfigDict(
|
|
387
399
|
extra="allow",
|
|
388
400
|
)
|
|
@@ -391,7 +403,7 @@ class LayerSource6(BaseModel):
|
|
|
391
403
|
type_name: str = Field(..., description="WFS TypeName (ex: BATIMENT.BATIMENT).")
|
|
392
404
|
|
|
393
405
|
|
|
394
|
-
class
|
|
406
|
+
class LayerSource8(BaseModel):
|
|
395
407
|
model_config = ConfigDict(
|
|
396
408
|
extra="allow",
|
|
397
409
|
)
|
|
@@ -405,7 +417,7 @@ class LayerSource7(BaseModel):
|
|
|
405
417
|
)
|
|
406
418
|
|
|
407
419
|
|
|
408
|
-
class
|
|
420
|
+
class LayerSource9(BaseModel):
|
|
409
421
|
model_config = ConfigDict(
|
|
410
422
|
extra="allow",
|
|
411
423
|
)
|
|
@@ -424,9 +436,10 @@ class LayerSource(
|
|
|
424
436
|
| LayerSource6
|
|
425
437
|
| LayerSource7
|
|
426
438
|
| LayerSource8
|
|
439
|
+
| LayerSource9
|
|
427
440
|
]
|
|
428
441
|
):
|
|
429
|
-
root: LayerSource1 | LayerSource2 | LayerSource3 | LayerSource4 | LayerSource5 | LayerSource6 | LayerSource7 | LayerSource8 = Field(
|
|
442
|
+
root: LayerSource1 | LayerSource2 | LayerSource3 | LayerSource4 | LayerSource5 | LayerSource6 | LayerSource7 | LayerSource8 | LayerSource9 = Field(
|
|
430
443
|
...,
|
|
431
444
|
description="Source de données. Discriminated union sur 'type'. Alignée MapLibre style spec (v8+).",
|
|
432
445
|
)
|
|
@@ -912,7 +925,7 @@ class Layer(BaseModel):
|
|
|
912
925
|
extensions: dict[constr(pattern=r"^[a-z][a-z_0-9]*$"), dict[str, Any]] | None = None
|
|
913
926
|
|
|
914
927
|
|
|
915
|
-
class
|
|
928
|
+
class ScenemanifestV032(BaseModel):
|
|
916
929
|
model_config = ConfigDict(
|
|
917
930
|
extra="forbid",
|
|
918
931
|
)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
-
"$id": "https://nic01asfr.github.io/Passerelle/schemas/scene_manifest/0.3.
|
|
4
|
-
"title": "SceneManifest V0.3.
|
|
3
|
+
"$id": "https://nic01asfr.github.io/Passerelle/schemas/scene_manifest/0.3.2.json",
|
|
4
|
+
"title": "SceneManifest V0.3.2",
|
|
5
5
|
"description": "Contract unifie passerelle-geo-components pivot cross-projet (workspace SIG + editeur livrables + widget carto). Voir CONTRACT-V0.3.1.md pour la spec humaine complete.",
|
|
6
6
|
"type": "object",
|
|
7
7
|
"required": [
|
|
@@ -442,6 +442,16 @@
|
|
|
442
442
|
},
|
|
443
443
|
"additionalProperties": true
|
|
444
444
|
},
|
|
445
|
+
{
|
|
446
|
+
"type": "object",
|
|
447
|
+
"required": ["type", "url"],
|
|
448
|
+
"properties": {
|
|
449
|
+
"type": { "const": "geojson_url" },
|
|
450
|
+
"url": { "type": "string", "format": "uri", "description": "URL publique du GeoJSON (S3 public, CDN, endpoint sans auth). Fetche cote client sans credentials." },
|
|
451
|
+
"crs": { "type": "string", "const": "EPSG:4326", "default": "EPSG:4326" }
|
|
452
|
+
},
|
|
453
|
+
"additionalProperties": true
|
|
454
|
+
},
|
|
445
455
|
{
|
|
446
456
|
"type": "object",
|
|
447
457
|
"required": ["type", "url", "source_layer"],
|