passerelle-geo-components 0.2.0-alpha.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 ADDED
@@ -0,0 +1,77 @@
1
+ # passerelle-geo-components
2
+
3
+ **Web Components pour cartes MapLibre + composants controllers pilotes — brique commune multi-projet.**
4
+
5
+ Zero dependance runtime (peer `maplibre-gl@^5.6.1`, `geoai-kit@^0.2.0` optionnel), ES module, executable dans un navigateur — widget SPA, PWA, page HTML statique, iframe SSR, storymap.
6
+
7
+ > **Statut** : `0.2.0-alpha.1` (developpement). API stable prevue en `0.2.0` a l'issue du Sprint V0.2.
8
+
9
+ ---
10
+
11
+ ## Installation
12
+
13
+ ```bash
14
+ npm install passerelle-geo-components
15
+ ```
16
+
17
+ Peer runtime :
18
+
19
+ ```bash
20
+ npm install maplibre-gl
21
+ ```
22
+
23
+ ## Usage
24
+
25
+ Import direct dans le HTML ou via bundler :
26
+
27
+ ```html
28
+ <script type="module" src="node_modules/passerelle-geo-components/geo-components.js"></script>
29
+
30
+ <geo-map params='{"basemap":"osm","bbox":[5.3,43.28,5.42,43.34]}'></geo-map>
31
+ ```
32
+
33
+ Ou via bundler :
34
+
35
+ ```js
36
+ import "passerelle-geo-components";
37
+ ```
38
+
39
+ ## Composants exposes
40
+
41
+ - `<geo-map>` — carte MapLibre pilotee par attribut `params` (SceneManifest V0.3.1)
42
+ - `<geo-timeline>` — controller temporel `input[type=range]` liee par `target`
43
+ - `<geo-legend>` — legende declarative (mode auto ou items explicites)
44
+ - `<geo-3d-scene>` — slot composition Three.js chargee a la demande (opt-in 3D)
45
+
46
+ Voir la source `geo-components.js` pour l'API complete des attributs.
47
+
48
+ ## Contract SceneManifest V0.3.1
49
+
50
+ Le composant `<geo-map>` accepte un `params` conforme au JSON Schema `scene_manifest.schema.json` livre dans ce package :
51
+
52
+ ```js
53
+ import schema from "passerelle-geo-components/schemas/scene-manifest";
54
+ ```
55
+
56
+ Ou en TypeScript :
57
+
58
+ ```ts
59
+ import { SceneManifestSchema } from "passerelle-geo-components/schemas/scene-manifest-zod";
60
+ ```
61
+
62
+ 9 exemples canoniques disponibles dans `examples/` — minimal, diagnostic-temporel, corpus-documentaire, choropleth-demographique, heatmap-rag, timeline-overlay-simple, multi-layers-narrative, maquette-3d, validation-terrain.
63
+
64
+ ## Peer dependencies
65
+
66
+ - `maplibre-gl@^5.6.1` (obligatoire)
67
+ - `geoai-kit@^0.2.0` (optionnel — primitives geo)
68
+
69
+ ## Licence
70
+
71
+ EUPL-1.2 — Licence publique de l'Union europeenne.
72
+
73
+ ## Repository
74
+
75
+ Source : https://github.com/nic01asFr/Passerelle/tree/main/sdk/js/geo-components
76
+
77
+ Rapports de bug, discussions, roadmap et documentation etendue dans le repo.
@@ -0,0 +1,175 @@
1
+ # Exemples canoniques — SceneManifest V0.3.1
2
+
3
+ Ce dossier contient les **8 exemples canoniques** du contract `SceneManifest V0.3.1`, un par pattern métier prioritaire de l'écosystème.
4
+
5
+ Chaque exemple est **complet, valide V0.3.1**, testable via `pnpm run validate:examples`, et sert de :
6
+ - **Contract-book pour agents LLM** (retournés par `describe_entity_schema` filtré par `use_case`)
7
+ - **Fixtures CI cross-projet** (garantissent la non-régression sur PR de la lib)
8
+ - **Point de départ par analogie** pour un dev démarrant un nouveau projet
9
+
10
+ Voir [`../CONTRACT-V0.3.1.md`](../CONTRACT-V0.3.1.md) pour la spec complète.
11
+
12
+ ---
13
+
14
+ ## Table des exemples
15
+
16
+ | # | Fichier | Pattern | Producer | Cas d'usage typique | Anti-pattern à éviter |
17
+ |---|---|---|---|---|---|
18
+ | 1 | [`minimal.json`](./minimal.json) | Point de départ incrémental | `manual` | Nouveau projet, prototype rapide, test agent | Rester bloqué à ce minimum en prod |
19
+ | 2 | [`diagnostic-temporel.json`](./diagnostic-temporel.json) | Classification graduated + timeline + hillshade | `qgis-sspcloud` | Diagnostic bâti par période, storymap DSFR | Oublier `opacity.reactive` (timeline devient statique) |
20
+ | 3 | [`corpus-documentaire.json`](./corpus-documentaire.json) | Rôles emprise/site + groupement N docs/emprise | `livrables` | Widget corpus documentaire, editeur narratif | Mélanger polygons+points dans même layer sans role |
21
+ | 4 | [`choropleth-demographique.json`](./choropleth-demographique.json) | `classification.color.mode=expression` + interpolate | `x_crm_grist_storymap` | Choroplèthe communal, dashboard CRM | Utiliser `mode=graduated` pour continuous data (préférer expression) |
22
+ | 5 | [`heatmap-rag.json`](./heatmap-rag.json) | Heatmap + `maplibre_paint_override` | `x_mobscidat` | RAG géo-sémantique, dashboard science des données | Créer une couche circle pour simuler heatmap (impossible) |
23
+ | 6 | [`timeline-overlay-simple.json`](./timeline-overlay-simple.json) | Timeline `opacity.reactive` sans classification | `qgis-sspcloud` | Animation temporelle simple (événements datés) | Rajouter classification par période inutile |
24
+ | 7 | [`multi-layers-narrative.json`](./multi-layers-narrative.json) | 3 layers avec rôles `context/primary/emprise` | `qgis-sspcloud` | Storymap narrative multi-échelles | Empiler des layers sans role (perte de sens) |
25
+ | 8 | [`maquette-3d.json`](./maquette-3d.json) | Terrain LIDAR + sky solaire + custom_layers GLTF | `atlas` | Maquette 3D territoriale immersive | Mettre les instances GLTF dans `custom_layers.params.instances[]` (utiliser `source_layer_id`) |
26
+ | 9* | [`validation-terrain-zebra.json`](./validation-terrain-zebra.json) | Grist bridge bidirectionnel + `feature_state.selected` | `x_zebra` | Validation terrain widget Grist | Oublier `feature_state_selected_field` (perte sync Grist↔carte) |
27
+
28
+ *Exemple bonus pour illustrer le pattern `source.type: "grist_table"` + `sync: "reactive"`.
29
+
30
+ ---
31
+
32
+ ## Comment lire un exemple
33
+
34
+ ### Structure standard
35
+
36
+ Tout exemple V0.3.1 comporte au minimum :
37
+ - `$schema` (URL du JSON Schema publié)
38
+ - `manifest_version: "0.3.1"`
39
+ - `produced_at` (ISO datetime)
40
+ - `provenance.producer` + `provenance.producer_version`
41
+ - `title`
42
+ - `basemap.id`
43
+ - `zone` (discriminated union sur `kind`)
44
+ - `layers[]` (peut être vide)
45
+
46
+ Les champs immuables (`scene_hash`, `manifest_id`) sont ajoutés par le hub au moment du publish — l'agent LLM ne les produit pas.
47
+
48
+ ### Discriminated unions
49
+
50
+ Certains sous-modèles utilisent des unions typées strictes :
51
+ - `source.type` : `geojson | geojson_path | vector | pmtiles | grist_table | wfs | boundary_admin`
52
+ - `classification.color.mode` : `single | categorized | graduated | expression`
53
+ - `legend.mode` : `auto | manual`
54
+ - `zone.kind` : `manual | commune | insee_arm | auto_bounds`
55
+ - `opacity.kind` : `static | reactive`
56
+ - `custom_layers.kind` : `gltf_instances | custom_3d_layer`
57
+
58
+ **Un agent LLM doit choisir UNE variante par union.** Ne pas mélanger les champs de plusieurs variantes.
59
+
60
+ ### Extensions par-projet
61
+
62
+ Chaque niveau du manifest accepte `extensions: {[prefix]: object}` avec prefix = nom projet (`x_livrables`, `x_atlas`, `x_zebra`, ...).
63
+
64
+ Le contenu de chaque extension est libre (`additionalProperties: true`). Les extensions inconnues sont **ignorées** par le renderer standard.
65
+
66
+ Voir `corpus-documentaire.json` (Livrables `sidebar_items`) et `maquette-3d.json` (Atlas `native_payload_hash`) pour des exemples réels.
67
+
68
+ ---
69
+
70
+ ## Anti-patterns communs à éviter
71
+
72
+ ### 1. `geojson_inline` catastrophique
73
+ ```jsonc
74
+ // ❌ MAUVAIS : 22 MB payload pour 14 270 features
75
+ "source": {
76
+ "type": "geojson",
77
+ "data": { "type": "FeatureCollection", "features": [ /* 14 270 features */ ] }
78
+ }
79
+
80
+ // ✅ BON : utilise geojson_path avec fetch séparé streamé
81
+ "source": {
82
+ "type": "geojson_path",
83
+ "path": "/data/.../batiments.geojson"
84
+ }
85
+ ```
86
+
87
+ Seuils : `geojson` inline autorisé uniquement si `n_features ≤ 500` OU `payload ≤ 500 KB`.
88
+
89
+ ### 2. Discriminated union floue
90
+ ```jsonc
91
+ // ❌ MAUVAIS : mélange champs de modes différents
92
+ "color": {
93
+ "mode": "graduated",
94
+ "value": "#1d70b8", // ❌ "value" appartient à mode=single
95
+ "categories": [ /* ... */ ], // ❌ "categories" appartient à mode=categorized
96
+ "breaks": [1900, 1945] // ✅ correct pour graduated
97
+ }
98
+
99
+ // ✅ BON : un seul mode, ses champs spécifiques
100
+ "color": {
101
+ "mode": "graduated",
102
+ "field": "date_d_apparition",
103
+ "method": "manual",
104
+ "breaks": [1900, 1945, 1975, 2000],
105
+ "palette": "ColorBrewer.PRGn.6",
106
+ "default": "#9e9e9e"
107
+ }
108
+ ```
109
+
110
+ ### 3. Enum inconnu sans convention `x_*`
111
+ ```jsonc
112
+ // ❌ MAUVAIS : "validation-cluster" hors enum + pas de convention x_*
113
+ "role": "validation-cluster"
114
+
115
+ // ✅ BON : préfixe x_<projet>_* + fallback graceful primary
116
+ "role": "x_zebra_validation_cluster"
117
+ ```
118
+
119
+ ### 4. Magic strings d'événements
120
+ ```jsonc
121
+ // ❌ MAUVAIS : nom d'événement custom (non émis par la lib)
122
+ "click_event": "custom-click-handler"
123
+
124
+ // ✅ BON : événements canoniques fixés par la lib
125
+ "emits_events": true
126
+ // → émet automatiquement geo:feature-click, geo:feature-hover
127
+ ```
128
+
129
+ ### 5. `dimension` explicite au manifest
130
+ ```jsonc
131
+ // ❌ MAUVAIS : dimension supprimée du contract V0.3.1
132
+ "dimension": "3d"
133
+
134
+ // ✅ BON : dérivée par le renderer selon présence de custom_layers[]
135
+ // (rien à mettre dans le manifest)
136
+ ```
137
+
138
+ ---
139
+
140
+ ## Tests de validation
141
+
142
+ Depuis `sdk/js/geo-components/` :
143
+
144
+ ```bash
145
+ # Valide un exemple contre le JSON Schema canonique
146
+ pnpm exec ajv validate -s schemas/scene_manifest.schema.json -d examples/minimal.json --spec=draft2020
147
+
148
+ # Valide tous les exemples
149
+ pnpm run validate:examples
150
+
151
+ # Round-trip Zod (à venir après build:zod)
152
+ pnpm run test:schemas
153
+ ```
154
+
155
+ ---
156
+
157
+ ## Ajout d'un nouvel exemple
158
+
159
+ Un nouvel exemple `examples/<nom>.json` doit :
160
+
161
+ 1. Illustrer un **pattern métier distinct** des 8 existants (pas de doublon)
162
+ 2. Valider contre `schemas/scene_manifest.schema.json`
163
+ 3. Ajouter une entrée dans le tableau ci-dessus
164
+ 4. Décrire l'anti-pattern à éviter dans un usage similaire
165
+ 5. Passer les tests CI cross-projet
166
+
167
+ Les nouveaux exemples deviennent **automatiquement disponibles** pour les agents LLM via `describe_entity_schema(entity_type="component", kind="interactive_map", use_case="<nom>")`.
168
+
169
+ ---
170
+
171
+ ## Références
172
+
173
+ - [CONTRACT-V0.3.1.md](../CONTRACT-V0.3.1.md) — spec humaine complète du contract
174
+ - [schemas/README.md](../schemas/README.md) — pipeline codegen 3 dialectes
175
+ - [SPRINT-V0.2-STATUS.md](../SPRINT-V0.2-STATUS.md) — Sprint V0.2 tracker
@@ -0,0 +1,90 @@
1
+ {
2
+ "$schema": "https://cerema.github.io/geo-components/schemas/scene_manifest/0.3.1.json",
3
+ "manifest_version": "0.3.1",
4
+ "produced_at": "2026-07-10T11:00:00+02:00",
5
+ "provenance": {
6
+ "producer": "x_crm_grist_storymap",
7
+ "producer_version": "v17.0"
8
+ },
9
+ "title": "Concentration adhérents CEREMA par EPCI",
10
+ "source_text": "Table Grist Organismes — CRM CEREMA V17",
11
+ "basemap": {
12
+ "id": "cartodb-positron-nolabels"
13
+ },
14
+ "zone": {
15
+ "kind": "manual",
16
+ "bbox": [-5.0, 41.3, 9.5, 51.2],
17
+ "center": [2.5, 46.5],
18
+ "zoom": 5
19
+ },
20
+ "layers": [
21
+ {
22
+ "id": "epci_metric_norm",
23
+ "name": "Concentration adhérents (métrique normalisée)",
24
+ "role": "primary",
25
+ "geometry_type": "polygon",
26
+ "source": {
27
+ "type": "boundary_admin",
28
+ "catalog_id": "epci_france"
29
+ },
30
+ "n_features": 1245,
31
+ "bbox": [-5.0, 41.3, 9.5, 51.2],
32
+ "attribute_stats": {
33
+ "metric_norm": {
34
+ "min": 0.0,
35
+ "max": 1.0,
36
+ "null_count": 12,
37
+ "distinct_count": 1233
38
+ }
39
+ },
40
+ "style": {
41
+ "z_index": 10,
42
+ "opacity": { "kind": "static", "value": 0.75 },
43
+ "classification": {
44
+ "color": {
45
+ "mode": "expression",
46
+ "expr": [
47
+ "interpolate",
48
+ ["linear"],
49
+ ["coalesce", ["get", "metric_norm"], 0],
50
+ 0.0, "#f7f7f7",
51
+ 0.25, "#c6dbef",
52
+ 0.5, "#6baed6",
53
+ 0.75, "#2171b5",
54
+ 1.0, "#08306b"
55
+ ],
56
+ "default": "#e0e0e0"
57
+ }
58
+ },
59
+ "outline": {
60
+ "enabled": true,
61
+ "color": "#ffffff",
62
+ "width": 0.5
63
+ }
64
+ },
65
+ "interactions": {
66
+ "popup_template": {
67
+ "kind": "handlebars",
68
+ "body": "<h4>{{nom_epci}}</h4><p><strong>{{count_adherents}}</strong> adhérent(s)</p><p>Score : {{metric_norm}}</p>"
69
+ },
70
+ "tooltip_field": "nom_epci",
71
+ "hover_attributes": ["nom_epci", "count_adherents", "metric_norm"],
72
+ "emits_events": true
73
+ }
74
+ }
75
+ ],
76
+ "legend": {
77
+ "mode": "auto",
78
+ "from_layer": "epci_metric_norm",
79
+ "position": "bottom-left",
80
+ "format": "gradient_bar",
81
+ "title": "Concentration adhérents (0-1)"
82
+ },
83
+ "extensions": {
84
+ "x_crm_grist_storymap": {
85
+ "grist_table": "Organismes",
86
+ "metric_field": "score_norm",
87
+ "join_field": "code_epci"
88
+ }
89
+ }
90
+ }
@@ -0,0 +1,170 @@
1
+ {
2
+ "$schema": "https://cerema.github.io/geo-components/schemas/scene_manifest/0.3.1.json",
3
+ "manifest_version": "0.3.1",
4
+ "produced_at": "2026-07-10T10:00:00+02:00",
5
+ "provenance": {
6
+ "producer": "cerema-livrables",
7
+ "producer_version": "0.4.0",
8
+ "extensions": {
9
+ "cerema_livrables": {
10
+ "mode": "subject",
11
+ "corpus_version": "2026-07"
12
+ }
13
+ }
14
+ },
15
+ "title": "Livrables CEREMA — corpus Méditerranée",
16
+ "source_text": "CeremaDoc via Syracuse RSS + Strate — Licence Ouverte 2.0",
17
+ "basemap": {
18
+ "id": "plan-ign-v2"
19
+ },
20
+ "zone": {
21
+ "kind": "manual",
22
+ "bbox": [3.0, 42.5, 7.5, 44.5],
23
+ "center": [5.5, 43.5],
24
+ "zoom": 7
25
+ },
26
+ "layers": [
27
+ {
28
+ "id": "livrables_emprises",
29
+ "name": "Emprises documentées",
30
+ "role": "emprise",
31
+ "geometry_type": "polygon",
32
+ "source": {
33
+ "type": "geojson_path",
34
+ "path": "/data/publications/cerema-livrables/scenes/subject-corpus-mediterranee.geojson"
35
+ },
36
+ "n_features": 24,
37
+ "bbox": [3.0, 42.5, 7.5, 44.5],
38
+ "attribute_stats": {
39
+ "scale": { "distinct_values": ["batiment", "zone", "rue", "ville", "epci"] }
40
+ },
41
+ "style": {
42
+ "z_index": 10,
43
+ "opacity": { "kind": "static", "value": 0.35 },
44
+ "classification": {
45
+ "color": {
46
+ "mode": "categorized",
47
+ "field": "scale",
48
+ "categories": [
49
+ { "value": "batiment", "color": "#000091" },
50
+ { "value": "zone", "color": "#1212a3" },
51
+ { "value": "rue", "color": "#2e2eb5" },
52
+ { "value": "ville", "color": "#4a4ac7" },
53
+ { "value": "epci", "color": "#7676d9" }
54
+ ],
55
+ "default": "#9e9e9e"
56
+ }
57
+ },
58
+ "outline": {
59
+ "enabled": true,
60
+ "color": "#000091",
61
+ "width": 1.5
62
+ }
63
+ },
64
+ "interactions": {
65
+ "popup_template": {
66
+ "kind": "handlebars",
67
+ "body": "<h4>{{count}} livrable(s) sur {{territoire}}</h4><ul>{{#each members}}<li><a href=\"{{source_url}}\" target=\"_blank\">{{titre}}</a> ({{millesime}}) — {{commanditaire}}</li>{{/each}}</ul>"
68
+ },
69
+ "tooltip_field": "titre",
70
+ "hover_attributes": ["scale", "count", "territoire"],
71
+ "emits_events": true,
72
+ "feature_state_selected_field": "group_key"
73
+ },
74
+ "extensions": {
75
+ "cerema_livrables": {
76
+ "role_semantic": "emprise",
77
+ "group_key_field": "group_key"
78
+ }
79
+ }
80
+ },
81
+ {
82
+ "id": "livrables_points_approx",
83
+ "name": "Livrables sans emprise vérifiée",
84
+ "role": "site",
85
+ "geometry_type": "point",
86
+ "source": {
87
+ "type": "geojson_path",
88
+ "path": "/data/publications/cerema-livrables/scenes/subject-points-approximatifs.geojson"
89
+ },
90
+ "n_features": 6,
91
+ "bbox": [3.5, 42.8, 7.0, 44.0],
92
+ "style": {
93
+ "z_index": 15,
94
+ "opacity": { "kind": "static", "value": 0.9 },
95
+ "classification": {
96
+ "color": {
97
+ "mode": "categorized",
98
+ "field": "geom_statut",
99
+ "categories": [
100
+ { "value": "verifie", "color": "#000091" },
101
+ { "value": "approximatif", "color": "#e1000f" }
102
+ ],
103
+ "default": "#9e9e9e"
104
+ }
105
+ },
106
+ "hollow_point": {
107
+ "enabled": true,
108
+ "stroke_color_from_field": "geom_statut",
109
+ "radius": 8
110
+ }
111
+ },
112
+ "interactions": {
113
+ "popup_template": {
114
+ "kind": "handlebars",
115
+ "body": "<h4>{{titre}}</h4><p>Emprise : <strong>{{geom_statut}}</strong> ({{geom_source}})</p><p>{{count}} livrable(s) groupé(s)</p>"
116
+ },
117
+ "tooltip_field": "titre",
118
+ "emits_events": true
119
+ }
120
+ }
121
+ ],
122
+ "legend": {
123
+ "mode": "manual",
124
+ "items": [
125
+ { "label": "Bâtiment", "color": "#000091" },
126
+ { "label": "Zone / ZI", "color": "#1212a3" },
127
+ { "label": "Rue", "color": "#2e2eb5" },
128
+ { "label": "Ville", "color": "#4a4ac7" },
129
+ { "label": "EPCI", "color": "#7676d9" },
130
+ { "label": "Approximatif (point)", "color": "#e1000f" }
131
+ ],
132
+ "position": "bottom-left",
133
+ "format": "chips",
134
+ "title": "Échelle documentaire"
135
+ },
136
+ "scalebar": {
137
+ "position": "bottom-right",
138
+ "unit": "metric"
139
+ },
140
+ "extensions": {
141
+ "cerema_livrables": {
142
+ "sidebar_items": [
143
+ {
144
+ "id": "doc-uuid-1",
145
+ "titre": "Rapport littoral MTPM 2024",
146
+ "scale": "epci",
147
+ "zoom": 10,
148
+ "domaine": "Risques",
149
+ "commanditaire": "Métropole TPM",
150
+ "millesime": 2024,
151
+ "source_url": "https://doc.cerema.fr/...",
152
+ "group_key": "epci:200055529",
153
+ "lon": 5.926, "lat": 43.124,
154
+ "geom_statut": "verifie"
155
+ }
156
+ ],
157
+ "national": [
158
+ {
159
+ "id": "doc-nat-1",
160
+ "titre": "Guide méthodologique passages piétons",
161
+ "domaine": "Risques",
162
+ "commanditaire": "DGITM",
163
+ "millesime": 2024,
164
+ "source_url": "https://doc.cerema.fr/..."
165
+ }
166
+ ],
167
+ "count": 42
168
+ }
169
+ }
170
+ }
@@ -0,0 +1,160 @@
1
+ {
2
+ "$schema": "https://cerema.github.io/geo-components/schemas/scene_manifest/0.3.1.json",
3
+ "manifest_version": "0.3.1",
4
+ "produced_at": "2026-07-10T09:15:23+02:00",
5
+ "provenance": {
6
+ "producer": "qgis-sspcloud",
7
+ "producer_version": "v1.20.6",
8
+ "source_project": "/data/studies/c9fef0955a53/projects/72e3da47b1f7/project.qgz",
9
+ "recipe_used": {
10
+ "slug": "diagnostic-parc-bati",
11
+ "version_sha": "sha256:9f8a1e7c4d2b3f5e8a1c9d0e2f3b4a5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a"
12
+ }
13
+ },
14
+ "title": "Parc bâti du 4e arrondissement, par année de construction",
15
+ "subtitle": "Diagnostic CEREMA – juillet 2026",
16
+ "description": "Cartographie de l'ensemble du parc bâti du 4e arrondissement de Marseille à partir de la BD TOPO® de l'IGN et de son attribut de date d'apparition. Explore visuellement l'évolution du bâti au fil du temps.",
17
+ "source_text": "BD TOPO® IGN 2024 — CEREMA, juillet 2026",
18
+ "caveat": "Étude à vocation pédagogique — chiffres fictifs, non exploitables opérationnellement.",
19
+ "projection": "mercator",
20
+ "basemap": {
21
+ "id": "plan-ign-v2-gris"
22
+ },
23
+ "zone": {
24
+ "kind": "insee_arm",
25
+ "insee": "13204",
26
+ "buffer_km": 0.2,
27
+ "pitch": 0,
28
+ "bearing": 0
29
+ },
30
+ "terrain": {
31
+ "source_id": "ign-lidar",
32
+ "encoding": "mapbox",
33
+ "exaggeration": 1.0,
34
+ "adapter": "ignmnt",
35
+ "hillshade": {
36
+ "opacity": 0.35,
37
+ "blend_mode": "multiply"
38
+ }
39
+ },
40
+ "layers": [
41
+ {
42
+ "id": "batiments_bd_topo",
43
+ "name": "Bâtiments par période",
44
+ "role": "primary",
45
+ "geometry_type": "polygon",
46
+ "source": {
47
+ "type": "geojson_path",
48
+ "path": "/data/studies/c9fef0955a53/scene_store/c52d9d669bf5d09c/layers/batiments_bd_topo.geojson",
49
+ "crs": "EPSG:4326"
50
+ },
51
+ "n_features": 14270,
52
+ "bbox": [5.379, 43.289, 5.427, 43.334],
53
+ "attribute_stats": {
54
+ "date_d_apparition": {
55
+ "min": 1850,
56
+ "max": 2024,
57
+ "null_count": 7550,
58
+ "distinct_count": 195
59
+ }
60
+ },
61
+ "style": {
62
+ "visible": true,
63
+ "z_index": 10,
64
+ "opacity": {
65
+ "kind": "reactive",
66
+ "prop": "time",
67
+ "matched": 0.85,
68
+ "before": 0.85,
69
+ "after": 0.12,
70
+ "null_value": 0.35
71
+ },
72
+ "classification": {
73
+ "color": {
74
+ "mode": "graduated",
75
+ "field": "date_d_apparition",
76
+ "method": "manual",
77
+ "breaks": [1900, 1945, 1975, 2000],
78
+ "palette": "ColorBrewer.PRGn.6",
79
+ "default": "#9e9e9e",
80
+ "_compiled_expression": [
81
+ "step",
82
+ ["get", "date_d_apparition"],
83
+ "#7b3294",
84
+ 1900, "#c2a5cf",
85
+ 1945, "#f7f7f7",
86
+ 1975, "#a6dba0",
87
+ 2000, "#008837"
88
+ ]
89
+ },
90
+ "size": { "mode": "single", "value": 8 },
91
+ "label": { "enabled": false }
92
+ },
93
+ "outline": {
94
+ "enabled": true,
95
+ "color": "#000091",
96
+ "width": 0.5
97
+ }
98
+ },
99
+ "interactions": {
100
+ "popup_template": {
101
+ "kind": "handlebars",
102
+ "body": "<h4>Bâtiment {{nature}}</h4><dl><dt>Construction</dt><dd>{{date_d_apparition}}</dd><dt>Usage principal</dt><dd>{{usage_1}}</dd><dt>Logements</dt><dd>{{nombre_de_logements}}</dd><dt>Hauteur</dt><dd>{{hauteur}} m ({{nombre_d_etages}} niveaux)</dd></dl>"
103
+ },
104
+ "tooltip_field": "date_d_apparition",
105
+ "hover_attributes": ["nature", "usage_1", "hauteur", "nombre_de_logements"],
106
+ "emits_events": true
107
+ }
108
+ },
109
+ {
110
+ "id": "arrondissement_13204",
111
+ "name": "4e arrondissement (limite)",
112
+ "role": "context",
113
+ "geometry_type": "polygon",
114
+ "source": {
115
+ "type": "boundary_admin",
116
+ "catalog_id": "arrondissements_marseille",
117
+ "filter": { "insee_arm": "13204" }
118
+ },
119
+ "style": {
120
+ "visible": true,
121
+ "z_index": 5,
122
+ "opacity": { "kind": "static", "value": 0.05 },
123
+ "classification": {
124
+ "color": { "mode": "single", "value": "#000091" }
125
+ },
126
+ "outline": {
127
+ "enabled": true,
128
+ "color": "#000091",
129
+ "width": 2
130
+ }
131
+ },
132
+ "interactions": {
133
+ "emits_events": false
134
+ }
135
+ }
136
+ ],
137
+ "legend": {
138
+ "mode": "auto",
139
+ "from_layer": "batiments_bd_topo",
140
+ "position": "bottom-left",
141
+ "format": "chips",
142
+ "title": "Période de construction"
143
+ },
144
+ "scalebar": {
145
+ "position": "bottom-right",
146
+ "unit": "metric"
147
+ },
148
+ "north_arrow": {
149
+ "position": "top-right"
150
+ },
151
+ "counters": [
152
+ {
153
+ "id": "cnt_bati_filtered",
154
+ "bind": "time",
155
+ "expr": "filtered_count",
156
+ "template": "{{count}} / 14 270 bâtiments ({{pct}} %)",
157
+ "position": "top-left"
158
+ }
159
+ ]
160
+ }