Skip to content

Style Map

Switch between base map styles (Street, Dark, Hybrid) and toggle the Traffic layer on/off as an overlay. For more information about available styles, see Map Style.

Example

<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8" />
  <title>Map SDK</title>
  <meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no" />
  <script src='https://unpkg.com/@vietmap/vietmap-gl-js@6.0.1/dist/vietmap-gl.js'></script>
  <link rel='stylesheet' href='https://unpkg.com/@vietmap/vietmap-gl-js@6.0.1/dist/vietmap-gl.css' />
  <style>
    body {
      margin: 0;
      padding: 0;
    }

    #map {
      position: absolute;
      top: 0;
      bottom: 0;
      width: 100%;
      height: 100%;
    }

    /*layerSwitcherControl*/
    .vietmapgl-ctrl-basemaps {
      display: flex;
      flex-direction: row;
      pointer-events: auto;
      bottom: 15px;
      position: relative;
      left: 20px;
    }

    .vietmapgl-ctrl-basemaps.reverse {
      flex-direction: row-reverse;
    }

    .vietmapgl-ctrl-basemaps.column {
      flex-direction: column;
    }

    .vietmapgl-ctrl-basemaps.column.reverse {
      flex-direction: column-reverse;
    }

    .vietmapgl-ctrl-basemaps .basemap {
      width: 90px;
      height: 90px;
      margin: 2px;
      border: 2px solid #ccc;
      box-shadow: 0 1px 5px rgba(0, 0, 0, 0.65);
      cursor: pointer;
    }

    .vietmapgl-ctrl-basemaps .basemap.active {
      border-color: orange;
      box-shadow: 2px 2px 4px #000;
    }

    .vietmapgl-ctrl-basemaps.closed .basemap {
      display: none;
    }

    .vietmapgl-ctrl-basemaps.closed .basemap.active {
      display: block;
      border: 2px solid #ccc;
    }
  </style>
</head>

<body>
  <div id="map"></div>
  <button id="traffic-toggle" style="position:absolute;top:10px;left:10px;z-index:1;padding:8px 14px;background:#fff;border:2px solid #ccc;border-radius:4px;cursor:pointer;font-size:13px;font-weight:600;">
    🚦 Traffic: OFF
  </button>
  <script> 
    const API_KEY = "YOUR_API_KEY_HERE";
    const baseMaps = {
      WHITE: {
        img: "./assets/light-vietmap.png",
        url: `https://maps.vietmap.vn/maps/styles/tm/style.json?apikey=${API_KEY}`,
      },
      DARK: {
        img: "./assets/dark-vietmap.png",
        url: `https://maps.vietmap.vn/maps/styles/dm/style.json?apikey=${API_KEY}`,
      },
      HYBRID: {
        img: "./assets/hybrid-vietmap.png",
        url: `https://maps.vietmap.vn/maps/styles/hm/style.json?apikey=${API_KEY}`,
      },
    };
    const map = new vietmapgl.Map({
      container: "map",
      style: baseMaps["WHITE"].url, // Set initial style
      center: [106.66817068179284, 10.803866192772915],
      zoom: 9,
    });

    class LayerSwitcherControl {
      constructor(options) {
        this._options = { ...options };
        this._container = document.createElement("div");
        this._container.classList.add(
          "vietmapgl-ctrl",
          "vietmapgl-ctrl-basemaps",
          "closed"
        );
        this._container.addEventListener("mouseenter", () => {
          this._container.classList.remove("closed");
        });
        this._container.addEventListener("mouseleave", () => {
          this._container.classList.add("closed");
        });
      }

      onAdd(map) {
        this._map = map;
        const basemaps = this._options.basemaps;
        Object.keys(basemaps).forEach((layerId) => {
          const base = basemaps[layerId];
          const basemapContainer = document.createElement("img");
          basemapContainer.src = base.img;
          basemapContainer.classList.add("basemap");
          basemapContainer.dataset.id = layerId;
          basemapContainer.addEventListener("click", () => {
            const activeElement = this._container.querySelector(".active");
            if (activeElement) {
              activeElement.classList.remove("active");
            }
            basemapContainer.classList.add("active");
            map.setStyle(base.url); // Set new style
          });
          this._container.appendChild(basemapContainer);
          if (this._options.initialBaseMap === base.url) {
            basemapContainer.classList.add("active");
          }
        });
        return this._container;
      }

      onRemove() {
        this._container.parentNode?.removeChild(this._container);
        delete this._map;
      }
    }

    // Add the layer switcher control
    map.addControl(
      new LayerSwitcherControl({
        basemaps: baseMaps,
        initialBaseMap: baseMaps["WHITE"].url,
      }),
      "bottom-left"
    );

    map.addControl(new vietmapgl.NavigationControl());

    // Traffic overlay toggle (vector style merge)
    let trafficVisible = false;
    let trafficStyle = null;   // cached after first fetch
    let trafficLayerIds = [];
    let trafficSourceIds = [];

    // Find the first symbol (text/icon) layer so traffic sits below labels
    function getFirstSymbolLayerId() {
      const layers = map.getStyle().layers || [];
      const found = layers.find((l) => l.type === "symbol");
      return found ? found.id : undefined;
    }

    // Apply already-fetched traffic style synchronously
    function applyTrafficLayers() {
      if (!trafficStyle) return;
      trafficSourceIds = [];
      trafficLayerIds = [];
      Object.entries(trafficStyle.sources || {}).forEach(([id, src]) => {
        if (!map.getSource(id)) map.addSource(id, src);
        trafficSourceIds.push(id);
      });
      const beforeId = getFirstSymbolLayerId();
      (trafficStyle.layers || []).forEach((layer) => {
        if (!map.getLayer(layer.id)) map.addLayer(layer, beforeId);
        trafficLayerIds.push(layer.id);
      });
    }

    function removeTrafficLayer() {
      trafficLayerIds.forEach((id) => { if (map.getLayer(id)) map.removeLayer(id); });
      trafficSourceIds.forEach((id) => { if (map.getSource(id)) map.removeSource(id); });
      trafficLayerIds = [];
      trafficSourceIds = [];
    }

    // Re-add synchronously on style.load — no async delay, so layers are never dropped
    map.on("style.load", () => {
      if (trafficVisible) applyTrafficLayers();
    });

    document.getElementById("traffic-toggle").addEventListener("click", async () => {
      trafficVisible = !trafficVisible;
      const btn = document.getElementById("traffic-toggle");
      if (trafficVisible) {
        // Fetch once, cache for all future style switches
        if (!trafficStyle) {
          const res = await fetch(`https://maps.vietmap.vn/maps/styles/tf/style.json?apikey=${API_KEY}`);
          trafficStyle = await res.json();
        }
        applyTrafficLayers();
        btn.style.borderColor = "orange";
        btn.textContent = "🚦 Traffic: ON";
      } else {
        removeTrafficLayer();
        btn.style.borderColor = "#ccc";
        btn.textContent = "🚦 Traffic: OFF";
      }
    });
  </script>
</body>

</html>
facebook
Tổng đài hỗ trợ
089.616.4567
facebook Chat Facebook zalo Chat Zalo