-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathrun_pipeline.sh
More file actions
178 lines (162 loc) · 8.09 KB
/
Copy pathrun_pipeline.sh
File metadata and controls
178 lines (162 loc) · 8.09 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
#!/usr/bin/env bash
# run_pipeline.sh
# Single entry point for the waterway gap analysis pipeline.
#
# Usage:
# bash run_pipeline.sh --name <slug> --bbox "<S> <W> <N> <E>"
# [--threshold N] [--cell-size M] [--skip-download]
# [--osm-file <path>]
#
# Example:
# bash run_pipeline.sh --name siquijor --bbox "9.0 123.4 9.4 123.8"
# bash run_pipeline.sh --name leyte --bbox "10.0 124.0 11.5 125.5"
# bash run_pipeline.sh --name catanduanes --bbox "13.4791 123.9807 14.1459 124.4971" \
# --osm-file /path/to/philippines-latest.osm.pbf
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# ── Defaults ──────────────────────────────────────────────────────────────────
NAME=""
BBOX=""
THRESHOLD=200
CELL_SIZE=200
SKIP_DOWNLOAD=false
OSM_FILE=""
STREAM_MEXP=0
USE_CARVE=false
CARVE_WIDTH=90
CARVE_DEPTH=5.0
# ── Arg parsing ───────────────────────────────────────────────────────────────
while [[ $# -gt 0 ]]; do
case "$1" in
--name) NAME="$2"; shift 2 ;;
--bbox) BBOX="$2"; shift 2 ;;
--threshold) THRESHOLD="$2"; shift 2 ;;
--cell-size) CELL_SIZE="$2"; shift 2 ;;
--skip-download) SKIP_DOWNLOAD=true; shift ;;
--osm-file) OSM_FILE="$2"; shift 2 ;;
--mexp) STREAM_MEXP="$2"; shift 2 ;;
--carve) USE_CARVE=true; shift ;;
--carve-width) CARVE_WIDTH="$2"; shift 2 ;;
--carve-depth) CARVE_DEPTH="$2"; shift 2 ;;
*) echo "Unknown argument: $1" >&2; exit 1 ;;
esac
done
if [[ -z "${NAME}" || -z "${BBOX}" ]]; then
echo "Usage: bash run_pipeline.sh --name <slug> --bbox \"<S> <W> <N> <E>\"" >&2
exit 1
fi
# ── Parse bbox ────────────────────────────────────────────────────────────────
read -r BBOX_S BBOX_W BBOX_N BBOX_E <<< "${BBOX}"
# ── Compute UTM zone from center longitude ────────────────────────────────────
read -r UTM_ZONE GRASS_EPSG <<< "$(python3 - <<PYEOF
import math
center_lon = ($BBOX_W + $BBOX_E) / 2
zone = int(math.floor((center_lon + 180) / 6) + 1)
epsg = 32600 + zone # N hemisphere
print(zone, epsg)
PYEOF
)"
echo "==> Name: ${NAME}"
echo "==> BBox: S=${BBOX_S} W=${BBOX_W} N=${BBOX_N} E=${BBOX_E}"
echo "==> UTM zone: ${UTM_ZONE} (EPSG:${GRASS_EPSG})"
echo "==> Threshold: ${THRESHOLD} cells"
echo "==> Cell size: ${CELL_SIZE} m"
echo "==> mexp: ${STREAM_MEXP}"
echo "==> Carve OSM: ${USE_CARVE} (width=${CARVE_WIDTH}m depth=${CARVE_DEPTH}m)"
# ── Set env vars for downstream scripts ───────────────────────────────────────
export NAME
export GRASS_LOCATION="ph_utm${UTM_ZONE}n"
export GRASS_EPSG="${GRASS_EPSG}"
export SRTM_DIR_OVERRIDE="${SCRIPT_DIR}/data/srtm"
export OSM_WATERWAYS_PATH="${SCRIPT_DIR}/data/osm/waterways_${NAME}.gpkg"
export OUTPUT_DIR_OVERRIDE="${SCRIPT_DIR}/output/${NAME}"
export BBOX_S BBOX_W BBOX_N BBOX_E
export OSM_FILE
export STREAM_MEXP USE_CARVE CARVE_WIDTH CARVE_DEPTH
mkdir -p "${OUTPUT_DIR_OVERRIDE}"
# ── Step 1: Download ──────────────────────────────────────────────────────────
if [[ "${SKIP_DOWNLOAD}" == "false" ]]; then
echo ""
echo "==> [1/3] Downloading data …"
if [[ -n "${OSM_FILE}" ]]; then
# Pre-create GPKG placeholder so 00_download.sh skips the Overpass OSM step.
# The conversion block below will overwrite it with the real data.
mkdir -p "${SCRIPT_DIR}/data/osm"
touch "${SCRIPT_DIR}/data/osm/waterways_${NAME}.gpkg"
fi
bash "${SCRIPT_DIR}/00_download.sh" --name "${NAME}" --bbox "${BBOX}"
else
echo ""
echo "==> [1/3] Skipping download (--skip-download)"
fi
# ── OSM file conversion (when --osm-file is provided) ─────────────────────────
if [[ -n "${OSM_FILE}" ]]; then
echo ""
echo "==> Converting OSM file to GPKG: ${OSM_FILE}"
OUT_GPKG="${SCRIPT_DIR}/data/osm/waterways_${NAME}.gpkg"
mkdir -p "${SCRIPT_DIR}/data/osm"
if [[ "${OSM_FILE}" == *.pbf ]]; then
TMP_WW=$(mktemp /tmp/ww_XXXXXX.osm.pbf)
osmium tags-filter "${OSM_FILE}" w/waterway=river,stream,canal,drain,ditch,tidal_channel \
-o "${TMP_WW}" --overwrite
ogr2ogr -f GPKG "${OUT_GPKG}" "${TMP_WW}" lines \
-nln waterways \
-where "waterway IN ('river','stream','canal','drain','ditch','tidal_channel')" \
-spat "${BBOX_W}" "${BBOX_S}" "${BBOX_E}" "${BBOX_N}" -overwrite
rm -f "${TMP_WW}"
else
# .gpkg / .shp / .geojson
ogr2ogr -f GPKG "${OUT_GPKG}" "${OSM_FILE}" \
-nln waterways \
-where "waterway IN ('river','stream','canal','drain','ditch','tidal_channel')" \
-spat "${BBOX_W}" "${BBOX_S}" "${BBOX_E}" "${BBOX_N}" -overwrite
fi
echo "==> OSM GPKG written: ${OUT_GPKG}"
fi
# ── OSM named lakes from PBF ──────────────────────────────────────────────────
LAKES_GPKG="${SCRIPT_DIR}/data/osm/lakes_${NAME}.gpkg"
if [[ -n "${OSM_FILE}" ]]; then
echo ""
echo "==> Extracting named lakes from OSM file: ${OSM_FILE}"
if [[ "${OSM_FILE}" == *.pbf ]]; then
TMP_WATER=$(mktemp /tmp/water_XXXXXX.osm.pbf)
osmium tags-filter "${OSM_FILE}" wr/natural=water \
-o "${TMP_WATER}" --overwrite
ogr2ogr -f GPKG "${LAKES_GPKG}" "${TMP_WATER}" multipolygons \
-nln lakes \
-where "natural = 'water' AND name IS NOT NULL AND (other_tags LIKE '%\"water\"=>\"lake\"%' OR other_tags LIKE '%\"water\"=>\"reservoir\"%' OR other_tags LIKE '%\"water\"=>\"lagoon\"%' OR other_tags LIKE '%\"wikidata\"%')" \
-spat "${BBOX_W}" "${BBOX_S}" "${BBOX_E}" "${BBOX_N}" -overwrite 2>/dev/null || true
rm -f "${TMP_WATER}"
else
echo "==> Non-PBF OSM file: attempting direct ogr2ogr for lakes …"
ogr2ogr -f GPKG "${LAKES_GPKG}" "${OSM_FILE}" \
-nln lakes \
-where "natural = 'water' AND name IS NOT NULL" \
-spat "${BBOX_W}" "${BBOX_S}" "${BBOX_E}" "${BBOX_N}" -overwrite 2>/dev/null || true
fi
echo "==> OSM lakes GPKG written: ${LAKES_GPKG}"
fi
# Validate: remove empty GPKG (no layers → v.import fails)
if [[ -f "${LAKES_GPKG}" ]]; then
_lake_feat=$(ogrinfo -al -so "${LAKES_GPKG}" 2>/dev/null | awk '/Feature Count/{sum+=$3} END{print sum+0}')
if [[ "${_lake_feat}" -eq 0 ]]; then
echo "==> No named lakes found in bbox; removing empty GPKG"
rm -f "${LAKES_GPKG}"
LAKES_GPKG=""
else
echo "==> ${_lake_feat} named lake feature(s) found"
fi
fi
export LAKES_GPKG
# ── Step 2: GRASS hydrology ───────────────────────────────────────────────────
echo ""
echo "==> [2/3] Running GRASS hydrology pipeline …"
bash "${SCRIPT_DIR}/01_grass_hydro.sh" "${THRESHOLD}" "${CELL_SIZE}"
# ── Step 3: Python gap analysis ───────────────────────────────────────────────
echo ""
echo "==> [3/3] Computing gap scores …"
python3 "${SCRIPT_DIR}/02_grid_analysis.py" --output-dir "${OUTPUT_DIR_OVERRIDE}"
python3 -c "import json; json.dump({'name':'${NAME}','bbox':[${BBOX_W},${BBOX_S},${BBOX_E},${BBOX_N}]}, open('${OUTPUT_DIR_OVERRIDE}/metadata.json','w'))"
echo ""
echo "==> Pipeline complete."
echo " Output: ${OUTPUT_DIR_OVERRIDE}/gap_analysis.geojson"