Vue에서 OpenLayers 매핑을 통합합니다.js, 단계별 안내

표지예술 Donato Giacola.

소개


안녕하세요.이것은 매우 유행하는 전단 자바스크립트 프레임워크입니다.그것은 방문하기 쉽고 기록이 양호하며 이해하기 쉽기로 유명하다.
다른 한편, 당신은 Vue.js을 들어 본 적이 없을 수도 있습니다. 이것은 가장 오래된 웹 맵 라이브러리 중의 하나입니다.괜찮아, 지도는 때때로 의외로 복잡할 때가 있어. OpenLayers과 같은 서비스가 일을 더욱 간단하게 할 때, 모든 사람들이 이런 복잡함을 깊이 연구하고 싶어 하는 것은 아니야.하지만 지도에 표시를 표시하는 것 외에 웹 지도 라이브러리는 당신을 위해 많은 일을 할 수 있다는 것을 기억하세요!

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와 OpenLayers는 작업할 수 있고, Vue 응용 프로그램에 상호작용 지도를 설치하여 실제적으로 유용하게 사용할 수 있습니다!본고의 마지막 부분에서 우리는 간단한 지리적 공간 대상 편집기를 구축할 것이다. 이것은 우리가 다음과 같이 할 수 있도록 할 것이다.
  • 은 GeoJSON 형식으로 대상을 수정하여 지도
  • 에 표시합니다
  • 지도에서 직접 편집 대상 기하학적
  • 프로젝트 Google Maps API의 실행 실례를 볼 수 있습니다.자료 출처는 here이다.

    여기 있다 응용 프로그램 설정


    Vue 응용 프로그램을 구축하는 방법에 대한 강좌가 많기 때문에 이 부분을 건너뛰겠습니다.어쨌든 을 사용하는 것은 매우 간단합니다. vue create my-app에 전화하면 대부분의 도움을 제공할 것입니다.
    지금부터 우리는 간단한 응용 프로그램이 있다고 가정한다. 홈페이지는 세 가지 프레임워크로 나뉜다.

    Vue CLI 응용 프로그램.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>
    
    이 세 프레임은 Map, Edit 및 Inspect로 명명됩니다.우리가 어떻게 을 사용하여 레이아웃을 완성했는지 주의하시겠습니까?결과는 다음과 같습니다.
    CSS Grid
    아름답다다음은 Map 구성 요소를 만들고 Edit 구성 요소를 만들고 마지막으로 Inspect 구성 요소를 만듭니다.

    지도 좀 줘!

    MapContainer 구성 요소를 만들고 주 응용 프로그램에 포함시킵니다.OpenLayers가 필요한 곳이므로 먼저 설치해야 합니다.
    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>
    
    이것은 갈수록 재미있어진다.봐라, OpenLayers로 지도를 만드는 것은 한 줄이 아니다. Layer 대상을 하나 이상 주고 View을 분배해야 한다.지도 보기의 constrainResolution: true 옵션을 보셨습니까?이것은 단지 지도의 크기를 정확하게 포착하여 OSM 블록을 뚜렷하게 보이기 위해서일 뿐이다(자세한 내용은 참조).
    또한 다음과 같이 API doc을 사용하여 맵 루트에 대한 참조를 유지합니다.
    <div ref="map-root"
    
    ref Vue directive은 CSS 선택기나 실제 HTML 요소를 사용할 수 있기 때문에 this.$refs['map-root']을 사용하여 맵 루트 요소를 가져오기만 하면 됩니다.
    결과는 다음과 같습니다.
    Map constructor
    알겠습니다. 우리는 지도를 한 장 가지고 있습니다. 그것은 상호작용적이지만 더 많은 내용은 없습니다.물론 그것은 전 세계를 포함하고 있지만, 그 외에... 우리는 그 위에 물체를 하나 더 넣는 것이 어떻습니까?

    컨테이너를 매핑합니다.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 아이템을 입력으로 합니다. 이 아이템은 MapContainer 구성 요소의 아이템과 같습니다.
    자, 이제 Vue 논리를 빠르게 알아보겠습니다.
    어셈블리 템플릿의 v-model="geojsonEdit" 속성은 입니다.이것은 geojsonEdit 로컬 속성과 양방향 데이터 연결을 정의합니다. 이것은 사용자의 모든 입력이 이 속성에 저장되고 이 속성에 대한 변경 사항이 화면에 반영된다는 것을 의미합니다.
    이 구성 요소의 역할을 수행하려면 GeoJSON 텍스트를 수정할 때 상위 구성 요소에 알립니다.이를 위해 Vue에서 완료된 이벤트를 다음과 같이 스케줄링합니다.
    this.$emit('change', JSON.parse(value))
    
    Vue directive commonly used for form inputs을 사용하여 상위 어셈블리에서 이러한 이벤트를 캡처할 수 있습니다.
    v-on:change="doSomethingWithData($event)"
    
    v-on은 사용자 정의 이벤트와 표준 HTML5 이벤트에 유용합니다.자세한 내용은 v-on directive 을 참조하십시오.
    지금 우리는 언제 change 사건이 발생했는지 어떻게 알 수 있습니까?즉각적인 반응은 geojsonEdit에 관찰자를 설정하고 사건이 변화할 때 사건을 촉발하는 것이다.
    위의 코드에서 우리는 Vue events guide을 정의하는 또 다른 해결 방안을 사용했다.이런 상황에서 계산 속성을 사용하는 것은 매우 유용하다. 왜냐하면 우리는 관찰자에게 도움을 청할 필요가 없이 두 가지 다른 행위 (읽기와 쓰기) 를 지정할 수 있기 때문이다.이렇게 하면 우리는 set() 방법에서 이벤트를 보내고 get() 방법의 입력 데이터에서 읽기만 하면 된다.추가적인 장점으로서 우리는 구성 요소에 있어서 어떠한 내부 상태도 유지하지 않았다. 장기적으로 보면 이것은 항상 좋은 일이다.
    우리 이제 정상 궤도로 돌아가자.다른 구성 요소는 현재 MapContainer 구성 요소에 하드코딩되어 있기 때문에 공간 대상의 업데이트를 처리할 수 없습니다.MapContainerApp 구성 요소를 수정하여 서로 다른 데이터를 처리합니다.

    속성 계산 컨테이너를 매핑합니다.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의 입력 도구를 보면서 OpenLayers 맵과 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을 리셋해서truthy 값을 되돌려주기 때문입니다.)
    그리고 (feature) => feature 구성 요소를 계속 만들 수 있습니다. 이 구성 요소는 모든 기능의 속성을 간단하게 표시합니다.

    검사하다 검사하다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())을 알아차리면 되나요?이것은 모든 피쳐 속성의 키를 되돌려줍니다. 그러나 저장된 형상의 속성은 제외됩니다. 이것은 읽을 수 없기 때문에 시도해 보십시오.자세한 내용은 OpenLayers 을 참조하십시오.
    마지막으로 모든 내용을 App 구성 요소에 붙입니다.

    기능 API 문서 응용 프로그램.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 객체 정의에 속성을 추가하고 inspect 프레임워크에 속성을 표시하는 방법을 볼 수 있습니다!
    원하는 경우 GeoJSON 파일(예: )을 복사하여 붙여넣을 수도 있습니다.어쨌든 우리는 GeoJSON 데이터에 포함된 내용에 대해 어떠한 가설도 하지 않았다!
    this one
    더 좋은 성능을 얻기 위해서는 특히 MapContainer 구성 요소가 필요할 때만 이벤트를 보내는지 확인하십시오. 즉, pointermove 이벤트마다 같은 기능을 보내지 않습니다.

    결론


    이 강좌에서, 우리는 공간 대상을 편집하고 검사할 수 있는 간단한 응용 프로그램을 만들었다.세 개의 구성 요소 (구성 요소마다 책임이 제한되어 있음) 와 응용 프로그램 상태를 저장하는 루트 구성 요소만 필요하다는 것을 감안하면 어렵지 않다.
    일부 핵심 Vue에서 정보를 제공하는 데 도움이 되기를 바랍니다.js 개념, 그리고 OpenLayers 개념.
    이 강좌에 대한 개선을 포함하는 최종 소스 코드 을 볼 수 있습니다.
    나는 네가 이 강좌를 좋아하길 바란다. 우리는 이 기초 위에서 많은 다른 일을 해서 확장할 수 있다. 만약 네가 어떤 생각이 있다면 나에게 알려줘!읽어주셔서 감사합니다!

    좋은 웹페이지 즐겨찾기