2014年9月17日水曜日

OpenLayers 3 を使ってみよう(番外:OpenLayers 3 について私なりにまとめてみた)

OpenLayers 3 を使ってみよう(その0:はじめに:地理院地図を表示)に目次がある。
ここでは OpenLayers 3.7.0 を使っている。
 前回の投稿まで「OpenLayers 3 を使ってみよう」と題して,OpenLayers 3 を使って,地図に経路データなどを表示するやり方について述べた。 それが一段落したので,覚えている間に備忘録として,OpenLayers 3 を使う時に気づいた点や注意すべき点について書いておこうと思う。 OpenLayers のバージョンは 3.7.0 を念頭においている。 (いまいちまとまってないなぁ…

 まず OpenLayers 3 関連のリンクを再び書いておこう。
  ・OpenLayers:メインのサイト (2014/9/8 現在は OpenLayers 3 が最新版になってる)
  ・User Documents:OpenLayers 3.7.0 の使い方の文章へのリンク
  ・API Documents:v3.7.0変数に関する説明
  ・Development examples:OpenLayers 3.7.0 の使用例
  ・complete list of releases:v3 全バージョンの更新情報
これらは全て英語である。
 この中でよく見たのが API DocumentsDevelopment examples であった。 examples は何かをしようと思ったら,それらしい例を探してきてそれを真似した。 また,特定のタイプ(クラス)の変数について調べたければAPI Documents を参考にした。 しかし,このAPI Documents が慣れないと何が書いてあるのかチンプンカンプンだった。 その辺りもあり,覚えている間にメモをしておこう,というのが今回の主旨である。


(1) OpenLayers 3 は JavaScript で書かれている

 当たり前と言えばそれまでだが,OpenLayers 3 は JavaScript で書かれている。 ということは JavaScript を多少なりとも使えないと OpenLayers 3 は全く使えない。

 私は JavaScript は web ページを動的に扱うための言語(だと思っている)。 言語としてはオブジェクト指向の言語であり,記述は C 言語に似ている。 オブジェクト指向のため,変数などのタイプの指定は「クラス」と呼ばれ, 特定のクラスを持つ変数(と言ってはいけないのだが…)の実体はオブジェクトと呼ばれるが, 一つのクラス(変数タイプ)から複数の実体(オブジェクト)を作れるので,個々のオブジェクトをインスタンスと呼ぶのかな?と勝手に思っている。 でも,やっぱりオブジェクトとインスタンスの違いがいまいちわからない…。 私は,クラスとかインスタンスとか書かれてもピンとこないので, 私はよくクラスを「変数のタイプ」と書いたりインスタンスを「変数」と書いたりしている。 その辺りはご容赦願いたい。

 JavaScript では,特定のクラスを持つインスタンスを作る時にコンストラクタというのを使う。 つまり,特定のクラス用のコンストラクタで作られたインスタンスは,そのクラスの属性を持つ,ということなのだが,わかりにくいなぁ…。 特定のタイプを持つように作られた変数,という感じでいいと思うのだが…。 ただし,JavaScript では,関数もインスタンスとなれるので,インスタンスがいつも変数とは限らない。 その辺りが余計にややこしい原因かもしれない。

 JavaScript は,特定のクラスの属性を引き継いで作られた子クラス(sub class)を作ることが多い。 その場合,クラス名が xxx.yyyy.zzzz みたいにピリオドでつながって記述されたりする。 また,関数は特定のクラスのみに作用するため,気をつけないといけない。 xxxx という変数に get() という関数を作用させるには「xxxx.get();」と記述される。 子クラスの指定でもピリオドが出てきて,関数の作用でもピリオドが出てくるので,気をつけないとどこからが関数で, どこまでがインスタンス(変数)かがわからなくなってしまうことがある。

 また,web ページ作成に JavaScript を使う時に出てくるものとして DOM がある。 これは web ページの特定の要素(例えば <div> の一個)に何かの処理を施したい時に,その特定の web 要素(element と呼ばれる)を指定する方法である。 基本形として「document.getElementById('xxx')」と書かれる。JavaScript の便利集みたいな jQuery などを使うと別の指定ができるが, JavaScript の基本としては,documento.getElementXXX 調のものが使われる。

 他にもいろいろ注意点はあるが JavaScript に関しては,ネット上にいろいろと情報があるので,検索して調べてみてい欲しい。


(2) OpenLayers 3 の基本は ol.Map クラスのインスタンス

 OpenLayers 3 で地図や記号を描画する基本は「ol.Map クラス」というタイプの変数(インスタンス)が必要である。 つまり,web 上に地図を一個表示したければ,何はさておきこの ol.Map クラスのインスタンス(変数)を作らないといけない。 OpenLayers 3 の使用例の多くの場合,インスタンス名は map と書かれる事が多い。
var map = new ol.Map({
    target : XXX,
    layers : [AAA],
    view : YYY,
    logo : LLL,
    overlays : [PPP],
    renderer : ZZZ,
    controls : [BBB],
    interactions : [CCC]
})
 上記が変数 map の設定である。 ここでオプション( { と } の間)にいろいろな設定をいれて map インスタンスが出来上がる。 このオプションにどんな地図(あるいは図形)を描くかや,どこに描画するか,などを指定する。 オプションを並べるにはコンマで区切らないといけないが,修正する時によくこのコンマを忘れたりするので,注意が必要である。
 以下に map のオプションについて述べよう。

 (a) target : 地図を web のどこに表示するか。DOM と呼ばれるもので指定する。
     <div id="map_canvas"> のように id で指定すると混同によるミスが少なくなる。
     id を直接書いてもいいみたい。

 (b) layers : 表示したい層(layer)を設定する。Tile 形式の地図 layer や Vector 形式の図形などを設定する。
     ベースとなる地図層の上に,他の地図や画像の層を積み重ねて全体を構成している。
     layer については後で詳しく述べよう。
     上記の例で [ と ] で囲んであるのは,複数の layer を設定するのが前提となっているためである。
     (複数を設定する時には,[AAA, BBB, CCC] のようにコンマで区切って複数並べればよい)
     map の定義の後で map.addLayer(xxx); ように追加することもできる。

 (c) view : 地図や図形の見え方を設定している。OpenLayers 2にはなかったものである。
     この view に対して,投影法(projection)を指定したり,ズームや中心座標を設定する。
     特に,投影法には注意が必要である。
     OpenLayers では地図の描画は投影法として "EPSG:3857" と呼ばれる球面メルカトル図法を使っている。
     しかし,普通地図と言えば,WGS84("EPSG:4326")と呼ばれる測地系による通常の経度,緯度による指定が思い浮かぶ。
     そのため,WGS84("EPSG:4326")から "EPSG:3857" への変換や,その逆の変換が必要になることが多い。

 (d) logo : 地図のタイトルとなるものであり,地図上を移動してもいつも同じ場所に表示される。

 (e) overlays : layer と似ている。違いは layer よりも頻繁に変更されるものに使うみたいである。
     (実は layer との違いがいまいちわからない…)
     表示する場所は地理的な座標(緯度,経度)で指定される。

 (f) renderer : 地図の描画方法を設定しているみたいなのだが,いまいち理解できていない。

 (g) controls : ズームのスイッチや,スケール表示などの制御スイッチ(control と呼んでいる)を設定する。
     動かない図形(例えばセンターマーカーや凡例など)の表示に使える。
     表示する場所の指定が pixel や画面のスミなど,画面のどこか,で指定される。
     複数指定が基本なので,たとえ1個の時でも [ と ] ではさんでおく。
     後で map.addControl(yyy); として追加可能である。

 (h) interactions : interaction は,マウスドラッグなど地図に対する動作を設定している。
     もし「マウスドラッグして地図を動かす」という interaction をオフにしておくと,ドラッグで地図上の移動ができなくなる。
     また,特定の layer に図形を描画するという動作を指定することもできる(ol.interaction.Draw など)。
     interaction も後で追加や変更が可能である。

 上記のみならず,Experimental(実験的)なものを含めると,さらに「deviceOptions」,「pixelRatio」,「keyboardEventTarget」があるらしい。


(3) 表示する地図や図形などの大きな図は layer インスタンスで表される

 OpenLayers 3 で描画される地図や図形の中で,地図自体などの大きくてあまり変更されない図は layer という「層」を形成する。 クラス(変数のタイプ)としては,「ol.layer.Layer クラス」のサブクラスである「ol.layer.Vector クラス」や「ol.layer.Tile クラス」などがある。

 Layer クラスでは,
  ・不透明度 (opacity)
  ・visible (表示/非表示の切替え)
  ・style (表示する際のスタイルの(この Layer に共通の)指定,線の幅や色など)
  ・source (表示対象の素)
などを指定できるが,一番大切なのが source である。 この source にどのような地図や図形を表示するかの「ソース(素)」を記述する。 その中には点の座標情報(features)やロゴ(logo),データの出所(attributions),データの URL など,描画対象の素となる情報が含まれる。 このソースは,ol.source.Vector など,layer の種類によってソースも異なるクラスのソースを指定しないといけない。


(4) アイコンやセンターマークなどは ol.Overlay や ol.control のインスタンスで表される

 アイコンや,センターマークなどの小さめの画像は layer ではなく,「ol.Overlay クラス」や 「ol.control クラス」を使って描画される。 これらの違いは,ol.Overlay クラスが,地図上の座標(緯度,経度)で表示する場所が決まるのに対して,ol.control クラスは画面上の決まった位置に表示される。 したがって,ユーザーが地図をスクロールしても,ol.control クラスは動かない。 ズーム用のボタンやスケールなど,地図を変更(コントロール)や状態の表示など,を表示するのに使える。

 いずれの場合も,画像を表示する element(web の要素)が必要となる。 ただし,これらの web 要素は,通常の web 要素のようには画面上に現れない。 いわばこれらの画像を入れるダミーの箱みたいなものである。 (画像が表示されるので,現れるといえば現れるが…) また,どの場所に表示するかは,ol.Overlay クラスの場合はコンストラクターのオプションや専用の関数で地図上の座標を指定するが,ol.control クラスの場合は,スタイルファイル(css)で指定する。
(追記 v3.7.0 の時点のサンプルを見ると,ol.Overlay も css で指定しているような気がする…)


(5) layer を使って描画された図形の詳細は ol.Feature インスタンスに情報が入っている

 図形を描画しようと思えば,ol.layer.Vector や ol.layer.Image などのインスタンスが必要となるが, 点の座標などの情報は,layer の中の source の中に含まれる ol.Feature クラスのインスタンスに入ってる。この ol.Feature クラスのインスタンス(変数)には,点や線の座標などが入った geometry や,ラベルを表示する座標など,描画対象の図形の特徴(情報)が含まれる。また,個々の feature(図形)に style を指定することもできる。ol.Feature クラスのインスタンス内で個別の style の指定がなければ,その feature(図形)に対しては,ol.layer.Vector や ol.FeatureOverlay の様なもっと上(下?)のクラスで指定される style が使われる。

 ol.Feature クラスのインスタンスの中の geometry に具体的な経路点の座標などが含まれている。 geometry は,点なら点の形式,線なら線の形式でデータを保持している。 図にすると以下のようになる。
  座標データ → geometry (ol.geom.LineString etc.) → feature (ol.Feature) → source (ol.source.Vector etc.)
                              → layer (ol.layer.Vector etc.) → map (ol.Map)
場合によっては,以下のような感じになることもある。
  座標データ → geometry (ol.geom.LineString etc.) → feature (ol.Feature) → (ol.FeatureOverlay) → (ol.interaction.Draw) → map (ol.Map)
(OpenLayers v3.7.0 で ol.featureOverlay は廃止された。2015/7/30 追記)

いずれにせよ,feature (ol.Feature クラスのインスタンス) から座標データが取り出せる。 つまり feature が図形の情報(特徴)を持っている情報源と言える。 feature の中の geometry は,'LineString' や 'Polygon' などの図形の種類で指定される。 図形によって保持すべき座標情報は異なる。 この geometry の中身に応じて情報の取り出し方が異なるので,状況に応じて場合分けが必要であろう。


(6) 起動時に KML や GPX 形式のデータなどの処理をしたい時にはイベント処理が必要なこともある

 最近(2016/6)に気づいたのだが,OpenLayers で KML や GPX 形式のデータを読みこませる場合,状況によっては ol.layer.Vector の定義の直後だと,実際にはデータが読み込まれておらず,範囲指定や経路点の座標の取得に失敗することがある。 私の経験では,OpenLayers 関連の JavaScript を web の <body> 要素の前のヘッダー部分に記述した場合に起こっていた。 ヘッダー部分に記述するため,<body> 要素の読み込み時に実行する関数 (init_map() 関数みたいな名前) が必要になる。 その中で,KML フォーマットを持った ol.layer.Vector や ol.Map クラスの変数(インスタンス)を定義するのだが,そこで KML データから座標情報を得ようとしても,「それは定義されていません」というエラーによく遭遇した。 これは KML データの実際の読み込みがまだ行われていないために起こるエラーである。

 そんな場合の対策は,以下のようにイベント処理を使う方法が有効である。 例えば,kml_vector という KML format を持つ ol.layer.Vector クラスのインスタンスがあったとしよう。 その場合は,以下のようなイベント処理を作るとよい。
    kml_vector.once('change',function() {
        kml_extent = kml_vector.getSource().getExtent();
        view.fit(kml_extent, map.getSize());
    }); // kml_vector.once('change',function()
これは,kml_vector の入っている範囲 (extent) を取得して,地図の表示領域を kml_vector の範囲に合わせるものだが,ここにあるように,kml_vector が変化したら一度だけ function() で定義される項目を実行しなさい,というイベント処理を行っている。 このようにすれば,kml_vector に実際に経路データが読み込まれた際(kml_vector に変化があった際)に,データの広がっている範囲を取得して,その範囲が収まるように表示範囲を指定できる。

0 件のコメント: