<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>Cesium Digital Twin</title>
  <link
    rel="stylesheet"
    href="https://cesium.com/downloads/cesiumjs/releases/1.114/Build/Cesium/Widgets/widgets.css"
  />
  <script src="https://cesium.com/downloads/cesiumjs/releases/1.114/Build/Cesium/Cesium.js"></script>
  <style>
    html, body, #cesiumContainer {
      width: 100%;
      height: 100%;
      margin: 0;
      padding: 0;
      overflow: hidden;
    }
    #toolbar {
      position: absolute;
      top: 12px;
      left: 12px;
      z-index: 10;
      background: rgba(0, 0, 0, 0.65);
      color: #fff;
      padding: 10px 12px;
      border-radius: 6px;
      font-family: Arial, sans-serif;
      font-size: 13px;
    }
    #toolbar select {
      width: 300px;
    }
    #toolbar .field {
      margin-top: 8px;
    }
    #toolbar .infoPanel {
      margin-top: 8px;
      visibility: hidden;
    }
    #toolbar table {
      border-collapse: collapse;
    }
    #toolbar td {
      padding: 4px 6px;
    }
  </style>
</head>
<body>
  <div id="toolbar">
    <div class="field">
      <select id="basemap">
        <option value="osm">OpenStreetMap</option>
        <option value="esri">ESRI World Imagery</option>
        <option value="carto_light">CARTO Light</option>
        <option value="carto_dark">CARTO Dark</option>
      </select>
    </div>
    <div class="field">
      <select id="dropdown">
        <option value="0">Color By Building Material</option>
        <option value="1">Color By Distance To Selected Location</option>
        <option value="2">Highlight Residential Buildings</option>
        <option value="3">Show Office Buildings Only</option>
        <option value="4">Show Apartment Buildings Only</option>
      </select>
    </div>
    <table class="infoPanel">
      <tbody>
        <tr>
          <td>Click a building to set the center point</td>
        </tr>
      </tbody>
    </table>
  </div>
  <div id="cesiumContainer"></div>
  <script>
    Cesium.Ion.defaultAccessToken = "{{ ion_token }}";

    const viewer = new Cesium.Viewer("cesiumContainer", {
      imageryProvider: false,
      animation: false,
      timeline: false,
      baseLayerPicker: false,
      geocoder: false,
      homeButton: false,
      navigationHelpButton: false,
      sceneModePicker: false,
      infoBox: false,
      selectionIndicator: false,
    });

    (async () => {
      const handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
      let osmBuildingsTileset = null;
      const infoPanel = document.querySelector(".infoPanel");
      const menu = document.getElementById("dropdown");
      const basemap = document.getElementById("basemap");
      const basemapLayers = {};

      try {
        viewer.terrainProvider = await Cesium.createWorldTerrainAsync();
      } catch (err) {
        console.error("Failed to load world terrain", err);
      }

      try {
        osmBuildingsTileset = await Cesium.createOsmBuildingsAsync();
        viewer.scene.primitives.add(osmBuildingsTileset);
      } catch (err) {
        console.error("Failed to load OSM Buildings", err);
      }

      const lat = Number("{{ lat }}");
      const lon = Number("{{ long }}");
      const defaultLat = Number.isNaN(lat) ? 47.62051 : lat;
      const defaultLon = Number.isNaN(lon) ? -122.34931 : lon;
      if (!Number.isNaN(lat) && !Number.isNaN(lon)) {
        viewer.camera.flyTo({
          destination: Cesium.Cartesian3.fromDegrees(lon, lat, 900),
          orientation: {
            heading: Cesium.Math.toRadians(0),
            pitch: Cesium.Math.toRadians(-35),
            roll: 0,
          },
        });
      }

      function getBasemapProvider(key) {
        if (key === "osm") {
          return new Cesium.OpenStreetMapImageryProvider({
            url: "https://tile.openstreetmap.org/",
          });
        }
        if (key === "esri") {
          return new Cesium.UrlTemplateImageryProvider({
            url: "https://services.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}",
            credit: "Esri",
          });
        }
        if (key === "carto_light") {
          return new Cesium.UrlTemplateImageryProvider({
            url: "https://cartodb-basemaps-a.global.ssl.fastly.net/light_all/{z}/{x}/{y}.png",
            credit: "CARTO",
          });
        }
        if (key === "carto_dark") {
          return new Cesium.UrlTemplateImageryProvider({
            url: "https://cartodb-basemaps-a.global.ssl.fastly.net/dark_all/{z}/{x}/{y}.png",
            credit: "CARTO",
          });
        }
        return null;
      }

      function setBaseLayer(key) {
        let layer = basemapLayers[key];
        if (!layer) {
          const provider = getBasemapProvider(key);
          if (!provider) {
            return;
          }
          layer = viewer.imageryLayers.addImageryProvider(provider);
          basemapLayers[key] = layer;
        }
        Object.keys(basemapLayers).forEach((k) => {
          basemapLayers[k].show = k === key;
        });
        viewer.imageryLayers.raiseToTop(layer);
      }

      function setTilesetStyle(style) {
        if (osmBuildingsTileset) {
          osmBuildingsTileset.style = style;
        }
      }

      function colorByMaterial() {
        setTilesetStyle(new Cesium.Cesium3DTileStyle({
          defines: {
            material: "${feature['building:material']}",
          },
          color: {
            conditions: [
              ["${material} === null", "color('white')"],
              ["${material} === 'glass'", "color('skyblue', 0.5)"],
              ["${material} === 'concrete'", "color('grey')"],
              ["${material} === 'brick'", "color('indianred')"],
              ["${material} === 'stone'", "color('lightslategrey')"],
              ["${material} === 'metal'", "color('lightgrey')"],
              ["${material} === 'steel'", "color('lightsteelblue')"],
              ["true", "color('white')"],
            ],
          },
        }));
      }

      function highlightAllResidentialBuildings() {
        setTilesetStyle(new Cesium.Cesium3DTileStyle({
          color: {
            conditions: [
              [
                "${feature['building']} === 'apartments' || ${feature['building']} === 'residential'",
                "color('cyan', 0.9)",
              ],
              ["true", "color('white')"],
            ],
          },
        }));
      }

      function showByBuildingType(buildingType) {
        if (!buildingType) {
          return;
        }
        setTilesetStyle(new Cesium.Cesium3DTileStyle({
          show: "${feature['building']} === '" + buildingType + "'",
        }));
      }

      function colorByDistanceToCoordinate(pickedLatitude, pickedLongitude) {
        setTilesetStyle(new Cesium.Cesium3DTileStyle({
          defines: {
            distance:
              "distance(vec2(${feature['cesium#longitude']}, ${feature['cesium#latitude']}), " +
              "vec2(" + pickedLongitude + "," + pickedLatitude + "))",
          },
          color: {
            conditions: [
              ["${distance} > 0.014", "color('blue')"],
              ["${distance} > 0.010", "color('green')"],
              ["${distance} > 0.006", "color('yellow')"],
              ["${distance} > 0.0001", "color('red')"],
              ["true", "color('white')"],
            ],
          },
        }));
      }

      function removeCoordinatePickingOnLeftClick() {
        if (infoPanel) {
          infoPanel.style.visibility = "hidden";
        }
        handler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_CLICK);
      }

      menu.addEventListener("change", () => {
        const idx = menu.selectedIndex;
        if (idx === 0) {
          removeCoordinatePickingOnLeftClick();
          colorByMaterial();
        } else if (idx === 1) {
          if (infoPanel) {
            infoPanel.style.visibility = "visible";
          }
          colorByDistanceToCoordinate(defaultLat, defaultLon);
          handler.setInputAction((movement) => {
            viewer.selectedEntity = undefined;
            const pickedBuilding = viewer.scene.pick(movement.position);
            if (pickedBuilding) {
              const pickedLatitude = pickedBuilding.getProperty("cesium#latitude");
              const pickedLongitude = pickedBuilding.getProperty("cesium#longitude");
              if (pickedLatitude != null && pickedLongitude != null) {
                colorByDistanceToCoordinate(pickedLatitude, pickedLongitude);
              }
            }
          }, Cesium.ScreenSpaceEventType.LEFT_CLICK);
        } else if (idx === 2) {
          removeCoordinatePickingOnLeftClick();
          highlightAllResidentialBuildings();
        } else if (idx === 3) {
          removeCoordinatePickingOnLeftClick();
          showByBuildingType("office");
        } else if (idx === 4) {
          removeCoordinatePickingOnLeftClick();
          showByBuildingType("apartments");
        }
      });

      basemap.addEventListener("change", () => {
        setBaseLayer(basemap.value);
      });

      setBaseLayer(basemap.value);
      colorByMaterial();
    })();
  </script>
</body>
</html>
