|
1 | 1 | # Fonctionnement du widget **Panoramax** |
2 | 2 |
|
3 | | ---- |
| 3 | +## Architecture et cycle de vie |
| 4 | + |
| 5 | +`ol.control.Panoramax` étend `ol.control.Control`. Son implémentation se trouve dans `src/packages/Controls/Panoramax/` : |
| 6 | + |
| 7 | +- `Panoramax.js` pilote les couches OpenLayers, le viewer et les interactions ; |
| 8 | +- `PanoramaxDOM.js` construit les panneaux et boutons ; |
| 9 | +- `PictureLegendWidget.js` fournit la légende, le géocodage inverse et le lien de partage ; |
| 10 | +- `PnxMiniMapWidget.js` ajoute une mini-carte au viewer. |
| 11 | + |
| 12 | +À la construction, le contrôle initialise ses options et son DOM. Lors de l'ouverture, il charge le groupe de couches Panoramax, le fond optionnel, le panneau d'options, la fenêtre de visualisation et le composant `<pnx-photo-viewer>`. Le viewer est créé une seule fois par instance ; son cycle de vie est nettoyé lors d'un retrait de la carte afin de permettre un `map.removeControl()` suivi d'un `map.addControl()`. |
| 13 | + |
| 14 | +`collapsed: false` ouvre le contrôle dès son attachement. Avec `auto: true` (valeur par défaut), les écouteurs de clic et de survol sont ajoutés automatiquement à la carte. |
| 15 | + |
| 16 | +## Configuration utile |
| 17 | + |
| 18 | +```js |
| 19 | +var panoramax = new ol.control.Panoramax({ |
| 20 | + collapsed: true, |
| 21 | + auto: true, |
| 22 | + hover: true, |
| 23 | + position: "bottom-left", |
| 24 | + layer: { |
| 25 | + url: "https://api.panoramax.xyz/api/map/style.json", |
| 26 | + name: "Panoramax" |
| 27 | + }, |
| 28 | + background: { |
| 29 | + active: false |
| 30 | + }, |
| 31 | + buttonsWindow: { |
| 32 | + filters: { |
| 33 | + display: true, |
| 34 | + exclusive: false, |
| 35 | + content: { types: true, dates: true, periodes: true } |
| 36 | + } |
| 37 | + }, |
| 38 | + visualizationWindow: { |
| 39 | + size: "fullscreen-map" |
| 40 | + }, |
| 41 | + viewer: { |
| 42 | + endpoint: "https://explore.panoramax.fr/api", |
| 43 | + share: { |
| 44 | + url: "https://cartes.gouv.fr/explorer-les-cartes/", |
| 45 | + type: "geoplateforme" |
| 46 | + }, |
| 47 | + pnxOptions: { |
| 48 | + psvOptions: {} |
| 49 | + } |
| 50 | + } |
| 51 | +}); |
| 52 | + |
| 53 | +map.addControl(panoramax); |
| 54 | +``` |
4 | 55 |
|
5 | | -## Architecture générale |
| 56 | +Les cibles expérimentales `buttonsWindow.target` et `visualizationWindow.target` acceptent un `HTMLElement`, un identifiant ou un sélecteur CSS. L'option `viewer.pnxOptions.psvOptions` est affectée à la propriété `psv-options` du web component ; ne pas la transmettre avec `setAttribute`. |
6 | 57 |
|
7 | | -Le widget est une classe `Panoramax extends Control` (OpenLayers) composée de |
8 | | -deux fichiers : |
| 58 | +## Interactions avec la carte |
9 | 59 |
|
10 | | -- Panoramax.js — logique principale (~2800 lignes) |
11 | | -- PanoramaxDOM.js — génération du DOM |
| 60 | +| Couche | Comportement par défaut au clic | |
| 61 | +|---|---| |
| 62 | +| `grid` | Zoom sur la position sélectionnée | |
| 63 | +| `sequences` | Zoom ou recentrage vers le niveau 17 | |
| 64 | +| `pictures` | Ouvre l'image dans le viewer | |
12 | 65 |
|
13 | | ---- |
| 66 | +Les interactions se configurent avec `interactions.grid`, `interactions.sequences` et `interactions.pictures`, chacun possédant `active` et `actions`. Le survol affiche une prévisualisation lorsque `hover: true`. |
14 | 67 |
|
15 | | -## Cycle de vie |
| 68 | +## Ouverture programmée |
16 | 69 |
|
17 | | -### 1. Construction |
| 70 | +Une image peut être ouverte depuis une URL ou une action externe en définissant, dans cet ordre, les propriétés OpenLayers `sequence`, `picture` et `display` : |
18 | 71 |
|
| 72 | +```js |
| 73 | +panoramax.setCollapsed(false); |
| 74 | +panoramax.set("sequence", sequenceId); |
| 75 | +panoramax.set("picture", pictureId); |
| 76 | +panoramax.set("display", true); |
19 | 77 | ``` |
20 | | -constructor → initialize() → initContainer() |
21 | | -``` |
22 | | - |
23 | | -- `initialize()` : stocke les options, crée les propriétés d'état (`collapsed`, `hover`, `auto`, références DOM, listeners…) |
24 | | -- `initContainer()` : construit tout le DOM — deux panneaux principaux : |
25 | | - - **`panelPanoramaxViewerContainer`** : le visualiseur de photos |
26 | | - - **`panelPanoramaxButtonsContainer`** : les boutons de contrôle (filtres, contributions, fond de carte…) |
27 | | - |
28 | | -### 2. Attachement à la carte (`setMap`) |
29 | | - |
30 | | -- Active le mode **draggable** si besoin |
31 | | -- Déclenche l'ouverture si `collapsed: false` |
32 | | -- Appelle `addEventsListeners(map)` si `auto: true` (écoute `click` et `pointermove`) |
33 | | - |
34 | | -### 3. Ouverture du panneau (`onShowPanoramaxClick`) |
35 | 78 |
|
36 | | -Appelle `load()` qui enchaîne de façon asynchrone : |
| 79 | +Si le viewer n'est pas encore prêt, le contrôle attend l'événement `pnx:ready` avant de sélectionner l'image. Pour fermer le viewer sans fermer le contrôle, utiliser `panoramax.set("display", false)`. |
37 | 80 |
|
38 | | -1. `setLayerGroup()` — crée un `LayerGroup` OL pour regrouper les couches |
39 | | -2. `setBackground()` — charge une couche de fond (style Mapbox Vector) |
40 | | -3. `setLayer()` — charge la couche Panoramax (TMS vecteur `MapboxVectorLayer`) |
41 | | -4. `initButtons()` — affiche le panneau des boutons |
42 | | -5. `initVisualizationWindow()` → `setSizeWindow()` — applique la taille (small/medium/large/fullscreen/fullscreen-map) |
43 | | -6. `initPhotoViewer()` — crée le web component `<pnx-photo-viewer>` et ses widgets |
| 81 | +## Viewer et partage |
44 | 82 |
|
45 | | -### 4. Fermeture (`reset`) |
| 83 | +Le widget repose sur `<pnx-photo-viewer>` de `@panoramax/web-viewer`. Les widgets optionnels sont `btnBack`, `btnClose`, `btnZoom`, `btnFullscreen`, `cmpPictureLegend` et `cmpMinimap`. Au signal `ready` du viewer, les widgets natifs Player, annotations et légende basse sont retirés au profit des composants intégrés au contrôle. |
46 | 84 |
|
47 | | -Supprime les couches, réinitialise le viewer, les boutons, les overlays de prévisualisation. |
| 85 | +`viewer.share` configure le lien affiché dans la légende personnalisée : |
48 | 86 |
|
49 | | ---- |
50 | | - |
51 | | -## Interactions carte |
52 | | - |
53 | | -| Événement | Comportement | |
| 87 | +| `type` | URL produite | |
54 | 88 | |---|---| |
55 | | -| `click` sur `pictures` | Ouvre le viewer avec `displayPhotoViewer(sequenceId, pictureId)` | |
56 | | -| `click` sur `grid` | Zoom +4 niveaux | |
57 | | -| `click` sur `sequences` | Zoom +2 niveaux | |
58 | | -| `pointermove` (debounce 300ms) | Affiche une popup de prévisualisation (`displayPreview`) si `hover: true` | |
59 | | - |
60 | | ---- |
| 89 | +| `panoramax` (défaut) | URL Explore Panoramax avec `pic`, `seq` et la position courante | |
| 90 | +| `geoplateforme` | URL `.../photo/{sequence}/{picture}/{lat},{lon}/{zoom}` | |
61 | 91 |
|
62 | | -## Viewer de photos |
| 92 | +`viewer.share.url` permet de remplacer la base utilisée pour le type choisi. Les identifiants et les coordonnées sont encodés lors de la construction du lien. |
63 | 93 |
|
64 | | -Basé sur le web component `<pnx-photo-viewer>` de `@panoramax/web-viewer`. Des widgets personnalisés y sont injectés via des slots : |
| 94 | +## Filtres |
65 | 95 |
|
66 | | -- `pnx-button` (retour, fermeture, plein écran) → slots `top-left`/`top-right`/`bottom-right` |
67 | | -- `pnx-widget-zoom` |
68 | | -- `pnx-picture-legend` |
| 96 | +Les filtres modifient le style Mapbox de la couche puis appliquent le style mis à jour avec `applyStyle()` : type d'image, intervalle de dates et période relative. Le bouton de réinitialisation restaure le style initial de la couche. |
69 | 97 |
|
70 | | -Au `ready`, certains widgets natifs sont supprimés (`pnx-widget-player`, `pnx-annotations-switch`, `pnx-bottom-drawer`). |
| 98 | +`buttonsWindow.filters.exclusive` contrôle leur combinaison : à `true` (défaut), l'activation d'un filtre désactive les autres ; à `false`, les filtres actifs sont cumulés. |
71 | 99 |
|
72 | | ---- |
| 100 | +## Événements publics |
73 | 101 |
|
74 | | -## Filtres (couche Mapbox) |
75 | | - |
76 | | -Les filtres modifient directement le JSON de style Mapbox de la couche, puis rappellent `applyStyle()` de `ol-mapbox-style` : |
77 | | -- **Type** : filtre `["==", ["get", "type"], "flat"|"equirectangular"]` sur les couches `pictures` et `sequences` |
78 | | -- **Dates** : filtre `[">=", "ts", ...]` / `["<=", "ts", ...]` |
79 | | -- **Période** : calcule un intervalle de dates avec `date-fns/subMonths` |
80 | | -- **Reset** : réapplique `originalStyleLayerPanoramax` (snapshot du style initial) |
81 | | - |
82 | | -> ⚠️ Limites connues : les filtres ne sont **pas cumulatifs** (chaque filtre écrase le précédent). |
| 102 | +| Événement | Déclenchement | |
| 103 | +|---|---| |
| 104 | +| `pnx:opened` / `pnx:closed` | Ouverture ou fermeture du contrôle | |
| 105 | +| `pnx:ready` | Viewer initialisé et prêt à être utilisé | |
| 106 | +| `pnx:fullscreen` | Changement du mode plein écran | |
| 107 | +| `pnx:data:clicked` / `pnx:data:hovered` | Interaction avec une entité Panoramax | |
| 108 | +| `pnx:filter:init`, `pnx:filter:dates`, `pnx:filter:periode`, `pnx:filter:type`, `pnx:filter:render` | Initialisation ou application d'un filtre | |
83 | 109 |
|
84 | | ---- |
| 110 | +Les changements des propriétés `picture`, `sequence` et `display` émettent respectivement `change:picture`, `change:sequence` et `change:display`. |
85 | 111 |
|
86 | | -## Modes de fenêtre (`setSizeWindow`) |
| 112 | +## Modes de fenêtre |
87 | 113 |
|
88 | 114 | | Mode | Comportement | |
89 | 115 | |---|---| |
90 | | -| `small/medium/large` | Classes CSS fixes, `stopMapViewportSync()` | |
91 | | -| `fullscreen` | `<dialog>` en position fixe 100vw×100vh | |
92 | | -| `fullscreen-map` | Synchronise position/taille avec `map.getViewport().getBoundingClientRect()` via `startMapViewportSync()` (écoute `resize`, `scroll`, `change:size`) | |
| 116 | +| `small`, `medium`, `large` | Taille fixe via classe CSS | |
| 117 | +| `fullscreen` | `<dialog>` fixe sur toute la fenêtre (`100dvw` x `100dvh`) | |
| 118 | +| `fullscreen-map` | Fenêtre calée sur `map.getViewport()` et resynchronisée lors de `resize`, `scroll` et `change:size` | |
0 commit comments