본문 바로가기
/ 학습 / 안내서 / 공공데이터·소방통계
L3 심화 · 13분 읽기

소방용수 접근성 분석, 공백 구역을 지도로 찾기

2026.07.12 갱신 공간정보로 소방 자원 배치를 분석하려는 담당자·연구자
#공간정보#GeoPandas#소방용수#접근성
검토 상태: 전문 검토 전 위험도 미분류

이 안내서는 공식 절차·법령·현장 판단을 대체하지 않으며, 적용 전 소속 기관 기준과 담당자 검토를 확인하세요.

"우리 관할에 소화전이 몇 개 있나"는 표로 답하지만, "어디가 부족한가"는 지도라야 답합니다. 소방용수는 개수가 아니라 분포가 중요하기 때문입니다 — 100개가 도심에 몰려 있고 외곽이 비어 있다면, 총량은 충분해도 그 외곽은 위험 구역입니다. 이 안내서는 소방용수 위치와 건물 위치를 공간적으로 겹쳐, 도달 반경 밖 공백 구역을 지도로 찾아냅니다.

이 안내서는 프로젝트 아이디어 소방용수시설 접근성 분석 지도의 기술 배경을 설명합니다.

🎯 이 장의 목표
  • 소방용수·건물·관할 경계의 좌표계와 기준 시점을 대조할 수 있습니다.
  • 중복·결측·이설 기록을 점검한 뒤 최근접·버퍼 분석을 수행할 수 있습니다.
  • 직선거리 스크리닝과 도로 경로·현장 확인의 역할을 나눌 수 있습니다.
  • 지도의 공백 후보를 확정 판정이 아닌 사람 확인 우선순위로 설명할 수 있습니다.

데이터와 도구

공공데이터 지도에서 받습니다.

데이터출처쓰는 것
전국소방용수시설 표준데이터공공데이터포털소화전·저수조 좌표·유형
건물/주거지건축물대장·브이월드분석 대상 지점
행정경계SGIS·브이월드관할 범위·집계 단위

도구는 레포 레이더의 GEO 클러스터 — geopandas(공간 연산)·folium(지도 시각화)입니다.

1. 좌표계 — 여기서 대부분의 오류가 난다

공간분석의 가장 흔한 실수는 좌표계입니다. 공공데이터의 경위도(위도·경도, EPSG:4326)는 "도(度)" 단위라 그대로 거리를 재면 엉터리가 나옵니다 — "0.003도"는 위치에 따라 실제 거리가 다릅니다. 거리를 재려면 미터 단위 투영좌표계로 변환해야 합니다. 한국 표준은 EPSG:5179(UTM-K)입니다.

왜 이런 일이 생기는지는 지구본과 종이지도의 차이로 이해하면 쉽습니다. 둥근 지구를 평면에 펴는 방식이 좌표계인데, 경위도(EPSG:4326)는 세로줄·가로줄을 도(度)로 매긴 격자라 한 칸이 나타내는 실제 거리가 위치마다 다릅니다 — 적도에서 멀어질수록 경도 한 칸의 실제 폭이 좁아지기 때문입니다. 반면 투영좌표계(UTM-K)는 우리나라 영역을 아예 '미터 자'로 바꿔 편 것이라, 두 점 좌표를 빼면 곧바로 미터가 나옵니다. 그래서 거리를 잴 땐 미터 자로 갈아탄 뒤 재야 합니다.

PYTHON
import geopandas as gpd

# 소방용수·건물을 GeoDataFrame으로 (원본은 보통 경위도 EPSG:4326)
water = gpd.read_file("fire_water.geojson").set_crs(4326, allow_override=True)
bldg  = gpd.read_file("buildings.geojson").set_crs(4326, allow_override=True)

# 거리 계산 전 반드시 미터 단위 투영좌표계로 — 한국 표준 UTM-K
water_m = water.to_crs(5179)
bldg_m  = bldg.to_crs(5179)
⚠️ 흔한 실수
이 코드는 좌표계를 검증하는 코드가 아니라 강제로 선언하는 최소 예제입니다. set_crs(4326, allow_override=True)는 좌표값·축 순서·원천 메타데이터를 살펴보지 않고 “이 값은 EPSG:4326”이라는 꼬리표를 덮어씁니다. 원천이 다른 투영좌표이거나 CRS가 불명인데 이 줄을 그대로 쓰면, 잘못된 선언을 기준으로 to_crs(5179)가 변환해 이후 거리도 함께 왜곡됩니다. 실제 분석에서는 원천 메타데이터·데이터 사전·좌표 범위·알려진 기준 지점을 먼저 대조해 원래 CRS를 확인한 뒤 선언해야 합니다.

빠뜨리면 어떻게 되는지 숫자로 보면 분명합니다. 200m 떨어진 두 지점을 경위도 그대로 재면 약 0.0018 같은 정체불명의 값이 나옵니다(도 단위라 그렇습니다). 변환 후 재면 200이 나오죠. 문제는 앞의 0.0018이 뒤에서 쓸 "200m 초과" 필터(> 200)를 통과하지 못해, 진짜 공백 지점이 멀쩡한 곳으로 둔갑한다는 것입니다.

⚠️ 흔한 실수
to_crs(5179) 한 줄을 빠뜨리면 이후 모든 거리·버퍼가 무의미해집니다. 그런데 코드는 에러 없이 돌고 숫자도 그럴듯하게 나옵니다 — 위험도 예측의 데이터 누수처럼, 조용히 틀리는 종류의 실수라 가장 위험합니다. 분석 시작 전 좌표계부터 확인하세요.

2. 최근접 거리 — 각 지점에서 가장 가까운 용수까지

각 건물에서 가장 가까운 소방용수까지의 거리를 잽니다. geopandas의 최근접 공간조인이 이걸 한 번에 해 줍니다.

PYTHON
# 각 건물에 가장 가까운 소방용수를 붙이고, 그 거리(m)를 함께 계산
joined = gpd.sjoin_nearest(bldg_m, water_m, how="left", distance_col="용수거리m")

print(joined["용수거리m"].describe())          # 분포 — 중앙값·최댓값을 본다
공백 = joined[joined["용수거리m"] > 200]         # 도달 기준 초과(예: 200m)
print(f"공백 건물 {len(공백)}개 / 전체 {len(joined)}개")
⚠️ 흔한 실수
이 최소 예제의 NaN 처리 한계:
  • 용수거리m 결측을 따로 처리하지 않습니다
  • NaN > 200 비교가 False가 되어 판단 불가 후보를 잡지 못합니다
  • 실제 분석: joined[joined["용수거리m"].isna()]를 별도 후보로 분리해야 합니다:
  • 거리 데이터가 없는 용수시설 있는가?
  • 건물 좌표가 누락되었는가?
  • CRS 변환 오류인가?
  • 거리 기준 공백 + NaN 판단 불가를 합쳐 최종 해석합니다

기준 거리(예 200m)는 관할 특성·장비·규정에 맞춰 정합니다. 하나로 고정하지 말고 100·200·300m로 나눠 단계별 공백을 보는 편이 유용합니다.

📌 핵심
경계에서 한 가지 주의 — 관할 안 용수만으로 계산하면, 경계에 붙은 건물이 실제로는 옆 관할 소화전에 더 가까운데도 '공백'으로 잡힐 수 있습니다. 경계 부근을 분석할 땐 인접 관할의 용수도 함께 넣어야 실제에 가깝습니다.

3. 도달 반경(버퍼) — 용수가 덮는 범위

거꾸로, 각 소방용수가 반경 안에서 덮는 범위를 그려 겹치지 않는 빈 곳을 봅니다. 버퍼(buffer)는 각 점 둘레에 원을 그립니다 — 투영좌표계라 단위가 미터입니다.

PYTHON
covered = water_m.buffer(200).union_all()       # 모든 용수의 200m 도달 범위 합집합
# 관할 경계에서 도달 범위를 빼면 = 공백 구역
gap_area = boundary_m.geometry.union_all().difference(covered)

4. 지도로 — 숫자가 아니라 눈으로

공백 건물과 공백 구역을 지도에 얹어야 의사결정자에게 전달됩니다. folium으로 대화형 지도를 만듭니다(시각화는 경위도로 되돌려서).

PYTHON
import folium

m = folium.Map(location=[37.55, 126.9], zoom_start=13, tiles="CartoDB positron")
# 공백 건물(빨강) · 소방용수(파랑)를 4326으로 되돌려 표시
for _, r in 공백.to_crs(4326).iterrows():
    folium.CircleMarker([r.geometry.y, r.geometry.x], radius=3, color="crimson").add_to(m)
m.save("소방용수_공백.html")
▸ 소방 활용 포인트
소화전·저수조가 어디에 부족한지를 지도 한 장으로 보여, 점검 순찰 동선과 증설 우선순위를 근거 있게 정합니다. 총량 통계로는 안 보이던 외곽·신개발지 공백이 드러나, "느낌"이 아니라 데이터로 자원 배치를 설명할 수 있습니다.

5. 한계 — 직선거리는 도달시간이 아니다

이 분석의 가장 큰 한계를 분명히 알아야 합니다. 직선거리(as-the-crow-flies)는 소방차 실제 도달거리가 아닙니다. 강·철길·일방통행·경사가 가로막으면 200m 안의 용수도 실제로는 멀 수 있습니다. 더 정밀하게 하려면 도로 네트워크 기반 경로 거리를 써야 하지만, 그건 훨씬 무거운 분석입니다.

지표이 분석현실
거리직선도로 경로
함정강·철길을 무시실제 우회 필요
용도1차 스크리닝후보 구역 좁히기
📌 핵심
그래서 이 지도는 "여기가 확실히 문제"가 아니라 "여기를 먼저 현장 확인하라"는 우선순위 도구입니다. 위험도 예측과 같은 성격 — 최종 판단은 현장 지식과 함께 내립니다.

설계 판단

분석 단계를 높일수록 현실에 가까운 대신 데이터·계산·운영 비용이 늘어납니다. 첫 지도에서부터 현실을 완벽히 재현하려 하기보다, 질문에 필요한 정밀도를 단계별로 고릅니다.

방식맞는 질문트레이드오프사람이 확인할 것
직선거리·버퍼 스크리닝넓은 범위에서 현장 확인 후보를 줄일 때도로·지형·통행 제약을 반영하지 못함공백 후보의 좌표·시설 상태
도로 네트워크 경로 비교우회·단절 경로가 중요하고 신뢰할 수 있는 도로 자료가 있을 때네트워크 갱신과 경로 조건 설계가 필요경로 자료의 최신성과 통행 조건
현장 확인 중심좌표·시설 상태·도로 자료를 믿기 어려운 후보일 때여러 곳을 한번에 보기 어려움실제 접근 가능성과 시설 상태
💡 팁
지도에는 결과뿐 아니라 불확실성도 얹어야 합니다. 좌표 누락, 낡은 확인일, 인접 관할 자료 부재, 직선거리 가정을 같은 후보에 표시하면 담당자는 왜 현장 확인이 필요한지까지 알 수 있습니다.

운영 확인 기록

분석 결과와 함께 다음 세 계보를 남기면 다시 계산할 때 어디서 차이가 생겼는지 추적할 수 있습니다.

  • 좌표계 확인: 원천에 선언된 CRS, 실제로 해석한 CRS, 계산 단위, 좌표 범위를 기록합니다. 알려진 합성 기준 지점을 지도에 올려 축척·축 바꾸기·단위 오해를 계산 전에 걸러냅니다.
  • 네트워크 거리 확인: 도로 그래프의 기준일·통행 조건·경로 없음 건수를 기록합니다. 경로를 못 찾은 지점을 거리 0이나 공백으로 바꾸지 않고 판단 불가로 보내며, 직선거리와 경로 거리가 크게 다른 표본을 사람이 확인합니다.
  • 최신성 확인: 용수·건물·경계·도로별 원천 기준일과 추출일을 따로 남깁니다. 폐지·이설·신설 확인일이 서로 다르면 한 날의 현황처럼 합치지 않고, 시점 불일치와 현장 확인 필요를 지도에 함께 표시합니다.

운영 시나리오

다음은 특정 지역·시설의 실제 상태와 무관한 가상 데이터 운영 사례입니다. 공백 지도의 모양이 달라졌을 때 현실이 바뀐 것으로 단정하지 않고 입력 계보를 먼저 따라갑니다.

가상 시나리오 1: 누락된 CRS를 경위도로 강제 선언해 거리가 왜곡된 경우

  • 탐지: 코드는 오류 없이 끝났지만 용수 점이 예상 범위 밖에 놓이고, 알려진 두 기준 지점 사이의 계산 거리도 원천 지도에서 확인한 관계와 맞지 않았습니다. 본문의 set_crs(..., allow_override=True) 자체는 이 이상을 탐지하지 않습니다.
  • 원인 가설: CRS가 없거나 다른 투영좌표인 원천을 좌표값 확인 없이 EPSG:4326으로 강제 선언한 뒤 EPSG:5179로 변환해, 틀린 출발 좌표계가 모든 거리 계산에 전파됐다고 봅니다.
  • 대응: 공백 지도 공유를 멈추고 원본 파일을 다시 열어 원천 메타데이터·데이터 사전·좌표 범위·알려진 기준 지점을 대조합니다. 원래 CRS를 확인한 뒤에만 선언하고 투영하며, 결과에 맞추려고 좌표 숫자를 임의로 이동시키지 않습니다.
  • 재발 확인: CRS가 올바른 합성 기준 지점과 CRS 누락·오지정 픽스처를 별도로 두고, 거리 계산 전에 선언 CRS·좌표 범위·기준 지점 위치가 맞는지 사람이 확인합니다. 이 검사는 본문의 최소 예제 밖에 별도로 추가해야 합니다.

가상 시나리오 2: 최근접 거리가 NaN이라 공백 필터에서 빠진 경우

  • 탐지: 전체 건물 수와 거리 기준 결과를 대조하니 일부 행이 충분 후보와 공백 후보 어디에도 없었고, 해당 용수거리m 값은 NaN이었습니다.
  • 원인 가설: 본문의 joined["용수거리m"] > 200 비교에서 NaNFalse가 되므로, 최근접 용수를 찾지 못한 행이 공백이 아닌 정상처럼 필터 밖으로 빠졌다고 봅니다.
  • 대응: 결과 공유를 멈추고 isna()로 거리 결측 행을 별도 판단 불가 후보로 분리합니다. 빈 용수 자료·잘못된 geometry·조인 범위 문제를 확인하기 전에는 공백이나 충분으로 바꾸지 않습니다.
  • 재발 확인: 정상 거리·기준 초과·거리 NaN 합성 행을 함께 실행해 세 집합의 합이 전체와 일치하고, NaN 행은 거리 기준 결과가 아니라 판단 불가 큐에 남는지 확인합니다.

한계와 리스크

리스크발생 조건영향완화·사람 승인
좌표계·축척 오해경위도와 미터 단위 좌표를 혼용하거나 CRS를 틀리게 지정함거리·버퍼·공백 면적이 의미 없게 변함원천 CRS·단위를 기록하고 알려진 표본 지점을 사람이 지도에서 확인
스냅샷 시점 불일치건물·용수·경계가 서로 다른 기준일임공백 또는 충분 후보가 데이터 버전 차이로 생김기준일을 지도와 보고서에 표시하고 시점 불일치 구역은 담당자가 판단 불가로 승인
우선순위의 운영 명령 오인직선거리 공백 후보를 현장 확인 없이 확정 판정으로 설명함지형·도로·시설 상태를 놓친 자원 판단결과를 확인 후보로만 표시하고 점검·증설·배치는 소속 기관 기준과 담당자 승인 필요

자주 묻는 질문

Q. 직선거리 기준 안에 있으면 접근성이 충분한가요?

아닙니다. 직선거리는 넓은 범위의 확인 후보를 줄이는 스크리닝입니다. 도로·지형·시설 상태·소속 기관 기준을 현장에서 따로 확인합니다.

Q. 같은 좌표의 시설은 자동으로 하나로 합쳐도 되나요?

좌표만으로는 부족합니다. 고유 ID·시설 유형·상태·기준일을 함께 보아야 같은 시설의 중복인지, 한 지점의 다른 시설인지 구분할 수 있습니다.

Q. 데이터가 없는 구역을 공백으로 표시해도 되나요?

결측과 공백은 다릅니다. 본문 코드에서는 거리 NaN> 200 비교를 통과하지 않아 자동으로 판단 불가가 되지 않습니다. 최신 시설·경계 자료가 없거나 최근접 거리가 NaN이면 isna()로 별도 판단 불가 후보를 만들어야 자료 부재를 시설 부재나 충분으로 오해하지 않습니다.

Q. 지도만 보고 점검·증설 순서를 확정해도 되나요?

안 됩니다. 이 지도는 현장 확인 후보를 정렬할 뿐입니다. 실제 점검·증설·자원 배치는 현장 상태와 소속 기관의 공식 기준을 담당자가 확인해 결정합니다.

핵심 정리

  • 좌표계·고유 ID·기준일은 거리 계산 전에 통과해야 할 입력 계약입니다.
  • 직선거리와 버퍼는 현장 확인 후보를 줄이는 1차 스크리닝입니다.
  • 현재 예제의 > 200 필터는 거리 NaN을 잡지 못하므로 isna() 판단 불가 후보를 별도로 분리합니다.
  • 결측·낡은 자료·시점 불일치는 공백이 아니라 판단 불가로 분리합니다.
  • 지도에는 후보와 함께 가정·기준일·불확실성을 표시하고 최종 판단은 사람에게 둡니다.

다음 단계

사람 검토 체크리스트

TEXT
- [ ] 거리·버퍼 계산 전에 미터 단위 투영좌표계(EPSG:5179)로 변환했습니다.
- [ ] 소방용수 표준데이터의 좌표 정확성·최신성을 확인했습니다(폐지·이설 반영).
- [ ] 도달 기준 거리를 관할 특성에 맞게 정하고, 여러 단계로 나눠 봤습니다.
- [ ] 결과가 직선거리 기반이라는 한계를 보고서에 명시했습니다.
- [ ] 공백 구역은 현장 확인 우선순위로만 쓰고 단정하지 않았습니다.
⚠️ 흔한 실수
데이터가 낡으면 분석도 틀립니다 — 폐지된 소화전이 데이터에 남아 있으면 없는 용수를 있다고 셉니다. 좌표 오류·누락도 흔합니다. 그리고 직선거리 분석은 지형·도로를 모르니, 지도가 "충분"이라 해도 실제 도달성은 현장이 확인합니다. 이 분석은 사람의 현장 지식을 대체하지 않고 어디를 먼저 볼지 알려 줄 뿐입니다.