산림청 공공데이터로 등산로 가져오기

깡스·2023년 7월 26일
1

내 산행이력을 기록해주는 프로젝트를 진행하던 도중, 그냥 기록만 하기에는 허전하기에 산림청에 공공데이터를 활용해 등산로의 난이도, 길이, 소요시간 등을 표기해주었습니다.

해당 데이터는 링크에서 제공하고 있습니다.
데이터에는 등산로 경로를 제공하는 polyline과 각 주요 지점의 정보를 제공하는 point 두가지 형태로 제공하고 있습니다.

공간 데이터 타입은 POINT, LINESTRING, POLYGON 등 여러 타입이 존제하며 이 블로그를 통해서 확인하실 수 있습니다.

등산로 데이터

데이터를 JSON형태로 다운받아 열어보면 해당 지점에 대한 좌표값이 배열 형태로 첨부되어 있습니다.

이 좌표는 PCS_ITRF2000_TM라는 좌표계 타입으로 카카오 지도에서 사용하고 있는 위경도 기준의 EPSG:4326타입으로 변경해주어야 사용할 수 있었습니다.

import proj4 from "proj4";

function positionToLatLon(position) {
  const first =
    'PROJCS["PCS_ITRF2000_TM",GEOGCS["GCS_ITRF_2000",DATUM["D_ITRF_2000",SPHEROID["GRS_1980",6378137.0,298.257222101]],PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]],PROJECTION["Transverse_Mercator"],PARAMETER["False_Easting",200000.0],PARAMETER["False_Northing",600000.0],PARAMETER["Central_Meridian",127.0],PARAMETER["Scale_Factor",1.0],PARAMETER["Latitude_Of_Origin",38.0],UNIT["Meter",1.0]]';
  const second = "EPSG:4326"; // WGS84

  const [lng, lat] = proj4(first, second, position);

  return [lat, lng];
}

좌표 타입의 변환은 Pro4j라는 라이브러리를 사용해주었습니다.
첫번째 인자로 변환 전의 좌표계 타입을 넣어주여야 하는데, 이 데이터는 공공데이터의 JSON파일에 spatialRefernce를 통해서 확인하실 수 있습니다.

데이터 가공 및 저장

등산로 데이터에는 다양한 데이터를 제공하고 있으나 저는

  • 등산로명
  • 등산로 길이
  • 등산로 난이도
  • 등산로 상행 시간
  • 등산로 하행 시간
  • 등산로 좌표 (polyline)

이렇게 6가지 데이터만 따로 수집하여 활용하려 합니다.

const insertRoute = async (mountainId, features) => {
  for (const {
    attributes: {
      PMNTN_DFFL: difficulty,
      PMNTN_UPPL: upTime,
      PMNTN_GODN: downTime,
      PMNTN_LT: length,
      PMNTN_NM: name
    },
    geometry: { paths }
  } of features) {
    const _path = paths.map(positions => positions.map(position => positionToLatLon(position)));

    const route = {
      name,
      mountainId,
      difficulty,
      upTime,
      downTime,
      length: length * 1000,
      paths: JSON.stringify(_path)
    };

    await supabase.from("routes").insert(route);
  }
};

const processMountain = async ([name, { features }]) => {
  const { data } = await supabase.from("mountains").select("*").eq("name", name).single();

  if (!data) return;

  const mountainId = data.id;

  await insertRoute(mountainId, features);
};

export const processMountainList = async () => {
  const mountainEntries = Object.entries(MOUNTAIN_LIST_DATA);

  for (const entry of mountainEntries) {
    await processMountain(entry);
  }
};

위와 같은 함수를 활용해서 자동으로 데이터의 형태를 변환, DB에 저장해주고 있습니다.

이 과정에서 등산로 좌표의 경우 [[x, y], [x, y], [x, y]]와 같이 2중 배열의 형태로 저장되어 있습니다.

supabase에 배열을 저장하는 행위는 가능하나, 2중 배열의 경우 방법을 찾지 못해 배열을 분해해 따로 따로 rows로 만들어야 하나 고민했으나, 데이터의 크기가 크지 않기에 해당 이중 배열을 JSON.stringify를 통해 문자열로 만들어 하나의 column에 저장한 후, 사용할 때 JSON.parse를 통해 다시 객체 형태로 변환해 사용해주었습니다.

지도에 그리기

마지막으로 가져온 데이터를 지도에 그려주기만 하면 작업은 종료됩니다.
카카오 맵 SDK를 활용하고 있기에, kakao.maps.Polyline을 사용해 선을 그려주었습니다.

난이도에 따라, 쉬움은 초록색, 중간은 주황색, 어려움은 빨간색으로 선을 구분해주었습니다.

0개의 댓글

관련 채용 정보