빅데이터

국토정보플랫폼에서 DEM 다운로드 및 고도 데이터 추출 후 DB 저장하는 방법

곽코딩루카 2025. 3. 10. 14:34
반응형

1. 프로젝트 개요

고도 데이터를 기반으로 경사로를 분석하는 프로젝트를 진행하게 되었습니다.
전국의 고도 데이터를 확보해야 하는 상황에서, 국내에서 가장 신뢰할 수 있는 국토정보플랫폼에서 제공하는 DEM 데이터를 무료로 다운로드할 수 있다는 사실을 알게 되었습니다.

이번 글에서는 국토정보플랫폼에서 DEM 파일을 다운로드하는 방법과, 이를 활용하여 고도 데이터를 추출하고 MySQL에 저장하는 과정을 설명하겠습니다.

 

2. DEM(Digital Elevation Model) 개념 정리

**DEM(수치표고모델, Digital Elevation Model)**은 지구 표면의 고도(높낮이)를 디지털 데이터로 표현한 모델입니다.
DEM 데이터는 위도(latitude), 경도(longitude), 고도(elevation) 값을 포함하며, 격자(Grid) 형태로 고도를 저장합니다.

DEM 활용 분야

  • 지도 제작 및 공간 분석
  • 자율주행 및 도로 경사 분석
  • 홍수 예측 및 재해 방지
  • 3D 모델링 및 도시 계획

DEM 데이터 다운로드: 국토정보플랫폼 DEM 다운로드

 

국토교통부 국토지리정보원_DEM_20240924

수치표고모델로, 공간정보(지도 및 항공사진, 위성영상 등)를 입체화하여 행정ㆍ환경ㆍ농산림ㆍ국방ㆍ방재 등 다양한 분야와 융ㆍ복합할 수 있는 자료<br/><br/>국토정보플랫폼 다운로드를 위해

www.data.go.kr

 

 

 

 

 

3. 국토정보플랫폼에서 DEM 다운로드 방법

(1) 국토정보플랫폼 접속 및 회원가입

DEM 데이터를 다운로드하려면 국토정보플랫폼(국토지리정보원) 사이트에서 회원가입이 필요합니다.
공식 홈페이지: 국토정보플랫폼 바로가기

 

국토정보플랫폼

국토교통부 국토지리정보원 (우)16517 경기도 수원시 영통구 월드컵로 92(원천동) 전화 : 031-210-2700 팩스 : 031-210-2644 본 홈페이지는 게시된 이메일 주소가 자동 수집되는 것을 거부하며 이를 위반시

map.ngii.go.kr

 

  1. 국토정보플랫폼에 접속
  2. 회원가입 후 로그인
  3. 국토정보맵 클릭
  4. 통합검색창에서 원하는 지역 검색
  5. "공개 DEM" 선택 후 다운로드 진행

주의사항: 다운로드가 정상적으로 되지 않는다면 대용량 파일 전송 S/W를 설치해야 할 수도 있습니다.

 

 

 

공식 홈페이지: http://map.ngii.go.kr/mn/mainPage.do

 

국토정보플랫폼

국토교통부 국토지리정보원 (우)16517 경기도 수원시 영통구 월드컵로 92(원천동) 전화 : 031-210-2700 팩스 : 031-210-2644 본 홈페이지는 게시된 이메일 주소가 자동 수집되는 것을 거부하며 이를 위반시

map.ngii.go.kr

 

 

 

 

국토정보맵을 클릭합니다.

 

 

 

 

통합검색창에 원하는 지역을 검색 후 공개DEM을 클릭하여 다운로드를 진행합니다.

 

 

DEM을 다운받으려면 국토리지연구원 홈페이지에 회원가입을 해야합니다. 회원가입 후 진행해주세요.

 

 

 

사용 목적 및 상세용도를 자세히 적어주세요.

 

 

zip형식으로 다운로드가 진행됩니다. 다운로드가 안될 시 해당 홈페이지에서 다운로드가 안될 시 아래 링크를 통해


대용량 파일전송 S/W 제공 파일을 다운받아야 합니다.

 

https://map.ngii.go.kr/ic/notice/noticeView.do?notice_id=1408

 

국토정보플랫폼

 

map.ngii.go.kr

 

 

 

다운을 받고 압축을 풀면 위와같이 img확장자로 DEM파일이 있을것이다.

 

.img 파일은 "Erdas Imagine"이라는 GIS 소프트웨어에서 개발한 "Raster 데이터 형식"이야.
DEM(수치표고모델) 데이터를 포함할 수 있으며, 각 픽셀 값이 고도(Elevation) 정보를 나타냄.
그래픽 이미지가 아니라, 지형 데이터를 포함하는 고급 GIS 파일 형식 중 하나다.

 

 

4. DEM 파일(.img) 형식 및 활용 방법

다운로드한 파일을 압축 해제하면 .img 확장자의 DEM 파일이 포함되어 있을 것입니다.

.img 파일이란?

  • Raster 데이터 형식: 각 픽셀 값이 고도 데이터를 포함하는 GIS 파일
  • GIS 소프트웨어에서 사용 가능: QGIS, ArcGIS, GDAL 등에서 분석 가능
  • 고해상도 DEM 데이터를 저장하는 표준 형식 중 하나
  • 좌표 정보 포함 가능: EPSG 코드 등을 포함하여 저장 가능

DEM 파일을 활용하여 Python을 이용해 고도 데이터를 추출하고, MySQL DB에 저장하는 과정을 설명하겠습니다.

 

 

5. Python으로 DEM 파일에서 고도 데이터 추출 및 MySQL DB 저장

(1) 프로젝트 구조

다음과 같은 프로젝트 구조를 사용합니다.

 

 

📂 project_root/
 ├── app.py  # Flask 서버 실행
 ├── db.py  # MySQL 데이터 조회
 ├── map_generator.py  # 지도 HTML 생성
 ├── location.py  # DEM 파일에서 고도 데이터 추출 및 저장

 

 

 

(2) 필수 라이브러리 설치

IDE는 VSCODE를 사용하였으며 pip명령어를 사용하여 라이브러리를 다운받았다.

pip install SQLAlchemy pandas MySQLdb rasterio Flask

 

필자는 mySQL을 사용하였으며 로컬포트 3307에 mysql을 설치하였다. (보통 3306일것이다 설마 따라하겠어?)
DB명은 location이며 , 예시로 root 1234로 소스를 제공하겠다. 


 

 

 

location.py 파일 생성 후 작성 (아래 소스에서 dem_files경로는 본인꺼로 해야한다 무식하게 복붙하는일 없길 바란다.)
(특정 지역 좌표 범위 설정 부분 lat_min,lat_max,lon_min,lon_max도 본인이 수집할 좌표를 넣어야한다.. 부평구는 우리동네이다 따라하지마라...)

원하는 위치를 수집해아하는데 위도 경도를 모르겠다 싶으면 아래 블로그를 참고하자.

 

https://letsplaycoding.tistory.com/26

 

구글 지도에서 위도 경도 확인 하는 방법

위도와 경로는 무엇일까?지금 보이는 세계지도에서 우리나라는 어디에 있다고 표현할 수 있을까?지도에 있는 어떤 나라와 어느 지역의 위치를 정확히 설명하는 것은 매우 어렵다.그래서 만든

letsplaycoding.tistory.com

 

 
 

(3) location.py - DEM 파일에서 고도 데이터 추출 및 MySQL 저장

import MySQLdb
import rasterio
from rasterio.warp import transform

# MySQL 연결 설정
DB_HOST = "localhost"
DB_PORT = 3307
DB_NAME = "location"
DB_USER = "root"
DB_PASSWORD = "1234"

# DEM 파일 경로
dem_files = [
    "C:\\Users\\USER\\Downloads\\silvercar\\37611.img",
    "C:\\Users\\USER\\Downloads\\silvercar\\37607.img"
]

datasets = [rasterio.open(dem) for dem in dem_files]

# 특정 지역 좌표 범위 설정 (부평구 일부, EPSG:4326 기준)
lat_min, lat_max = 37.487783, 37.497900
lon_min, lon_max = 126.735929, 126.745736
grid_size = 0.00009  # 10m 간격

print(f"📍 변환 전 위도 범위: {lat_min} ~ {lat_max}")
print(f"📍 변환 전 경도 범위: {lon_min} ~ {lon_max}")

data_to_insert = []

for dataset in datasets:
    dem_crs = dataset.crs  # DEM 좌표계 확인
    target_crs = "EPSG:4326"

    lat_range = int((lat_max - lat_min) / grid_size)
    lon_range = int((lon_max - lon_min) / grid_size)

    for i in range(lat_range):
        for j in range(lon_range):
            lat_val = lat_min + (i * grid_size)
            lon_val = lon_min + (j * grid_size)

            # 위도·경도를 DEM 파일의 좌표계(EPSG:5179)로 변환
            try:
                x, y = transform(target_crs, dem_crs, [lon_val], [lat_val])
                x, y = x[0], y[0]

                row, col = dataset.index(x, y)  # 변환된 좌표로 DEM 데이터 읽기
                elevation = dataset.read(1)[row, col]

                # 고도 데이터가 정상적으로 읽히면 저장
                if elevation is not None:
                    data_to_insert.append((lat_val, lon_val, elevation))
                    print(f"✅ 고도 데이터 삽입: 위도={lat_val}, 경도={lon_val}, 고도={elevation}")

            except IndexError:
                # DEM 데이터가 범위를 벗어난 경우
                print(f"⚠️ DEM 데이터 범위 벗어남: 위도={lat_val}, 경도={lon_val}")
                continue

print(f"📊 총 {len(data_to_insert)}개의 고도 데이터를 DB에 저장 중...")

# MySQL에 데이터 삽입
try:
    print("🚀 MySQL 연결 중...")
    conn = MySQLdb.connect(
        host=DB_HOST,
        port=DB_PORT,
        user=DB_USER,
        passwd=DB_PASSWORD,
        db=DB_NAME,
        connect_timeout=5
    )
    cursor = conn.cursor()
    print("✅ MySQL 연결 성공!")

    insert_query = "INSERT INTO elevation_data (latitude, longitude, elevation) VALUES (%s, %s, %s)"
    
    for data in data_to_insert:
        lat, lon, elevation = data
        cursor.execute(insert_query, (lat, lon, elevation))

    conn.commit()
    print(f"✅ 총 {len(data_to_insert)}개의 고도 데이터 저장 완료!")

    cursor.close()
    conn.close()

except MySQLdb.Error as err:
    print(f"❌ MySQL 연결 실패: {err}")

 

 

DEM 파일 경로에 본인이 다운받은 img파일 경로를 넣어준다. (고도를 추출할 DEM 파일 경로)

python location.py

 

명령어 실행시 아래와 같이 고도 데이터가 삽입되는것을 확인할 수 있다.

 

 

 

근데 이상한 점을 발견하였다..

 

분명 10m 간격으로 고도 데이터를 삽입하였으나 이상하게도 100m~90m간격은 값이 일치하는 것이였다..

 

국토정보 지리원 데이터를 분석해본 결과 무료로 제공해주는 DEM 해상도가 90m인것으로 확인되었다.

 

90m마다 고도데이터를 추출할 수 있다는 것인데 ,,, 이런 상황이면 우리로써는 개발을 진행할 수 없어 다른 방안으로 google Eleavation API를 사용해보려고 한다. 물론 이건 어느정도 횟수 초과시 유료이기에 당장은 API를 통해 고도데이터를 캐시화하여 수집할 예정이다.

 

 

 

 

아래는 flask를 활용하여 웹페이지로 우리가 수집한 데이터를 지도상에 그리드 형식으로 만드는 코드이다. 

 

 

# 📂 파일명: db.py
import pandas as pd
from sqlalchemy import create_engine

# MySQL 연결 설정
DB_HOST = "localhost"
DB_PORT = 3307
DB_NAME = "location"
DB_USER = "root"
DB_PASSWORD = "1234"

# SQLAlchemy 엔진 생성
engine = create_engine(f"mysql+mysqldb://{DB_USER}:{DB_PASSWORD}@{DB_HOST}:{DB_PORT}/{DB_NAME}")

# MySQL에서 데이터 가져오는 함수
def fetch_elevation_data():
    try:
        print(" MySQL에서 데이터 가져오는 중...")
        query = "SELECT latitude, longitude, elevation FROM elevation_data"
        df = pd.read_sql(query, engine)  # SQLAlchemy 엔진 사용
        
        if df.empty:
            print("!! 데이터가 없습니다.")
        else:
            print(f" {len(df)}개의 데이터 로드 완료!")
            print(df.head())  # 데이터 일부 출력

        return df
    except Exception as err:
        print(f"!! 데이터 가져오기 실패: {err}")
        return None

# 직접 실행할 경우 데이터를 가져오는 코드 추가
if __name__ == "__main__":
    fetch_elevation_data()

 

 

 

// map_generator.py 파일명

#파일명 map_generator.py
import folium
from db import fetch_elevation_data

def generate_map():
    df = fetch_elevation_data()
    if df is None or df.empty:
        print("!! 데이터 없음. 지도 생성 실패.")
        return None

    print(f"!! {len(df)}개의 데이터 로드 완료!")  # 데이터 개수 출력

    # 지도 중심 좌표 설정
    center_lat = df["latitude"].mean()
    center_lon = df["longitude"].mean()
    m = folium.Map(location=[center_lat, center_lon], zoom_start=15)

    # 데이터 포인트 추가
    for _, row in df.iterrows():
        elevation = row["elevation"]
        color = "green" if elevation < 10 else "orange" if elevation < 50 else "red"

        folium.CircleMarker(
            location=[row["latitude"], row["longitude"]],
            radius=3,
            color=color,
            fill=True,
            fill_color=color,
            fill_opacity=0.7,
            popup=f"고도: {elevation}m"
        ).add_to(m)

    # HTML 파일 저장
    m.save("templates/elevation_map.html")
    print("!! 지도 생성 완료! 'templates/elevation_map.html' 파일을 확인하세요.")

if __name__ == "__main__":
    generate_map()

 

 

 

//파일명 app.py

# 📂 파일명: app.py
from flask import Flask, render_template
from map_generator import generate_map

app = Flask(__name__)

@app.route("/")
def map_view():
    generate_map()  # 지도 생성
    return render_template("elevation_map.html")

if __name__ == "__main__":
    app.run(debug=True, host="0.0.0.0", port=5000)

 

 내가 아까 위에서 놓힌부분이 있는데 flask 라이브러리도 다운받아라.. 아래 명령어 실행

pip install Flask

 

 

python app.py

python app.py를 실행하면 로컬 5000포트로 지도 페이지가 열릴것이다.

 

사실 html에서 이미 생성되어서 굳이 flask 실행을 안하고 html파일만 열어도 됩니다.

 

 

 

 

localhost:5000 으로 접속시 해당 지도가 열릴것이다. 그리드를 클릭하면 각 고도의 좌표가 나온다.

 

하지만 문제가 10개의 그리드의 고도가 동일하게 나온다.. 해상도가 90m이기 때문..

 

고해상도 DEM은 공개적으로 지원하지 않으며 국토정보플랫폼에서 구매할경우 보안심사를 거친 이후 구매할 수 있다.

 

다음 게시글에서는 구글 고도 API (google Elevation API) 사용방법에 대해 포스트 하겠다. 

 

 

DEM 데이터의 해상도가 90m여서 더 정밀한 데이터를 수집하기 어려운 상황입니다.
Google Elevation API를 활용한 대체 방법을 검토 중이며, 다음 글에서 다룰 예정입니다.

다음 글 미리보기: Google Elevation API를 활용한 고도 데이터 수집 방법
👉 다음 글 보기

 

Google Elevation API를 활용한 고도 데이터 수집 및 DB 저장 방법

https://kwakscoding.tistory.com/73 [고도데이터수집]국토정보플랫폼에서 DEM 다운로드 + 고도 데이터 추출 후 DB저장고도데이터를 기반으로 경사로를 파악해야하는 프로젝트를 진행하게 되었다. 전국에

kwakscoding.tistory.com

 

 

반응형