Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
188 changes: 183 additions & 5 deletions bin/lib.sh
Original file line number Diff line number Diff line change
Expand Up @@ -413,6 +413,7 @@ n/tourism=hotel,motel,guest_house
n/shop=gas
n/cuisine
n/highway=rest_area,services
w/highway=construction
w/highway=motorway,motorway_link,trunk,trunk_link,rest_area,services
w/amenity=fuel,restaurant,fast_food,cafe,toilets,charging_station
w/tourism=hotel,motel,guest_house
Expand Down Expand Up @@ -447,7 +448,7 @@ oi_filter_pbf() {
)"
if [[ "$force" != true && -s "$output_pbf" ]]; then
if [[ "$(oi_state_read "$state_file" signature 2>/dev/null || true)" == "$expected_signature" ]] \
|| [[ ! "$input_pbf" -nt "$output_pbf" ]]; then
&& [[ ! "$input_pbf" -nt "$output_pbf" ]]; then
oi_log "Skipping canonical filter; output is current"
echo " output: $output_pbf" >&2
oi_state_write "$state_file" \
Expand Down Expand Up @@ -485,6 +486,123 @@ oi_filter_pbf() {
printf '%s\n' "$output_pbf"
}

oi_safe_slug() {
local raw="${1:-}"
local slug

slug="$(
printf '%s' "$raw" \
| LC_ALL=C tr -cs 'A-Za-z0-9._-' '-' \
| sed -e 's/^-*//' -e 's/-*$//'
)"

if [[ -z "$slug" ]]; then
slug="focus"
fi

printf '%s\n' "$slug"
}

oi_expand_bbox() {
local bbox="$1"
local buffer_km="${2:-0}"

python3 - "$bbox" "$buffer_km" <<'PY'
import math
import sys

bbox = sys.argv[1]
buffer_km = float(sys.argv[2])
parts = [float(part.strip()) for part in bbox.split(",") if part.strip()]
if len(parts) != 4:
raise SystemExit("bbox must be LONG1,LAT1,LONG2,LAT2")

left, bottom, right, top = parts
left, right = sorted((left, right))
bottom, top = sorted((bottom, top))

if buffer_km > 0:
lat_pad = buffer_km / 111.32
mid_lat = (bottom + top) / 2.0
lon_scale = max(math.cos(math.radians(mid_lat)) * 111.32, 0.01)
lon_pad = buffer_km / lon_scale
left -= lon_pad
right += lon_pad
bottom -= lat_pad
top += lat_pad

print(f"{left:.7f},{bottom:.7f},{right:.7f},{top:.7f}")
PY
}

oi_extract_region_pbf() {
local source_pbf="$1"
local output_pbf="$2"
local bbox="$3"
local buffer_km="${4:-0}"
local force="${5:-false}"
local output_tmp expanded_bbox state_file expected_signature

source_pbf="$(oi_abs_path "$source_pbf")"
output_pbf="$(oi_managed_path "$output_pbf")"

if [[ ! -f "$source_pbf" ]]; then
oi_die "source PBF not found: $source_pbf"
fi

mkdir -p "$(dirname "$output_pbf")"
expanded_bbox="$(oi_expand_bbox "$bbox" "$buffer_km")"
state_file="$(oi_state_file extract "$output_pbf")"
expected_signature="$(
{
oi_file_signature "$source_pbf"
printf 'bbox=%s\n' "$expanded_bbox"
printf 'strategy=complete_ways\n'
} | oi_hash_stdin
)"

if [[ "$force" != true && -s "$output_pbf" ]]; then
if [[ "$(oi_state_read "$state_file" signature 2>/dev/null || true)" == "$expected_signature" ]] \
&& [[ ! "$source_pbf" -nt "$output_pbf" ]]; then
oi_log "Skipping focused extract; output is current"
echo " bbox: $expanded_bbox" >&2
echo " output: $output_pbf" >&2
printf '%s\n' "$output_pbf"
return 0
fi
fi

if [[ "$output_pbf" == *.osm.pbf ]]; then
output_tmp="${output_pbf%.osm.pbf}.tmp.$$.osm.pbf"
else
output_tmp="${output_pbf}.tmp.$$"
fi

oi_log "Extracting focused regional PBF"
echo " source: $source_pbf" >&2
echo " bbox: $expanded_bbox" >&2
echo " output: $output_pbf" >&2

oi_require_cmd osmium
osmium extract \
--bbox "$expanded_bbox" \
--strategy=complete_ways \
--set-bounds \
"$source_pbf" \
-o "$output_tmp" \
-O

mv "$output_tmp" "$output_pbf"
oi_state_write "$state_file" \
signature "$expected_signature" \
source_pbf "$source_pbf" \
bbox "$expanded_bbox" \
output_pbf "$output_pbf" \
completed_at "$(date -u '+%Y-%m-%dT%H:%M:%SZ')"

printf '%s\n' "$output_pbf"
}

oi_canonical_tables_exist() {
local exists
exists="$(oi_db_query "SELECT CASE WHEN to_regclass('public.osm2pgsql_v2_highways') IS NULL THEN 0 ELSE 1 END;" 2>/dev/null || true)"
Expand Down Expand Up @@ -715,6 +833,7 @@ oi_import_canonical() {
oi_assert_canonical_import_ready
oi_state_write "$import_state_file" \
signature "$import_signature" \
source_pbf "$source_pbf" \
import_pbf "$import_pbf" \
mode "$import_mode" \
use_flatnodes "$use_flatnodes" \
Expand All @@ -723,10 +842,55 @@ oi_import_canonical() {
printf '%s\n' "$import_pbf"
}

oi_extract_interstate_relation_cache() {
local import_state_file source_pbf extractor_script cache_path cache_state_file
local cache_signature stored_signature

import_state_file="$(oi_state_file import "$OI_DATA_ROOT|$OI_DB_NAME")"
source_pbf="$(oi_state_read "$import_state_file" source_pbf 2>/dev/null || true)"
if [[ -z "$source_pbf" || ! -f "$source_pbf" ]]; then
if [[ -f "$OI_DOWNLOAD_DIR/us-latest.osm.pbf" ]]; then
source_pbf="$OI_DOWNLOAD_DIR/us-latest.osm.pbf"
else
printf '%s\n' ""
return 0
fi
fi

extractor_script="$REPO_ROOT/tooling/extract_interstate_relations.py"
cache_path="$OI_CACHE_DIR/interstate-relations.tsv"
cache_state_file="$(oi_state_file interstate-relations "$OI_DATA_ROOT|$OI_DB_NAME")"
cache_signature="$(
{
oi_file_signature "$source_pbf"
printf 'extractor=%s\n' "$(oi_hash_files "$extractor_script")"
} | oi_hash_stdin
)"
stored_signature="$(oi_state_read "$cache_state_file" signature 2>/dev/null || true)"

if [[ -f "$cache_path" && "$stored_signature" == "$cache_signature" ]]; then
printf '%s\n' "$cache_path"
return 0
fi

oi_log "Extracting Interstate relation cache from source PBF"
python3 "$extractor_script" \
--source-pbf "$source_pbf" \
--output "$cache_path"

oi_state_write "$cache_state_file" \
signature "$cache_signature" \
source_pbf "$source_pbf" \
cache_path "$cache_path" \
completed_at "$(date -u '+%Y-%m-%dT%H:%M:%SZ')"

printf '%s\n' "$cache_path"
}

oi_apply_derive() {
local derive_file="$REPO_ROOT/schema/derive.sql"
local derive_state_file import_state_file import_signature derive_signature derive_sql_signature derive_code_signature
local reachability_signature
local reachability_signature relation_cache_file relation_cache_signature
local -a derive_source_files=()

oi_guard_no_reachability_clears "$derive_file"
Expand All @@ -749,6 +913,12 @@ oi_apply_derive() {
)
derive_sql_signature="$(oi_hash_files "$derive_file")"
derive_code_signature="$(oi_hash_files "${derive_source_files[@]}")"
relation_cache_file="$(oi_extract_interstate_relation_cache)"
if [[ -n "$relation_cache_file" && -f "$relation_cache_file" ]]; then
relation_cache_signature="$(oi_file_signature "$relation_cache_file")"
else
relation_cache_signature="no-relation-cache"
fi
reachability_signature="$(
{
oi_db_query "SELECT COUNT(*), COALESCE(MAX(updated_at)::text, '') FROM exit_poi_reachability;" 2>/dev/null || true
Expand All @@ -760,6 +930,7 @@ oi_apply_derive() {
printf 'import=%s\n' "$import_signature"
printf 'derive_sql=%s\n' "$derive_sql_signature"
printf 'derive_code=%s\n' "$derive_code_signature"
printf 'relation_cache=%s\n' "$relation_cache_signature"
printf 'reachability=%s\n' "$reachability_signature"
} | oi_hash_stdin
)"
Expand All @@ -773,9 +944,16 @@ oi_apply_derive() {
oi_db_exec psql -U "$OI_DB_USER" -d "$OI_DB_NAME" -v ON_ERROR_STOP=1 < "$derive_file"

oi_log "Building graph, corridors, and reference routes"
oi_runner cargo run --release -p openinterstate-derive -- \
--database-url "$PRODUCT_DB_URL" \
all
if [[ -n "$relation_cache_file" && -f "$relation_cache_file" ]]; then
oi_runner cargo run --release -p openinterstate-derive -- \
--database-url "$PRODUCT_DB_URL" \
--interstate-relation-cache "$(oi_container_path "$relation_cache_file")" \
all
else
oi_runner cargo run --release -p openinterstate-derive -- \
--database-url "$PRODUCT_DB_URL" \
all
fi
oi_state_write "$derive_state_file" \
signature "$derive_signature" \
import_signature "$import_signature" \
Expand Down
100 changes: 100 additions & 0 deletions bin/openinterstate
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ Usage:

Commands:
build Download or use a source PBF, import it, derive data, and export a release
focus-build Extract a bounded regional PBF and run the same import, derive, and release flow on it
download Download the latest U.S. PBF into the managed data directory
import Import canonical OSM into local PostGIS
derive Build product tables, graph data, corridors, and reference routes
Expand All @@ -33,6 +34,13 @@ External data volume with explicit release output:
--data-dir /Volumes/goose-drive/openinterstate-data \
--release-dir /Volumes/goose-drive/openinterstate-releases \
build

Focused local iteration on the George Washington Bridge I-95 area:
./bin/openinterstate focus-build \
--source-pbf-file /abs/path/to/us-latest.osm.pbf \
--bbox -73.99,40.84,-73.94,40.88 \
--buffer-km 2 \
--extract-name i95-gwb
USAGE
}

Expand Down Expand Up @@ -153,6 +161,98 @@ case "$COMMAND" in
echo " release: $OUTPUT_ROOT/$RELEASE_ID"
echo " archive: $OUTPUT_ROOT/openinterstate-$RELEASE_ID.tar.gz"
;;
focus-build)
SOURCE_PBF_FILE=""
BBOX=""
BUFFER_KM="0"
EXTRACT_NAME=""
RELEASE_ID=""
OUTPUT_ROOT="$OI_RELEASE_DIR"
PREFILTER=true
FORCE_PREFILTER=false
FORCE_EXTRACT=false

while [[ $# -gt 0 ]]; do
case "$1" in
--source-pbf-file)
SOURCE_PBF_FILE="$2"
shift 2
;;
--bbox)
BBOX="$2"
shift 2
;;
--buffer-km)
BUFFER_KM="$2"
shift 2
;;
--extract-name)
EXTRACT_NAME="$2"
shift 2
;;
--release-id)
RELEASE_ID="$2"
shift 2
;;
--output-root)
OUTPUT_ROOT="$2"
shift 2
;;
--release-dir)
OUTPUT_ROOT="$2"
shift 2
;;
--skip-prefilter)
PREFILTER=false
shift 1
;;
--force-prefilter)
FORCE_PREFILTER=true
shift 1
;;
--force-extract)
FORCE_EXTRACT=true
shift 1
;;
-h|--help)
usage
exit 0
;;
*)
oi_die "unknown arg: $1"
;;
esac
done

[[ -n "$SOURCE_PBF_FILE" ]] || oi_die "--source-pbf-file is required"
[[ -n "$BBOX" ]] || oi_die "--bbox is required"

if [[ -z "$EXTRACT_NAME" ]]; then
EXTRACT_NAME="focus-$(printf '%s|%s|%s\n' "$SOURCE_PBF_FILE" "$BBOX" "$BUFFER_KM" | oi_hash_stdin | cut -c1-12)"
fi
EXTRACT_NAME="$(oi_safe_slug "$EXTRACT_NAME")"
if [[ -z "$RELEASE_ID" ]]; then
RELEASE_ID="focus-${EXTRACT_NAME}-$(date +%F)"
fi

EXTRACT_PBF="$OI_DOWNLOAD_DIR/${EXTRACT_NAME}.osm.pbf"
FOCUSED_SOURCE_PBF="$(oi_extract_region_pbf "$SOURCE_PBF_FILE" "$EXTRACT_PBF" "$BBOX" "$BUFFER_KM" "$FORCE_EXTRACT")" || exit $?

export OSM2PGSQL_MODE=create
export OI_ALLOW_CANONICAL_RESET=true

oi_db_up
oi_wait_for_db

IMPORT_PBF="$(oi_import_canonical "$FOCUSED_SOURCE_PBF" "$PREFILTER" "$FORCE_PREFILTER")" || exit $?
oi_apply_derive
oi_export_release "$RELEASE_ID" "$FOCUSED_SOURCE_PBF" "$IMPORT_PBF" "" "$OUTPUT_ROOT"

oi_log "Focused build complete"
echo " extract: $FOCUSED_SOURCE_PBF"
echo " release: $OUTPUT_ROOT/$RELEASE_ID"
echo " archive: $OUTPUT_ROOT/openinterstate-$RELEASE_ID.tar.gz"
;;
download)
SOURCE_URL="$OI_DEFAULT_US_PBF_URL"
OUTPUT=""
Expand Down
1 change: 1 addition & 0 deletions crates/derive/src/canonical_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ pub struct ParsedExit {

#[derive(Debug, Clone)]
pub struct ParsedHighway {
pub way_id: i64,
pub refs: Vec<String>,
pub nodes: Vec<i64>,
pub geometry: Vec<(f64, f64)>,
Expand Down
Loading
Loading