スクリーンショット 2019-01-14 10.09.53

様々な地図APIを見ていて見つけた「WRLD3D」。APIを使って簡単に3D地図を使ったサービスやアプリケーションが作成できるプラットフォームです。面白そうなのでサンプルを参考にして作ってみました。

screencapture-mapdesigner-wrld3d-portal-latest-dev-2019-01-11-22_36_46

まずは、WRLDのサイトに登録してAPI Keyをゲット。試しに使って見るなら無料のStarterで十分。

screencapture-accounts-wrld3d-users-edit-2019-01-11-22_38_35

数時間でこんなデモサイトができました。

https://digitallife.tokyo/x/map/

スクリーンショット 2019-01-14 10.12.19

まるでシムシティのようなかわいい3D地図に、飛行機が飛んでいたり、車や電車が走っています。
雨や雪といった天候や時間帯、季節も変えられます。
残念ながら一部の地域しか3D地図が提供されていないみたいですね。日本は東京だけかな?
上のメニューで場所、天気、時間帯、季節を変更できるようにしました。
拡大縮小移動はマウスやタッチパッドで簡単にできるようになっています。3D地図の左上の矢印で傾き(チルト)や向きを変更できるようにしました。

スクリーンショット 2019-01-14 10.11.10

ニューヨークやロンドンには青いドアのアイコンが表示されるところがあります。そこをクリックすると、

スクリーンショット 2019-01-14 10.11.34

建物の内部が3D表示されます。左上のメニューで階を移動したり、外に出たりすることを可能にしました。

スクリーンショット 2019-01-14 10.11.52

室内3Dモデルなども自分で作ったりすることができるみたいですね。APIが豊富で面白そうです。
作成したHTMLのソースは下記です(CSSやJavascriptを分けるのが面倒だったので中に埋め込んでいます)。これだけでここまで作れます。

<!DOCTYPE HTML>
<html>
  <head>
    <title>wrld.js test</title>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <script src="https://cdn-webgl.wrld3d.com/wrldjs/dist/latest/wrld.js"></script>
    <link href="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.0.1/leaflet.css" rel="stylesheet" />
    <style type="text/css">
    .button-radio input[type="radio"] {
      display: none;
    }
    .button-radio label {
      display:inline-block;
      color:#fff;
      background-color:#2780e3;
      width:20%;
      padding:5px 10px;
      text-align:center;
      margin: 5px;
    }
    .button-radio input[type="radio"]:checked + label {
      background-color:#000;
    }
    #controlButtons {
      position: absolute;
      z-index: 20;
    }
    #controlButtons button {
      display: block;
      width: 100%;
    }
    #floorButtons {
      position: absolute;
      z-index: 20;
      display: none;
    }
    #floorButtons button {
      display: block;
      width: 100%;
    }
  </style>
  </head>
  <body>
    <div>
      <form id="location" class="button-radio" onchange="setLocation()">
        <input id="l1" type="radio" name="location" value="0" checked>
        <label for="l1">東京</label>
        <input id="l2" type="radio" name="location" value="1">
        <label for="l2">ニューヨーク</label>
        <input id="l3" type="radio" name="location" value="2">
        <label for="l3">ロンドン</label>
        <input id="l4" type="radio" name="location" value="3">
        <label for="l4">ローマ</label>
      </form>
      <form id="weather" class="button-radio" onchange="setTheme()">
        <input id="w1" type="radio" name="weather" value="0" checked>
        <label for="w1">晴れ</label>
        <input id="w2" type="radio" name="weather" value="1">
        <label for="w2">曇り</label>
        <input id="w3" type="radio" name="weather" value="2">
        <label for="w3">雨</label>
        <input id="w4" type="radio" name="weather" value="3">
        <label for="w4">雪</label>
      </form>
      <form id="time" class="button-radio" onchange="setTheme()">
        <input id="t1" type="radio" name="time" value="0" checked>
        <label for="t1">夜明け</label>
        <input id="t2" type="radio" name="time" value="1">
        <label for="t2">昼</label>
        <input id="t3" type="radio" name="time" value="2">
        <label for="t3">夕焼け</label>
        <input id="t4" type="radio" name="time" value="3">
        <label for="t4">夜</label>
      </form>
      <form id="season" class="button-radio" onchange="setTheme()">
        <input id="s1" type="radio" name="season" value="0" checked>
        <label for="s1">春</label>
        <input id="s2" type="radio" name="season" value="1">
        <label for="s2">夏</label>
        <input id="s3" type="radio" name="season" value="2">
        <label for="s3">秋</label>
        <input id="s4" type="radio" name="season" value="3">
        <label for="s4">冬</label>
      </form>
    </div>
    <div style="position: relative">
      <div id="controlButtons">
        <button type="button" onclick="tiltUp()">↑</button>
        <button type="button" onclick="tiltDown()">↓</button>
        <button type="button" onclick="turnLeft()">←</button>
        <button type="button" onclick="turnRight()">→</button>
      </div>
      <div id="floorButtons">
        <button type="button" id="indoorName"></button>
        <button type="button" onclick="topFloor()">TOP</button>
        <button type="button" onclick="moveUp()">↑</button>
        <button type="button" onclick="moveDown()">↓</button>
        <button type="button" onclick="bottomFloor()">BOTTOM</button>
        <button type="button" onclick="exitIndoors()">EXIT</button>
      </div>
      <div id="map" style="height: 700px">
        <script>
          var locations = [[35.681236, 139.767125],
                          [40.712775, -74.005973],
                          [51.507351, -0.127758],
                          [41.902784, 12.496366]];
          var map = L.Wrld.map("map", "(API Keyを入力)", {
            center: locations[0],
            zoom: 15,
            indoorsEnabled: true,
            displayEntranceMarkers: true
          });
          var weathers = [L.Wrld.themes.weather.Clear,
                          L.Wrld.themes.weather.Overcast,
                          L.Wrld.themes.weather.Rainy,
                          L.Wrld.themes.weather.Snowy];
          var times = [L.Wrld.themes.time.Dawn,
                        L.Wrld.themes.time.Day,
                        L.Wrld.themes.time.Dusk,
                        L.Wrld.themes.time.Night];
          var seasons = [L.Wrld.themes.season.Spring,
                        L.Wrld.themes.season.Summer,
                        L.Wrld.themes.season.Autumn,
                        L.Wrld.themes.season.Winter];
          function setTheme() {
            var seasonIndex = document.getElementById("season").season.value;
            var timeIndex = document.getElementById("time").time.value;
            var weatherIndex = document.getElementById("weather").weather.value;
            map.themes.setTheme(seasons[seasonIndex],times[timeIndex],weathers[weatherIndex]);
          }
          function setLocation() {
            var locationIndex = document.getElementById("location").location.value;
            map.setView(locations[locationIndex]);
          }
          function tiltUp() {
            map.setCameraTiltDegrees(map.getCameraTiltDegrees()+10);
          }
          function tiltDown() {
            map.setCameraTiltDegrees(map.getCameraTiltDegrees()-10);
          }
          function turnLeft() {
            map.setCameraHeadingDegrees(map.getCameraHeadingDegrees()-15);
          }
          function turnRight() {
            map.setCameraHeadingDegrees(map.getCameraHeadingDegrees()+15);
          }
          map.indoors.on("indoormapenter", function(e) {
            document.getElementById("indoorName").innerText = e.indoorMap.getIndoorMapName();
            document.getElementById("floorButtons").style.display = "block";
            document.getElementById("controlButtons").style.display = "none";
          });
          function topFloor() {
            var indoorMap = map.indoors.getActiveIndoorMap();
            if (indoorMap) {
              map.indoors.setFloor(indoorMap.getFloorCount() - 1);
            }
          }
          function moveUp() {
            map.indoors.moveUp();
          }
          function moveDown() {
            map.indoors.moveDown();
          }
          function bottomFloor() {
            map.indoors.setFloor(0);
          }
          function exitIndoors() {
            map.indoors.exit();
            document.getElementById("floorButtons").style.display = "none";
            document.getElementById("controlButtons").style.display = "block";
          }
        </script>
      </div>
    </div>
  </body>
</html>