Viewer SDK
Le viewer affiche une scène 3D réaliste à partir d'une coordonnée (lat, lon) — relief, végétation, ombres calculées selon l'heure. Vous pouvez y déposer des modèles 3D (format GLB) que l'utilisateur peut déplacer ou orienter.
Deux formats au choix selon votre stack :
UMD (.umd.js) chargé en <script src> et exposant window.DioramapViewer,
ou ES module (.es.js) à import depuis votre bundler.
Les deux exposent la même API init(config).
Exemple minimal — option A : <script> (UMD)
Recommandé pour la majorité des intégrations — aucun outil de build requis côté embarqueur. Depuis la version 0.8.0 le SDK ship ses styles dans une feuille externe (dioramap-viewer.css) qu'il faut charger en plus du JavaScript : voir Personnalisation visuelle & CSP pour la justification (durcissement de la CSP).
<link rel="stylesheet" href="https://static.dioramap.com/sdk/v0.8.1/dioramap-viewer.css">
<script src="https://static.dioramap.com/sdk/v0.8.1/dioramap-viewer.umd.js"></script>
<div id="viewer" style="width:100%;height:600px"></div>
<script>
const viewer = await DioramapViewer.init({
container: '#viewer',
apiKey: 'pk_live_…',
lat: 48.8698, lon: 2.3076,
onError: (err) => console.error(err),
});
// Pose un modèle 3D au centre de la scène.
await viewer.addModel({
url: 'https://votre-site.fr/models/cabane.glb',
label: 'Cabane',
});
</script>
Exemple minimal — option B : import (ES module)
Pour les sites construits avec Vite, webpack, esbuild, etc. La feuille de style externe reste requise.
import 'https://static.dioramap.com/sdk/v0.8.1/dioramap-viewer.css';
import { init } from 'https://static.dioramap.com/sdk/v0.8.1/dioramap-viewer.es.js';
const viewer = await init({
container: '#viewer',
apiKey: 'pk_live_…',
lat: 48.8698, lon: 2.3076,
});
Choix du format : les deux fichiers exposent la même API et bundlent les mêmes dépendances (Three.js, GLTFLoader, etc.). Choisissez selon votre infrastructure : .umd.js si vous intégrez par <script>, .es.js si votre site utilise un bundler avec import. Ne chargez jamais les deux simultanément.
DioramapViewer.init(config)
Monte le viewer dans config.container et retourne une Promise résolue avec un objet SDK contenant les méthodes documentées plus bas.
Options
| Champ | Type | Défaut | Description |
|---|---|---|---|
container | string | HTMLElement | requis | Sélecteur CSS ou élément DOM dans lequel monter le viewer. La hauteur de l'élément doit être définie. |
apiKey | string | requis | Clé publique pk_… émise par Dioramap (demander un accès). |
backendUrl | string | 'wss://app.dioramap.com/ws' | URL WebSocket du processeur. À surcharger uniquement pour pointer un environnement non-prod. |
lat | number | requis | Latitude WGS84 du centre de scène (degrés décimaux). |
lon | number | requis | Longitude WGS84 du centre de scène (degrés décimaux). |
radius | number | 75 | Rayon en mètres autour de (lat, lon) à charger. Au-delà du rayon, la scène est tronquée — augmentez avec parcimonie, le coût de calcul croît avec la surface. |
onReady | (sdk) => void | — | Voir Callbacks. |
onError | (err) => void | — | Voir Callbacks. |
onModelSelected | (uuid, info) => void | — | Voir Callbacks. |
onModelMoved | (uuid, transform) => void | — | Voir Callbacks. |
onModelDeselected | () => void | — | Voir Callbacks. |
feedback | Object | — | Active le widget d'évaluation du rendu. Voir Évaluation du rendu. |
Erreurs
init() rejette si :
latoulonest absent ou nul.containerest introuvable.- La connexion WebSocket échoue (clé invalide, origine non autorisée, processeur indisponible) — l'erreur est aussi propagée à
onError.
Callbacks
onReady(sdk)
Appelé une fois la scène montée et la connexion WS établie. sdk est l'objet retourné par init(). Utile pour pré-positionner le soleil ou ajouter des modèles immédiatement.
onError(err)
Appelé sur erreur de connexion ou erreur fatale du moteur. err est un Error ; err.message est destiné à être lu côté humain.
onModelSelected(uuid, info)
Appelé quand l'utilisateur clique sur un modèle. info contient { label, position, rotationZ, scale }.
onModelMoved(uuid, transform)
Appelé pendant ou après une manipulation au gizmo. transform contient { position, rotationZ, scale }. Le débit est élevé pendant un drag — debouncez côté hôte si vous synchronisez à un backend.
onModelDeselected()
Appelé quand l'utilisateur clique en dehors d'un modèle ou appelle deselectModel().
Cycle de vie
sdk.dispose()
Démonte la scène, ferme la WebSocket, libère la mémoire WebGL et vide le conteneur. À appeler avant de retirer l'élément du DOM ou de monter une nouvelle scène à un autre endroit.
Contrôle du soleil
sdk.setSunTime(date)
Recalcule la position solaire et les ombres pour la Date donnée (heure locale du lieu, fuseau France métropolitaine). Bon marché — appelable à 60 Hz pour animer une journée.
sdk.setSunTime(new Date('2026-06-21T15:00:00'));
sdk.setSunEnabled(bool)
Active ou désactive le rendu directionnel du soleil. false repasse en éclairage diffus uniforme (utile pour comparaisons sans ombres).
Modèles 3D
sdk.addModel(opts) → Promise<uuid>
Charge un GLB depuis opts.url, le pose dans la scène, et résout avec un identifiant à usage interne (utilisable avec les autres méthodes).
| Champ | Type | Défaut | Description |
|---|---|---|---|
url | string | requis | URL du fichier .glb. Doit être servi en CORS depuis votre origine ou autorisé par le navigateur. |
label | string | '' | Étiquette interne — affichée dans les callbacks et les overlays. |
position | {x, y, z} | {0,0,0} | Position en mètres dans le repère local de scène (X = est, Y = nord, Z = altitude). |
rotationZ | number | 0 | Cap (heading) en degrés autour de l'axe vertical. |
scale | number | 1 | Facteur d'échelle. Le SDK auto-normalise le GLB à ~4 m de large ; scale s'applique en plus. |
groundSnap | boolean | true | Au placement, force z à l'altitude du terrain sous le modèle. |
groundFollow | boolean | true | Pendant un drag au gizmo, suit l'altitude du terrain. |
allowedRotationAxes | string[] | ['z'] | Axes de rotation autorisés au gizmo. ['z'] ne laisse tourner qu'autour de la verticale (cas usuel). |
sdk.removeModel(uuid)
Retire le modèle de la scène et libère ses ressources GPU.
sdk.getModels() → Array
Retourne la liste des modèles présents avec leur état actuel (uuid, label, position, rotationZ, scale).
sdk.getModel(uuid)
Retourne l'état d'un modèle, ou null si le uuid n'existe plus.
sdk.setModelTransform(uuid, transform)
Applique { position?, rotationZ?, scale? } à un modèle existant. Les champs absents restent inchangés. Utile pour synchroniser un état persisté côté serveur.
Placement par fantôme
sdk.startGhostPlace(opts) → Promise<uuid | null>
Active un mode où une copie translucide du GLB suit le curseur. Clic gauche pour confirmer (équivalent à un addModel à la position pointée), Échap ou cancelGhost() pour annuler. La promesse résout avec le uuid du modèle posé, ou null si annulé. Les options sont les mêmes que addModel ; position est ignoré (déterminé par le curseur).
const uuid = await sdk.startGhostPlace({ url: '/models/abri.glb' });
if (!uuid) console.log('placement annulé');
sdk.cancelGhost()
Annule un placement par fantôme en cours. Sans effet si aucun fantôme n'est actif.
Sélection & gizmo
sdk.selectModel(uuid)
Sélectionne le modèle programmatiquement (équivalent à un clic utilisateur). Affiche le gizmo de transformation autour du modèle.
sdk.deselectModel()
Retire la sélection actuelle. Idempotent.
sdk.getSelectedModel()
Retourne l'état du modèle sélectionné, ou null.
sdk.setGizmoMode(mode)
Bascule le gizmo entre les modes 'translate' (déplacement) et 'rotate' (rotation).
Évaluation du rendu
Un widget intégré demande à l'utilisateur final de noter la qualité du rendu une fois la scène chargée. Une pastille étoilée apparaît automatiquement en bas à gauche 60 secondes après chaque rendu : un clic ouvre une fenêtre avec 5 étoiles. Les notes ≥ 4 sont envoyées immédiatement ; les notes ≤ 3 dévoilent un champ de commentaire optionnel avant l'envoi. Le tout est opt-in : désactivé tant que vous n'avez pas fourni de configuration.
Activé via l'option feedback dans DioramapViewer.init() :
DioramapViewer.init({
apiKey: 'pk_live_…',
container: '#viewer',
lat: 48.8584, lon: 2.2945,
feedback: {
embedderUserHash: 'a3f4…9b2e', // sha256 hex, 64 chars (voir « Confidentialité »)
onSubmitted: ({ rating, hasComment }) => {
analytics.track('dioramap_feedback', { rating, hasComment });
},
},
});
Sous-options de feedback
| Champ | Type | Défaut | Description |
|---|---|---|---|
enabled | boolean | true | Interrupteur principal. Mettre à false désactive complètement le widget ; aucune pastille, aucun timer, aucune requête. |
embedderUserHash | string | requis si activé | Empreinte SHA-256 (64 caractères hex) identifiant l'utilisateur final côté votre site. Voir Confidentialité. Le widget se désactive silencieusement (avec un avertissement console) si la valeur n'a pas le bon format. |
autoPromptDelayMs | number | 60000 | Délai en millisecondes après la fin d'un rendu avant que la pastille n'apparaisse. Mettre à 0 rend la pastille immédiate. |
backendUrl | string | 'https://app.dioramap.com' | Hôte qui reçoit les évaluations (POST /api/feedback/render). À surcharger uniquement pour pointer un environnement non-production. |
onSubmitted | ({rating, hasComment}) => void | — | Callback appelé après l'envoi réussi d'une évaluation. rating = entier 1–5, hasComment = true si l'utilisateur a saisi un commentaire. Utile pour relayer l'événement vers vos propres analytics. |
apiKey n'est pas une sous-option : le SDK la transmet automatiquement depuis l'option apiKey de niveau supérieur.
Confidentialité — embedderUserHash
Le hash identifie l'utilisateur final auprès de Dioramap sans révéler son identité. C'est à votre site de le calculer côté serveur à partir d'un identifiant stable (id de compte, email …) avec un sel propre à votre application.
// Exemple côté serveur (Node.js)
const { createHash } = require('node:crypto');
const hash = createHash('sha256')
.update(user.id + process.env.DIORAMAP_USER_SALT)
.digest('hex');
// Injectez ensuite ce `hash` dans la page rendue.
Dioramap utilise ce hash uniquement pour : (a) éviter les évaluations multiples du même rendu (déduplication via une garde localStorage), (b) corréler dans nos métriques internes les notes émises par un même utilisateur sur plusieurs rendus. Aucune donnée personnelle n'est demandée ni stockée à l'extérieur de ce hash opaque.
Comportement
- Apparition : la pastille apparaît
autoPromptDelayMsms après l'événement render-complete de chaque rendu ; pas plus d'une fois parrun_id. - Garde anti-doublon : une fois une évaluation envoyée, une entrée
localStorage(dioramap_fb_<run_id>) empêche toute nouvelle invitation pour ce rendu spécifique. - Note ≥ 4 : envoi immédiat, fenêtre fermée, brève notification « Merci de votre retour 🙂 ».
- Note ≤ 3 : déploie un champ texte optionnel (limite 500 caractères) avant le bouton Envoyer ;
onSubmittedreçoithasComment: truesi l'utilisateur a écrit quelque chose. - Échec d'envoi : message d'erreur en ligne + bouton Réessayer. Si le second essai échoue aussi, la fenêtre se ferme silencieusement et la garde n'est pas posée — le prochain rendu donnera une nouvelle invitation propre.
- Panneau de contrôle : si le panneau du soleil est ouvert, l'ouverture de la fenêtre d'évaluation le replie temporairement pour qu'il ne transparaisse pas sous le voile, puis restaure son état précédent à la fermeture.
Personnalisation visuelle & CSP
Depuis la version 0.8.0, l'intégralité du CSS du viewer vit dans une feuille externe (dioramap-viewer.css) plutôt que d'être injectée par le JavaScript au montage. Deux conséquences pour vous :
- vous pouvez retirer
'unsafe-inline'de la directivestyle-srcde votre Content Security Policy ; - vous pouvez surcharger l'apparence du viewer en chargeant votre propre feuille de style après la nôtre.
Politique de sécurité (CSP) recommandée
Aucune balise <script> ni <style> n'est injectée au runtime, aucun eval, aucun new Function. Vous pouvez donc supprimer 'unsafe-inline' des deux directives script-src et style-src :
// En-tête HTTP servi par votre serveur (nginx, Apache, CDN…)
Content-Security-Policy:
default-src 'self';
script-src 'self' https://static.dioramap.com;
style-src 'self' https://static.dioramap.com;
connect-src 'self' https://app.dioramap.com wss://app.dioramap.com;
img-src 'self' data: blob: https://static.dioramap.com;
worker-src 'self' blob:;
worker-src 'self' blob: couvre les web workers instanciés par Three.js (chargement GLB asynchrone). Ajoutez vos propres origines (CDN d'images, télémétrie, etc.) à côté.
Personnaliser les couleurs — variables CSS
Le viewer expose une dizaine de propriétés personnalisées (CSS custom properties) que vous pouvez écraser dans votre propre feuille de style chargée après dioramap-viewer.css. Le contrat de stabilité : ces variables sont garanties d'une version 0.8.x à la suivante ; toute évolution est annoncée dans le changelog du SDK.
| Variable | Défaut | Surface |
|---|---|---|
--dioramap-surface-bg | rgba(20, 20, 28, 0.85) | Fond des panneaux flottants (panneau soleil, badge attribution, modale d'évaluation). |
--dioramap-surface-bg-strong | rgba(13, 17, 23, 0.95) | Variante opaque (toast, pastille survolée). |
--dioramap-surface-border | #30363d | Trait de séparation des panneaux. |
--dioramap-text-primary | #e6edf3 | Texte principal sur surfaces sombres. |
--dioramap-text-secondary | #8b949e | Libellés secondaires, en-têtes uppercase. |
--dioramap-text-muted | #6e7681 | Texte atténué, états vides. |
--dioramap-text-on-light | #1a1a2e | Texte sur surfaces claires (modale). |
--dioramap-accent | #4f46e5 | Bouton primaire, anneau de focus. |
--dioramap-accent-hover | #4338ca | Survol du bouton primaire. |
--dioramap-accent-disabled | #a5b4fc | Bouton primaire désactivé. |
--dioramap-danger | #f85149 | Aiguille Nord de la boussole, états d'erreur. |
--dioramap-success | #fbbf24 | Étoiles d'évaluation remplies, icône d'invitation. |
--dioramap-radius | 6px | Rayon par défaut des coins arrondis. |
--dioramap-radius-lg | 10px | Rayon des modales. |
--dioramap-radius-pill | 999px | Rayon de la pastille d'évaluation. |
--dioramap-font-ui | -apple-system, system-ui, sans-serif | Stack typo des libellés. |
--dioramap-font-mono | ui-monospace, Menlo, Consolas, monospace | Stack typo des chiffres (boussole, pourcentage de chargement). |
Exemple d'override
Chargez votre feuille après celle du SDK. La dernière déclaration sur :root à spécificité égale gagne :
<link rel="stylesheet" href="https://static.dioramap.com/sdk/v0.8.1/dioramap-viewer.css">
<link rel="stylesheet" href="/css/votre-theme.css">
Dans /css/votre-theme.css :
:root {
--dioramap-accent: #2d5016;
--dioramap-accent-hover: #3a6b1e;
--dioramap-surface-bg: rgba(10, 30, 10, 0.85);
--dioramap-radius: 12px;
}
Cibler des éléments précis
Si une variable ne suffit pas, vous pouvez cibler directement les classes du SDK. Le préfixe est .dioramap- pour tout, et la liste des classes stables est :
.dioramap-attr-overlay— bandeau d'attribution « Powered by … » (bas-droit)..dioramap-controls,.dioramap-controls-toggle,.dioramap-controls-body— panneau soleil & toggles..dioramap-compass,.dioramap-compass-tilt,.dioramap-compass-readout— boussole marine..dioramap-fb-pill,.dioramap-fb-modal,.dioramap-fb-stars,.dioramap-fb-toast— widget d'évaluation..dioramap-loading-overlay,.dioramap-loading-card,.dioramap-loading-fill— voile de chargement..dioramap-action-buttons,.dioramap-action-btn— ronds d'action au-dessus d'un modèle sélectionné.
Le SDK n'utilise pas !important, ni de sélecteurs imbriqués au-delà d'une classe. Une règle de votre côté avec la même spécificité chargée après la nôtre gagne automatiquement la cascade. Évitez de surcharger les sélecteurs des composants Three.js (canvas, classes internes commençant par autre chose que .dioramap-) — ils ne font pas partie de la surface publique.