Cesiumによる3Dマップ

この記事は1年以上前に書かれました。
内容が古くなっている可能性がありますのでご注意下さい。


ブラウザベースの3Dマップを簡単に作れることで人気のあるJavaScriptライブラリCesiumを使って、箱根町の避難所マップを作成します。

避難所のデータは、箱根町のWebサイトの情報を元にCode for Kanagawaがデータセット化してLinkData.orgで公開しているオープンデータ「箱根町の避難所一覧」です。
3Dマップ上に避難所の情報を載せるには、各避難所の緯度経度に加えて標高も必要になります。
そこでまず、QGISとPoint sampling toolプラグインを使って国土地理院の数値標高モデルから標高を取得してデータに追加します。
数値標高モデルとPoint sampling toolプラグインの使い方についてはQGISによるデータ分析(6)QGISによるデータ分析(7)を参考にして下さい。
Point sampling toolを使うと、標高データは属性として保存されます。これをジオメトリのZ座標の値とするために、ogr2ogrコマンドを使用します。

ogr2ogr -f "ESRI Shapefile" -lco "ENCODING=UTF-8" hakone_shelter.shp hakone_shelter_elev.shp -zfield elev

この例では、hakone_shelter_elev.shpファイルのelev属性に入った標高の値をジオメトリに格納したhakone_shelter.shpファイルを作成します。
さらに、シェープファイルを以下のようなGeoJSON形式に変換します。

"features": [
  { "type": "Feature",
    "properties": { "name": "山崎公園前",
                    "住所": "神奈川県足柄下郡箱根町湯本" },
    "geometry": { "type": "Point",
                  "coordinates": [ 139.115824, 35.235114, 67.06 ]  } },
  { "type": "Feature", 
    "properties": { "name": "箱根医院前",
                    "住所": "神奈川県足柄下郡箱根町湯本81" },
    "geometry": { "type": "Point",
                  "coordinates": [ 139.117391, 35.236726, 63.14 ] } },
  :

index.htmlでは、必要なcssファイルとjsファイルを読み込み、地図を表示するdiv要素を用意します。

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>箱根町の避難所3Dマップ</title>
    <link rel="stylesheet" href="../Cesium/Widgets/widgets.css" type="text/css">
    <link rel="stylesheet" href="hakone.css" type="text/css">
    <script type="text/javascript" src="../Cesium/Cesium.js"></script>
    <script type="text/javascript" src="JapanGSITerrainProvider.js"></script>
    <script type="text/javascript" src="hakone.js"></script>
  </head>
  <body>
    <div id="mapdiv"></div>
  </body>
</html>

Cesium.jsはCesiumライブラリの本体です。
JapanGSITerrainProvider.jsは、国土地理院の数値標高タイルからCesiumの地形を生成するためのライブラリです。こちらからダウンロードすることができます。

JavaScriptプログラムは以下の通りです。

var hakone = 'data/hakone_shelter.geojson';

var promise = Cesium.GeoJsonDataSource.load(hakone);

promise.then(function(datasource){
  var viewer = new Cesium.Viewer('mapdiv', {
    animation : false,
    baseLayerPicker: false,
    fullscreenButton: false,
    geocoder: false,
    homeButton: false,
    navigationHelpButton: false,
    sceneModePicker: false,
    scene3DOnly: true,
    timeline: false,
    imageryProvider: new Cesium.OpenStreetMapImageryProvider({
      url: '//cyberjapandata.gsi.go.jp/xyz/relief/'
    }),
    terrainProvider: new Cesium.JapanGSITerrainProvider({
      heightPower: 1.0
    })
  });

  var layers = viewer.scene.imageryLayers;
  var osm = layers.addImageryProvider(
    new Cesium.OpenStreetMapImageryProvider()
  );
  osm.alpha = 0.6;

  viewer.dataSources.add(datasource);
  viewer.zoomTo(datasource);
});

はじめにGeoJSONファイルのパスを定義し、Cesium.GeoJsonDataSource.load()で読み込みます。

var hakone = 'data/hakone_shelter.geojson';

var promise = Cesium.GeoJsonDataSource.load(hakone);

読み込みが完了すると、promise.then()で登録した無名のコールバック関数が呼ばれ、引数として読み込んだデータソースが渡されます。

promise.then(function(datasource){

コールバック関数では、はじめにCesiumのViewerを作成します。

  var viewer = new Cesium.Viewer('mapdiv', {
    animation : false,
    baseLayerPicker: false,
    fullscreenButton: false,
    geocoder: false,
    homeButton: false,
    navigationHelpButton: false,
    sceneModePicker: false,
    scene3DOnly: true,
    timeline: false,
    imageryProvider: new Cesium.OpenStreetMapImageryProvider({
      url: '//cyberjapandata.gsi.go.jp/xyz/relief/'
    }),
    terrainProvider: new Cesium.JapanGSITerrainProvider({
      heightPower: 1.0
    })
  });

最初の引数はViewerを表示するdiv要素です。オプション指定のうち、

    animation : false,
    baseLayerPicker: false,
    geocoder: false,
    homeButton: false,
    navigationHelpButton: false,
    sceneModePicker: false,
    scene3DOnly: true,
    timeline: false,

までは、Cesiumが標準で提供する余計なユーザインタフェースを排除しています。
次の

    imageryProvider: new Cesium.OpenStreetMapImageryProvider({
      url: '//cyberjapandata.gsi.go.jp/xyz/relief/'
    }),

は、地理院タイルの色別標高図(段彩陰影図)をマップに表示するための指定です。
次の

    terrainProvider: new Cesium.JapanGSITerrainProvider({
      heightPower: 1.0
    })

は、国土地理院の数値標高タイルを使用してCesiumの地形を生成するためにterrainProviderを指定しています。heightPowerの値を大きくすると、起伏を強調することができます。
さらに、

  var layers = viewer.scene.imageryLayers;
  var osm = layers.addImageryProvider(
    new Cesium.OpenStreetMapImageryProvider()
  );
  osm.alpha = 0.6;

色別標高図の上に、透過度を指定してOpenStreetMapを重ねます。陰影が透けるため、立体感のある地図になります。
最後に、

  viewer.dataSources.add(datasource);
  viewer.zoomTo(datasource);

GeoJSONのデータソースをCesiumのViewerに追加し、データソースの範囲にズームします。

以上で出来上がりです。ファイル一式をWebサーバにデプロイしてブラウザからアクセスします。

実際に作成したマップはこちらです(表示には時間がかかります)。
マーカーをクリックすると、避難所の名前と住所が表示されます。Ctrlキーを押しながら、マウスを左ボタンをクリックしたまま動かすと、チルトや回転をすることができます。
角度によって、本来は山の後ろに隠れるべきマーカーが山の前に表示されることがあるなど、改善の余地があります。