😍

OpenLayersでGeoTIFFを直接表示できるようになった!

2021/10/11に公開

OpenLayersがv6.7.0でついにGeoTIFFに対応しました。
めでたい。

https://openlayers.org/en/latest/examples/cog.html
https://openlayers.org/en/latest/examples/cog-math-multisource.html
https://openlayers.org/en/latest/examples/cog-math.html
読み込んで画像が表示されるまで少し時間がかかります。

衛星データは一般的にGeoTIFFという形式で配布されますが、そのデータをブラウザで表示するには、地図タイルと呼ばれる形式でpngを事前に出力しておきそれを呼び出すのが一般的でした。
もしくはアプリを経由して動的にpngを生成する方法もあります。
https://zenn.dev/yonda/articles/f1ed8db1b2d6ae5a9155

それが今回のOpenlayersバージョンアップでpngを事前に作ることなく直にGeoTIFFを呼び出せるようになりました。

GeoTIFFをベースマップ(OSM)と一緒に表示してみます。

<!DOCTYPE HTML>
<html>

<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>sample cog</title>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io@master/en/v6.8.1/css/ol.css" type="text/css">
    <style>
      .map {
        height: 600px;
        width: 100%;
      }
    </style>

    <script src="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io@master/en/v6.8.1/build/ol.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/proj4@2.7.5/dist/proj4-src.min.js"></script>
</head>

<body>

    <div id="map" class="map"></div>

    <script type="text/javascript">
        const source = new ol.source.GeoTIFF({
          sources: [
            {
              url: 'https://s3-us-west-2.amazonaws.com/planet-disaster-data/hurricane-harvey/SkySat_Freeport_s03_20170831T162740Z3.tif',
              nodata: 0,
            },
          ],
        });
        (async ()=>{
          const view = await source.getView();
          const code = view.projection.getCode();
          console.log(code); // EPSG:32615
          // https://www.npmjs.com/package/epsg-index などを利用してgeotiffごとに設定する
          // このサンプルではepsg-indexにcdnがなかったのでdefを決め打ち
          const proj4Def = '+proj=utm +zone=15 +datum=WGS84 +units=m +no_defs'
          proj4.defs(
            code,
            proj4Def
          );
          ol.proj.proj4.register(proj4); // projを設定しておかないとベースマップがずれる

          new ol.Map({
            target: 'map',
            layers: [
              new ol.layer.Tile({
                source: new ol.source.OSM()
              }),
              new ol.layer.WebGLTile({
                source: source,
              }),
            ],
            view: source.getView(), 
	    // わざわざコメントに"Get a promise for view properties based on the source.  Use the result of this function as the `view` option in a map constructor."と書いてある
          });
        })();
    </script>
</body>
</html>

  • nodataを設定しないとデータの無い部分が黒く表示され、ベースマップが見えなくなります。
  • GeoTIFFの投影方法がEPSG:4326やEPSG:3857以外だとデフォルトでproj設定がないため、ベースマップがずれてしまいます。viewからcodeを読み取り、epsg-indexなどを使って設定します。

注意点としては以下です。

  • GeoTIFFのデータの型やサイズによってそれなりに動作が重い。
  • sourceに合わせてviewを設定しないといけない[1]ため、任意のresolutionsやcodeを設定することができない。よってcodeが異なる複数のシーンを同じmap上に表示するようなことはできない様子。

というわけで大変便利なのですが、すべてを解決する銀の弾丸というわけではなく、やりたいことによっては従来の方法が引き続き有効となります。

脚注
  1. 軽くコードを読んだ範囲では、sourceにsource.getView()以外のviewを渡して投影法を切り替える方法が見つかりませんでした(無理やり渡すと動かなくなる)。 ↩︎

Discussion