2020년 1월 25일 토요일

Vue와 Leaflet 기반 인터렉티브 맵 개발

이 글은 Vue기반 Leaflet 패키지를 이용한 인터렉티브 맵 개발을 간단히 소개한다.

맵을 웹사이트에 표현하는 방법은 다양한다. Google Maps, Mapbox, Leaflet 등을 사용하면 클릭 몇번으로 공간정보 맵 기반 서비스를 쉽게 개발할 수 있다.

이 글은 Leaflet 을 이용해 간단한 오픈 스트리트 맵(OSM. Open street map) 기반 인터렉티브 지도 개발 방법과 객체 함수 사용 방법 등을 간략히 보여준다.

Leaflet 개요
Leaflet은 손쉽게 상호동작을 지원하는 지도 개발을 지원하는 도구로 널리 사용된다. Leaflet을 이용하면, google map, open street map, bing map 등을 손쉽게 사용할 수 있고, 맵에 다양한 형식의 layer를 추가하거나 그래픽 정보를 만들 수 있다.
Leaflet 소개 영상

이 글은 Node.js 기반 Vue를 사용하는 Vue2Leaflet 을 이용한다.
이 패키지는 npm으로 설치할 수 있다.
npm install vue2-leaflet leaflet --save

yarn을 사용하므로 다음과 같이 설치한다(참고).
npm install --global yarn

이후 다음과 같은 절차로 소스 다운로드 받고, 라이브러리 심벌을 링크한 후 설치한다.
# clone the repository
git clone https://github.com/KoRiGaN/Vue2Leaflet.git
cd Vue2Leaflet
# install dependencies and build vue2-leaflet
npm install
# create a symlink for vue2-leaflet
yarn link
cd examples
yarn install
# create a symbolic link for vue2-leaflet in node_modules/
yarn link vue2-leaflet
# serve with hot reload at localhost:8080
yarn run serve

실행 결과를 다음과 같이 확인할 수 있다(심벌 에러가 날 경우 부록 참고).
실행 결과

소스 분석
소스 분석을 위해 Examples/src/componets 폴더 안의 GeometryTest.vue 예제를 확인해 본다. 이 예제는 leaflet l-map 객체와 l-circle, l-rectangle 객체를 이용해 맵과 그래픽을 표시한다. 그래픽 데이터는 data() 함수에서 종류별로 정의한다. 또한, Change rectangle sytle 이란 버튼을 추가하고, 이벤트 발생 시 선 두께를 변경한다.
<template>
  <div>
    <div style="height: 10%; overflow: auto;">
      <h3>Geometry</h3>
      <button @click="clickBtn">
        Change rectange style
      </button>
    </div>
    <l-map
      :zoom="zoom"
      :center="center"
      style="height: 90%"
    >
      <l-tile-layer
        :url="url"
        :attribution="attribution"
      />
      <l-circle
        :lat-lng="circle.center"
        :radius="circle.radius"
      />
      <l-rectangle
        :bounds="rectangle.bounds"
        :l-style="rectangle.style"
      />
      <l-polygon
        :lat-lngs="polygon.latlngs"
        :color="polygon.color"
      />
      <l-polyline
        :lat-lngs="polyline.latlngs"
        :color="polyline.color"
      />
    </l-map>
  </div>
</template>

<script>
import { latLng } from "leaflet";
import {
  LMap,
  LTileLayer,
  LCircle,
  LRectangle,
  LPolygon,
  LPolyline
} from "vue2-leaflet";  // library

export default {
  name: "GeometryTest",
  components: {
    LMap,
    LTileLayer,
    LCircle,
    LRectangle,
    LPolygon,
    LPolyline
  },
  data() {
    return {
      zoom: 11,
      center: [47.31322, -1.319482],
      circle: {
        center: latLng(47.41322, -1.0482),
        radius: 4500
      }, // Draw circle
      rectangle: {
        bounds: [[47.341456, -1.397133], [47.303901, -1.243813]],
        style: { color: "red", weight: 5 }
      }, // Draw rectangle
      polygon: {
        latlngs: [
          [47.2263299, -1.6222],
          [47.21024000000001, -1.6270065],
          [47.1969447, -1.6136169],
          [47.18527929999999, -1.6143036],
          [47.1794457, -1.6098404],
          [47.1775788, -1.5985107],
          [47.1676598, -1.5753365],
          [47.1593731, -1.5521622],
          [47.1593731, -1.5319061],
          [47.1722111, -1.5143967],
          [47.1960115, -1.4841843],
          [47.2095404, -1.4848709],
          [47.2291277, -1.4683914],
          [47.2533687, -1.5116501],
          [47.2577961, -1.5531921],
          [47.26828069, -1.5621185],
          [47.2657179, -1.589241],
          [47.2589612, -1.6204834],
          [47.237287, -1.6266632],
          [47.2263299, -1.6222]
        ],
        color: "#ff00ff"
      }, // Draw polygon
      polyline: {
        latlngs: [
          [47.334852, -1.509485],
          [47.342596, -1.328731],
          [47.241487, -1.190568],
          [47.234787, -1.358337]
        ],
        color: "green"
      }, // Draw polyline
      url: "http://{s}.tile.osm.org/{z}/{x}/{y}.png",
      attribution:
        '&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
    };
  },
  methods: {
    clickBtn() {
      this.rectangle.style.weight++;
      this.rectangle.style.color =
        this.rectangle.style.weight % 2 === 0 ? "blue" : "green";
    }
  }
};
</script>

맵을 다루다 보면, 미리 지정된 태그와 data() 함수로만 처리하기 어려운 경우가 있다. 이럴 경우, 맵 객체 등을 직접 접근해 코딩해야 한다. 다음과 같이 GeometryTest.vue 코드에 맵 태그를 ref attribute 로 설정하고, zoom 값을 재설정하는 함수를 만들어보자. 

<template>
  <div>
   ...
    <l-map
      ref="map"    
      :zoom="zoom"
      :center="center"
      style="height: 90%"
    >
   ...
  </div>
</template>

<script>
  methods: {
    clickBtn() {
      var z = this.$refs.map.mapObject.getZoom();
      this.$refs.map.mapObject.setZoom(z * 0.8);
    },
  },
</script>

이렇게 코딩하고 버튼을 클릭하면 zoom out이 될 것이다. 이와 관련된 상세한 내용은 Issues 리스트를 참고하라. refs 속성에 관한 내용은 아래 링크를 참고하라.
앞서 확인한 바와 같이 leaflet 을 이용하면 손쉽게 인터렉티브 맵 웹 어플리케이션을 개발할 수 있다.

부록 - 심벌 링크 이슈
run할 때 라이브러리 심벌 링크 처리 에러로 제대로 import 경로가 맞지 않는 경우 에러가 발생할 수 있다.
yarn link 심벌 에러

본인의 경우, 해당 소스가 많지 않아 코드의 import 경로를 수동으로 수정해 주었다. 소스가 많은 경우 yarn link 관련 레퍼런스를 참고해 문제를 해결해야 한다.
소스코드 import path 에러 수정 후

레퍼런스

댓글 없음:

댓글 쓰기