Filtrer avec des segments

Les segments sont les contrôles de filtrage que l'auteur a placés sur la carte. Les puces de gauche sont construites à partir de ce que la carte indique (les segments, les valeurs proposées par chacun et leur état actuel) et restent synchronisées avec les cartes de segment propres à la carte (en haut à droite de la carte) : cliquez sur une puce et la carte de segment suit, utilisez une carte de segment et les puces suivent. Définir un segment par script revient exactement à ce que le lecteur l'utilise : chaque couche et chaque graphique qui lui est lié suit.

Ouvrir cet exemple seul

Points à remarquer

  • getSlicers() les liste ; getSlicerValues() renvoie les valeurs proposées par un segment de liste.
  • L'état d'un segment de liste est { values: [...] } ; null l'efface. Les autres types acceptent value, range, date, dateRange, relativeDate, timeRange ou toggled.
  • Un seul gestionnaire slicerChanged couvre vos modifications (reason: "command") et celles du lecteur ("user").
  • Masquer un segment le désactive : sa sélection cesse de filtrer, comme dans un signet. Pour filtrer sans aucune carte de segment sur la carte, utilisez des filtres déclarés (l'exemple suivant).

Le code

La page complète. Remplacez l'identifiant de publication par le vôtre, et demandez à l'auteur de la carte d'ajouter votre site à ses origines autorisées.

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Filter with slicers - Icon Map Embed API</title>
  <link rel="stylesheet" href="https://www.icon-map.com/embed-examples/examples.css"> <!-- demo styling only -->
</head>
<body>
  <div class="ex-main">
    <div class="ex-side">
      <div id="panels">loading...</div>
      <h2>Result</h2>
      <pre class="ex-readout" id="result"></pre>
      <div class="ex-chips" style="margin-top:10px">
        <button id="clear">Clear all</button>
        <button id="fit">Frame the result</button>
      </div>
    </div>
    <div id="map"></div>
  </div>

  <script src="https://www.icon-map.com/js/iconmap-embed/2/iconmap-embed.umd.min.js"></script>
  <script>
    const el = document.getElementById("map");
    const map = IconMapEmbed.embed(el, {
      publishId: "pub_...",
      // The map is a globe: let this page's own background show in the space around it.
      background: "transparent",
      chrome: { legend: false, layerControl: false, bookmarksBar: false, tourTransport: false },
      // The map's slicer cards stay on (top right): use one and the chips follow, click a chip and
      // the card follows. Only the chart is hidden. Hiding a slicer would switch it OFF - to filter
      // with no card on the map, use filters instead (see "Filter from your page").
      visibility: { visuals: { "chart-continent": false } },
      events: ["slicerChanged"],
    });
    map.ready.catch((error) => console.error(error.code, error.message));

    const chosen = {};   // slicerId -> Set of values

    // The slicers, their fields and the values each offers all come from the map.
    map.getSlicers().then(async (slicers) => {
      const host = document.getElementById("panels");
      host.textContent = "";
      for (const slicer of slicers) {
        chosen[slicer.id] = new Set(slicer.state?.values ?? []);
        const { values } = await map.getSlicerValues({ slicerId: slicer.id });
        const title = Object.assign(document.createElement("h2"), { textContent: slicer.name });
        const chips = Object.assign(document.createElement("div"), { className: "ex-chips" });
        for (const value of values) {
          const chip = Object.assign(document.createElement("button"), { textContent: String(value).replace(/^\d\. /, "") });
          chip.dataset.slicer = slicer.id;
          chip.dataset.value = value;
          chip.onclick = () => toggle(slicer.id, String(value));
          chips.append(chip);
        }
        host.append(title, chips);
      }
      paint();
    });

    // Setting a slicer is exactly the reader using it: every layer and chart bound to it follows.
    // A list slicer's state is { values: [...] }; null clears it.
    function toggle(slicerId, value) {
      const set = chosen[slicerId];
      set.has(value) ? set.delete(value) : set.add(value);
      return map.setSlicerState({ slicerId, state: set.size ? { values: [...set] } : null });
    }

    document.getElementById("clear").onclick = () => map.clearAllSlicers();
    document.getElementById("fit").onclick = () => map.fitLayer({ layerId: "cities", padding: 30, maxZoom: 6 });

    // One handler covers your own changes (reason "command") and the reader's ("user").
    map.on("slicerChanged", ({ slicerId, state }) => {
      chosen[slicerId] = new Set(state?.values ?? []);
      setTimeout(paint, 300);   // give the map a moment to re-filter before counting
    });

    async function paint() {
      for (const chip of document.querySelectorAll("[data-slicer]")) {
        chip.classList.toggle("on", chosen[chip.dataset.slicer]?.has(chip.dataset.value) === true);
      }
      const stats = await map.getLayerStats({ layerId: "cities" });
      document.getElementById("result").textContent = `${stats.visibleFeatureCount.toLocaleString()} cities match`;
    }
  </script>
</body>
</html>

Pour aller plus loin