만들어야 했다.
먼저 각 행정구역별로 지도를 그릴 수 있게 좌표 정보를 받아야 했다.
여기서 좌표계 prj 파일(GRS80 UTM-K / EPSG:5179)과 최신 행정구역(SHP) 정보를 다운로드하였다.
간단하게 이 정보들이 무얼 의미하는지 살펴보자.
Shapefile은 지리/공간 백터 데이터를 저장하는 파일 형식이다.
벡터이미지는 svg 이미지를 다뤄봤다면 알고 있을텐데, 점과 점을 연결해 그림을 그리는 방식이다. path를 기억하기 때문에 이미지의 크기를 줄이고 늘리는 것에 구애받지 않는 것이 특징이다.
이런 정보들을 지형 정보로 담는 것이다. .shp
확장자를 가진 SHP파일은 벡터 지리공간 정보를 담고 있다. 하지만 공간정보시스템에서 다운로드 받은 파일은 총 4가지가 될텐데, 각각은 다음과 같다.
결국 Shapefile은 하나의 파일 포맷이 아니라 3개의 확장 포맷(dbf, shp, shx)을 통틀어 Shapefile이라 한다.
하지만 저런 파일은 기본적으로 컴퓨터에서 열 수 있게 제공되는 파일 형식도 아니고, 웹(Javascript)도 읽지 못한다. 따라서 자바스크립트가 읽을 수 있게끔 Geometry + JSON 형식으로 변환하여 주는 과정이 필요하다.
변환은 간편하게 웹에서 가능하다.
위 사이트에 접속해서 공간정보시스템에서 다운로드 받은 4개 파일을 업로드하면 된다.
파일명을 모두 동일하게 맞춰주는 것이 좋은데, 하지 않아도 작동되는 것으로 확인되었다.
여기서 제대로 행정구역 정보를 사용하고 싶다면! Options에 encoding="euc-kr"
인코딩을 꼭 입력해주자.
옵션을 입력하지 않은 경우 기껏 변환한 json 파일을 열었을 때 한글이 깨져 있는 것을 볼 수 있을 것이다. (자전적 발언)
import 버튼을 누르면 예쁘게 잘 그려져 있는 지도를 볼 수 있다.
이제 GeoJSON으로 내보내기 하면 된다. 우측 상단에 Export 버튼을 클릭 후 File Format으로는 GeoJson을 선택해주자.
json파일을 열어보면 다음과 같은 형식으로 구성되어 있다.
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"geometry": {
"type": "Polygon",
"coordinates": [
[...],
[...],
...
],
"properties": {
"CTPRVN_CD": "11",
"CTP_ENG_NM": "Seoul",
"CTP_KOR_NM": "서울특별시"
}
},
},
{
"type": "Feature",
"geometry": {
"type": "Polygon",
"coordinates": [
[...],
[...],
...
],
"properties": {
"CTPRVN_CD": "28",
"CTP_ENG_NM": "Incheon",
"CTP_KOR_NM": "인천광역시"
}
},
},
{
...
},
]
}
features
배열에 각 행정구역별로 properties
와 geometry
정보가 들어가 있어 이걸 기준으로 관리하기로 했다.
Leaflet에도, React Leaflet에도 GeoJSON 기능을 제공한다.
leafletjs react-leaflet GeoJSON
간편하게 사용하기 위해 React Leaflet을 설치하였다.
앞서 GeoJSON으로 변환해 다운로드한 파일을 프로젝트 내부에 넣어준 후 import한다.
나는 Nextjs를 사용 중이라 public 폴더에 GeoJSON파일을 넣었다.
// components/MapComponent.tsx
import { MapContainer, GeoJSON } from "react-leaflet";
import "leaflet/dist/leaflet.css";
// public 폴더에 위치한 GeoJSON 파일 사용
const geoJsonData = require("public/ctprvn.json");
const MapComponent = () => {
// 선택한 지형을
const [selectedFeature, setSelectedFeature] = useState(
geoJsonData.features[0] // 기본으로 서울특별시 선택
);
const onEachFeature = (
feature, // 지도에서 선택된 feature
layer
) => {
layer.on({
click: (e) => {
setSelectedFeature(feature);
},
});
};
const style = (feature: any) => {
// 파라미터로 받은 feature가 이미 선택된 feature인지 확인
const isSelected = feature === selectedFeature;
// 결과에 따라 return할 스타일 정의
return {
color: isSelected ? "blue" : "pink",
weight: isSelected ? 4 : 2,
opacity: isSelected ? 1 : 0.7,
};
};
return (
<>
<MapContainer>
zoomControl={false}
center={[36.638392, 127.696118]}
minZoom={6}
zoom={6}
style={{ height: "100%", width: "100%", background: "#fff" }}
>
<GeoJSON
data={geoJSONData}
style={style}
onEachFeature={onEachFeature}
/>
</MapContainer>
</>
)
}
최종 결과는 위와 같다.