Dioramap

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

ChampTypeDéfautDescription
containerstring | HTMLElementrequisSélecteur CSS ou élément DOM dans lequel monter le viewer. La hauteur de l'élément doit être définie.
apiKeystringrequisClé publique pk_… émise par Dioramap (demander un accès).
backendUrlstring'wss://app.dioramap.com/ws'URL WebSocket du processeur. À surcharger uniquement pour pointer un environnement non-prod.
latnumberrequisLatitude WGS84 du centre de scène (degrés décimaux).
lonnumberrequisLongitude WGS84 du centre de scène (degrés décimaux).
radiusnumber75Rayon 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) => voidVoir Callbacks.
onError(err) => voidVoir Callbacks.
onModelSelected(uuid, info) => voidVoir Callbacks.
onModelMoved(uuid, transform) => voidVoir Callbacks.
onModelDeselected() => voidVoir Callbacks.
feedbackObjectActive le widget d'évaluation du rendu. Voir Évaluation du rendu.

Erreurs

init() rejette si :

  • lat ou lon est absent ou nul.
  • container est 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).

ChampTypeDéfautDescription
urlstringrequisURL du fichier .glb. Doit être servi en CORS depuis votre origine ou autorisé par le navigateur.
labelstring''É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).
rotationZnumber0Cap (heading) en degrés autour de l'axe vertical.
scalenumber1Facteur d'échelle. Le SDK auto-normalise le GLB à ~4 m de large ; scale s'applique en plus.
groundSnapbooleantrueAu placement, force z à l'altitude du terrain sous le modèle.
groundFollowbooleantruePendant un drag au gizmo, suit l'altitude du terrain.
allowedRotationAxesstring[]['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

ChampTypeDéfautDescription
enabledbooleantrueInterrupteur principal. Mettre à false désactive complètement le widget ; aucune pastille, aucun timer, aucune requête.
embedderUserHashstringrequis 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.
autoPromptDelayMsnumber60000Délai en millisecondes après la fin d'un rendu avant que la pastille n'apparaisse. Mettre à 0 rend la pastille immédiate.
backendUrlstring'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}) => voidCallback 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 autoPromptDelayMs ms après l'événement render-complete de chaque rendu ; pas plus d'une fois par run_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 ; onSubmitted reçoit hasComment: true si 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 directive style-src de 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.

VariableDéfautSurface
--dioramap-surface-bgrgba(20, 20, 28, 0.85)Fond des panneaux flottants (panneau soleil, badge attribution, modale d'évaluation).
--dioramap-surface-bg-strongrgba(13, 17, 23, 0.95)Variante opaque (toast, pastille survolée).
--dioramap-surface-border#30363dTrait de séparation des panneaux.
--dioramap-text-primary#e6edf3Texte principal sur surfaces sombres.
--dioramap-text-secondary#8b949eLibellés secondaires, en-têtes uppercase.
--dioramap-text-muted#6e7681Texte atténué, états vides.
--dioramap-text-on-light#1a1a2eTexte sur surfaces claires (modale).
--dioramap-accent#4f46e5Bouton primaire, anneau de focus.
--dioramap-accent-hover#4338caSurvol du bouton primaire.
--dioramap-accent-disabled#a5b4fcBouton primaire désactivé.
--dioramap-danger#f85149Aiguille Nord de la boussole, états d'erreur.
--dioramap-success#fbbf24Étoiles d'évaluation remplies, icône d'invitation.
--dioramap-radius6pxRayon par défaut des coins arrondis.
--dioramap-radius-lg10pxRayon des modales.
--dioramap-radius-pill999pxRayon de la pastille d'évaluation.
--dioramap-font-ui-apple-system, system-ui, sans-serifStack typo des libellés.
--dioramap-font-monoui-monospace, Menlo, Consolas, monospaceStack 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.