프론트엔드/React.js

[React]  카카오맵 API 이용해 서울시 행정구 Polygon으로 구분하기

earthssu 2021. 5. 30. 18:13

위의 사진과 같이 서울시 행정구를 폴리곤으로 구분하고, 해당 위치에 마우스를 얻을 경우 색이 바뀌는 이벤트를 발생시키는 것까지 해보겠다.

 

 

그 전에 행정 구역을 구분하는 geojson 파일이 필요하다.

해당 파일은 아래 링크를 통해 쉽게 생성할 수 있다!

https://neurowhai.tistory.com/350

 

대한민국 행정구역(시도, 시군구) GeoJSON 파일 다운로드 및 SHP 파일 단순화 후 변환 방법 설명

원본 출처이자 해상도가 높은 파일(SHP)은 아래 링크에서 구하실 수 있습니다. http://www.gisdeveloper.co.kr/?p=2332 대한민국 최신 행정구역(SHP) 다운로드 – GIS Developer www.gisdeveloper.co.kr 위 데이터..

neurowhai.tistory.com

 

import geojson from '../../lib/data/json 파일 이름';

const { kakao } = window;

let data = geojson.features;
let coordinates = []; //좌표 저장 배열
let name = ''; //행정구 이름

let polygons = [];

const mapContainer = document.getElementById('pollution-map'); // 지도를 표시할 div
const mapOption = {
  center: new kakao.maps.LatLng(37.566826, 126.9786567), // 지도의 중심좌표
  level: 9, // 지도의 확대 레벨
};

const map = new kakao.maps.Map(mapContainer, mapOption);
const customOverlay = new kakao.maps.CustomOverlay({});
const infowindow = new kakao.maps.InfoWindow({ removable: true });

먼저 카카오맵을 생성해준다. 여기까진 일반 카카오맵 생성 과정과 크게 다르지 않다.

geojson의 features 부분에 해당 구역의 이름, 좌표 등의 정보가 담겨 있다. 이것을 data에 저장해준다.

 

const displayArea = (coordinates, name) => {
  let path = [];
  let points = [];
  let areaResult = pollution.filter((item) => item[0] === name); //없어도 됨

  coordinates[0].forEach((coordinate) => {
    let point = {};
    point.x = coordinate[1];
    point.y = coordinate[0];
    points.push(point);
    path.push(new kakao.maps.LatLng(coordinate[1], coordinate[0]));
  });

  let polygon = new kakao.maps.Polygon({
    map: map,
    path: path, // 그려질 다각형의 좌표 배열입니다
    strokeWeight: 2, // 선의 두께입니다
    strokeColor: '#004c80', // 선의 색깔입니다
    strokeOpacity: 0.8, // 선의 불투명도 입니다 1에서 0 사이의 값이며 0에 가까울수록 투명합니다
    strokeStyle: 'solid', // 선의 스타일입니다
    fillColor: '#fff', // 채우기 색깔입니다
    fillOpacity: 0.7, // 채우기 불투명도 입니다
  });

  polygons.push(polygon);

  // 다각형에 mouseover 이벤트를 등록하고 이벤트가 발생하면 폴리곤의 채움색을 변경합니다
  // 지역명을 표시하는 커스텀오버레이를 지도위에 표시합니다
  kakao.maps.event.addListener(polygon, 'mouseover', function (mouseEvent) {
    polygon.setOptions({ fillColor: '#09f' });

    customOverlay.setContent('<div class="area">' + name + '</div>');

    customOverlay.setPosition(mouseEvent.latLng);
    customOverlay.setMap(map);
  });

  // 다각형에 mousemove 이벤트를 등록하고 이벤트가 발생하면 커스텀 오버레이의 위치를 변경합니다
  kakao.maps.event.addListener(polygon, 'mousemove', function (mouseEvent) {
    customOverlay.setPosition(mouseEvent.latLng);
  });

  // 다각형에 mouseout 이벤트를 등록하고 이벤트가 발생하면 폴리곤의 채움색을 원래색으로 변경합니다
  // 커스텀 오버레이를 지도에서 제거합니다
  kakao.maps.event.addListener(polygon, 'mouseout', function () {
    polygon.setOptions({ fillColor: '#fff' });
    customOverlay.setMap(null);
  });

  // 다각형에 click 이벤트를 등록하고 이벤트가 발생하면 다각형의 이름과 면적을 인포윈도우에 표시합니다
  kakao.maps.event.addListener(polygon, 'click', function (mouseEvent) {
    const content =
      '<div style="padding:2px;"><p><b>' +
      name +
      '</b></p><p>이산화질소농도: ' +
      areaResult[1] +
      '</p><p>오존농도: ' +
      areaResult[2] +
      '</p><p>일산화탄소농도: ' +
      areaResult[3] +
      '</p><p>아황산가스: ' +
      areaResult[4] +
      '</p><p>미세먼지: ' +
      areaResult[5] +
      '</p><p>초미세먼지: ' +
      areaResult[6] +
      '</div>';

    infowindow.setContent(content);
    infowindow.setPosition(mouseEvent.latLng);
    infowindow.setMap(map);
    });
  };

  data.forEach((val) => {
  coordinates = val.geometry.coordinates;
  name = val.properties.SIG_KOR_NM;

  displayArea(coordinates, name);
});

geojson을 생성하고 해당 파일을 보면  coordinate[0] 부분에 여러 개의 좌표가 담긴 것을 확인할 수 있다.

이 부분을 일일이 돌면서 좌표 배열에 담아준다. 이때 꼭 new map 형태로 담아주어야 좌표가 제대로 찍힌다.

 

그리고 이때 담은 좌표들을 기반으로 폴리곤을 만들어준다.

 

이제 다음으로 마우스 이벤트를 만들어 추가해준다. 마우스를 올릴 경우 색이 바뀌면서 해당 지역의 이름이 뜨도록 했다.

그리고 마우스를 움직이고 해당 영역에서 벗어날 경우 다시 본래 상태로 돌아오도록 했다.

 

아래의 클릭 이벤트는 해당 구역을 클릭했을 때 콘텐츠가 뜨도록 하는 함수이다.

구역을 클릭했을 경우 내가 불러온  API의 콘텐츠가 뜨도록 했다. 이 부분은 각자 원하는 방식대로 내용을 채워주면 된다.

(이 포스팅에서 API를 불러오는 부분은 따로 표시하지 않았다.)

 

맨 아래의 forEach 부분이 행정구마다 돌면서 폴리곤을 생성하는 부분이다.

geojson을 보면 geometry의 coordinates 부분에 좌표 등의 정보가 담겨 있고, properties.SIG_KOR_NM 이 행정구의 이름이다.

 

이 모든 코드를  useEffect 함수 안에 담아 새로고침할 때마다 랜더링되도록 했다.

 

 

전체 코드는 아래와 같다.

 

 

useEffect(() => {
    let data = geojson.features;
    let coordinates = []; //좌표 저장 배열
    let name = ''; //행정구 이름

    let polygons = [];

    const mapContainer = document.getElementById('pollution-map'); // 지도를 표시할 div
    const mapOption = {
      center: new kakao.maps.LatLng(37.566826, 126.9786567), // 지도의 중심좌표
      level: 9, // 지도의 확대 레벨
    };

    const map = new kakao.maps.Map(mapContainer, mapOption);
    const customOverlay = new kakao.maps.CustomOverlay({});
    const infowindow = new kakao.maps.InfoWindow({ removable: true });

    const displayArea = (coordinates, name) => {
      let path = [];
      let points = [];
      let areaResult = pollution.filter((item) => item[0] === name);
      console.log(areaResult);

      coordinates[0].forEach((coordinate) => {
        let point = {};
        point.x = coordinate[1];
        point.y = coordinate[0];
        points.push(point);
        path.push(new kakao.maps.LatLng(coordinate[1], coordinate[0]));
      });

      let polygon = new kakao.maps.Polygon({
        map: map,
        path: path, // 그려질 다각형의 좌표 배열입니다
        strokeWeight: 2, // 선의 두께입니다
        strokeColor: '#004c80', // 선의 색깔입니다
        strokeOpacity: 0.8, // 선의 불투명도 입니다 1에서 0 사이의 값이며 0에 가까울수록 투명합니다
        strokeStyle: 'solid', // 선의 스타일입니다
        fillColor: '#fff', // 채우기 색깔입니다
        fillOpacity: 0.7, // 채우기 불투명도 입니다
      });

      polygons.push(polygon);

      // 다각형에 mouseover 이벤트를 등록하고 이벤트가 발생하면 폴리곤의 채움색을 변경합니다
      // 지역명을 표시하는 커스텀오버레이를 지도위에 표시합니다
      kakao.maps.event.addListener(polygon, 'mouseover', function (mouseEvent) {
        polygon.setOptions({ fillColor: '#09f' });

        customOverlay.setContent('<div class="area">' + name + '</div>');

        customOverlay.setPosition(mouseEvent.latLng);
        customOverlay.setMap(map);
      });

      // 다각형에 mousemove 이벤트를 등록하고 이벤트가 발생하면 커스텀 오버레이의 위치를 변경합니다
      kakao.maps.event.addListener(polygon, 'mousemove', function (mouseEvent) {
        customOverlay.setPosition(mouseEvent.latLng);
      });

      // 다각형에 mouseout 이벤트를 등록하고 이벤트가 발생하면 폴리곤의 채움색을 원래색으로 변경합니다
      // 커스텀 오버레이를 지도에서 제거합니다
      kakao.maps.event.addListener(polygon, 'mouseout', function () {
        polygon.setOptions({ fillColor: '#fff' });
        customOverlay.setMap(null);
      });

      // 다각형에 click 이벤트를 등록하고 이벤트가 발생하면 다각형의 이름과 면적을 인포윈도우에 표시합니다
      kakao.maps.event.addListener(polygon, 'click', function (mouseEvent) {
        const content =
          '<div style="padding:2px;"><p><b>' +
          name +
          '</b></p><p>이산화질소농도: ' +
          areaResult[1] +
          '</p><p>오존농도: ' +
          areaResult[2] +
          '</p><p>일산화탄소농도: ' +
          areaResult[3] +
          '</p><p>아황산가스: ' +
          areaResult[4] +
          '</p><p>미세먼지: ' +
          areaResult[5] +
          '</p><p>초미세먼지: ' +
          areaResult[6] +
          '</div>';

        infowindow.setContent(content);
        infowindow.setPosition(mouseEvent.latLng);
        infowindow.setMap(map);
      });
    };

    data.forEach((val) => {
      coordinates = val.geometry.coordinates;
      name = val.properties.SIG_KOR_NM;

      displayArea(coordinates, name);
    });
 }, []);