"근대 교통로 DB"의 두 판 사이의 차이
| 65번째 줄: | 65번째 줄: | ||
|} | |} | ||
| − | == 네트워크 데이터셋 기반 경로 분석 | + | == 활용 모델 1: 네트워크 데이터셋 기반 최소 비용 경로 분석 == |
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | + | === 개요 === | |
| − | + | 라인 네트워크 데이터셋에 비용 속성과 평가기를 설정하여 각 세그먼트의 이동 방향(Along/Against)에 따른 비대칭(asymmetric) 비용을 반영하는 최소 비용 경로 분석 방법론이다. | |
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | * | + | '''비용 속성''': |
| − | * | + | * Tobler(1993) 및 Kondo-Seino(2010)의 보행 속도 함수를 적용한 구간별 이동 시간 |
| − | + | * 이동 방향에 따른 경사각의 반전(오르막과 내리막)을 구분하여 정방향과 역방향 필드로 구성 | |
| − | |||
| − | + | === 분석 프로세스 === | |
| − | |||
| − | |||
| − | + | ==== 1단계: 네트워크 데이터 준비 ==== | |
| − | |||
| − | |||
| − | |||
| − | + | ===== 라인 데이터 분할 ===== | |
| − | |||
| − | |||
| − | + | 도로 네트워크 레이어의 각 세그먼트에 길이 및 고도 속성을 할당하기 위해 폴리라인을 분할한다. | |
| − | |||
| − | |||
| − | |||
| − | |||
| − | + | '''점 생성''': | |
| − | |||
| − | |||
| − | |||
| − | + | '''도구''': Generate Points Along Lines | |
| − | |||
| − | |||
| − | |||
| − | + | {| class="wikitable" | |
| − | + | |- | |
| + | ! 매개변수 !! 설정값 | ||
| + | |- | ||
| + | | 입력 피처 || 네트워크 라인 레이어 | ||
| + | |- | ||
| + | | 거리 || 10m (분석 목적에 따라 조정) | ||
| + | |} | ||
| − | * | + | '''라인 분할''': |
| − | ** | + | |
| − | ** Cost Attribute 추가 | + | '''도구''': Split Line at Point (Data Management Tools → Features) |
| − | + | ||
| − | *** | + | {| class="wikitable" |
| − | ** | + | |- |
| − | ** 경로 분석 | + | ! 매개변수 !! 설정값 |
| + | |- | ||
| + | | Input Features || 네트워크 라인 레이어 | ||
| + | |- | ||
| + | | Point Features || 생성된 점 레이어 | ||
| + | |- | ||
| + | | 검색 반경 || 1mm | ||
| + | |} | ||
| + | |||
| + | ==== 2단계: 좌표계 변환 (투영) ==== | ||
| + | |||
| + | '''DEM 투영''': | ||
| + | |||
| + | '''도구''': Project Raster | ||
| + | |||
| + | {| class="wikitable" | ||
| + | |- | ||
| + | ! 매개변수 !! 설정값 | ||
| + | |- | ||
| + | | 입력 래스터 || SRTM DEM | ||
| + | |- | ||
| + | | 출력 좌표계 || EPSG:5179 (Korea 2000 / Central Belt) | ||
| + | |- | ||
| + | | 리샘플링 기법 || Bilinear (이중선형 보간) | ||
| + | |- | ||
| + | | 셀 크기 || 원본 유지 (약 30m) | ||
| + | |} | ||
| + | |||
| + | '''네트워크 라인 투영''': | ||
| + | |||
| + | '''도구''': Project | ||
| + | |||
| + | {| class="wikitable" | ||
| + | |- | ||
| + | ! 매개변수 !! 설정값 | ||
| + | |- | ||
| + | | 입력 피처 || 네트워크 라인 | ||
| + | |- | ||
| + | | 출력 좌표계 || EPSG:5179 | ||
| + | |} | ||
| + | |||
| + | '''중요''': XY, Z 모두 미터 단위로 맞춰야 구배/길이 계산이 정확함. | ||
| + | |||
| + | ==== 3단계: 라인에 Z 값 보간 (3D 라인 생성) ==== | ||
| + | |||
| + | '''도구''': Interpolate Shape (3D Analyst) | ||
| + | |||
| + | {| class="wikitable" | ||
| + | |- | ||
| + | ! 매개변수 !! 설정값 | ||
| + | |- | ||
| + | | Input Features || 네트워크 라인 (EPSG:5179) | ||
| + | |- | ||
| + | | Input Surface || DEM (EPSG:5179) | ||
| + | |- | ||
| + | | Sampling Distance || 0 (기본값) | ||
| + | |- | ||
| + | | Z Factor || 1 (기본값) | ||
| + | |} | ||
| + | |||
| + | '''결과''': 모든 vertex에 DEM Z 값이 추가된 3D 라인 | ||
| + | |||
| + | ==== 4단계: 표면거리 필드 생성 (3D 길이) ==== | ||
| + | |||
| + | '''도구''': Calculate Geometry (기하 계산) | ||
| + | |||
| + | '''필드 1: length_3d''' | ||
| + | * 데이터 타입: Double | ||
| + | * Geometry Properties: LENGTH (3D) = 길이(3D) | ||
| + | * 단위: Meters | ||
| + | |||
| + | '''필드 2: length_3dkm''' | ||
| + | * Expression: <code>!length_3d! / 1000.0</code> | ||
| + | * 단위: Kilometers | ||
| + | |||
| + | '''품질 검증''': <code>length_3d >= Shape_Length</code> 확인 (표면거리 ≥ 평면거리) | ||
| + | |||
| + | ==== 5단계: 구배(Grade) 계산 ==== | ||
| + | |||
| + | 구배는 수평거리(2D)를 분모로 하여 계산하며, 이동 방향에 따라 부호가 반전된다. | ||
| + | |||
| + | '''필드 생성''': | ||
| + | * <code>grade_f</code>: From→To 방향 구배 | ||
| + | * <code>grade_b</code>: To→From 방향 구배 | ||
| + | * 데이터 타입: Double | ||
| + | |||
| + | ===== 간단식 (데이터가 정상일 때) ===== | ||
| + | |||
| + | <syntaxhighlight lang="python"> | ||
| + | grade_f = (!shape.lastPoint.Z! - !shape.firstPoint.Z!) / !Shape_Length! | ||
| + | grade_b = -!grade_f! | ||
| + | </syntaxhighlight> | ||
| + | |||
| + | ===== 권장식 (Z 값 NaN/None 방지) ===== | ||
| + | |||
| + | '''Code Block (Python)''': | ||
| + | |||
| + | <syntaxhighlight lang="python"> | ||
| + | import math | ||
| + | |||
| + | def safe_grade(z_from, z_to, len2d): | ||
| + | if z_from is None or z_to is None or len2d is None: | ||
| + | return 0.0 | ||
| + | try: | ||
| + | if math.isnan(z_from) or math.isnan(z_to) or len2d <= 0: | ||
| + | return 0.0 | ||
| + | except Exception: | ||
| + | return 0.0 | ||
| + | return (z_to - z_from) / len2d | ||
| + | </syntaxhighlight> | ||
| + | |||
| + | '''Expression''': | ||
| + | |||
| + | <syntaxhighlight lang="python"> | ||
| + | grade_f = safe_grade(!shape.firstPoint.Z!, !shape.lastPoint.Z!, !Shape_Length!) | ||
| + | grade_b = -!grade_f! | ||
| + | </syntaxhighlight> | ||
| + | |||
| + | ==== 6단계: 속도 및 시간 계산 ==== | ||
| + | |||
| + | ===== 보행 속도 함수 ===== | ||
| + | |||
| + | '''Code Block (Python)''': | ||
| + | |||
| + | <syntaxhighlight lang="python"> | ||
| + | import math | ||
| + | |||
| + | def v_kmh_tobler(g): | ||
| + | v = 6.0 * math.exp(-3.5 * abs(g + 0.05)) | ||
| + | return max(min(v, 7.5), 1.0) | ||
| + | |||
| + | def v_kmh_kondoseino(g): | ||
| + | if g >= -0.07: | ||
| + | v = 5.1 * math.exp(-2.25 * abs(g + 0.07)) | ||
| + | else: | ||
| + | v = 5.1 * math.exp(-1.5 * abs(g + 0.07)) | ||
| + | return max(v, 0.5) | ||
| + | |||
| + | def hours(len_m, v_kmh): | ||
| + | return (len_m/1000.0) / v_kmh if v_kmh and v_kmh > 0 else 1e6 | ||
| + | </syntaxhighlight> | ||
| + | |||
| + | '''필드 생성 - 속도 (km/h)''': | ||
| + | |||
| + | <syntaxhighlight lang="python"> | ||
| + | sp_tob_f = v_kmh_tobler(!grade_f!) | ||
| + | sp_tob_b = v_kmh_tobler(!grade_b!) | ||
| + | sp_ks_f = v_kmh_kondoseino(!grade_f!) | ||
| + | sp_ks_b = v_kmh_kondoseino(!grade_b!) | ||
| + | </syntaxhighlight> | ||
| + | |||
| + | '''필드 생성 - 시간 (hr)''': | ||
| + | |||
| + | <syntaxhighlight lang="python"> | ||
| + | hr_tob_f = hours(!length_3d!, !sp_tob_f!) | ||
| + | hr_tob_b = hours(!length_3d!, !sp_tob_b!) | ||
| + | hr_ks_f = hours(!length_3d!, !sp_ks_f!) | ||
| + | hr_ks_b = hours(!length_3d!, !sp_ks_b!) | ||
| + | </syntaxhighlight> | ||
| + | |||
| + | ==== 7단계: 칼로리 소비량 계산 ==== | ||
| + | |||
| + | MET(Metabolic Equivalent of Task)를 이용한 칼로리 계산. ACSM 공식에 내리막 보정 계수를 적용한다. | ||
| + | |||
| + | '''기본 개념''': | ||
| + | * VO₂ = 3.5 + 0.1 × S + 1.8 × S × grade (S: 속도 m/min, ACSM 공식) | ||
| + | * MET = VO₂/3.5 = 1 + 0.476 × v + 8.571 × v × grade_eff (v: 속도 km/h) | ||
| + | * 내리막 보정: grade < 0일 때 grade_eff = λ × grade (λ = 0.5 권장) | ||
| + | * 최소 MET = 1.0 보장 | ||
| + | * kcal/kg = MET × 시간(hr) | ||
| + | |||
| + | '''Code Block (Python)''': | ||
| + | |||
| + | <syntaxhighlight lang="python"> | ||
| + | LAMBDA = 0.5 | ||
| + | |||
| + | def kcal_per_kg(speed_kmh, hour_h, grade): | ||
| + | if speed_kmh is None or hour_h is None or grade is None: | ||
| + | return None | ||
| + | grade_eff = grade if grade >= 0 else LAMBDA * grade | ||
| + | mets = 1 + 0.476 * speed_kmh + 8.571 * speed_kmh * grade_eff | ||
| + | if mets < 1.0: | ||
| + | mets = 1.0 | ||
| + | return mets * hour_h | ||
| + | </syntaxhighlight> | ||
| + | |||
| + | '''필드 생성 - 칼로리 (kcal/kg)''': | ||
| + | |||
| + | <syntaxhighlight lang="python"> | ||
| + | kcal_ks_f = kcal_per_kg(!sp_ks_f!, !hr_ks_f!, !grade_f!) | ||
| + | kcal_ks_b = kcal_per_kg(!sp_ks_b!, !hr_ks_b!, !grade_b!) | ||
| + | kcal_tob_f = kcal_per_kg(!sp_tob_f!, !hr_tob_f!, !grade_f!) | ||
| + | kcal_tob_b = kcal_per_kg(!sp_tob_b!, !hr_tob_b!, !grade_b!) | ||
| + | </syntaxhighlight> | ||
| + | |||
| + | ==== 8단계: 수상 영역 속도 및 칼로리 보정 ==== | ||
| + | |||
| + | 바다 및 하천 구간은 도보가 아닌 선박 이동으로 가정하여 속도를 고정하고 칼로리를 0으로 설정한다. | ||
| + | |||
| + | {| class="wikitable" | ||
| + | |- | ||
| + | ! 구간 유형 !! 고정 속도 (km/h) !! 칼로리 (kcal/kg) | ||
| + | |- | ||
| + | | 바다 || 5.0 || 0 | ||
| + | |- | ||
| + | | 하천 || 1.2 || 0 | ||
| + | |- | ||
| + | | 육상 || Tobler/Kondo-Seino 함수 || MET 기반 계산 | ||
| + | |} | ||
| + | |||
| + | '''구현 방법''': | ||
| + | * <code>ID</code> 필드에 "바다" 또는 "하천" 값을 가진 구간 식별 | ||
| + | * 해당 구간의 속도 필드를 고정값으로 설정 | ||
| + | * 칼로리 필드를 0으로 설정 | ||
| + | |||
| + | ==== 9단계: 네트워크 데이터셋 생성 ==== | ||
| + | |||
| + | ===== 네트워크 데이터셋 생성 ===== | ||
| + | |||
| + | # Catalog Pane에서 지오데이터베이스 선택 | ||
| + | # 오른쪽 클릭 → New → Network Dataset | ||
| + | # 네트워크 데이터셋 이름 지정 및 폴리라인 레이어 선택 | ||
| + | |||
| + | ===== 비용 속성(Cost Attribute) 설정 ===== | ||
| + | |||
| + | Catalog → 네트워크 데이터셋 → Properties → Attributes 탭 | ||
| + | |||
| + | '''비용 속성 1: hour_tob (Tobler 시간)''' | ||
| + | |||
| + | {| class="wikitable" | ||
| + | |- | ||
| + | ! 설정 항목 !! 값 | ||
| + | |- | ||
| + | | 속성 유형 || Cost | ||
| + | |- | ||
| + | | 단위 || Hours | ||
| + | |- | ||
| + | | Evaluator - Along || Field: <code>!hr_tob_f!</code> | ||
| + | |- | ||
| + | | Evaluator - Against || Field: <code>!hr_tob_b!</code> | ||
| + | |} | ||
| + | |||
| + | '''비용 속성 2: hour_ks (Kondo-Seino 시간)''' | ||
| + | |||
| + | {| class="wikitable" | ||
| + | |- | ||
| + | ! 설정 항목 !! 값 | ||
| + | |- | ||
| + | | 속성 유형 || Cost | ||
| + | |- | ||
| + | | 단위 || Hours | ||
| + | |- | ||
| + | | Evaluator - Along || Field: <code>!hr_ks_f!</code> | ||
| + | |- | ||
| + | | Evaluator - Against || Field: <code>!hr_ks_b!</code> | ||
| + | |} | ||
| + | |||
| + | '''비용 속성 3: length_3d (3D 거리)''' | ||
| + | |||
| + | {| class="wikitable" | ||
| + | |- | ||
| + | ! 설정 항목 !! 값 | ||
| + | |- | ||
| + | | 속성 유형 || Cost | ||
| + | |- | ||
| + | | 단위 || Kilometers | ||
| + | |- | ||
| + | | Evaluator - Along || Field: <code>!length_3dkm!</code> | ||
| + | |- | ||
| + | | Evaluator - Against || Field: <code>!length_3dkm!</code> | ||
| + | |} | ||
| + | |||
| + | '''비용 속성 4: kcal_ks (Kondo-Seino 칼로리)''' | ||
| + | |||
| + | {| class="wikitable" | ||
| + | |- | ||
| + | ! 설정 항목 !! 값 | ||
| + | |- | ||
| + | | 속성 유형 || Cost | ||
| + | |- | ||
| + | | 단위 || Unknown (kcal/kg) | ||
| + | |- | ||
| + | | Evaluator - Along || Field: <code>!kcal_ks_f!</code> | ||
| + | |- | ||
| + | | Evaluator - Against || Field: <code>!kcal_ks_b!</code> | ||
| + | |} | ||
| + | |||
| + | '''비용 속성 5: kcal_tob (Tobler 칼로리)''' | ||
| + | |||
| + | {| class="wikitable" | ||
| + | |- | ||
| + | ! 설정 항목 !! 값 | ||
| + | |- | ||
| + | | 속성 유형 || Cost | ||
| + | |- | ||
| + | | 단위 || Unknown (kcal/kg) | ||
| + | |- | ||
| + | | Evaluator - Along || Field: <code>!kcal_tob_f!</code> | ||
| + | |- | ||
| + | | Evaluator - Against || Field: <code>!kcal_tob_b!</code> | ||
| + | |} | ||
| + | |||
| + | ===== 네트워크 데이터셋 빌드 ===== | ||
| + | |||
| + | # 네트워크 데이터셋을 마우스 오른쪽 버튼으로 클릭 | ||
| + | # Build Network 선택하여 빌드 실행 | ||
| + | |||
| + | ==== 10단계: 경로 분석 실행 ==== | ||
| + | |||
| + | # 네트워크 데이터셋 레이어를 마우스 오른쪽 버튼으로 클릭 | ||
| + | # Network Analyst 탭에서 '경로(Route)' 선택 | ||
| + | # 경로 레이어에서 출발지, 도착지 추가 | ||
| + | # 경로 레이어 Properties에서 임피던스(Impedance) 선택 | ||
| + | #* hour_tob, hour_ks, length_3d, kcal_ks, kcal_tob 중 선택 | ||
| + | # '비용 속성 누적'에서 추가 속성 선택 (경로 레이어 리본의 Σ 버튼) | ||
| + | # 네트워크 분석 실행 | ||
| + | |||
| + | === 네트워크 데이터셋 속성: 제한(Restriction) === | ||
| + | |||
| + | 특정 구간을 회피(Avoid)하거나 선호(Prefer)하도록 설정할 수 있다. | ||
| + | |||
| + | ==== 회피(Avoid) 설정 ==== | ||
| + | |||
| + | 특정 필드 값을 가진 구간에 높은 비용을 부여하여 우회하도록 유도한다. | ||
| + | |||
| + | '''설정 과정''': | ||
| + | |||
| + | # Network Dataset Properties → Attributes 탭 | ||
| + | # Add → Restriction 선택 | ||
| + | # Restriction 이름 지정 (예: <code>Avoid_River</code>) | ||
| + | # Usage Type: '''Avoid''' | ||
| + | # Scale Factor 설정 (예: 2.0~5.0) | ||
| + | #* 값이 클수록 강한 회피 효과 | ||
| + | # Evaluators 탭에서 Expression 설정 | ||
| + | |||
| + | '''Evaluator 설정 예시 (하천 회피)''': | ||
| + | |||
| + | Expression: | ||
| + | <syntaxhighlight lang="python"> | ||
| + | Avoid(!ID!) | ||
| + | </syntaxhighlight> | ||
| + | |||
| + | Code Block: | ||
| + | <syntaxhighlight lang="python"> | ||
| + | def Avoid(ID): | ||
| + | if ID == "하천": | ||
| + | return 1 | ||
| + | else: | ||
| + | return 0 | ||
| + | </syntaxhighlight> | ||
| + | |||
| + | '''Scale Factor 가이드''': | ||
| + | |||
| + | {| class="wikitable" | ||
| + | |- | ||
| + | ! Scale Factor !! 효과 | ||
| + | |- | ||
| + | | 1.0 || 정상 통행 (회피 없음) | ||
| + | |- | ||
| + | | 1.1~1.2 || 약간 우회 | ||
| + | |- | ||
| + | | 2.0~5.0 || 중간 정도 우회 | ||
| + | |- | ||
| + | | 10.0 이상 || 강한 우회 | ||
| + | |} | ||
| + | |||
| + | ==== 선호(Prefer) 설정 ==== | ||
| + | |||
| + | 특정 구간을 우선적으로 이용하도록 설정한다. | ||
| + | |||
| + | '''예시: 역 인근 200m 구간 선호''' | ||
| + | |||
| + | # 역 위치 포인트와 최근접 라인 세그먼트 추출 (Export Feature) | ||
| + | # Select by Location → Within a distance geodesic (200m) | ||
| + | # 선택된 구간에 필드 값 부여 (<code>!NEAR_FID! = 1</code>) | ||
| + | # Restriction 속성 추가 (Usage Type: Prefer) | ||
| + | # Evaluators 설정 | ||
| + | |||
| + | '''Evaluator 설정 예시''': | ||
| + | |||
| + | Expression: | ||
| + | <syntaxhighlight lang="python"> | ||
| + | Prefer(!NEAR_FID!) | ||
| + | </syntaxhighlight> | ||
| + | |||
| + | Code Block: | ||
| + | <syntaxhighlight lang="python"> | ||
| + | def Prefer(near_fid): | ||
| + | if near_fid == 1: | ||
| + | return 1 | ||
| + | else: | ||
| + | return 0 | ||
| + | </syntaxhighlight> | ||
| + | |||
| + | === 네트워크 데이터셋 속성: 계층(Hierarchy) === | ||
| + | |||
| + | 도로의 상대적 중요도를 정의하여 현실적인 경로 선택과 계산 속도 향상을 도모한다. | ||
| + | |||
| + | ==== 계층 속성의 역할 ==== | ||
| + | |||
| + | * 도로의 우선순위 정의 (고속도로 > 국도 > 대로 > 중로 > 소로) | ||
| + | * 경로 분석 시 큰 도로를 우선적으로 탐색 | ||
| + | * 계산 효율성 증가 | ||
| + | |||
| + | ==== 필드 조건 ==== | ||
| + | |||
| + | 계층 속성을 추가하려면 먼저 도로 데이터에 계층 값 필드가 필요하다. | ||
| + | |||
| + | '''필드 예시''': | ||
| + | * 필드명: <code>HIERARCHY</code> | ||
| + | * 데이터 타입: Short Integer | ||
| + | * 값 범위: | ||
| + | ** 1 = 고속도로 | ||
| + | ** 2 = 국도 | ||
| + | ** 3 = 대로 | ||
| + | ** 4 = 중로 | ||
| + | ** 5 = 소로 | ||
| + | |||
| + | '''중요''': ArcGIS는 값이 작을수록 높은 계층(중요한 도로)으로 인식함. | ||
| + | |||
| + | ==== 계층 속성 추가 과정 ==== | ||
| + | |||
| + | # Network Dataset Properties → Attributes 탭 | ||
| + | # Add → Hierarchy 선택 | ||
| + | # Evaluators 탭에서 Along, Against, Default 모두 동일 필드 지정 | ||
| + | #* Field: <code>!HIERARCHY!</code> | ||
| + | # 네트워크 데이터셋 빌드 | ||
| + | |||
| + | ==== Travel Mode에서 계층 적용 ==== | ||
| + | |||
| + | 계층 속성을 추가해도 분석에서 활성화해야 적용된다. | ||
| + | |||
| + | * Travel Mode 속성에서 '''Use Hierarchy = True''' 설정 | ||
| + | * 또는 분석 레이어(Route, Service Area 등)에서 '''Use Network Hierarchy''' 체크 | ||
| + | |||
| + | ==== 계층 값 역매핑 ==== | ||
| + | |||
| + | 데이터가 반대로 저장된 경우 (큰 값 = 중요한 도로) 필드 계산기로 변환한다. | ||
| + | |||
| + | <syntaxhighlight lang="python"> | ||
| + | NewField = 6 - !OldValue! | ||
| + | </syntaxhighlight> | ||
| + | |||
| + | === 네트워크 데이터셋 필드 목록 === | ||
| + | |||
| + | {| class="wikitable" | ||
| + | |- | ||
| + | ! 필드명 !! 의미 !! 단위 !! 타입 | ||
| + | |- | ||
| + | | <code>length_3d</code> || 3D 길이(표면거리) || m || DOUBLE | ||
| + | |- | ||
| + | | <code>length_3dkm</code> || 3D 길이 || km || DOUBLE | ||
| + | |- | ||
| + | | <code>grade_f</code> || 구배 (From→To) || 무차원 || DOUBLE | ||
| + | |- | ||
| + | | <code>grade_b</code> || 구배 (To→From) || 무차원 || DOUBLE | ||
| + | |- | ||
| + | | <code>sp_tob_f</code> || Tobler 속도 (From→To) || km/h || DOUBLE | ||
| + | |- | ||
| + | | <code>sp_tob_b</code> || Tobler 속도 (To→From) || km/h || DOUBLE | ||
| + | |- | ||
| + | | <code>sp_ks_f</code> || Kondo-Seino 속도 (From→To) || km/h || DOUBLE | ||
| + | |- | ||
| + | | <code>sp_ks_b</code> || Kondo-Seino 속도 (To→From) || km/h || DOUBLE | ||
| + | |- | ||
| + | | <code>hr_tob_f</code> || Tobler 소요시간 (From→To) || hr || DOUBLE | ||
| + | |- | ||
| + | | <code>hr_tob_b</code> || Tobler 소요시간 (To→From) || hr || DOUBLE | ||
| + | |- | ||
| + | | <code>hr_ks_f</code> || Kondo-Seino 소요시간 (From→To) || hr || DOUBLE | ||
| + | |- | ||
| + | | <code>hr_ks_b</code> || Kondo-Seino 소요시간 (To→From) || hr || DOUBLE | ||
| + | |- | ||
| + | | <code>kcal_ks_f</code> || 칼로리 소비 (From→To, KS) || kcal/kg || DOUBLE | ||
| + | |- | ||
| + | | <code>kcal_ks_b</code> || 칼로리 소비 (To→From, KS) || kcal/kg || DOUBLE | ||
| + | |- | ||
| + | | <code>kcal_tob_f</code> || 칼로리 소비 (From→To, Tobler) || kcal/kg || DOUBLE | ||
| + | |- | ||
| + | | <code>kcal_tob_b</code> || 칼로리 소비 (To→From, Tobler) || kcal/kg || DOUBLE | ||
| + | |- | ||
| + | | <code>ID</code> || 수상 영역 구분 || "바다" or "하천" || TEXT | ||
| + | |} | ||
| + | |||
| + | === 품질 검증 (QA 체크포인트) === | ||
| + | |||
| + | * <code>length_3d >= Shape_Length</code> 확인 (표면거리 ≥ 평면거리) | ||
| + | * 오르막 구간 (<code>grade > 0</code>): <code>hr_f > hr_b</code> | ||
| + | * 내리막 구간 (<code>grade < 0</code>): <code>hr_f < hr_b</code> | ||
| + | * 샘플 구간의 속도/시간/칼로리 값이 현실적인 범위 내에 있는지 확인 | ||
| + | |||
| + | === 필드 계산 자동화 스크립트 === | ||
| + | |||
| + | ==== 개요 ==== | ||
| + | |||
| + | 3D 길이, 구배, 속도(Tobler/Kondo-Seino), 시간, 칼로리를 일괄 계산하는 ArcGIS Pro Python 스크립트. | ||
| + | |||
| + | '''주요 기능''': | ||
| + | * Z 값이 포함된 라인 피처에서 3D 거리 및 구배 계산 | ||
| + | * Tobler 및 Kondo-Seino 보행 속도 함수 적용 | ||
| + | * MET 기반 칼로리 소비량 계산 | ||
| + | * 수상 구간(바다/하천) 처리: 고정 속도, 칼로리=0 | ||
| + | |||
| + | ==== 스크립트 ==== | ||
| + | |||
| + | <syntaxhighlight lang="python"> | ||
| + | import arcpy | ||
| + | import math | ||
| + | |||
| + | arcpy.env.overwriteOutput = True | ||
| + | |||
| + | in_lines = arcpy.GetParameterAsText(0) | ||
| + | |||
| + | F_ID = "ID" | ||
| + | F_LEN_3D = "length_3d" | ||
| + | F_LEN_3DKM = "length_3dkm" | ||
| + | F_GRADE_F = "grade_f" | ||
| + | F_GRADE_B = "grade_b" | ||
| + | F_SP_TOB_F = "sp_tob_f" | ||
| + | F_SP_TOB_B = "sp_tob_b" | ||
| + | F_SP_KS_F = "sp_ks_f" | ||
| + | F_SP_KS_B = "sp_ks_b" | ||
| + | F_HR_TOB_F = "hr_tob_f" | ||
| + | F_HR_TOB_B = "hr_tob_b" | ||
| + | F_HR_KS_F = "hr_ks_f" | ||
| + | F_HR_KS_B = "hr_ks_b" | ||
| + | F_KCAL_KS_F = "kcal_ks_f" | ||
| + | F_KCAL_KS_B = "kcal_ks_b" | ||
| + | F_KCAL_TB_F = "kcal_tob_f" | ||
| + | F_KCAL_TB_B = "kcal_tob_b" | ||
| + | |||
| + | V_SEA = 5.0 | ||
| + | V_RIVER = 1.2 | ||
| + | |||
| + | desc = arcpy.Describe(in_lines) | ||
| + | sr = desc.spatialReference | ||
| + | hasZ = getattr(desc, "hasZ", False) | ||
| + | |||
| + | if not hasZ: | ||
| + | arcpy.AddError("입력 라인에 Z가 없습니다.") | ||
| + | raise SystemExit(1) | ||
| + | |||
| + | def ensure_field(fc, name, ftype="DOUBLE", length=None): | ||
| + | names = [f.name for f in arcpy.ListFields(fc)] | ||
| + | if name not in names: | ||
| + | if ftype.upper() == "TEXT" and length is not None: | ||
| + | arcpy.management.AddField(fc, name, ftype, field_length=length) | ||
| + | else: | ||
| + | arcpy.management.AddField(fc, name, ftype) | ||
| + | |||
| + | for fld in [F_LEN_3D, F_LEN_3DKM, F_GRADE_F, F_GRADE_B, | ||
| + | F_SP_TOB_F, F_SP_TOB_B, F_SP_KS_F, F_SP_KS_B, | ||
| + | F_HR_TOB_F, F_HR_TOB_B, F_HR_KS_F, F_HR_KS_B, | ||
| + | F_KCAL_KS_F, F_KCAL_KS_B, F_KCAL_TB_F, F_KCAL_TB_B]: | ||
| + | ensure_field(in_lines, fld, "DOUBLE") | ||
| + | |||
| + | def v_kmh_tobler(g): | ||
| + | v = 6.0 * math.exp(-3.5 * abs(g + 0.05)) | ||
| + | return max(min(v, 7.5), 1.0) | ||
| + | |||
| + | def v_kmh_kondoseino(g): | ||
| + | if g >= -0.07: | ||
| + | v = 5.1 * math.exp(-2.25 * abs(g + 0.07)) | ||
| + | else: | ||
| + | v = 5.1 * math.exp(-1.5 * abs(g + 0.07)) | ||
| + | return max(v, 0.5) | ||
| + | |||
| + | LAMBDA_DOWN = 0.5 | ||
| + | |||
| + | def met_acsm_hybrid(v_kmh, g, lam=LAMBDA_DOWN): | ||
| + | if not v_kmh or v_kmh <= 0: | ||
| + | return 1.0 | ||
| + | v_mpm = v_kmh * 1000.0 / 60.0 | ||
| + | g_eff = g if g >= 0 else lam * g | ||
| + | vo2 = 0.1 * v_mpm + 1.8 * v_mpm * g_eff + 3.5 | ||
| + | return max(vo2 / 3.5, 1.0) | ||
| + | |||
| + | def _valid_z(z): | ||
| + | try: | ||
| + | return (z is not None) and (not math.isnan(z)) | ||
| + | except Exception: | ||
| + | return False | ||
| + | |||
| + | fields = ["SHAPE@", F_ID, F_LEN_3D, F_LEN_3DKM, F_GRADE_F, F_GRADE_B, | ||
| + | F_SP_TOB_F, F_SP_TOB_B, F_SP_KS_F, F_SP_KS_B, | ||
| + | F_HR_TOB_F, F_HR_TOB_B, F_HR_KS_F, F_HR_KS_B, | ||
| + | F_KCAL_KS_F, F_KCAL_KS_B, F_KCAL_TB_F, F_KCAL_TB_B] | ||
| + | IDX = {name: i for i, name in enumerate(fields)} | ||
| + | |||
| + | with arcpy.da.UpdateCursor(in_lines, fields) as cur: | ||
| + | for row in cur: | ||
| + | shp = row[IDX["SHAPE@"]] | ||
| + | idv = (row[IDX[F_ID]] or "").strip() | ||
| + | |||
| + | try: | ||
| + | l3d = shp.length3D | ||
| + | except AttributeError: | ||
| + | l3d = None | ||
| + | if not l3d or l3d <= 0: | ||
| + | l3d = shp.length | ||
| + | l3dkm = l3d / 1000.0 | ||
| + | l2d = shp.length | ||
| + | |||
| + | try: | ||
| + | z_from = shp.firstPoint.Z | ||
| + | z_to = shp.lastPoint.Z | ||
| + | except AttributeError: | ||
| + | z_from = z_to = None | ||
| + | |||
| + | if not (_valid_z(z_from) and _valid_z(z_to)) or not l2d or l2d <= 0: | ||
| + | g_f = g_b = 0.0 | ||
| + | else: | ||
| + | dz = (z_to - z_from) | ||
| + | g_f = dz / l2d | ||
| + | g_b = -g_f | ||
| + | |||
| + | sp_tob_f = v_kmh_tobler(g_f) | ||
| + | sp_tob_b = v_kmh_tobler(g_b) | ||
| + | sp_ks_f = v_kmh_kondoseino(g_f) | ||
| + | sp_ks_b = v_kmh_kondoseino(g_b) | ||
| + | |||
| + | fixed_mode = None | ||
| + | if idv == "바다": | ||
| + | fixed_mode = "SEA" | ||
| + | fixed_v = V_SEA | ||
| + | elif idv == "하천": | ||
| + | fixed_mode = "RIVER" | ||
| + | fixed_v = V_RIVER | ||
| + | |||
| + | if fixed_mode: | ||
| + | sp_tob_f = sp_tob_b = fixed_v | ||
| + | sp_ks_f = sp_ks_b = fixed_v | ||
| + | |||
| + | hr_tob_f = (l3dkm / sp_tob_f) if sp_tob_f > 0 else 1e6 | ||
| + | hr_tob_b = (l3dkm / sp_tob_b) if sp_tob_b > 0 else 1e6 | ||
| + | hr_ks_f = (l3dkm / sp_ks_f) if sp_ks_f > 0 else 1e6 | ||
| + | hr_ks_b = (l3dkm / sp_ks_b) if sp_ks_b > 0 else 1e6 | ||
| + | |||
| + | if fixed_mode: | ||
| + | kcal_ks_f = kcal_ks_b = kcal_tob_f = kcal_tob_b = 0.0 | ||
| + | else: | ||
| + | kcal_ks_f = met_acsm_hybrid(sp_ks_f, g_f) * hr_ks_f | ||
| + | kcal_ks_b = met_acsm_hybrid(sp_ks_b, g_b) * hr_ks_b | ||
| + | kcal_tob_f = met_acsm_hybrid(sp_tob_f, g_f) * hr_tob_f | ||
| + | kcal_tob_b = met_acsm_hybrid(sp_tob_b, g_b) * hr_tob_b | ||
| + | |||
| + | row[IDX[F_LEN_3D]] = l3d | ||
| + | row[IDX[F_LEN_3DKM]] = l3dkm | ||
| + | row[IDX[F_GRADE_F]] = g_f | ||
| + | row[IDX[F_GRADE_B]] = g_b | ||
| + | row[IDX[F_SP_TOB_F]] = sp_tob_f | ||
| + | row[IDX[F_SP_TOB_B]] = sp_tob_b | ||
| + | row[IDX[F_SP_KS_F]] = sp_ks_f | ||
| + | row[IDX[F_SP_KS_B]] = sp_ks_b | ||
| + | row[IDX[F_HR_TOB_F]] = hr_tob_f | ||
| + | row[IDX[F_HR_TOB_B]] = hr_tob_b | ||
| + | row[IDX[F_HR_KS_F]] = hr_ks_f | ||
| + | row[IDX[F_HR_KS_B]] = hr_ks_b | ||
| + | row[IDX[F_KCAL_KS_F]] = kcal_ks_f | ||
| + | row[IDX[F_KCAL_KS_B]] = kcal_ks_b | ||
| + | row[IDX[F_KCAL_TB_F]] = kcal_tob_f | ||
| + | row[IDX[F_KCAL_TB_B]] = kcal_tob_b | ||
| + | |||
| + | cur.updateRow(row) | ||
| + | |||
| + | arcpy.AddMessage("완료") | ||
| + | </syntaxhighlight> | ||
| + | |||
| + | ==== 사용자 정의 도구 생성 ==== | ||
| + | |||
| + | '''Tool Properties → Parameters 설정''': | ||
| + | |||
| + | {| class="wikitable" | ||
| + | |- | ||
| + | ! Label !! Data Type !! 유형 !! Direction !! 필터 | ||
| + | |- | ||
| + | | 네트워크 데이터(라인 피처) || Feature Class || 필요한 정보 || Input || 피처 유형(Polyline) | ||
| + | |} | ||
| + | |||
| + | === 관련 도구 === | ||
| + | |||
| + | * [[ArcGIS Pro]] | ||
| + | * [[Network Analyst]] | ||
| + | * [[3D Analyst]] | ||
| + | * [[Network Dataset]] | ||
| + | |||
| + | === 참고 문헌 === | ||
| + | |||
| + | * Tobler, W. (1993). Three presentations on geographical analysis and modeling: Non-isotropic geographic modeling speculations on the geometry of geography global spatial analysis. ''Technical Report'', 93-1. National Center for Geographic Information and Analysis, University of California, Santa Barbara. | ||
| + | * Kondo, Y., & Seino, Y. (2010). GPS-aided walking experiments and data-driven travel cost modeling on the historical road of Nakasendō-Kisoji (Central Highland Japan). In B. Frischer (Ed.), ''Making history interactive: Computer applications and quantitative methods in archaeology (CAA). Proceedings of the 37th international conference'' (pp. 158-165). Archaeopress. | ||
| + | * American College of Sports Medicine (ACSM). (2013). ''ACSM's guidelines for exercise testing and prescription'' (9th ed.). Lippincott Williams & Wilkins. | ||
| + | |||
| + | === 외부 링크 === | ||
| + | |||
| + | * [https://pro.arcgis.com/en/pro-app/latest/help/analysis/networks/what-is-a-network-dataset-.htm ArcGIS Pro - What is a network dataset?] | ||
| + | * [https://pro.arcgis.com/en/pro-app/latest/help/analysis/networks/network-attributes.htm ArcGIS Pro - Network attributes] | ||
| + | * [https://pro.arcgis.com/en/pro-app/latest/tool-reference/3d-analyst/interpolate-shape.htm ArcGIS Pro - Interpolate Shape] | ||
| + | |||
| + | [[분류:GIS]] | ||
| + | [[분류:공간분석]] | ||
| + | [[분류:네트워크분석]] | ||
| + | [[분류:경로분석]] | ||
| + | |||
| + | == 활용 모델 2: 래스터 기반 최소 비용 경로 분석 == | ||
| + | |||
| + | === 개요 === | ||
| + | DEM(Digital Elevation Model) 기반의 비등방(anisotropic) 비용면을 구성하고, Tobler 및 Kondo-Seino 도보 함수를 수직 인자(Vertical Factor)로 정의하여 오르막/내리막에 따른 방향 비대칭 비용을 반영하는 최소 비용 경로 분석 방법론이다. | ||
| + | |||
| + | * '''프로젝트 좌표계''': EPSG:5179 (Korea 2000 / Central Belt) | ||
| + | * '''DEM 해상도''': SRTM 30m | ||
| + | |||
| + | === 분석 프로세스 === | ||
| + | |||
| + | ==== 1단계: 평지 비용 래스터 생성 ==== | ||
| + | |||
| + | ===== 시간 비용 래스터 (단위: 초/30m) ===== | ||
| + | |||
| + | {| class="wikitable" | ||
| + | |- | ||
| + | ! 지형 유형 !! Tobler 함수 !! Kondo-Seino 함수 | ||
| + | |- | ||
| + | | 평지 || 21.44 || 24.79 | ||
| + | |- | ||
| + | | 하천 영역 || 90 || 90 | ||
| + | |- | ||
| + | | 바다 영역 || 21.6 || 21.6 | ||
| + | |} | ||
| + | |||
| + | '''Tobler 시간 비용 래스터 생성 (Raster Calculator)''' | ||
| + | |||
| + | <syntaxhighlight lang="python"> | ||
| + | # 1단계: DEM 있는 곳을 평지 값으로 | ||
| + | Con(~IsNull("srtm_5179"), 21.44) | ||
| + | # 출력: base_land_tob | ||
| + | |||
| + | # 2단계: 바다 영역 확장 | ||
| + | Con(IsNull("바다_조선5179"), 0, "바다_조선5179") | ||
| + | # 출력: sea_5179 | ||
| + | |||
| + | # 3단계: 바다 적용 | ||
| + | Con("sea_5179" == 1, 21.6, "base_land_tob") | ||
| + | # 출력: with_sea_tob | ||
| + | |||
| + | # 4단계: 하천 영역 확장 | ||
| + | Con(IsNull("하천_5179"), 0, "하천_5179") | ||
| + | # 출력: river_5179 | ||
| + | |||
| + | # 5단계: 하천 적용 (최종) | ||
| + | Con("river_5179" == 1, 90, "with_sea_tob") | ||
| + | # 출력: cost_tob | ||
| + | </syntaxhighlight> | ||
| + | |||
| + | '''Kondo-Seino 시간 비용 래스터 생성''' | ||
| + | |||
| + | <syntaxhighlight lang="python"> | ||
| + | # 6단계: DEM 있는 곳을 평지 값으로 | ||
| + | Con(~IsNull("srtm_5179"), 24.79) | ||
| + | # 출력: base_land_ks | ||
| + | |||
| + | # 7단계: 바다 적용 | ||
| + | Con("sea_5179" == 1, 21.6, "base_land_ks") | ||
| + | # 출력: with_sea_ks | ||
| + | |||
| + | # 8단계: 하천 적용 (최종) | ||
| + | Con("river_5179" == 1, 90, "with_sea_ks") | ||
| + | # 출력: cost_ks | ||
| + | </syntaxhighlight> | ||
| + | |||
| + | ===== 칼로리 비용 래스터 (단위: kcal/kg/30m) ===== | ||
| + | |||
| + | {| class="wikitable" | ||
| + | |- | ||
| + | ! 지형 유형 !! Tobler 함수 !! Kondo-Seino 함수 | ||
| + | |- | ||
| + | | 평지 || 0.0202 || 0.0212 | ||
| + | |- | ||
| + | | 하천 영역 || NoData || NoData | ||
| + | |- | ||
| + | | 바다 영역 || NoData || NoData | ||
| + | |} | ||
| + | |||
| + | '''Tobler 칼로리 비용 래스터 생성''' | ||
| + | |||
| + | <syntaxhighlight lang="python"> | ||
| + | # 9단계: DEM 있는 곳을 평지 값으로 | ||
| + | Con(~IsNull("srtm_5179"), 0.0202) | ||
| + | # 출력: base_land_tk | ||
| + | |||
| + | # 10단계: 바다 적용 | ||
| + | SetNull("sea_5179" == 1, "base_land_tk") | ||
| + | # 출력: with_sea_tk | ||
| + | |||
| + | # 11단계: 하천 적용 (최종) | ||
| + | SetNull("river_5179" == 1, "with_sea_tk") | ||
| + | # 출력: cost_tob_k | ||
| + | </syntaxhighlight> | ||
| + | |||
| + | '''Kondo-Seino 칼로리 비용 래스터 생성''' | ||
| + | |||
| + | <syntaxhighlight lang="python"> | ||
| + | # 12단계: DEM 있는 곳을 평지 값으로 | ||
| + | Con(~IsNull("srtm_5179"), 0.0212) | ||
| + | # 출력: base_land_kk | ||
| + | |||
| + | # 13단계: 바다 적용 | ||
| + | SetNull("sea_5179" == 1, "base_land_kk") | ||
| + | # 출력: with_sea_kk | ||
| + | |||
| + | # 14단계: 하천 적용 (최종) | ||
| + | SetNull("river_5179" == 1, "with_sea_kk") | ||
| + | # 출력: cost_ks_k | ||
| + | </syntaxhighlight> | ||
| + | |||
| + | ==== 2단계: 출발지와 도착지 설정 ==== | ||
| + | |||
| + | * 출발지점과 도착지점을 각각 포인트 피처로 생성 | ||
| + | * 좌표계: EPSG:5179 | ||
| + | |||
| + | ==== 3단계: 분석 범위 설정 ==== | ||
| + | |||
| + | '''도구''': Extract by Mask (공간 분석 도구) | ||
| + | |||
| + | * '''입력 래스터''': 비용 래스터 (cost_tob, cost_ks 등) | ||
| + | * '''마스크 피처''': 분석 범위 폴리곤 | ||
| + | * '''목적''': 분석 범위에 해당하는 비용 래스터 추출 | ||
| + | |||
| + | ==== 4단계: 거리 누적 및 역방향 래스터 생성 ==== | ||
| + | |||
| + | ===== 방법 1: Distance Accumulation (ArcGIS Pro 내장 함수) ===== | ||
| + | |||
| + | '''도구''': Distance Accumulation | ||
| + | |||
| + | {| class="wikitable" | ||
| + | |- | ||
| + | ! 매개변수 !! 설정값 | ||
| + | |- | ||
| + | | 입력 래스터 또는 피처 시작지점 || 출발지점 포인트 피처 | ||
| + | |- | ||
| + | | 입력 표면 래스터 || 마스크로 추출한 DEM | ||
| + | |- | ||
| + | | 입력 비용 래스터 || 마스크로 추출한 비용 래스터 | ||
| + | |- | ||
| + | | 결과 역방향 래스터 || (경로 지정) | ||
| + | |- | ||
| + | | 거리 방법 || 측지 | ||
| + | |- | ||
| + | | 입력 수직 래스터 || DEM | ||
| + | |- | ||
| + | | 수직 계수 || '''하이킹 시간''' (Hiking Time) | ||
| + | |- | ||
| + | | 임계각 || -90 ~ 90 | ||
| + | |} | ||
| + | |||
| + | '''특징''': | ||
| + | * ArcGIS Pro에 내장된 Tobler(1993) 함수 근사치 사용 | ||
| + | * 수직 상대 이동각(VRMA)에 따라 평지 대비 소요 시간 배율(VF) 자동 계산 | ||
| + | * 오르막과 내리막이 비대칭적으로 처리됨 | ||
| + | * '''주의''': 일정 경사 구간으로 나누어 근사한 함수이므로, 세밀한 지형 분석에서는 실제 Tobler 함수와 차이가 발생할 수 있음 | ||
| + | |||
| + | ===== 방법 2: Path Distance (사용자 정의 수직 계수) ===== | ||
| + | |||
| + | '''도구''': Path Distance | ||
| + | |||
| + | {| class="wikitable" | ||
| + | |- | ||
| + | ! 매개변수 !! 설정값 | ||
| + | |- | ||
| + | | 입력 래스터 또는 피처 소스 데이터 || 출발지점 포인트 피처 | ||
| + | |- | ||
| + | | 입력 비용 래스터 || 마스크로 추출한 비용 래스터 | ||
| + | |- | ||
| + | | 입력 표면 래스터 || 마스크로 추출한 DEM | ||
| + | |- | ||
| + | | 결과 백링크 래스터 || (경로 지정) | ||
| + | |- | ||
| + | | 입력 수직 래스터 || DEM | ||
| + | |- | ||
| + | | 수직 계수 || '''VF Table''' (사용자 정의) | ||
| + | |} | ||
| + | |||
| + | '''특징''': | ||
| + | * Tobler 또는 Kondo-Seino 함수를 정확히 구현한 VF 테이블 사용 | ||
| + | * 시간 비용(s/m) 및 칼로리 비용(kcal/kg/m) 모두 계산 가능 | ||
| + | |||
| + | ==== 5단계: Dijkstra 알고리즘 기반 최소 비용 경로 계산 ==== | ||
| + | |||
| + | ===== 알고리즘 개요 ===== | ||
| + | |||
| + | Dijkstra 알고리즘은 가중치가 있는 그래프에서 한 시작 지점으로부터 다른 모든 지점까지의 최단 경로를 구하는 기법이다. 간선의 가중치가 음수가 아닌 경우 정확한 최단 거리를 보장한다. | ||
| + | |||
| + | '''계산 과정''': | ||
| + | |||
| + | # '''비용 래스터 준비''': 평지 비용 + 수직 계수 | ||
| + | # '''Cost Distance 계산''': | ||
| + | #* 초기화: 모든 셀의 누적비용을 ∞로 설정, 출발 셀만 0으로 설정 | ||
| + | #* 가장 낮은 비용 셀 선택: Open List에서 누적비용이 가장 작은 셀을 확정(visited) 처리 | ||
| + | #* 이웃 셀 비용 계산: 확정된 셀의 주변 8개 이웃에 대해 새비용 = 확정된 셀 비용 + 이웃 셀 비용 계산 | ||
| + | #* 더 낮은 비용 발견 시 업데이트 및 Backlink Raster에 방향 기록 | ||
| + | #* 목적지 도달까지 반복 | ||
| + | # '''Cost Path 추출''': 도착지에서 Backlink를 따라 역추적 | ||
| + | |||
| + | '''결과물''': | ||
| + | * '''Distance Raster''': 출발지에서 각 셀까지의 최소 누적비용 | ||
| + | * '''Backlink Raster''': 각 셀에서 출발 지점으로 돌아갈 때 다음에 밟아야 할 셀의 방향 (0=소스, 1=E, 2=SE, 3=S, 4=SW, 5=W, 6=NW, 7=N, 8=NE) | ||
| + | |||
| + | ===== Backlink 방향 코드 ===== | ||
| + | |||
| + | {| class="wikitable" | ||
| + | |- | ||
| + | ! 코드 !! 방향 !! 설명 | ||
| + | |- | ||
| + | | 0 || - || 소스(출발지) | ||
| + | |- | ||
| + | | 1 || E || 동쪽 | ||
| + | |- | ||
| + | | 2 || SE || 남동쪽 | ||
| + | |- | ||
| + | | 3 || S || 남쪽 | ||
| + | |- | ||
| + | | 4 || SW || 남서쪽 | ||
| + | |- | ||
| + | | 5 || W || 서쪽 | ||
| + | |- | ||
| + | | 6 || NW || 북서쪽 | ||
| + | |- | ||
| + | | 7 || N || 북쪽 | ||
| + | |- | ||
| + | | 8 || NE || 북동쪽 | ||
| + | |} | ||
| + | |||
| + | ==== 6단계: 최저비용경로 추출 ==== | ||
| + | |||
| + | ===== Distance Accumulation 사용 시 ===== | ||
| + | |||
| + | '''도구''': Optimal Path As Line | ||
| + | |||
| + | {| class="wikitable" | ||
| + | |- | ||
| + | ! 매개변수 !! 설정값 | ||
| + | |- | ||
| + | | 입력 래스터 또는 피처 목적지 || 도착지점 포인트 피처 | ||
| + | |- | ||
| + | | 입력 거리 누적 래스터 || Distance Raster | ||
| + | |- | ||
| + | | 입력 역방향 또는 흐름 방향 래스터 || Backlink Raster | ||
| + | |- | ||
| + | | 래스터 형식 결과 최적 경로 || (경로 지정) | ||
| + | |- | ||
| + | | 경로 유형 || '''최적(BEST_SINGLE)''' | ||
| + | |} | ||
| + | |||
| + | ===== Path Distance 사용 시 ===== | ||
| + | |||
| + | '''도구''': Cost Path | ||
| + | |||
| + | {| class="wikitable" | ||
| + | |- | ||
| + | ! 매개변수 !! 설정값 | ||
| + | |- | ||
| + | | 래스터 또는 피처 대상 데이터 입력 || 도착지점 포인트 피처 | ||
| + | |- | ||
| + | | 대상 필드 || (자동 선택) | ||
| + | |- | ||
| + | | 입력 비용 거리 래스터 || Distance Raster | ||
| + | |- | ||
| + | | 입력 비용 백링크 래스터 || Backlink Raster | ||
| + | |- | ||
| + | | 결과 래스터 || (경로 지정) | ||
| + | |- | ||
| + | | 경로 유형 || '''최적(BEST_SINGLE)''' | ||
| + | |} | ||
| + | |||
| + | ==== 7단계: 래스터를 벡터로 변환 ==== | ||
| + | |||
| + | '''도구''': Raster to Polyline | ||
| + | |||
| + | * '''입력 래스터''': 최적 경로 래스터 | ||
| + | * '''폴리라인 단순화''': 체크 해제 | ||
| + | * '''출력''': 최적 경로 라인 피처 | ||
| + | |||
| + | ==== 8단계: 구간별 시간/칼로리 계산 ==== | ||
| + | |||
| + | * '''구간 분할 간격''': 10m | ||
| + | * '''사용자 정의 도구''': LineCarculate | ||
| + | * '''계산 항목''': 구간별 이동 시간, 칼로리 소비량 | ||
| + | |||
| + | === 비용 래스터 보정 === | ||
| + | |||
| + | 특정 구간에서 비용을 조정하여 제한 요소(선호, 회피)를 반영할 수 있다. | ||
| + | |||
| + | ==== 1단계: 네트워크 라인 래스터화 ==== | ||
| + | |||
| + | '''도구''': Feature to Raster | ||
| + | |||
| + | * '''입력''': 네트워크 라인 피처 | ||
| + | * '''출력 셀 크기''': DEM과 동일 | ||
| + | * '''결과''': 라인 셀=1, 비라인 셀=0 또는 NoData | ||
| + | |||
| + | 필요시 Reclassify 도구로 NoData를 0으로 변경 | ||
| + | |||
| + | ==== 2단계: 비용 조정 래스터 계산 ==== | ||
| + | |||
| + | '''Raster Calculator 수식''': | ||
| + | |||
| + | <syntaxhighlight lang="python"> | ||
| + | # 네트워크 라인 구역에서 비용 1/1.6배 감소 (선호) | ||
| + | cost_li = "cost_" * Con("line_raster" == 1, 1/1.6, 1) | ||
| + | |||
| + | # 네트워크 라인 구역에서 비용 1.3배 증가 (회피) | ||
| + | cost_li = "cost_" * Con("line_raster" == 1, 1.3, 1) | ||
| + | |||
| + | # 도로는 선호(1/1.6배), 하천은 회피(1.3배) | ||
| + | cost_li = "cost_" * Con("도로_srtm30" == 1, 1/1.6, 1) * Con("하천_srtm30" == 1, 1.3, 1) | ||
| + | </syntaxhighlight> | ||
| + | |||
| + | '''조정 계수''': | ||
| + | * '''선호 경로''': 1/1.6 ≈ 0.625 (비용 약 37.5% 감소) | ||
| + | * '''회피 경로''': 1.3 (비용 30% 증가) | ||
| + | |||
| + | === 참고 문헌 === | ||
| + | |||
| + | * Tobler, W. (1993). Three presentations on geographical analysis and modeling: Non-isotropic geographic modeling speculations on the geometry of geography global spatial analysis. ''Technical Report'', 93-1. National Center for Geographic Information and Analysis, University of California, Santa Barbara. | ||
| + | * Kondo, Y., & Seino, Y. (2010). GPS-aided walking experiments and data-driven travel cost modeling on the historical road of Nakasendō-Kisoji (Central Highland Japan). In B. Frischer (Ed.), ''Making history interactive: Computer applications and quantitative methods in archaeology (CAA). Proceedings of the 37th international conference'' (pp. 158-165). Archaeopress. | ||
| + | |||
| + | === 외부 링크 === | ||
| + | |||
| + | * [https://pro.arcgis.com/en/pro-app/latest/tool-reference/spatial-analyst/distance-accumulation.htm ArcGIS Pro - Distance Accumulation] | ||
| + | * [https://pro.arcgis.com/en/pro-app/latest/tool-reference/spatial-analyst/path-distance.htm ArcGIS Pro - Path Distance] | ||
| + | * [https://pro.arcgis.com/en/pro-app/latest/tool-reference/spatial-analyst/optimal-path-as-line.htm ArcGIS Pro - Optimal Path As Line] | ||
| + | |||
| + | [[분류:GIS]] | ||
| + | [[분류:공간분석]] | ||
| + | [[분류:래스터분석]] | ||
| + | [[분류:경로분석]] | ||
== 참고 문헌 == | == 참고 문헌 == | ||
* 양정현, 2023, HGIS를 통해 본 조선 전기 양주의 도로 체계와 역사적 경로 재현 - 양주 회암사의 사례를 중심으로, 남도문화연구 50 | * 양정현, 2023, HGIS를 통해 본 조선 전기 양주의 도로 체계와 역사적 경로 재현 - 양주 회암사의 사례를 중심으로, 남도문화연구 50 | ||
* 양정현, 2024, 조선시대 도로 네트워크 분석 방법론과 사례 연구 - 양주의 사례를 중심으로, 문화역사지리 36-3 | * 양정현, 2024, 조선시대 도로 네트워크 분석 방법론과 사례 연구 - 양주의 사례를 중심으로, 문화역사지리 36-3 | ||
| + | * 양정현, 2025, '신출귀몰'의 재구성 - 역사 연구와 GIS가 연결되는 한 방식, 역사비평 153 (발행 예정) | ||
== 메모 == | == 메모 == | ||
| 136번째 줄: | 1,146번째 줄: | ||
== DB 변화 이력 == | == DB 변화 이력 == | ||
* 2025년 09월 03일 : 최초 공개 | * 2025년 09월 03일 : 최초 공개 | ||
| + | * 2025년 11월 14일 : '네트워크 데이터셋 기반 최소 비용 경로 분석' 항목 보완 및 '래스터 기반 최소 비용 경로 분석' 항목 추가 | ||
2025년 11월 14일 (금) 16:41 판
DB 개요
- DB 설명
- 1910년대 제작된 1:5만 조선지형도의 도로 정보를 GIS 데이터로 구축 (기존 성신여자대학교 GIS 연구사업에서 구축한 1·2등 도로 데이터를 기초 레이어로 참조)
- 라인 벡터 데이터에 Tobler(1993), Kondo&Seino(2010)에서 발표된 보행 속도 함수를 적용하여 비등방 경로 분석을 실행할 수 있도록 구축한 네트워크 데이터셋
- 2025년 9월 시점 데이터 구축 상황 : 1910년대 조선지형도의 경기, 충청도, 경상도 내 1·2등 도로, 달로, 연로 등급까지 디지타이징 후 네트워크 데이터셋 가공 완료
- DB 구축자
- DB 설계 : 양정현
- DB 구축 : 양정현
- 데이터 검수 : 김종혁, 김현종, 박선영, 최유식
- 자료 공개
- 비영리적 활동(논문, 연구서)에 본 자료 사용시, 인용 문구만 표시하면 사용에 제한 없음.
- 영리적인 목적(프로젝트)으로 사용할 경우에는 관계자(yachagye@naver.com)와 상의 후 처리.
- 본 자료 전체를 다른 곳에서 서비스하는 것은 제한함.
- 본 DB에 오류가 발견된 경우, yachagye@naver.com에 오류 신고.
- 인용 표기 : 1910년대 한반도 도로 네트워크 데이터셋, 202X년 X월 기준, 양정현, 역지사지(https://www.hisgeo.info).
공간 데이터
- 데이터 구성
- 압축 해제 후 파일명: 도로_1914.gdb
- 하위 경로: 도로_1914.gdb / 도로_1914_5179(피처 데이터셋) / 도로_1914_10m_5179_20250831 ; 도로_1914_nd ; 도로_1914_nd_Junctions
- 도로 라인 피처: 도로_1914_10m_5179_20250831
- 네트워크 데이터셋: 도로_1914_nd
- 정션: 도로_1914_nd_Junctions
- 다운로드 링크(구글 드라이브)
필드 설계
| 필드명 | 의미 | 단위 | 타입 |
|---|---|---|---|
| length_3d | 3D 길이(표면거리) | m | DOUBLE |
| length_3dkm | 3D 길이 | km | DOUBLE |
| grade_f | 구배(From→To) | 무차원 | DOUBLE |
| grade_b | 구배(To→From) | 무차원 | DOUBLE |
| sp_tob_f | Tobler(1993) 보행 함수 속도 (From→To) | km/h | DOUBLE |
| sp_tob_b | Tobler(1993) 보행 함수 속도 (To→From) | km/h | DOUBLE |
| sp_ks_f | Kondo-Seino(2010) 보행 함수 속도 (From→To) | km/h | DOUBLE |
| sp_ks_b | Kondo-Seino(2010) 보행 함수 속도 (To→From) | km/h | DOUBLE |
| hr_tob_f | Tobler(1993) 보행 함수 시간 (From→To) | hr | DOUBLE |
| hr_tob_b | Tobler(1993) 보행 함수 시간 (To→From) | hr | DOUBLE |
| hr_ks_f | Kondo-Seino(2010) 보행 함수 시간 (From→To) | hr | DOUBLE |
| hr_ks_b | Kondo-Seino(2010) 보행 함수 시간 (To→From) | hr | DOUBLE |
| kcal_ks_f | ACSM 함수 칼로리 (From→To, Kondo-Seino 함수 시간) | kcal/kg | DOUBLE |
| kcal_ks_b | ACSM 함수 칼로리 (To→From, Kondo-Seino 함수 시간) | kcal/kg | DOUBLE |
| kcal_tob_f | ACSM 함수 칼로리 (From→To, Tobler 함수 시간) | kcal/kg | DOUBLE |
| kcal_tob_b | ACSM 함수 칼로리 (To→From, Tobler 함수 시간) | kcal/kg | DOUBLE |
| ID | 수상 영역 | - | TEXT |
활용 모델 1: 네트워크 데이터셋 기반 최소 비용 경로 분석
개요
라인 네트워크 데이터셋에 비용 속성과 평가기를 설정하여 각 세그먼트의 이동 방향(Along/Against)에 따른 비대칭(asymmetric) 비용을 반영하는 최소 비용 경로 분석 방법론이다.
비용 속성:
- Tobler(1993) 및 Kondo-Seino(2010)의 보행 속도 함수를 적용한 구간별 이동 시간
- 이동 방향에 따른 경사각의 반전(오르막과 내리막)을 구분하여 정방향과 역방향 필드로 구성
분석 프로세스
1단계: 네트워크 데이터 준비
라인 데이터 분할
도로 네트워크 레이어의 각 세그먼트에 길이 및 고도 속성을 할당하기 위해 폴리라인을 분할한다.
점 생성:
도구: Generate Points Along Lines
| 매개변수 | 설정값 |
|---|---|
| 입력 피처 | 네트워크 라인 레이어 |
| 거리 | 10m (분석 목적에 따라 조정) |
라인 분할:
도구: Split Line at Point (Data Management Tools → Features)
| 매개변수 | 설정값 |
|---|---|
| Input Features | 네트워크 라인 레이어 |
| Point Features | 생성된 점 레이어 |
| 검색 반경 | 1mm |
2단계: 좌표계 변환 (투영)
DEM 투영:
도구: Project Raster
| 매개변수 | 설정값 |
|---|---|
| 입력 래스터 | SRTM DEM |
| 출력 좌표계 | EPSG:5179 (Korea 2000 / Central Belt) |
| 리샘플링 기법 | Bilinear (이중선형 보간) |
| 셀 크기 | 원본 유지 (약 30m) |
네트워크 라인 투영:
도구: Project
| 매개변수 | 설정값 |
|---|---|
| 입력 피처 | 네트워크 라인 |
| 출력 좌표계 | EPSG:5179 |
중요: XY, Z 모두 미터 단위로 맞춰야 구배/길이 계산이 정확함.
3단계: 라인에 Z 값 보간 (3D 라인 생성)
도구: Interpolate Shape (3D Analyst)
| 매개변수 | 설정값 |
|---|---|
| Input Features | 네트워크 라인 (EPSG:5179) |
| Input Surface | DEM (EPSG:5179) |
| Sampling Distance | 0 (기본값) |
| Z Factor | 1 (기본값) |
결과: 모든 vertex에 DEM Z 값이 추가된 3D 라인
4단계: 표면거리 필드 생성 (3D 길이)
도구: Calculate Geometry (기하 계산)
필드 1: length_3d
- 데이터 타입: Double
- Geometry Properties: LENGTH (3D) = 길이(3D)
- 단위: Meters
필드 2: length_3dkm
- Expression:
!length_3d! / 1000.0 - 단위: Kilometers
품질 검증: length_3d >= Shape_Length 확인 (표면거리 ≥ 평면거리)
5단계: 구배(Grade) 계산
구배는 수평거리(2D)를 분모로 하여 계산하며, 이동 방향에 따라 부호가 반전된다.
필드 생성:
grade_f: From→To 방향 구배grade_b: To→From 방향 구배- 데이터 타입: Double
간단식 (데이터가 정상일 때)
grade_f = (!shape.lastPoint.Z! - !shape.firstPoint.Z!) / !Shape_Length!
grade_b = -!grade_f!권장식 (Z 값 NaN/None 방지)
Code Block (Python):
import math
def safe_grade(z_from, z_to, len2d):
if z_from is None or z_to is None or len2d is None:
return 0.0
try:
if math.isnan(z_from) or math.isnan(z_to) or len2d <= 0:
return 0.0
except Exception:
return 0.0
return (z_to - z_from) / len2dExpression:
grade_f = safe_grade(!shape.firstPoint.Z!, !shape.lastPoint.Z!, !Shape_Length!)
grade_b = -!grade_f!6단계: 속도 및 시간 계산
보행 속도 함수
Code Block (Python):
import math
def v_kmh_tobler(g):
v = 6.0 * math.exp(-3.5 * abs(g + 0.05))
return max(min(v, 7.5), 1.0)
def v_kmh_kondoseino(g):
if g >= -0.07:
v = 5.1 * math.exp(-2.25 * abs(g + 0.07))
else:
v = 5.1 * math.exp(-1.5 * abs(g + 0.07))
return max(v, 0.5)
def hours(len_m, v_kmh):
return (len_m/1000.0) / v_kmh if v_kmh and v_kmh > 0 else 1e6필드 생성 - 속도 (km/h):
sp_tob_f = v_kmh_tobler(!grade_f!)
sp_tob_b = v_kmh_tobler(!grade_b!)
sp_ks_f = v_kmh_kondoseino(!grade_f!)
sp_ks_b = v_kmh_kondoseino(!grade_b!)필드 생성 - 시간 (hr):
hr_tob_f = hours(!length_3d!, !sp_tob_f!)
hr_tob_b = hours(!length_3d!, !sp_tob_b!)
hr_ks_f = hours(!length_3d!, !sp_ks_f!)
hr_ks_b = hours(!length_3d!, !sp_ks_b!)7단계: 칼로리 소비량 계산
MET(Metabolic Equivalent of Task)를 이용한 칼로리 계산. ACSM 공식에 내리막 보정 계수를 적용한다.
기본 개념:
- VO₂ = 3.5 + 0.1 × S + 1.8 × S × grade (S: 속도 m/min, ACSM 공식)
- MET = VO₂/3.5 = 1 + 0.476 × v + 8.571 × v × grade_eff (v: 속도 km/h)
- 내리막 보정: grade < 0일 때 grade_eff = λ × grade (λ = 0.5 권장)
- 최소 MET = 1.0 보장
- kcal/kg = MET × 시간(hr)
Code Block (Python):
LAMBDA = 0.5
def kcal_per_kg(speed_kmh, hour_h, grade):
if speed_kmh is None or hour_h is None or grade is None:
return None
grade_eff = grade if grade >= 0 else LAMBDA * grade
mets = 1 + 0.476 * speed_kmh + 8.571 * speed_kmh * grade_eff
if mets < 1.0:
mets = 1.0
return mets * hour_h필드 생성 - 칼로리 (kcal/kg):
kcal_ks_f = kcal_per_kg(!sp_ks_f!, !hr_ks_f!, !grade_f!)
kcal_ks_b = kcal_per_kg(!sp_ks_b!, !hr_ks_b!, !grade_b!)
kcal_tob_f = kcal_per_kg(!sp_tob_f!, !hr_tob_f!, !grade_f!)
kcal_tob_b = kcal_per_kg(!sp_tob_b!, !hr_tob_b!, !grade_b!)8단계: 수상 영역 속도 및 칼로리 보정
바다 및 하천 구간은 도보가 아닌 선박 이동으로 가정하여 속도를 고정하고 칼로리를 0으로 설정한다.
| 구간 유형 | 고정 속도 (km/h) | 칼로리 (kcal/kg) |
|---|---|---|
| 바다 | 5.0 | 0 |
| 하천 | 1.2 | 0 |
| 육상 | Tobler/Kondo-Seino 함수 | MET 기반 계산 |
구현 방법:
ID필드에 "바다" 또는 "하천" 값을 가진 구간 식별- 해당 구간의 속도 필드를 고정값으로 설정
- 칼로리 필드를 0으로 설정
9단계: 네트워크 데이터셋 생성
네트워크 데이터셋 생성
- Catalog Pane에서 지오데이터베이스 선택
- 오른쪽 클릭 → New → Network Dataset
- 네트워크 데이터셋 이름 지정 및 폴리라인 레이어 선택
비용 속성(Cost Attribute) 설정
Catalog → 네트워크 데이터셋 → Properties → Attributes 탭
비용 속성 1: hour_tob (Tobler 시간)
| 설정 항목 | 값 |
|---|---|
| 속성 유형 | Cost |
| 단위 | Hours |
| Evaluator - Along | Field: !hr_tob_f!
|
| Evaluator - Against | Field: !hr_tob_b!
|
비용 속성 2: hour_ks (Kondo-Seino 시간)
| 설정 항목 | 값 |
|---|---|
| 속성 유형 | Cost |
| 단위 | Hours |
| Evaluator - Along | Field: !hr_ks_f!
|
| Evaluator - Against | Field: !hr_ks_b!
|
비용 속성 3: length_3d (3D 거리)
| 설정 항목 | 값 |
|---|---|
| 속성 유형 | Cost |
| 단위 | Kilometers |
| Evaluator - Along | Field: !length_3dkm!
|
| Evaluator - Against | Field: !length_3dkm!
|
비용 속성 4: kcal_ks (Kondo-Seino 칼로리)
| 설정 항목 | 값 |
|---|---|
| 속성 유형 | Cost |
| 단위 | Unknown (kcal/kg) |
| Evaluator - Along | Field: !kcal_ks_f!
|
| Evaluator - Against | Field: !kcal_ks_b!
|
비용 속성 5: kcal_tob (Tobler 칼로리)
| 설정 항목 | 값 |
|---|---|
| 속성 유형 | Cost |
| 단위 | Unknown (kcal/kg) |
| Evaluator - Along | Field: !kcal_tob_f!
|
| Evaluator - Against | Field: !kcal_tob_b!
|
네트워크 데이터셋 빌드
- 네트워크 데이터셋을 마우스 오른쪽 버튼으로 클릭
- Build Network 선택하여 빌드 실행
10단계: 경로 분석 실행
- 네트워크 데이터셋 레이어를 마우스 오른쪽 버튼으로 클릭
- Network Analyst 탭에서 '경로(Route)' 선택
- 경로 레이어에서 출발지, 도착지 추가
- 경로 레이어 Properties에서 임피던스(Impedance) 선택
- hour_tob, hour_ks, length_3d, kcal_ks, kcal_tob 중 선택
- '비용 속성 누적'에서 추가 속성 선택 (경로 레이어 리본의 Σ 버튼)
- 네트워크 분석 실행
네트워크 데이터셋 속성: 제한(Restriction)
특정 구간을 회피(Avoid)하거나 선호(Prefer)하도록 설정할 수 있다.
회피(Avoid) 설정
특정 필드 값을 가진 구간에 높은 비용을 부여하여 우회하도록 유도한다.
설정 과정:
- Network Dataset Properties → Attributes 탭
- Add → Restriction 선택
- Restriction 이름 지정 (예:
Avoid_River) - Usage Type: Avoid
- Scale Factor 설정 (예: 2.0~5.0)
- 값이 클수록 강한 회피 효과
- Evaluators 탭에서 Expression 설정
Evaluator 설정 예시 (하천 회피):
Expression:
Avoid(!ID!)Code Block:
def Avoid(ID):
if ID == "하천":
return 1
else:
return 0Scale Factor 가이드:
| Scale Factor | 효과 |
|---|---|
| 1.0 | 정상 통행 (회피 없음) |
| 1.1~1.2 | 약간 우회 |
| 2.0~5.0 | 중간 정도 우회 |
| 10.0 이상 | 강한 우회 |
선호(Prefer) 설정
특정 구간을 우선적으로 이용하도록 설정한다.
예시: 역 인근 200m 구간 선호
- 역 위치 포인트와 최근접 라인 세그먼트 추출 (Export Feature)
- Select by Location → Within a distance geodesic (200m)
- 선택된 구간에 필드 값 부여 (
!NEAR_FID! = 1) - Restriction 속성 추가 (Usage Type: Prefer)
- Evaluators 설정
Evaluator 설정 예시:
Expression:
Prefer(!NEAR_FID!)Code Block:
def Prefer(near_fid):
if near_fid == 1:
return 1
else:
return 0네트워크 데이터셋 속성: 계층(Hierarchy)
도로의 상대적 중요도를 정의하여 현실적인 경로 선택과 계산 속도 향상을 도모한다.
계층 속성의 역할
- 도로의 우선순위 정의 (고속도로 > 국도 > 대로 > 중로 > 소로)
- 경로 분석 시 큰 도로를 우선적으로 탐색
- 계산 효율성 증가
필드 조건
계층 속성을 추가하려면 먼저 도로 데이터에 계층 값 필드가 필요하다.
필드 예시:
- 필드명:
HIERARCHY - 데이터 타입: Short Integer
- 값 범위:
- 1 = 고속도로
- 2 = 국도
- 3 = 대로
- 4 = 중로
- 5 = 소로
중요: ArcGIS는 값이 작을수록 높은 계층(중요한 도로)으로 인식함.
계층 속성 추가 과정
- Network Dataset Properties → Attributes 탭
- Add → Hierarchy 선택
- Evaluators 탭에서 Along, Against, Default 모두 동일 필드 지정
- Field:
!HIERARCHY!
- Field:
- 네트워크 데이터셋 빌드
Travel Mode에서 계층 적용
계층 속성을 추가해도 분석에서 활성화해야 적용된다.
- Travel Mode 속성에서 Use Hierarchy = True 설정
- 또는 분석 레이어(Route, Service Area 등)에서 Use Network Hierarchy 체크
계층 값 역매핑
데이터가 반대로 저장된 경우 (큰 값 = 중요한 도로) 필드 계산기로 변환한다.
NewField = 6 - !OldValue!네트워크 데이터셋 필드 목록
| 필드명 | 의미 | 단위 | 타입 |
|---|---|---|---|
length_3d |
3D 길이(표면거리) | m | DOUBLE |
length_3dkm |
3D 길이 | km | DOUBLE |
grade_f |
구배 (From→To) | 무차원 | DOUBLE |
grade_b |
구배 (To→From) | 무차원 | DOUBLE |
sp_tob_f |
Tobler 속도 (From→To) | km/h | DOUBLE |
sp_tob_b |
Tobler 속도 (To→From) | km/h | DOUBLE |
sp_ks_f |
Kondo-Seino 속도 (From→To) | km/h | DOUBLE |
sp_ks_b |
Kondo-Seino 속도 (To→From) | km/h | DOUBLE |
hr_tob_f |
Tobler 소요시간 (From→To) | hr | DOUBLE |
hr_tob_b |
Tobler 소요시간 (To→From) | hr | DOUBLE |
hr_ks_f |
Kondo-Seino 소요시간 (From→To) | hr | DOUBLE |
hr_ks_b |
Kondo-Seino 소요시간 (To→From) | hr | DOUBLE |
kcal_ks_f |
칼로리 소비 (From→To, KS) | kcal/kg | DOUBLE |
kcal_ks_b |
칼로리 소비 (To→From, KS) | kcal/kg | DOUBLE |
kcal_tob_f |
칼로리 소비 (From→To, Tobler) | kcal/kg | DOUBLE |
kcal_tob_b |
칼로리 소비 (To→From, Tobler) | kcal/kg | DOUBLE |
ID |
수상 영역 구분 | "바다" or "하천" | TEXT |
품질 검증 (QA 체크포인트)
length_3d >= Shape_Length확인 (표면거리 ≥ 평면거리)- 오르막 구간 (
grade > 0):hr_f > hr_b - 내리막 구간 (
grade < 0):hr_f < hr_b - 샘플 구간의 속도/시간/칼로리 값이 현실적인 범위 내에 있는지 확인
필드 계산 자동화 스크립트
개요
3D 길이, 구배, 속도(Tobler/Kondo-Seino), 시간, 칼로리를 일괄 계산하는 ArcGIS Pro Python 스크립트.
주요 기능:
- Z 값이 포함된 라인 피처에서 3D 거리 및 구배 계산
- Tobler 및 Kondo-Seino 보행 속도 함수 적용
- MET 기반 칼로리 소비량 계산
- 수상 구간(바다/하천) 처리: 고정 속도, 칼로리=0
스크립트
import arcpy
import math
arcpy.env.overwriteOutput = True
in_lines = arcpy.GetParameterAsText(0)
F_ID = "ID"
F_LEN_3D = "length_3d"
F_LEN_3DKM = "length_3dkm"
F_GRADE_F = "grade_f"
F_GRADE_B = "grade_b"
F_SP_TOB_F = "sp_tob_f"
F_SP_TOB_B = "sp_tob_b"
F_SP_KS_F = "sp_ks_f"
F_SP_KS_B = "sp_ks_b"
F_HR_TOB_F = "hr_tob_f"
F_HR_TOB_B = "hr_tob_b"
F_HR_KS_F = "hr_ks_f"
F_HR_KS_B = "hr_ks_b"
F_KCAL_KS_F = "kcal_ks_f"
F_KCAL_KS_B = "kcal_ks_b"
F_KCAL_TB_F = "kcal_tob_f"
F_KCAL_TB_B = "kcal_tob_b"
V_SEA = 5.0
V_RIVER = 1.2
desc = arcpy.Describe(in_lines)
sr = desc.spatialReference
hasZ = getattr(desc, "hasZ", False)
if not hasZ:
arcpy.AddError("입력 라인에 Z가 없습니다.")
raise SystemExit(1)
def ensure_field(fc, name, ftype="DOUBLE", length=None):
names = [f.name for f in arcpy.ListFields(fc)]
if name not in names:
if ftype.upper() == "TEXT" and length is not None:
arcpy.management.AddField(fc, name, ftype, field_length=length)
else:
arcpy.management.AddField(fc, name, ftype)
for fld in [F_LEN_3D, F_LEN_3DKM, F_GRADE_F, F_GRADE_B,
F_SP_TOB_F, F_SP_TOB_B, F_SP_KS_F, F_SP_KS_B,
F_HR_TOB_F, F_HR_TOB_B, F_HR_KS_F, F_HR_KS_B,
F_KCAL_KS_F, F_KCAL_KS_B, F_KCAL_TB_F, F_KCAL_TB_B]:
ensure_field(in_lines, fld, "DOUBLE")
def v_kmh_tobler(g):
v = 6.0 * math.exp(-3.5 * abs(g + 0.05))
return max(min(v, 7.5), 1.0)
def v_kmh_kondoseino(g):
if g >= -0.07:
v = 5.1 * math.exp(-2.25 * abs(g + 0.07))
else:
v = 5.1 * math.exp(-1.5 * abs(g + 0.07))
return max(v, 0.5)
LAMBDA_DOWN = 0.5
def met_acsm_hybrid(v_kmh, g, lam=LAMBDA_DOWN):
if not v_kmh or v_kmh <= 0:
return 1.0
v_mpm = v_kmh * 1000.0 / 60.0
g_eff = g if g >= 0 else lam * g
vo2 = 0.1 * v_mpm + 1.8 * v_mpm * g_eff + 3.5
return max(vo2 / 3.5, 1.0)
def _valid_z(z):
try:
return (z is not None) and (not math.isnan(z))
except Exception:
return False
fields = ["SHAPE@", F_ID, F_LEN_3D, F_LEN_3DKM, F_GRADE_F, F_GRADE_B,
F_SP_TOB_F, F_SP_TOB_B, F_SP_KS_F, F_SP_KS_B,
F_HR_TOB_F, F_HR_TOB_B, F_HR_KS_F, F_HR_KS_B,
F_KCAL_KS_F, F_KCAL_KS_B, F_KCAL_TB_F, F_KCAL_TB_B]
IDX = {name: i for i, name in enumerate(fields)}
with arcpy.da.UpdateCursor(in_lines, fields) as cur:
for row in cur:
shp = row[IDX["SHAPE@"]]
idv = (row[IDX[F_ID]] or "").strip()
try:
l3d = shp.length3D
except AttributeError:
l3d = None
if not l3d or l3d <= 0:
l3d = shp.length
l3dkm = l3d / 1000.0
l2d = shp.length
try:
z_from = shp.firstPoint.Z
z_to = shp.lastPoint.Z
except AttributeError:
z_from = z_to = None
if not (_valid_z(z_from) and _valid_z(z_to)) or not l2d or l2d <= 0:
g_f = g_b = 0.0
else:
dz = (z_to - z_from)
g_f = dz / l2d
g_b = -g_f
sp_tob_f = v_kmh_tobler(g_f)
sp_tob_b = v_kmh_tobler(g_b)
sp_ks_f = v_kmh_kondoseino(g_f)
sp_ks_b = v_kmh_kondoseino(g_b)
fixed_mode = None
if idv == "바다":
fixed_mode = "SEA"
fixed_v = V_SEA
elif idv == "하천":
fixed_mode = "RIVER"
fixed_v = V_RIVER
if fixed_mode:
sp_tob_f = sp_tob_b = fixed_v
sp_ks_f = sp_ks_b = fixed_v
hr_tob_f = (l3dkm / sp_tob_f) if sp_tob_f > 0 else 1e6
hr_tob_b = (l3dkm / sp_tob_b) if sp_tob_b > 0 else 1e6
hr_ks_f = (l3dkm / sp_ks_f) if sp_ks_f > 0 else 1e6
hr_ks_b = (l3dkm / sp_ks_b) if sp_ks_b > 0 else 1e6
if fixed_mode:
kcal_ks_f = kcal_ks_b = kcal_tob_f = kcal_tob_b = 0.0
else:
kcal_ks_f = met_acsm_hybrid(sp_ks_f, g_f) * hr_ks_f
kcal_ks_b = met_acsm_hybrid(sp_ks_b, g_b) * hr_ks_b
kcal_tob_f = met_acsm_hybrid(sp_tob_f, g_f) * hr_tob_f
kcal_tob_b = met_acsm_hybrid(sp_tob_b, g_b) * hr_tob_b
row[IDX[F_LEN_3D]] = l3d
row[IDX[F_LEN_3DKM]] = l3dkm
row[IDX[F_GRADE_F]] = g_f
row[IDX[F_GRADE_B]] = g_b
row[IDX[F_SP_TOB_F]] = sp_tob_f
row[IDX[F_SP_TOB_B]] = sp_tob_b
row[IDX[F_SP_KS_F]] = sp_ks_f
row[IDX[F_SP_KS_B]] = sp_ks_b
row[IDX[F_HR_TOB_F]] = hr_tob_f
row[IDX[F_HR_TOB_B]] = hr_tob_b
row[IDX[F_HR_KS_F]] = hr_ks_f
row[IDX[F_HR_KS_B]] = hr_ks_b
row[IDX[F_KCAL_KS_F]] = kcal_ks_f
row[IDX[F_KCAL_KS_B]] = kcal_ks_b
row[IDX[F_KCAL_TB_F]] = kcal_tob_f
row[IDX[F_KCAL_TB_B]] = kcal_tob_b
cur.updateRow(row)
arcpy.AddMessage("완료")사용자 정의 도구 생성
Tool Properties → Parameters 설정:
| Label | Data Type | 유형 | Direction | 필터 |
|---|---|---|---|---|
| 네트워크 데이터(라인 피처) | Feature Class | 필요한 정보 | Input | 피처 유형(Polyline) |
관련 도구
참고 문헌
- Tobler, W. (1993). Three presentations on geographical analysis and modeling: Non-isotropic geographic modeling speculations on the geometry of geography global spatial analysis. Technical Report, 93-1. National Center for Geographic Information and Analysis, University of California, Santa Barbara.
- Kondo, Y., & Seino, Y. (2010). GPS-aided walking experiments and data-driven travel cost modeling on the historical road of Nakasendō-Kisoji (Central Highland Japan). In B. Frischer (Ed.), Making history interactive: Computer applications and quantitative methods in archaeology (CAA). Proceedings of the 37th international conference (pp. 158-165). Archaeopress.
- American College of Sports Medicine (ACSM). (2013). ACSM's guidelines for exercise testing and prescription (9th ed.). Lippincott Williams & Wilkins.
외부 링크
- ArcGIS Pro - What is a network dataset?
- ArcGIS Pro - Network attributes
- ArcGIS Pro - Interpolate Shape
활용 모델 2: 래스터 기반 최소 비용 경로 분석
개요
DEM(Digital Elevation Model) 기반의 비등방(anisotropic) 비용면을 구성하고, Tobler 및 Kondo-Seino 도보 함수를 수직 인자(Vertical Factor)로 정의하여 오르막/내리막에 따른 방향 비대칭 비용을 반영하는 최소 비용 경로 분석 방법론이다.
- 프로젝트 좌표계: EPSG:5179 (Korea 2000 / Central Belt)
- DEM 해상도: SRTM 30m
분석 프로세스
1단계: 평지 비용 래스터 생성
시간 비용 래스터 (단위: 초/30m)
| 지형 유형 | Tobler 함수 | Kondo-Seino 함수 |
|---|---|---|
| 평지 | 21.44 | 24.79 |
| 하천 영역 | 90 | 90 |
| 바다 영역 | 21.6 | 21.6 |
Tobler 시간 비용 래스터 생성 (Raster Calculator)
# 1단계: DEM 있는 곳을 평지 값으로
Con(~IsNull("srtm_5179"), 21.44)
# 출력: base_land_tob
# 2단계: 바다 영역 확장
Con(IsNull("바다_조선5179"), 0, "바다_조선5179")
# 출력: sea_5179
# 3단계: 바다 적용
Con("sea_5179" == 1, 21.6, "base_land_tob")
# 출력: with_sea_tob
# 4단계: 하천 영역 확장
Con(IsNull("하천_5179"), 0, "하천_5179")
# 출력: river_5179
# 5단계: 하천 적용 (최종)
Con("river_5179" == 1, 90, "with_sea_tob")
# 출력: cost_tobKondo-Seino 시간 비용 래스터 생성
# 6단계: DEM 있는 곳을 평지 값으로
Con(~IsNull("srtm_5179"), 24.79)
# 출력: base_land_ks
# 7단계: 바다 적용
Con("sea_5179" == 1, 21.6, "base_land_ks")
# 출력: with_sea_ks
# 8단계: 하천 적용 (최종)
Con("river_5179" == 1, 90, "with_sea_ks")
# 출력: cost_ks칼로리 비용 래스터 (단위: kcal/kg/30m)
| 지형 유형 | Tobler 함수 | Kondo-Seino 함수 |
|---|---|---|
| 평지 | 0.0202 | 0.0212 |
| 하천 영역 | NoData | NoData |
| 바다 영역 | NoData | NoData |
Tobler 칼로리 비용 래스터 생성
# 9단계: DEM 있는 곳을 평지 값으로
Con(~IsNull("srtm_5179"), 0.0202)
# 출력: base_land_tk
# 10단계: 바다 적용
SetNull("sea_5179" == 1, "base_land_tk")
# 출력: with_sea_tk
# 11단계: 하천 적용 (최종)
SetNull("river_5179" == 1, "with_sea_tk")
# 출력: cost_tob_kKondo-Seino 칼로리 비용 래스터 생성
# 12단계: DEM 있는 곳을 평지 값으로
Con(~IsNull("srtm_5179"), 0.0212)
# 출력: base_land_kk
# 13단계: 바다 적용
SetNull("sea_5179" == 1, "base_land_kk")
# 출력: with_sea_kk
# 14단계: 하천 적용 (최종)
SetNull("river_5179" == 1, "with_sea_kk")
# 출력: cost_ks_k2단계: 출발지와 도착지 설정
- 출발지점과 도착지점을 각각 포인트 피처로 생성
- 좌표계: EPSG:5179
3단계: 분석 범위 설정
도구: Extract by Mask (공간 분석 도구)
- 입력 래스터: 비용 래스터 (cost_tob, cost_ks 등)
- 마스크 피처: 분석 범위 폴리곤
- 목적: 분석 범위에 해당하는 비용 래스터 추출
4단계: 거리 누적 및 역방향 래스터 생성
방법 1: Distance Accumulation (ArcGIS Pro 내장 함수)
도구: Distance Accumulation
| 매개변수 | 설정값 |
|---|---|
| 입력 래스터 또는 피처 시작지점 | 출발지점 포인트 피처 |
| 입력 표면 래스터 | 마스크로 추출한 DEM |
| 입력 비용 래스터 | 마스크로 추출한 비용 래스터 |
| 결과 역방향 래스터 | (경로 지정) |
| 거리 방법 | 측지 |
| 입력 수직 래스터 | DEM |
| 수직 계수 | 하이킹 시간 (Hiking Time) |
| 임계각 | -90 ~ 90 |
특징:
- ArcGIS Pro에 내장된 Tobler(1993) 함수 근사치 사용
- 수직 상대 이동각(VRMA)에 따라 평지 대비 소요 시간 배율(VF) 자동 계산
- 오르막과 내리막이 비대칭적으로 처리됨
- 주의: 일정 경사 구간으로 나누어 근사한 함수이므로, 세밀한 지형 분석에서는 실제 Tobler 함수와 차이가 발생할 수 있음
방법 2: Path Distance (사용자 정의 수직 계수)
도구: Path Distance
| 매개변수 | 설정값 |
|---|---|
| 입력 래스터 또는 피처 소스 데이터 | 출발지점 포인트 피처 |
| 입력 비용 래스터 | 마스크로 추출한 비용 래스터 |
| 입력 표면 래스터 | 마스크로 추출한 DEM |
| 결과 백링크 래스터 | (경로 지정) |
| 입력 수직 래스터 | DEM |
| 수직 계수 | VF Table (사용자 정의) |
특징:
- Tobler 또는 Kondo-Seino 함수를 정확히 구현한 VF 테이블 사용
- 시간 비용(s/m) 및 칼로리 비용(kcal/kg/m) 모두 계산 가능
5단계: Dijkstra 알고리즘 기반 최소 비용 경로 계산
알고리즘 개요
Dijkstra 알고리즘은 가중치가 있는 그래프에서 한 시작 지점으로부터 다른 모든 지점까지의 최단 경로를 구하는 기법이다. 간선의 가중치가 음수가 아닌 경우 정확한 최단 거리를 보장한다.
계산 과정:
- 비용 래스터 준비: 평지 비용 + 수직 계수
- Cost Distance 계산:
- 초기화: 모든 셀의 누적비용을 ∞로 설정, 출발 셀만 0으로 설정
- 가장 낮은 비용 셀 선택: Open List에서 누적비용이 가장 작은 셀을 확정(visited) 처리
- 이웃 셀 비용 계산: 확정된 셀의 주변 8개 이웃에 대해 새비용 = 확정된 셀 비용 + 이웃 셀 비용 계산
- 더 낮은 비용 발견 시 업데이트 및 Backlink Raster에 방향 기록
- 목적지 도달까지 반복
- Cost Path 추출: 도착지에서 Backlink를 따라 역추적
결과물:
- Distance Raster: 출발지에서 각 셀까지의 최소 누적비용
- Backlink Raster: 각 셀에서 출발 지점으로 돌아갈 때 다음에 밟아야 할 셀의 방향 (0=소스, 1=E, 2=SE, 3=S, 4=SW, 5=W, 6=NW, 7=N, 8=NE)
Backlink 방향 코드
| 코드 | 방향 | 설명 |
|---|---|---|
| 0 | - | 소스(출발지) |
| 1 | E | 동쪽 |
| 2 | SE | 남동쪽 |
| 3 | S | 남쪽 |
| 4 | SW | 남서쪽 |
| 5 | W | 서쪽 |
| 6 | NW | 북서쪽 |
| 7 | N | 북쪽 |
| 8 | NE | 북동쪽 |
6단계: 최저비용경로 추출
Distance Accumulation 사용 시
도구: Optimal Path As Line
| 매개변수 | 설정값 |
|---|---|
| 입력 래스터 또는 피처 목적지 | 도착지점 포인트 피처 |
| 입력 거리 누적 래스터 | Distance Raster |
| 입력 역방향 또는 흐름 방향 래스터 | Backlink Raster |
| 래스터 형식 결과 최적 경로 | (경로 지정) |
| 경로 유형 | 최적(BEST_SINGLE) |
Path Distance 사용 시
도구: Cost Path
| 매개변수 | 설정값 |
|---|---|
| 래스터 또는 피처 대상 데이터 입력 | 도착지점 포인트 피처 |
| 대상 필드 | (자동 선택) |
| 입력 비용 거리 래스터 | Distance Raster |
| 입력 비용 백링크 래스터 | Backlink Raster |
| 결과 래스터 | (경로 지정) |
| 경로 유형 | 최적(BEST_SINGLE) |
7단계: 래스터를 벡터로 변환
도구: Raster to Polyline
- 입력 래스터: 최적 경로 래스터
- 폴리라인 단순화: 체크 해제
- 출력: 최적 경로 라인 피처
8단계: 구간별 시간/칼로리 계산
- 구간 분할 간격: 10m
- 사용자 정의 도구: LineCarculate
- 계산 항목: 구간별 이동 시간, 칼로리 소비량
비용 래스터 보정
특정 구간에서 비용을 조정하여 제한 요소(선호, 회피)를 반영할 수 있다.
1단계: 네트워크 라인 래스터화
도구: Feature to Raster
- 입력: 네트워크 라인 피처
- 출력 셀 크기: DEM과 동일
- 결과: 라인 셀=1, 비라인 셀=0 또는 NoData
필요시 Reclassify 도구로 NoData를 0으로 변경
2단계: 비용 조정 래스터 계산
Raster Calculator 수식:
# 네트워크 라인 구역에서 비용 1/1.6배 감소 (선호)
cost_li = "cost_" * Con("line_raster" == 1, 1/1.6, 1)
# 네트워크 라인 구역에서 비용 1.3배 증가 (회피)
cost_li = "cost_" * Con("line_raster" == 1, 1.3, 1)
# 도로는 선호(1/1.6배), 하천은 회피(1.3배)
cost_li = "cost_" * Con("도로_srtm30" == 1, 1/1.6, 1) * Con("하천_srtm30" == 1, 1.3, 1)조정 계수:
- 선호 경로: 1/1.6 ≈ 0.625 (비용 약 37.5% 감소)
- 회피 경로: 1.3 (비용 30% 증가)
참고 문헌
- Tobler, W. (1993). Three presentations on geographical analysis and modeling: Non-isotropic geographic modeling speculations on the geometry of geography global spatial analysis. Technical Report, 93-1. National Center for Geographic Information and Analysis, University of California, Santa Barbara.
- Kondo, Y., & Seino, Y. (2010). GPS-aided walking experiments and data-driven travel cost modeling on the historical road of Nakasendō-Kisoji (Central Highland Japan). In B. Frischer (Ed.), Making history interactive: Computer applications and quantitative methods in archaeology (CAA). Proceedings of the 37th international conference (pp. 158-165). Archaeopress.
외부 링크
참고 문헌
- 양정현, 2023, HGIS를 통해 본 조선 전기 양주의 도로 체계와 역사적 경로 재현 - 양주 회암사의 사례를 중심으로, 남도문화연구 50
- 양정현, 2024, 조선시대 도로 네트워크 분석 방법론과 사례 연구 - 양주의 사례를 중심으로, 문화역사지리 36-3
- 양정현, 2025, '신출귀몰'의 재구성 - 역사 연구와 GIS가 연결되는 한 방식, 역사비평 153 (발행 예정)
메모
- todo: 2차 및 3차 지형도의 도로 등급 반영 후 네트워크 데이터셋에 계층 속성 추가
DB 변화 이력
- 2025년 09월 03일 : 최초 공개
- 2025년 11월 14일 : '네트워크 데이터셋 기반 최소 비용 경로 분석' 항목 보완 및 '래스터 기반 최소 비용 경로 분석' 항목 추가