feat(panoramax) : ✨ nouveau widget Panoramax#492
Conversation
…lioration des interactions de la carte
…ramax, y compris le support du survol et des filtres
…anoramax, amélioration de la gestion des boutons
…amélioration de la gestion des tailles et des styles
…de style, ajout d'un bouton de retour et refactorisation du code
…oration de la structure du DOM et du style
There was a problem hiding this comment.
Pull request overview
This PR introduces a new Panoramax widget for OpenLayers that enables users to visualize panoramic images from the Panoramax service directly on the map. The widget integrates the @panoramax/web-viewer library (v4.4.0) and provides an interactive interface for browsing and viewing 360° and flat panoramic images with preview capabilities, filtering options, and customizable visualization settings.
Changes:
- Added new Panoramax control with comprehensive DOM generation, event handling, and photo viewer integration
- Implemented preview system with hover functionality showing image metadata and thumbnails for grid, sequence, and picture layers
- Integrated
@panoramax/web-viewerdependency and configured webpack builds to include Panoramax module and CSS assets
Reviewed changes
Copilot reviewed 12 out of 14 changed files in this pull request and generated 11 comments.
Show a summary per file
| File | Description |
|---|---|
| src/packages/bundle.js | Exports the new Panoramax control in the bundle |
| src/index.js | Exports Panoramax control for module consumption |
| src/packages/Controls/Panoramax/Panoramax.js | Main control implementation with initialization, event handling, layer management, preview system, and photo viewer integration |
| src/packages/Controls/Panoramax/PanoramaxDOM.js | DOM generation for widget UI including buttons, panels, toggles, and viewer container |
| src/packages/CSS/Controls/Panoramax/GPFpanoramax.css | Core layout and positioning styles for the Panoramax widget |
| src/packages/CSS/Controls/Panoramax/DSFRpanoramaxStyle.css | DSFR theme-specific styles for icons, buttons, panels, popups, and markers |
| src/packages/CSS/Controls/Panoramax/GPFpanoramaxStyle.css | Empty placeholder file for additional custom styles |
| src/packages/CSS/Controls/Panoramax/img/dsfr/panoramax.svg | Panoramax icon in SVG format for widget button |
| package.json | Adds @panoramax/web-viewer@^4.4.0 dependency |
| build/webpack/modules.webpack.config.js | Configures GpfExtOlPanoramax module entry and includes web-viewer CSS in build |
| build/webpack/extend.themes.webpack.js | Adds Panoramax style files to GPF and DSFR theme builds |
| build/webpack/extend.base.webpack.js | Includes @panoramax/web-viewer CSS in base webpack configuration |
| build/webpack/bundle.webpack.config.js | Includes @panoramax/web-viewer CSS in bundle webpack configuration |
| samples-src/pages/tests/Panoramax/pages-ol-panoramax-modules-options-dsfr.html | Sample HTML demonstrating Panoramax widget usage with default options |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| var api = (this.options.viewer.endpoint || "https://explore.panoramax.fr/api").replace(/\/+$/, ""); | ||
| var className = "pnx-preview-sequence-popup"; | ||
| var imageHtml = encodedPictureId | ||
| ? `<img src="${api}/collections/${encodedPictureId}/thumb.jpg" alt="Preview image" class="pnx-preview-picture-popup__img">` |
There was a problem hiding this comment.
The XSS prevention using the _escape method properly escapes the id and date values, but the API endpoint URL is not validated before being used to construct the image src attribute. If options.viewer.endpoint comes from user input, this could potentially be exploited. Consider validating that the endpoint URL uses an expected protocol (https) and domain pattern before using it in the image src.
| var newZoom = zoom + 4; // FIXME valeur aleatoire !? | ||
| e.map.getView().animate({ | ||
| center : feature.coordinates, | ||
| zoom : newZoom, | ||
| duration : 500 | ||
| }); | ||
| } | ||
| if (type === "sequences") { | ||
| // zoom on the clicked coordinates with a zoom level | ||
| // depending on the density of images in the sequence | ||
| var zoom = e.map.getView().getZoom(); | ||
| var newZoom = zoom + 2; // FIXME valeur aleatoire !? |
There was a problem hiding this comment.
The zoom calculation uses arbitrary values (4 and 2) for grid and sequences types. These hardcoded magic numbers should be documented with a comment explaining why these specific values were chosen, or they should be extracted as named constants with descriptive names to improve code maintainability and readability.
…e boutons et de visualisation dans le widget Panoramax
…ichier de configuration Webpack
…mélioration des filtres et des méthodes associées
…t des options de date et de type d'image
… dans le widget Panoramax
…amax, amélioration des méthodes de chargement et de réinitialisation
…ion debounce pour les événements de mouvement de la souris et nettoyage des attributs du visualiseur photo
…ax, incluant des événements pour les dates de début et de fin
…mes de cache dans MapBox GL JS lors du rechargement des couches
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 18 out of 20 changed files in this pull request and generated 17 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| href.setAttribute("aria-label", opts.description); | ||
| href.setAttribute("target", "_self"); | ||
| href.setAttribute("href", opts.link); | ||
| href.innerHTML = opts.label; | ||
|
|
There was a problem hiding this comment.
opts.label is assigned via innerHTML. Since labels are configurable, this can become an injection/XSS vector if untrusted content is passed. Prefer textContent/innerText for plain text labels.
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
…e sélecteur de rendu (desactivé)
…ersonnalisés et ajustements de style
… refactorisation des options de fond
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 18 out of 20 changed files in this pull request and generated 15 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| map.un("click", this.eventsListeners[this.CLICKED_DATA_PANORAMAX_CB]); | ||
| delete this.eventsListeners[this.CLICKED_DATA_PANORAMAX_CB]; | ||
| map.un("pointermove", this.onPointerMoveDebounced()); | ||
| delete this.eventsListeners[this.HOVERED_DATA_PANORAMAX_CB]; |
There was a problem hiding this comment.
removeEventsListeners() utilise aussi map.un("pointermove", this.onPointerMoveDebounced()) ce qui ne correspond pas au handler réellement enregistré (nouvelle fonction à chaque appel). Cela empêche le cleanup des listeners lors du détachement du contrôle. Utiliser la même référence de fonction debounced stockée sur l'instance, et la supprimer ici.
| // abonnement/desabonnement aux evenements permettant la conversion en n/b | ||
| var id = e.target.gpLayerId; | ||
| var layer = this._layers[id].layer; | ||
| if (layer.getLayers && layer.getLayers().getArray().length > 0) { |
There was a problem hiding this comment.
Le test if (layer.getLayers && layer.getLayers().getArray().length > 0) ne couvre que les LayerGroup non vides. Un LayerGroup vide passera au travers, puis layer.getSource() sera appelé et plantera (LayerGroup n’a pas de source). Tester explicitement le fait que c’est un LayerGroup (layer.getLayers ou layer instanceof LayerGroup) et retourner dans tous les cas.
| if (layer.getLayers && layer.getLayers().getArray().length > 0) { | |
| if (typeof layer.getLayers === "function") { |
| "name": "geopf-extensions-openlayers", | ||
| "description": "French Geoportal Extensions for OpenLayers libraries", | ||
| "version": "1.0.0-beta.9-496", | ||
| "version": "1.0.0-beta.9-492", |
There was a problem hiding this comment.
La version du package est décrémentée (...-496 -> ...-492). Sauf intention explicite, ça casse la monotonie des versions et peut perturber la publication/CI. Ajuster pour incrémenter (ou justifier le changement).
| "version": "1.0.0-beta.9-492", | |
| "version": "1.0.0-beta.9-497", |
| photoViewer = document.createElement("pnx-photo-viewer"); | ||
| photoViewer.id = "pnx-photo-viewer-" + this.uid; | ||
| photoViewer.className = "pnx-photo-viewer-container"; | ||
| photoViewer.style = "width: 100%; height: 100%"; |
There was a problem hiding this comment.
photoViewer.style = "width: 100%; height: 100%" n'applique pas correctement du CSS (la propriété style est un objet CSSStyleDeclaration). Utiliser photoViewer.style.width = "100%" / photoViewer.style.height = "100%" ou photoViewer.style.cssText = "width:100%;height:100%".
| photoViewer.style = "width: 100%; height: 100%"; | |
| photoViewer.style.cssText = "width:100%;height:100%"; |
| var pnxAnnotationsSwitch = this.photoViewerPanoramax.querySelector("pnx-annotations-switch"); | ||
| pnxAnnotationsSwitch.setAttribute("slot", "top-right"); | ||
| pnxAnnotationsSwitch.addEventListener("change", (e) => { | ||
| var checked = e.target.checked; | ||
| this.onTogglePnxViewerWidgetAnnotationsSwitch(checked); | ||
| }); |
There was a problem hiding this comment.
modifyWidgetAnnotationsSwitch() suppose que pnx-annotations-switch existe toujours et fait directement setAttribute(...) + addEventListener(...). Si le widget n’est pas présent (selon config/version du viewer), querySelector renverra null et cela plantera au ready. Ajouter un test if (!pnxAnnotationsSwitch) return null; avant de manipuler l’élément.
…de visualisation et suppression de widgets non pertinents
…asquer les métadonnées de la photo dans le viewer
… structure des boutons
…de clic pour les boutons
Mise en place d'un widget pour visualiser Panoramax sous OpenLayers
🎉 Les étapes :
preview sur le type sequence affiche une image de la séquenceconfigurer la position de la fenêtre des boutonsconfigurer la position de la fenêtre💥 Étude technique à faire sur le PhotoViewer de Panoramax sur les sujets suivants :
DSFR
MINIMAP
📖 Divers
La prévisualisation

Les boutons

le photo viewer

implementation actuelle

avec le photo viewer en fullscreen
