#!/bin/bash # -------------------------------------------------------------------------- # overture_ingest.sh — سحب وحقن بيانات Overture (مباني، شوارع، أماكن، حدود) # # ⚠️ تغيير جوهري عن النسخة القديمة: # القديمة كانت تبدأ بـ DROP TABLE overture_place, overture_building, # overture_segment — أي أن تشغيلها لأي دولة يمسح بيانات كل الدول الأخرى. # الآن الحذف *محصور بالنطاق الجغرافي* الجاري إعادة حقنه فقط، فتبقى بيانات # بقية الدول سليمة. هذا يجعل السكربت قابلاً للتشغيل الجزئي وإعادة التشغيل. # # الاستخدام: # bash infrastructure/scripts/overture_ingest.sh # كل النطاقات # bash infrastructure/scripts/overture_ingest.sh baghdad # نطاق واحد # bash infrastructure/scripts/overture_ingest.sh iraq baghdad # عدة نطاقات # # ملاحظة عن الأحجام: المباني تُسحب على مستوى المدينة لا الدولة — مباني دولة # كاملة قد تتجاوز عدة جيجابايت GeoJSON ولا يحتملها قرص السيرفر الحالي. # لذلك: building/segment ← نطاق مدينة، place/division_area ← نطاق دولة. # -------------------------------------------------------------------------- set -euo pipefail APP_DIR="${APP_DIR:-/home/hamzadoctor/app}" DB_HOST="${DB_HOST:-localhost}" DB_PORT="${DB_PORT:-5432}" DB_USER="${DB_USER:-mapuser}" DB_NAME="${DB_NAME:-mapdb}" DB_PASS="${DB_PASS:-mappass}" cd "$APP_DIR" # ── النطاقات: "minlon,minlat,maxlon,maxlat" ─────────────────────────────── # نطاقات الدول (للأماكن والحدود الإدارية) BBOX_JORDAN_NORTH="35.5,31.7,39.3,33.4" BBOX_JORDAN_SOUTH="34.9,29.1,38.0,31.7" BBOX_SYRIA="35.7,32.3,42.4,37.3" BBOX_EGYPT="24.7,21.9,36.9,31.7" BBOX_IRAQ="38.7,29.0,48.8,37.4" # نطاقات المدن (للمباني والشوارع — أثقل بكثير) BBOX_AMMAN="35.75,31.75,36.15,32.15" BBOX_DAMASCUS="36.15,33.40,36.45,33.62" BBOX_CAIRO="31.10,29.85,31.50,30.20" # بغداد المركز فقط (~25×17كم). النطاق الأوسع 44.10,33.10,44.70,33.50 تجاوز # 632 ألف مبنى وملأ قرص السيرفر قبل أن ينتهي. وسّعه بعد ترقية التخزين. BBOX_BAGHDAD="44.28,33.25,44.55,33.40" psql_run() { docker compose -f "${APP_DIR}/docker-compose.yml" exec -T db \ psql -U "$DB_USER" -d "$DB_NAME" "$@" } # ── حذف محصور بالنطاق: ينظّف ما سنعيد حقنه فقط، ولا يمس الدول الأخرى ────── purge_bbox() { local table=$1 bbox=$2 IFS=',' read -r minx miny maxx maxy <<< "$bbox" # to_regclass يمنع الفشل في أول تشغيل قبل أن ينشئ ogr2ogr الجدول psql_run -c " DO \$\$ BEGIN IF to_regclass('public.${table}') IS NOT NULL THEN DELETE FROM ${table} WHERE location && ST_MakeEnvelope(${minx}, ${miny}, ${maxx}, ${maxy}, 4326); END IF; END \$\$; " } process() { local region=$1 bbox=$2 theme=$3 local output_file="overture_${region}_${theme}.geojson" echo "🌍 [$region] السمة: $theme ..." overturemaps download --bbox="$bbox" -f geojson --type="$theme" -o "$output_file" if [ "$theme" == "division_area" ]; then echo "📍 الحدود الإدارية جاهزة: $output_file" echo " استوردها عبر: POST /geocoding/import-boundaries?country=&filePath=/data/${output_file}" return fi echo "🧹 تنظيف نطاق ${region} من overture_${theme} (بدون المساس بغيره)..." purge_bbox "overture_${theme}" "$bbox" echo "🔌 الحقن في PostGIS (overture_${theme})..." export PGPASSWORD=$DB_PASS # `if !` ضروري: مع set -e يخرج السكربت فوراً عند فشل ogr2ogr فلا يُحذف # الملف المؤقت. ملف مباني مدينة كبيرة قد يبلغ عدة جيجابايت، وتَركه بعد # فشل سببه امتلاء القرص أصلاً يجعل المحاولة التالية تفشل أسرع. if ! ogr2ogr -f "PostgreSQL" \ PG:"host=$DB_HOST port=$DB_PORT user=$DB_USER dbname=$DB_NAME password=$DB_PASS" \ "$output_file" \ -nln "overture_${theme}" \ -update -append \ -nlt PROMOTE_TO_MULTI \ -lco GEOMETRY_NAME=location then echo "❌ فشل حقن ${region}/${theme} — يُحذف الملف المؤقت ($(du -h "$output_file" 2>/dev/null | cut -f1))" rm -f "$output_file" return 1 fi echo "✅ انتهى ${region}/${theme}" rm -f "$output_file" } # ── تجهيز بيئة بايثون ───────────────────────────────────────────────────── if [ ! -d "venv_overture" ]; then echo "📦 إنشاء بيئة افتراضية..." python3 -m venv venv_overture fi source venv_overture/bin/activate pip install --quiet --upgrade pip pip install --quiet overturemaps # ── تعريف النطاقات القابلة للتشغيل ──────────────────────────────────────── run_jordan() { process jordan_north "$BBOX_JORDAN_NORTH" division_area process jordan_north "$BBOX_JORDAN_NORTH" place process jordan_north "$BBOX_JORDAN_NORTH" land_cover process jordan_south "$BBOX_JORDAN_SOUTH" division_area process jordan_south "$BBOX_JORDAN_SOUTH" place process jordan_south "$BBOX_JORDAN_SOUTH" land_cover; } run_syria() { process syria "$BBOX_SYRIA" division_area process syria "$BBOX_SYRIA" place process syria "$BBOX_SYRIA" land_cover; } run_egypt() { process egypt "$BBOX_EGYPT" division_area process egypt "$BBOX_EGYPT" place process egypt "$BBOX_EGYPT" land_cover; } run_iraq() { process iraq "$BBOX_IRAQ" division_area process iraq "$BBOX_IRAQ" place process iraq "$BBOX_IRAQ" land_cover; } run_amman() { process amman "$BBOX_AMMAN" building process amman "$BBOX_AMMAN" segment; } run_damascus() { process damascus "$BBOX_DAMASCUS" building process damascus "$BBOX_DAMASCUS" segment; } run_cairo() { process cairo "$BBOX_CAIRO" building process cairo "$BBOX_CAIRO" segment; } run_baghdad() { process baghdad "$BBOX_BAGHDAD" building process baghdad "$BBOX_BAGHDAD" segment; } ALL_REGIONS=(jordan syria egypt iraq amman damascus cairo baghdad) TARGETS=("$@") if [ ${#TARGETS[@]} -eq 0 ]; then TARGETS=("${ALL_REGIONS[@]}") fi echo "🚀 نطاقات هذا التشغيل: ${TARGETS[*]}" for r in "${TARGETS[@]}"; do if ! declare -F "run_${r}" >/dev/null; then echo "❌ نطاق غير معروف: ${r} — المتاح: ${ALL_REGIONS[*]}" exit 2 fi "run_${r}" done # ── فهارس مكانية + تسطيح الأسماء (idempotent) ───────────────────────────── # name_primary إلزامي: عمود names من نوع json وبلاطات MVT لا تحمل كائنات، فلا # يستطيع الستايل قراءة الاسم منه. التفاصيل: infrastructure/sql/09_overture_name_primary.sql for t in overture_building overture_segment overture_place overture_land_cover; do psql_run -c " DO \$\$ BEGIN IF to_regclass('public.${t}') IS NOT NULL THEN EXECUTE 'CREATE INDEX IF NOT EXISTS idx_${t}_location ON ${t} USING gist (location)'; EXECUTE 'ALTER TABLE ${t} ADD COLUMN IF NOT EXISTS name_primary text'; EXECUTE 'UPDATE ${t} SET name_primary = names->>''primary'' WHERE names IS NOT NULL AND name_primary IS NULL'; EXECUTE 'CREATE INDEX IF NOT EXISTS idx_${t}_name_primary ON ${t} (name_primary) WHERE name_primary IS NOT NULL'; END IF; END \$\$; " done # ── إنشاء واستخراج الجروف والموانع التكتيكية آلياً في PostGIS ───────────── echo "⛰️ توليد واستخراج الجروف والموانع الصخرية التكتيكية في PostGIS..." psql_run -c " DO \$\$ BEGIN -- 1. جدول الموانع الصخرية والتكتيكية المدمجة CREATE TABLE IF NOT EXISTS tactical_terrain_obstacles ( id SERIAL PRIMARY KEY, osm_id BIGINT, obstacle_type VARCHAR(64), severity VARCHAR(32), name VARCHAR(255), geometry GEOMETRY(Geometry, 4326), created_at TIMESTAMPTZ DEFAULT NOW() ); CREATE INDEX IF NOT EXISTS idx_tactical_obs_geom ON tactical_terrain_obstacles USING gist (geometry); CREATE INDEX IF NOT EXISTS idx_tactical_obs_type ON tactical_terrain_obstacles (obstacle_type); -- 2. دمج المقاطع الصخرية والموانع من OSM و Overture IF to_regclass('public.planet_osm_line') IS NOT NULL THEN INSERT INTO tactical_terrain_obstacles (osm_id, obstacle_type, severity, name, geometry) SELECT osm_id, COALESCE(natural, barrier, man_made, waterway) AS obstacle_type, CASE WHEN natural = 'cliff' THEN 'SEVERE_NO_GO' WHEN barrier = 'retaining_wall' THEN 'RESTRICTED' WHEN barrier IN ('ditch', 'berm') THEN 'TACTICAL_BARRIER' WHEN waterway = 'wadi' THEN 'DRAINAGE_DEFILE' ELSE 'OBSTACLE' END, name, geometry FROM planet_osm_line WHERE (natural IN ('cliff', 'ridge', 'arete') OR barrier IN ('retaining_wall', 'berm', 'ditch') OR waterway IN ('wadi', 'waterfall')) ON CONFLICT DO NOTHING; END IF; END \$\$; " echo "🎉 اكتمل. البلاطات تُخدم عبر martin تلقائياً (WATCH_DB=true)."