2016年12月15日木曜日

tile splash

はじめに

ウェブ上で地図の配信を行う手法は、タイルに分割された地図画像をajaxにより書き換える方法と、ベクトルデータを全部・あるいは部分的に読み込む手法が主でした。近年、ベクトルデータをタイル分割し、ajax により書き換える方法が登場しました。それが VectorTileです。
 このVectorTileの配信を行うオープンソースのモジュールのうちのひとつがTileSplashです。TileSplash は node.js という javascript によるWebサーバのエンジン用ライブラリとして書かれています。プロジェクトは、GitHubで下記にて公開されています。

 https://github.com/faradayio/tilesplash

実行に必要なもの

TileSplash を実行させるには、以下のモジュールが必要です。 node.js npm PostgreSQL server PostGIS インストール方法については、割愛します。

TileSplash 環境の構築

コマンド・プロンプトにて、npm を利用して SplashTile をインストールします。
$ npm install tilesplash
尚、-g オプションを使用すると、グローバル環境にモジュールがインストールされますが、ここでは、コマンドを実行したディレクトリ上にモジュールがインストールされるようにします。
 PostgreSQL Server に図形テーブルを構築します。注意点として、図形の座標系は、EPSG4326 でないと動作しません。また、ST_Transform(geom,4326) と言った書き方をしてもエラーになるので注意してください。既存のgeometryフィールドが異なるSRIDで作成している場合は、以下のようにフィールドを追加すると良いでしょう。
 alter table target_table add column geom2 geometry(‘MultiPolygon’, 4326);
    update table target_table set geom2 = ST_Transform(geom,4326); 
エディタで、sptile.js を作成し編集します。
var Tilesplash = require('tilesplash');
// username に postgresql へ接続するユーザ名を指定
// localhost にpostgresql server ホスト名を指定
// dbname に postgresql のデータベース名を指定
var app = new Tilesplash('postgres://username@localhost/dbname);

// layer_name はレイヤ名で、ajax によるリクエストの一部として扱われる
// table_name は図形テーブル名
// geom は図形フィールド名
// ここではシンプリファイをかけていますが、tile パラメータの x 値により
// シンプリファイをかけるパラメータを変更する必要があると思われます
app.layer('layer_name', function(tile, render){
  render('SELECT ST_AsGeoJSON(ST_Simplify(geom,0.005)) as the_geom_geojson’
     ‘FROM target_table WHERE ST_Intersects(St_Simplify(geom,0.005), !bbox_4326!)');
});
// ポート3000にて、Webサーバを起動します
app.server.listen(3000);
このまま稼働させると、レスポンスが javascript になるため、違うサイト間でのリクエストで CORS(Cross Origin Resource Sharing)の制約にひっかかって動作しない可能性があります。具体的には「No 'Access-Control-Allow-Origin' header is present」といったエラーがブラウザにより返されます。これを解消するには、以下のモジュールを利用します。
http://www.slideshare.net/kitfactory/web-api-34814937
$npm install corser

でモジュールをインストールし、
node_mojules/tilesplash/lib/index.js
ファイルに以下の修正を施します。
  this.server.use(pgMiddleware(dbOptions));

  var corser = require("corser");      // 以下の2行を CORS 対策として挿入
  this.server.use(corser.create());

  this.cacheOptions = cacheOptions || {};
  this._cache = new Caching(cacheType || 'memory', cacheOptions);
これは、レスポンス・ヘッダに
  Access-Control-Allow-Origin: *

を付加する事になります。

クライアント実装例

ローカルホストの node サーバに対して、アクセスする例です。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>GSI Tiles on OpenLayers 3</title>
<link rel="stylesheet" href="http://openlayers.org/en/v3.10.1/css/ol.css" type="text/css">
<link rel="stylesheet" href="http://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css">
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="http://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/js/bootstrap.min.js"></script>
<script src="http://openlayers.org/en/v3.10.1/build/ol.js" type="text/javascript"></script>


<style>
  body {padding: 0; margin: 0}
  html, body, #map {height: 100%; width: 100%;}
</style>
</head>
<body>
  <div id="page">
    <div id="head"></div>
    <div id="main">  
      <div id="map" style="float:right"; width:640px; margin:0; padding:0; ></div>
      <div id="left" style="float:left; width:300px; margin:0; padding:0;"></div>
    </div>
  </div>       
<script>
var map = new ol.Map({
  target: "map",
  renderer: ['canvas', 'dom'],
  layers: [
    // 地理院タイル・レイヤ
    new ol.layer.Tile({
      source: new ol.source.XYZ({
        attributions: [
          new ol.Attribution({
            html: "<a href='http://maps.gsi.go.jp/development/ichiran.html' target='_blank'>地理院タイル</a>"
          })
        ],
        url: "http://cyberjapandata.gsi.go.jp/xyz/std/{z}/{x}/{y}.png",
        projection: "EPSG:3857"
      })
    }),
    
    // topoJson レイヤ (splashtile から引く)
    new ol.layer.Vector({
      source: new ol.source.TileVector({
        format: new ol.format.TopoJSON(),
        projection: 'EPSG:3857',
        tileGrid: new ol.tilegrid.createXYZ({
          maxZoom: 19
        }),
        url: 'http://localhost:3000/' +
          'test_layer/{z}/{x}/{y}.topojson'
        }),
       style: new ol.style.Style({
         fill: new ol.style.Fill({ color: '#9db9e8' }),
         stroke: new ol.style.Stroke({ color: '#FF0000' })
       })
    })
  ],
  controls: ol.control.defaults({
    attributionOptions: ({
      collapsible: false
    })
  }),
  view: new ol.View({
    projection: "EPSG:3857",
    center: ol.proj.transform([138.7313889, 35.3622222], "EPSG:4326", "EPSG:3857"),
    maxZoom: 18,
    zoom: 5
    })
});
</script>
</body>
</html>
実行すると、こんな感じになります。 file:/// 上から実行すると、CORSにひっかかります。chrome ではなく、safari で実行しました。 CORS対策の部分は、もうちょい真面目にやらないとダメかもしれません。