Introducción

Esta página le lleva de un mapa publicado a una página que lo controla. Se supone que ya se ha publicado un mapa y que dispone de su id de publicación (tiene el aspecto pub_... y es la última parte de la URL de inserción).

1. Permita su sitio

Un mapa solo acepta comandos de los sitios que su autor ha indicado. Pida al autor que abra el mapa en Icon Map, elija Publicar y:

  1. Añada su sitio a Allowed origins: el esquema y el host desde los que se sirve su página, por ejemplo https://portal.contoso.com. Se admite un subdominio comodín (https://*.contoso.com); una ruta, no.
  2. Deje activada la opción Let the hosting page control this map from script (Embed API), y deje activados los grupos de comandos que necesite.
  3. Publique. Los orígenes permitidos y los conmutadores de grupo forman parte de la publicación firmada, por lo que un cambio requiere volver a publicar.

Durante el desarrollo, añada también su origen local, por ejemplo http://localhost:5173. Un origen es esquema + host + puerto, por lo que http://localhost:5173 y http://localhost:3000 son sitios distintos.

Si falta su origen, el mapa se sigue mostrando en su página; simplemente no se puede controlar. map.ready se rechaza con el código denied_origin, de modo que lo sabrá de inmediato en lugar de preguntarse por qué no ocurre nada.

2. Instale el SDK

Cargue el SDK con una etiqueta script. Define una única variable global, IconMapEmbed:

<script src="https://www.icon-map.com/js/iconmap-embed/2/iconmap-embed.umd.min.js"></script>
<script>
  const { embed } = IconMapEmbed;
</script>

El SDK ocupa unos 17 KB y no tiene dependencias. Funciona en cualquier lugar donde funcione una etiqueta script, incluidas páginas de SharePoint, Confluence y CMS.

Hay un paquete npm en camino (@iconmap/embed, con tipos de TypeScript, para usar con un empaquetador). La API es la misma; los fragmentos siguientes que usan import se aplicarán cuando se publique. Hasta entonces, use la variable global IconMapEmbed.

3. Monte el mapa

Asigne al mapa un elemento con tamaño. El marco lo ocupa por completo.

<div id="map" style="height: 600px"></div>
const map = embed(document.getElementById("map"), {
  publishId: "pub_...",
  onError: (e) => console.error(e.code, e.message),
});

const capabilities = await map.ready;
console.log(capabilities.commands);   // exactly what THIS map answers

ready se resuelve con el descriptor de capacidades. Úselo para decidir qué ofrece su página:

const can = (name) => capabilities.commands.includes(name);
if (!can("setFilters")) hideMyFilterPanel();

Base sus decisiones en las capacidades, no en números de versión: la lista ya tiene en cuenta la compilación del visor, el tipo de inserción y los conmutadores del autor.

4. Envíe comandos

Cada comando es un método que devuelve una promesa.

await map.flyTo({ center: [-3.18, 51.48], zoom: 12 });

const layers = await map.getLayers();
await map.setLayerVisibility({ layerId: layers[0].id, visible: false });

await map.setSlicerState({ slicerId: "slicer-region", state: { values: ["Wales"] } });

Puede llamar a los comandos en cuanto tenga el identificador. Todo lo que se envía antes de que el mapa termine de iniciarse se retiene y se responde cuando existe la parte del mapa que necesita, así que esto es correcto:

const map = embed(el, { publishId });
await map.fitLayer({ layerId: "depots" });   // no need to wait for anything first

5. Escuche eventos

No se envía nada hasta que lo solicita.

const stop = map.on("featureClick", (feature) => {
  if (!feature) return;                       // a click on empty map
  showDetails(feature.layerName, feature.fields, feature.featureKey);
});

map.on("viewChanged", (view) => {
  if (view.reason === "user") saveLastView(view);   // ignore moves your own code caused
});

stop();   // unsubscribe

Cada evento de estado lleva un reason; consulte Eventos para saber por qué es importante.

6. Gestione los errores

Un comando rechazado es un EmbedError con un code estable:

const { EmbedError } = IconMapEmbed;

try {
  await map.setFilters({ region: ["EMEA"], colour: "red" });
} catch (e) {
  if (e instanceof EmbedError && e.code === "undeclared_filter") {
    console.warn("not a filter on this map:", e.detail.undeclared);
  } else {
    throw e;
  }
}

Los códigos se enumeran en la referencia y se explican en Errores y control de versiones.

Empiece en el lugar correcto

Todo lo que puede cambiar en tiempo de ejecución también se puede establecer al montar el mapa, de modo que el lector nunca ve aparecer primero la vista predeterminada del autor:

embed(el, {
  publishId: "pub_...",
  filters: { region: ["EMEA"] },
  view: { center: [2.35, 48.85], zoom: 9 },
  visibility: { layers: { depots: false } },
  basemapId: "slate",
  theme: { mode: "dark" },
  language: "fr",
  chrome: { legend: false, layerControl: false },
  background: "transparent",
  hyperlinkBehavior: "raiseEvent",
  events: ["featureClick", "linkClicked"],
});

bookmarkId aplica uno de los marcadores del autor, y scene restaura una vista que capturó antes.

Mapas protegidos con token

Si el autor publicó el mapa exigiendo tokens firmados, su servidor emite un token de corta duración con el secreto de inserción de la publicación y su página lo entrega. El secreto nunca debe llegar al navegador.

const map = embed(el, {
  publishId: "pub_...",
  getToken: () => fetch("/api/iconmap-token").then((r) => r.text()),
});

map.on("tokenExpiring", async () => {
  await map.setToken({ token: await fetch("/api/iconmap-token").then((r) => r.text()) });
});

getToken se llama al montar el mapa y cada vez que este pide uno nuevo. tokenExpiring se desencadena un minuto antes de la caducidad para que pueda enviar un token nuevo con antelación.

Si la emisión del token es lenta, inicie primero la descarga y proporcione el token cuando lo tenga:

const map = embed(el, { publishId: "pub_...", deferRender: true });   // frame starts loading now
const token = await mintToken();
map.render({ getToken: () => token });

Inserción para su organización

Para insertar un mapa en directo para compañeros con la sesión iniciada, cambie el punto de entrada. El identificador es el mismo.

const { embedForOrganization } = IconMapEmbed;

const map = embedForOrganization(el, {
  mapItem: { workspaceId: "...", itemId: "..." },
  getToken: (scopes) => myMsal.acquireTokenSilent({ scopes }).then((r) => r.accessToken),
});

getToken recibe los ámbitos que el mapa necesita en ese momento (OneLake, Fabric y otros según los datos del mapa) y devuelve un token delegado para el usuario que ha iniciado sesión. Si lo omite, el mapa ejecuta su propio inicio de sesión dentro del marco. Consulte Inserción para su organización para ver la configuración completa.

Los sitios que pueden controlar una inserción organizativa se establecen en el elemento del mapa, en Publicar > Embedding for your organization. De forma predeterminada son los mismos sitios que los Allowed origins de la publicación ("Use the allowed origins above"), y se guardan con el mapa al publicar, o con Save origins si nunca publica. Desmarque la casilla para mantener una lista aparte, por ejemplo cuando cualquier sitio puede insertar la copia publicada pero solo su intranet puede controlar por script el mapa en directo.

En una inserción organizativa no existen los filtros declarados ni refreshData (no aparecen en capabilities.commands), highlight responde unsupported_command (use setSelection), y exportImage / print se rechazan con denied_capability cuando el mapa o alguno de sus orígenes de datos lleva una etiqueta de confidencialidad.

TypeScript

El paquete npm (próximamente; consulte la nota anterior) incluye sus tipos, de modo que los comandos, sus argumentos y resultados, y las cargas de los eventos tienen tipo:

import { embed, type EmbedHandle, type EmbedScene, type EmbedFeatureClick } from "@iconmap/embed";

const map: EmbedHandle = embed(el, { publishId });
const scene = (await map.captureScene()) as EmbedScene;
map.on("featureClick", (f: EmbedFeatureClick | null) => { /* ... */ });

React

No hay ningún contenedor que instalar: el identificador encaja en una ref y un efecto:

function IconMap({ publishId, filters, onFeatureClick }) {
  const el = useRef(null);
  const map = useRef(null);

  useEffect(() => {
    map.current = embed(el.current, { publishId, filters });
    const stop = map.current.on("featureClick", onFeatureClick);
    return () => { stop(); map.current.destroy(); };
  }, [publishId]);

  useEffect(() => { map.current?.setFilters(filters).catch(console.error); }, [filters]);

  return <div ref={el} style={{ height: 600 }} />;
}

Ejemplos en directo

Los ejemplos son mapas en directo que puede usar, cada uno seguido de la página completa que lo genera: cámara, capas, segmentaciones, filtros, selección y resaltado, escenas y recorridos, cambio de estilo, comandos de la ventana emergente y eventos.

Solución de problemas

Síntoma Causa
ready se rechaza con denied_origin El origen de su página no está entre los orígenes permitidos del mapa, o el mapa no se volvió a publicar después de añadirlo. Compruebe exactamente el esquema, el host y el puerto.
El marco está en blanco y la consola informa de una infracción de frame-ancestors La misma lista de orígenes permitidos controla también qué sitios pueden enmarcar el mapa. Añada su origen y vuelva a publicar.
ready se resuelve, pero commands está casi vacío El autor desactivó la Embed API (o la mayoría de los grupos) para esta publicación.
Un comando se rechaza con denied_capability El grupo de ese comando está desactivado para este mapa.
Un comando se rechaza con unsupported_command El mapa, o este tipo de inserción, no lo tiene. Compruebe capabilities.commands antes de ofrecer la función.
Los comandos agotan el tiempo de espera No se pudo cargar el marco: revise onError y la pestaña de red.