VUEにおけるOpenlayerマップの統合ステップバイステップガイド


カバーアートDonato Giacola .

導入


こんにちは!チャンスは、少なくともあなたのVue.js , 非常に人気のフロントエンドJavaScriptフレームワーク.それは、よく文書化され、簡単に理解できるアクセスの評判があります.
一方で、あなたが聞いたことがありますかOpenLayers , 周りの最も古いWebマッピングライブラリの1つ.それはOKです、マップは時々驚くほど複雑になることができます、そして、誰もがこの複雑さに入り込むことを切望していませんGoogle Maps API 物事をもっと簡単にする.しかし、念頭に置いて、多くの、より多くのことは、Webマッピングライブラリは、地図上のマーカーを表示する以外に行うことができます!

Note that there are other similar libraries around (see for a quick tour). We will stick with OpenLayers since it offers the most possibilities in the long run.


この記事では両方のVueに飛び込むつもりです.JSとOpenWireの仕事、およびどのようにVueアプリでインタラクティブマップを入れて、それは実際に便利に!この記事の最後に、シンプルなGeospatialオブジェクトエディタを構築します.
  • Geojson形式でオブジェクトを変更し、地図上に表示されるのを見る
  • マップ上のオブジェクトのジオメトリを直接編集する
  • プロジェクトの実行中のインスタンスを見ることができますhere . ソースは利用可能ですhere .

    アプリケーションを設定する


    そこにvueアプリケーションを足場にする方法についてはすでに多くのチュートリアルがあるので、我々はその部分をジャンプするつもりです.使用Vue CLI とにかく非常に簡単です、そしてvue create my-app あなたの方法のほとんどを取得します.
    これからは、メインページを3つのフレームに分割した単純なアプリケーションを仮定しましょう.

    アプリ。Vue


    <template>
      <div id="app">
        <div class="cell cell-map">
          Map
        </div>
        <div class="cell cell-edit">
          Edit
        </div>
        <div class="cell cell-inspect">
          Inspect
        </div>
      </div>
    </template>
    
    <script>
      export default {
        name: 'App'
      }
    </script>
    
    <style>
      html, body {
        height: 100%;
        margin: 0;
      }
    
      #app {
        font-family: Avenir, Helvetica, Arial, sans-serif;
        height: 100%;
        display: grid;
        grid-template-columns: 100vh;
        grid-auto-rows: 1fr;
        grid-gap: 1rem;
        padding: 1rem;
        box-sizing: border-box;
      }
    
      .cell {
        border-radius: 4px;
        background-color: lightgrey;
      }
    
      .cell-map {
        grid-column: 1;
        grid-row-start: 1;
        grid-row-end: 3;
      }
    
      .cell-edit {
        grid-column: 2;
        grid-row: 1;
      }
    
      .cell-inspect {
        grid-column: 2;
        grid-row: 2;
      }
    </style>
    
    つのフレームは、マップ、編集、検査と呼ばれます.使用方法CSS Grid レイアウトを行うには?これが結果です.

    いいね次の手順を実行しますMap コンポーネントEdit 最後にInspect 一つ

    地図をください!


    を作成しましょうMapContainer コンポーネントは、メインのアプリケーションに含まれます.ここではOpenWayerを必要とするので、最初にインストールしてください.
    npm install --save ol
    
    Vueコンポーネントを作成します

    マップコンテナ。Vue


    <template>
      <div ref="map-root"
           style="width: 100%; height: 100%">
      </div>
    </template>
    
    <script>
      import View from 'ol/View'
      import Map from 'ol/Map'
      import TileLayer from 'ol/layer/Tile'
      import OSM from 'ol/source/OSM'
    
      // importing the OpenLayers stylesheet is required for having
      // good looking buttons!
      import 'ol/ol.css'
    
      export default {
        name: 'MapContainer',
        components: {},
        props: {},
        mounted() {
          // this is where we create the OpenLayers map
          new Map({
            // the map will be created using the 'map-root' ref
            target: this.$refs['map-root'],
            layers: [
              // adding a background tiled layer
              new TileLayer({
                source: new OSM() // tiles are served by OpenStreetMap
              }),
            ],
    
            // the map view will initially show the whole world
            view: new View({
              zoom: 0,
              center: [0, 0],
              constrainResolution: true
            }),
          })
        },
      }
    </script>
    
    これはおもしろいです.を参照してください、OpenLayerでマップを作成する1行で行われていない:あなたはそれを1つまたは複数のLayer オブジェクトとそれを割り当てるView . あなたは気づいたconstrainResolution: true マップビューのオプション?これは、単純にマップのズームが正しいレベルでは、OSMのタイルを鮮明に見えるようにスナップすることを確認するAPI doc を参照).
    また、マップルートへのリファレンスを ref Vue directive 以下のように:
    <div ref="map-root"
    
    The Map constructor CSSセレクターまたは実際のHTML要素を取ることができます.したがって、this.$refs['map-root'] .
    結果は次のようになります.

    を、私たちは地図を持って、それは対話的だが、それに多くはない.もちろん、それは全世界を含みます、しかし、それから離れてください...

    マップコンテナ。Vue


    <script>
      // ...
    
      // we’ll need these additional imports
      import VectorLayer from 'ol/layer/Vector'
      import VectorSource from 'ol/source/Vector'
      import GeoJSON from 'ol/format/GeoJSON'
    
      // this is a simple triangle over the atlantic ocean
      const data = {
        type: 'Feature',
        properties: {},
        geometry: {
          type: 'Polygon',
          coordinates: [
            [
              [
                -27.0703125,
                43.58039085560784
              ],
              [
                -28.125,
                23.563987128451217
              ],
              [
                -10.8984375,
                32.84267363195431
              ],
              [
                -27.0703125,
                43.58039085560784
              ]
            ]
          ]
        }
      };
    
      export default {
        // ...
    
        mounted() {
          // a feature (geospatial object) is created from the GeoJSON
          const feature = new GeoJSON().readFeature(data, {
            // this is required since GeoJSON uses latitude/longitude,
            // but the map is rendered using “Web Mercator”
            featureProjection: 'EPSG:3857'
          });
    
          // a new vector layer is created with the feature
          const vectorLayer = new VectorLayer({
            source: new VectorSource({
              features: [feature],
            }),
          })
    
          new Map({
            // ...
            layers: [
              new TileLayer({
                source: new OSM(),
              }),
              // the vector layer is added above the tiled OSM layer
              vectorLayer
            ],
            // ...
          })
        }
      }
    </script>
    
    簡単!

    オブジェクトの変更


    我々が現在示しているオブジェクトはGeoJSON . 良いことは、この形式は簡単に手で編集されます!を行うための新しいコンポーネントを作成しましょう.

    エディット.Vue


    <template>
      <textarea v-model="geojsonEdit"></textarea>
    </template>
    
    <script>
      export default {
        name: 'Edit',
        props: {
          geojson: Object
        },
        computed: {
          geojsonEdit: {
            set(value) {
              // when the text is modified, a `change` event is emitted
              // note: we’re emitting an object from a string
              this.$emit('change', JSON.parse(value))
            },
            get() {
              // the text content is taken from the `geojson` prop
              // note: we’re getting a string from an object
              return JSON.stringify(this.geojson, null, ' ')
            }
          }
        }
      }
    </script>
    
    <style>
      textarea {
        width: 100%;
        height: 100%;
        resize: none;
      }
    </style>
    
    このコンポーネントはgeojson 入力としてpropを指定します.MapContainer コンポーネント.
    を、いくつかのVueロジックをすぐに見てみましょう.
    The v-model="geojsonEdit" コンポーネントテンプレートの属性はVue directive commonly used for form inputs . これは、geojsonEdit ローカルプロパティは、ユーザーが任意の入力を意味するプロパティに保存され、プロパティへの変更は、画面上に反映されます.
    このコンポーネントを有効にするには、Geojsonテキストが変更されるたびに親コンポーネントに通知します.そのためには、vueで実行されるイベントを送ります.
    this.$emit('change', JSON.parse(value))
    
    このようなイベントは、 v-on directive :
    v-on:change="doSomethingWithData($event)"
    
    注意v-on は、カスタム定義されたイベントと標準のHTML 5イベントの両方に便利です.この動画を見るVue events guide 詳細は
    今、どのように我々はそれを発するときに知っているつもりですかchange イベント?即時の応答はウォッチャーをセットアップすることですgeojsonEdit イベントが変更されるたびにトリガーします.
    上のコードでは、別の解決策がありましたcomputed property . 計算されたプロパティを使用すると、ウォッチャーに頼らずに2つの異なる動作(読み書き)を指定することができます.このようにして、我々は単にset() の入力データから読み込むget() メソッド.追加ボーナスとして、私たちは長期的には常に良いことであるコンポーネント上の任意の内部状態を維持していません.
    すぐにトラックに戻りましょう.他のコンポーネントはまだ現在のオブジェクトの更新を扱うことができませんMapContainer コンポーネント.
    両方とも変更しますMapContainer 同様にApp さまざまなデータを扱うコンポーネント

    マップコンテナ。Vue


    <script>
      // ...
    
      export default {
        name: 'MapContainer',
        components: {},
        props: {
          // the GeoJSON data is now taken as an input
          geojson: Object
        },
        data: () => ({
          // store OL objects on the component instance
          olMap: null,
          vectorLayer: null
        }),
        mounted() {
          this.vectorLayer = new VectorLayer({
            source: new VectorSource({
              features: [], // the vector layer is now created empty
            }),
          })
    
          this.olMap = new Map({
            // ..
          })
    
          // we’re calling `updateSource` to show the object initially
          this.updateSource(this.geojson)
        },
        watch: {
          geojson(value) {
            // call `updateSource` whenever the input changes as well
            this.updateSource(value)
          }
        },
        methods: {
          // this will parse the input data and add it to the map
          updateSource(geojson) {
            const view = this.olMap.getView()
            const source = this.vectorLayer.getSource()
    
            const features = new GeoJSON({
              featureProjection: 'EPSG:3857',
            }).readFeatures(geojson)
    
            source.clear();
            source.addFeatures(features);
    
            // this zooms the view on the created object
            view.fit(source.getExtent())
          }
        }
      }
    </script>
    

    アプリ。Vue


    <template>
      <div id="app">
        <div class="cell cell-map">
          <!-- the GeoJSON data is now given as input -->
          <MapContainer :geojson="geojson"></MapContainer>
        </div>
        <div class="cell cell-edit">
          <!-- update the app state on `change` events -->
          <Edit :geojson="geojson" v-on:change="geojson = $event">
          </Edit>
        </div>
        <div class="cell cell-inspect">
         Inspect
        </div>
      </div>
    </template>
    
    <script>
      import MapContainer from './components/MapContainer'
      import Edit from './components/Edit'
      export default {
        name: 'App',
        components: {
          Edit,
          MapContainer
        },
        data: () => ({
          // this is the initial GeoJSON data
          geojson: {
            type: 'Feature',
            properties: {},
            geometry: {
              type: 'Polygon',
              coordinates: [
                [
                  [
                    -27.0703125,
                    43.58039085560784
                  ],
                  [
                    -28.125,
                    23.563987128451217
                  ],
                  [
                    -10.8984375,
                    32.84267363195431
                  ],
                  [
                    -27.0703125,
                    43.58039085560784
                  ]
                ]
              ]
            }
          }
        })
      }
    </script>
    
    への変更App コンポーネントはかなり簡単だった.我々は本当にこのレベルではなく、データを格納しているMapContainer つの子要素への入力として渡します.
    例えばMapContainer , この修正はもう少しではなく、多くのものであったgeojson 入力プロップ、私たちは、オープン層マップがVueコンポーネント状態と同期していることを確認しています.
    結果は次のようになります.

    追加ボーナスとして、地図表示を自動的に表示されるオブジェクトにズーム!しかし、最良の部分は、右側にGeojsonの定義を変更する場合オブジェクトはリアルタイムで変更されます!かなり良い右?

    検査の時期


    空間的特徴は、しばしば一連のいわゆる属性、本質的に重要な値対を持ちます.Geojsonでは、いくつかのproperties 機能のフィールド.これらは時々画面上に表示されます(例えば、ラベル)しかし、それらのほとんどは“隠されている”とのみツールヒントまたは同様に表示されます.
    さらに少し練習をプッシュするには、新しいを作成しましょうInspect ポインターの下にある機能のすべての属性が表示されるコンポーネント.
    我々は、それを作ることから始めますMapContainer コンポーネントselect ポインタの下にある機能が見つかったときはいつでもイベントを実行します.

    マップコンテナ。Vue


    <script>
      // ...
    
      export default {
        // ...
        mounted() {
          // ...
    
          this.olMap = new Map({
            // ...
          })
    
          // this binds a callback to the `pointermove` event
          this.olMap.on('pointermove', (event) => {
            // will return the first feature under the pointer
            const hovered = this.olMap.forEachFeatureAtPixel(
              event.pixel,
              (feature) => feature
            )
    
            // emit a `select` event, either with a feature or without
            this.$emit('select', hovered)
          })
    
          this.updateSource(this.geojson)
        },
        // ...
      }
    </script>
    
    再び、我々はその後、使用してキャプチャされるカスタムイベントを放出しているv-on:select="..." 親コンポーネント.
    また、我々は forEachFeatureAtPixel カーソルの下の機能を見つけるための方法.そして、それはある画素のいかなるレイヤーの全ての機能も探して、各々のための与えられたコールバックを適用する.この場合、1つだけの機能が必要なので、コールバック以降(feature) => feature 真の値を返します.
    その後、我々はInspect コンポーネントは、すべての機能の属性を表示します.

    検査する。Vue


    <template>
      <ul>
        <!-- loop on the feature’s attributes -->
        <li :key="prop" v-for="prop in props">
          <b>{{prop}}:</b> {{feature.get(prop)}}
        </li>
      </ul>
    </template>
    
    <script>
      import Feature from 'ol/Feature'
      export default {
        name: 'Inspect',
        props: {
          // the only input is an OpenLayers Feature instance
          feature: Feature
        },
        computed: {
          // this will return an empty array if no feature available
          props() {
            return this.feature
              ? this.feature
                  .getKeys()
                  .filter(key => key !== this.feature.getGeometryName())
              : []
          }
        }
      }
    </script>
    
    <style>
      ul {
        list-style: none;
      }
    </style>
    
    行に注意するthis.feature.getKeys().filter(key => key !== this.feature.getGeometryName()) ? これはすべての機能の属性のキーを返します.これはジオメトリを保持するものを除いて(これは物事が読めなくなるので、試してみたいです).Openレイヤを見てくださいFeature API doc を参照してください.
    最後に、一緒にすべてを接着しようApp コンポーネント

    アプリ。Vue


    <template>
      <div id="app">
        <div class="cell cell-map">
          <!-- update app state when a feature is selected -->
          <MapContainer :geojson="geojson"
                        v-on:select="selected = $event">
          </MapContainer>
        </div>
        <div class="cell cell-edit">
          <Edit :geojson="geojson" v-on:change="geojson = $event">
          </Edit>
        </div>
        <div class="cell cell-inspect">
          <!-- give the selected feature as input -->
          <Inspect :feature="selected"></Inspect>
        </div>
      </div>
    </template>
    
    <script>
      import MapContainer from './components/MapContainer'
      import Edit from './components/Edit'
      import Inspect from './components/Inspect'
    
      export default {
        name: 'App',
        components: {
          Inspect,
          Edit,
          MapContainer
        },
    
        data: () => ({
          // the selected feature is part of the app state
          selected: undefined,
          geojson: {
            // ...
          }
        })
      }
    </script>
    
    まあ、それはかなりだ.空間オブジェクト上でカーソルをホブ化すると、そのオブジェクトからプロパティのリストが表示されます.

    GeoJSONオブジェクトの定義に属性を追加したり、検査フレームを表示する方法を参照してください.
    あなたが望むならば、あなたは試みることもできますthis one (簡略化された形を持つすべての国を含む).結局のところ、我々はgeojsonデータが含まれているかどうかを仮定していない!

    より良いパフォーマンスのために作られる最適化があることに注意してくださいMapContainer コンポーネントは、必要に応じてイベントを発行するpointermove イベント.

    結論


    このチュートリアルを通して我々は両方の編集と空間オブジェクトを検査することができますシンプルなアプリケーションを作成するために管理しました.これは、私たちが3つのコンポーネントを必要としたと考えて、それほど厳しくはありませんでした、彼らの各々は限られた責任で、そして、アプリケーション状態を保存するための1つのルート構成要素を必要としました.
    うまくいけば、これはいくつかのコアVueの両方のいくつかの光を流すのを助けた.JSのコンセプトだけでなく、Openレイヤのもの.
    最終的なソースコードを見ることができますhere , このチュートリアルではいくつかの改善が含まれます.
    私はあなたがこのチュートリアルを楽しんだことを願って、私たちがこの基礎に展開することができる他の多くのことがあります、あなたが何か考えを持っているかどうか知らせてください!読書ありがとう!