vue-leafet Vworld 지도에 shp을 geojson으로 뿌려보기

2020-08-17

지오서버 설정 포스팅

지오서버 설정이 끝났음을 전제로 작성합니다. 레이어 미리 보기에서 이렇게 우측에서 선택을 해서 GeoJson으로 출력합니다. start geoserver로 서버도 실행시켜 줍니다.

http://localhost:8090/geoserver/test/ows?service=WFS&version=1.0.0&request=GetFeature&typeName=test:CTPRVN&maxFeatures=50&outputFormat=application/json

혹시 URL 사이사이 문자가 에러가 났다면 이런식으로 찾아서 변환해주면 됩니다. %2F = / %3A = :

그리고 vue create demo 명령어로 vue 프로젝트를 하나 생성합니다.

프로젝트 생성이 완료되면 HelloWorld.vue의 코드를 지우고 이렇게 만듭니다.

<template>
  <div class="hello">
  </div>
</template>

<script>
export default {
  name: 'HelloWorld',
}
</script>

이제 여기 위에 leaflet과 vworld의 지도를 올릴 예정입니다.

npm에 가셔서 vue2-leaflet, leaflet, axios를 다운로드 해주세요. 안가실거 알고있어서 명령어를 써놓겠습니다.

npm i leaflet
npm i vue2-leaflet
npm i axios

그리고 vworld에서 open api 신청을 하셔서 키를 받아 지도를 받아와야 합니다. http://www.vworld.kr/dev/v4dv_wmtsguide_s001.do

이 문서를 기준으로 작업하였고요. 그러면 지도를 얻어오는 주소는 이런 형식이 됩니다.

http://api.vworld.kr/req/wmts/1.0.0/본인 API 키/Base/{z}/{y}/{x}.png

그리고 코드를 아래와 같이 작성합니다.

// HelloWorld.vue
<template>
  <div class="hello">
    <l-map
      id="map"
      :center="center"
      :min-zoom="7"
    >
      <l-tile-layer :url="url" />
    </l-map>
  </div>
</template>

<script>
import { LMap, LTileLayer } from 'vue2-leaflet'
import { latLng } from 'leaflet'
export default {
  name: 'HelloWorld',
  components: {
    LMap,
    LTileLayer
  },
  data() {
    return {
      url: 'http://api.vworld.kr/req/wmts/1.0.0/본인 API 키/Base/{z}/{y}/{x}.png',
      center: latLng(37.560364, 126.984091)
    }
  }
}
</script>
<style scoped>
#map {
    width: 100%;
    height: 500px;
    border: 1px solid black;
  }
</style>

// main.js
import Vue from 'vue'
import App from './App.vue'
import 'leaflet/dist/leaflet.css'

Vue.config.productionTip = false

new Vue({
  render: h => h(App),
}).$mount('#app')

center, min-zoom 옵션이 중요해 보이지 않는다고 없애시면 지도가 제대로 출력되지 않습니다. center 옵션으로 처음 지도가 로딩됐을 때 보여 줄 중심 좌표를 설정하고 min-zoom으로 최소 줌을 설정하였습니다. 이렇게 하면 Leafet에 LTileLyaer를 이용하여 맵 이미지를 올린것이 됩니다.

이제 이 위에 shp를 기반으로 한 geojson을 출력해봅시다.

다시 한 번 코드를 수정합니다. 포스팅이 길어지는 것을 최대한 방지하기 위해 위에서 적었던 코드는 생략합니다.

// HelloWorld.vue
<template>
...
    <l-map
      <l-geo-json
        :geojson="getGeoShpData"
      >
      </l-geo-json>
    </l-map>
...
</template>

<script>
import { LGeoJson } from 'vue2-leaflet'
export default {
  components: {
    ...
    LGeoJson,
  },
  mounted() {
    this.getGeoShp();
  },
  data() {
    return {
      ...
      getGeoShpData: null
    }
  },
  methods: {
    async getGeoShp() {
      this.$axios.get('http://localhost:8090/geoserver/test/ows?service=WFS&version=1.0.0&request=GetFeature&typeName=test:CTPRVN&outputFormat=application/json')
      .then(res => {
        this.getGeoShpData = res.data
      })
      .catch(error => {
        console.log(error);
      })
    },
  }
}
</script>
// main.js
import axios from 'axios'

Vue.prototype.$axios = axios

new Vue({
  axios,
  render: h => h(App),
}).$mount('#app')

이렇게 하고 다시 실행해봅니다. 하지만 변한게 없습니다. 개발자 도구를 열어서 보면 axios 요청한 주소가 쭉 있고 net::ERR_CONNECTION_REFUSED 에러가 발생했습니다. CORS라는 규칙을 위반했기 때문인데 프록시를 사용해서 해결하겠습니다.

package.json이 있는 폴더에 vue.config.js 파일을 생성합니다. demo\vue.config.js

그렇게 아래와 같이 작성합니다.

const path = require('path');
module.exports = {
    devServer: {
        proxy: {
            '/geoserver' : {
                target : 'http://localhost:8090'
            }
        }
    },
    configureWebpack: {
        resolve: {
            alias: {
                '@': path.join(__dirname, 'src/')
            }
        }
    }  
}

vue.config.js 설정을 핫 리로드를 지원하지 않기 때문에 서버를 재시작합니다. 그러면 아래와 같은 화면을 볼 수 있습니다. 만약 지도가 여전히 올바르게 나오지 않는다면 vue.config.js를 잘못된 위치에 생성했을 가능성도 있습니다. GeoJson 로딩을 시간이 조금 소요됩니다.

이제 조금 활용해보기 위해 GeoJson을 올려놓고 특정 위치를 클릭하면 해당 지역을 팝업으로 띄워주는 기능을 추가해봅시다.

// HelloWorld.vue
<template>
    <l-geo-json
    :geojson="getGeoShpData"
    :options="options"
    >
    </l-geo-json>
</template>
<script>
data() {
    return {
      options: {
        onEachFeature(feature, layer) {
          layer.bindPopup(feature.properties.CTP_KOR_NM);
        }
      }
    }
  },
</script>

l-geo-json의 기본 옵션인 options를 사용하여 저런 형식으로 추가해 주면 클릭할 때마다 해당 지역의 정보를 읽어서 팝업으로 출력해줍니다. 그리고 문자가 올바르게 출력이 되지 않으면 geoserver 해당 저장소에서 문자셋을 EUC-KR로 지정해 주면 됩니다. 결국 geojson을 클릭할 때 geoserver가 출력해 준 데이터를 읽어서 지역명을 출력하는 것이므로 인코딩이 올바르게 되지 않으면 문제가 발생합니다.