tanaka's Programming Memo

プログラミングについてのメモ。

OpenLayers Spherical Mercator

Open Street Map(OSM)やGoogle Mapsを利用するのに必要なProjectionについてまとめる。元はこちら
http://docs.openlayers.org/library/spherical_mercator.html

Spherical Mercatorとは

Spherical Mercator(球面メルカトル図法)とは、Google Maps、Microsoft Virtual Earth、Yahoo Maps、そして、その他の商用APIの提供者が利用している投影方法で、OpenLayersのコミュニティーやその他のオープンソースのGISコミュニティーでそのように呼んでいる。

この用語は、この図法を用いる地図の提供者が、地球を楕円というよりも、球体として捉えているメルカトル図法を用いていることを表している。それらの提供者が提供する地図が、平面として計算する際に、どのタイミングで球面を平面に変換しているかを把握することが重要である。

そういった商業プロバイダーが提供する地図上に、正確にデータを重ねるためには、これらの投影を使わなくてはならない。これは主に、商業的なAPIのレイヤーの上に、TMSやWMSに類似するラスタータイルを重ねることに当てはまる。

既存の商業API上で動作させるために、多くのユーザーはGoogle Mapsで使われている投影方法でデータを作成している。代表例が’Open Street Mapで、そのラスタータイル地図は完全に’spherical mercator’ projection(球面メルカトル図法)で投影される。

GISの投影方法は”EPSG”というコードで共通化されている。このコードを管理しているのはEuropean Petroleum Survey Groupである。代表例の一つが”EPSG:4326”で、これは経度と緯度をXとY座標に対応付ける。Spherical Mercatorは公式には”EPSG:3857”で定義されているが、これが普及する前に多くのソフトウェアが”EPSG:900913”として使っている。”EPSG:900913”は非公式なコードだが、OpenLayersでは依然としてこのコードが使われている。”EPSG:4326”では、経度と緯度で座標指定ができる。”EPSG:900913”では、XとY座標をメートルで指定する。

ラスターデータとは?

デジタル地図を扱う時に、形状を図形で表現するデータを「ベクターデータ」、画像やフォントなどの矩形状のセルで表現するデータを「ラスターデータ」と呼ぶ。地図をタイル画像で表現する場合は後者なのでラスターデータとなる。
(「PASCO , http://www.pasco.co.jp/recommend/word/word023/ , ラスターデータとは?」より)

First Map

Spherical Mercator Projectionを使うには、まずはその投影をする地図を作成する。以下はMicrosoft Vertual Earth APIを基底地図にするものである。地図のために以下のHTMLを記述する。

<html>
<head>
  <title>OpenLayers Example</title>
    <script src='http://dev.virtualearth.net/mapcontrol/mapcontrol.ashx?v=6.1'></script>
    <script src="http://openlayers.org/api/OpenLayers.js"></script>
    </head>
    <body>
      <div style="width:100%; height:100%" id="map"></div>
      <script defer='defer' type='text/javascript'>
        // Code goes here
      </script>
    </body>
</html>

次に、Microsoft Virtual Earthレイヤーを基底地図として生成する。

var map = new OpenLayers.Map('map');
var layer = new OpenLayers.Layer.VirtualEarth("Virtual Earth",
 {
     sphericalMercator: true,
     maxExtent: new OpenLayers.Bounds(-20037508.34,-20037508.34,20037508.34,20037508.34)
 });
map.addLayer(layer);
map.zoomToMaxExtent();

これで地図は作れた。次に、setCenter()で地図の中心を指定したいのだが、現状では単位がメートルのため緯度と経度を使うことができない。この地図をドラッグ操作で利用することは可能だが、このままメートルでしか操作できないのでは様々な用途で利用するのは難しい。

この地図は、最大解像度(maxResolution)のデータを保持している。重要なのは、Spherical Mercator mapsは、世界を-180度~180度の経度と、-85.0511~85.0511度の緯度の矩形として扱っているという点である。経度が-90~90度になっていないのは、メルカトル図法では北極や南極を扱おうとすると無限の値が出てしまうためで、これにより、メートルで投影された完全な正方形に置き換えている。maxExtentパラメータは、レイヤーを作成するコンストラクター内で指定されており、この例ではX/Yどちらも-20037508.34m~20037508.34mに座標が当てはめられている。

地図のmaxResolutionの初期値は、上記の矩形範囲を256ピクセルに割り当てている。その結果のmaxResolutionは156543.0399である。これはレイヤーにより内部的に適用されるので、レイヤーオプションに設定する必要はない。

もし、Spherical MercatorのスタンドアロンのWMSやTMSレイヤーを使う場合は、レイヤーのmaxResolutionプロパティーを設定する必要がある。maxExtentの定義の追加例が、ここに示されている。

投影された座標系の挙動

OpenLayersには、あなたのデータをクライアント側で再投影するためのヘルプツールが用意されている。これらにより、経度緯度からSpherical Mercatorの座標系に変換をして、通常の操作ができるようになる。最初にsetCenter()のために座標変換を行い、それから座標データの表示を、基底地図(Base Map)の投影にあてはめるためのdisplayProjectionオプションの使い方を提示する。

点や範囲の再投影

まずは初期値の投影のためのProjectionオブジェクトを作成する。標準的な緯度/経度の投影は”EPSG4326”である。これは、WGS84 datumに基づく緯度経度である。(Google Mapsにデータを重ねたい場合にこれを指定する)。

それから、座標を維持するためのオブジェクトを作成し、それを変換する。

var proj = new OpenLayers.Projection("EPSG:4326");
var point = new OpenLayers.LonLat(-71, 42);
point.transform(proj, map.getProjectionObject());

これでpointがspherical mercator投影に変換されたので、地図のsetCenter()関数に渡すことができる。

map.setCenter(point);

これはsetCenter()を呼ぶ時に直接設定することもできる。

var proj = new OpenLayers.Projection("EPSG:4326");
var point = new OpenLayers.LonLat(-71, 42);
map.setCenter(point.transform(proj, map.getProjectionObject()));

この方法で、緯度経度で地図の中心を指定できるようになる。


同様のやり方で、OpenLayers.Boundsオブジェクトの再投影も出来る。単純に、変換したいBoundsオブジェクトのtransformメソッドを呼べばよい。

var bounds = new OpenLayers.Bounds(-74.047185, 40.679648, -73.907005, 40.882078)
bounds.transform(proj, map.getProjectionObject());

変換のためのオブジェクトは使いまわせるので、一度生成したら新しく生成する必要はない。

Geometriesの再投影

Geometryオブジェクトも、LonLatやBoundsオブジェクトと同様にtransformメソッドが使える。つまり、開発するアプリケーションで作成したGeometryオブジェクトは、それをレイヤーに追加する前にtransformを呼び出して座標変換を行う必要があるということである。また、レイヤーから情報を読み取って利用する前にも座標を戻す変換を行う必要がある。

一度レイヤーに追加したgeometryについては、直接transformを呼び出さず、クローンを生成して、それに対してtransformを呼び出すようにする。

var feature = vector_layer.features[0];
var geometry = feature.geometry.clone();
geometry.transform(layerProj, targetProj);

Vectorデータの再投影

投影された地図を作成した時は、Vectorデータを基底マップ(base map)に再投影することができる。単純にVectorデータの正しいprojectionを設定することで、地図は正しく表示される。

var map = new OpenLayers.Map("map", {
  projection: new OpenLayers.Projection("EPSG:900913")
});
var myBaseLayer = new OpenLayers.Layer.Google("Google",
              {'sphericalMercator': true,
               'maxExtent': new OpenLayers.Bounds(-20037508.34,-20037508.34,20037508.34,20037508.34)
              });
map.addLayer(myBaseLayer);
var myGML = new OpenLayers.Layer.GML("GML", "mygml.gml", {
  projection: new OpenLayers.Projection("EPSG:4326")
});
map.addLayer(myGML);

この設定は、OpenLayersがサポートする様々なVectorフォーマットの地図(WKT,GeoJSON,KMLなど)を読み込むときにも使える。単純にGMLレイヤーのformatオプションに指定する。

var geojson = new OpenLayers.Layer.GML("GeoJSON", "geo.json", {
  projection: new OpenLayers.Projection("EPSG:4326"),
  format: OpenLayers.Format.GeoJSON
});
map.addLayer(geojson);

レイヤーにprojectionオブジェクトを設定して、そのレイヤーにlayer.addFeature()などで手動でFeatureを追加するような場合は、それらのFeatureはレイヤーに追加する前にtransformを実行する必要がある。OpenLayersは、投影処理が何度も実行されないように、ライブラリーに内部的に作られるGeometryの投影を変えるだけである。

投影されたデータをJSON文字列に変換する(Serialize)

OpenLayersVectorデータをJSON文字列化する方法は、Vectorレイヤーからデータを配列化のオブジェクトにして、それをFormatクラスに渡して、データを出力する。しかし、投影済みの地図の場合、投影後のデータが得られる。コンバートする時にデータを再投影するためには、内部と外部用の投影方法をformatクラスに渡してから、データを出力する。

var format = new OpenLayers.Format.GeoJSON({
  'internalProjection': new OpenLayers.Projection("EPSG:900913"),
  'externalProjection': new OpenLayers.Projection("EPSG:4326")
});
var jsonstring = format.write(vector_layer.features);

Control上の表示投影(display Projection)

いくつかのControlは、直接的に、あるいは組み込まれたリンクに従って、ユーザーに地図の座標を表示する。マウスの座標やPermalink Controlやそれに類するControlのいずれも、投影されたSpherical Mercatorレイヤーのような地図の内部のprojectionの座標を利用する。ユーザーの混乱を避けるため、OpenLayersは’display’プロジェクションを1つ設定できる。これらのControlが使われる時は、地図のProjectionから表示のためのProjectionが生成される。

このオプションの利用するには、地図を作成する時に、’projection’と’displayProjection’オプションを指定する。この設定は1度行えばよい。

var map = new OpenLayers.Map("map", {
  projection: new OpenLayers.Projection("EPSG:900913"),
  displayProjection: new OpenLayers.Projection("EPSG:4326")
});
map.addControl(new OpenLayers.Control.Permalink());
map.addControl(new OpenLayers.Control.MousePosition());

この後は、通常通り地図を追加することができる。

Spherical Mercator Raster Imageの作成(この項目はしばらく利用する機会がないので略)

Spherical Mercator Projectionを使う最大の理由は、Google Mapsのようなトップの商業レイヤーに対して正しく画像データを重ねることができることである。ブラウザーでラスターイメージを利用する時に、投影方法を変更できないGISクライアントだった場合、イメージを再投影できない。よって、全てのイメージはGoogle Mapsと同じ投影方法にしておくべきである。

任意のイメージを、Spherical Mercatorで投影された地図タイルに変換するには、MapServerを利用する方法がある。

GeoServerの現行バージョンはEPSG900913が組み込まれているので、追加の投影データを設定する必要はない。単純にWMSのようにGeoServerレイヤーを地図に追加できる。

Spherical MercatorとEPSGエイリアス

OpenLayersではSpherical Mercator ProjectionはEPSG:900913というコードで利用する。OpenStreetMap、Bing、Yahooなどの多くのサービスでもOpenLayersと同じ投影方法が用いられている。しかし、EPSG900913をコードとして利用する必要はない。異なるコードであるEPSG:3857やEPSG:102113などが開発され、現在ではEPSG:900913の正規の登録コードはEPSG:3857となっている。よって、OpenLayersがEPSG:3857やその他のコードが要求された時に、EPSG:900913に変換するエイリアスを設定する方がよい。そのためには、地図にレイヤーを追加する前に、レイヤーのProjectionを上書きする。

// create sphericalmercator layers
var googleLayer = new OpenLayers.Layer.Google("Google", {"sphericalMercator": true});
var osmLayer = new OpenLayers.Layer.OSM("OpenStreetMap");

// override default epsg code
aliasproj = new OpenLayers.Projection("EPSG:3857");
googleLayer.projection = osmLayer.projection = aliasproj;


//add baselayers to map
map.addLayers([googleLayer, osmLayer]);

これにより、WMSレイヤーのようなオーバーレイにおいて、変換にEPSG:4326とEPSG:3857のいずれを使うか判断する際に、EPSG:3857が使われるようにする。

参考URL