chore: initialize insar management system v2
This commit is contained in:
@@ -0,0 +1,29 @@
|
||||
-- Migration: Create st_intersection_agg function for efficient geometry intersection
|
||||
-- This function reduces a set of geometries to their geometric intersection.
|
||||
-- If no geometries are provided, it returns NULL.
|
||||
|
||||
-- Drop existing aggregate and function if they exist (for idempotency)
|
||||
DROP AGGREGATE IF EXISTS st_intersection_agg(geometry);
|
||||
DROP FUNCTION IF EXISTS st_intersection_agg(geometry, geometry);
|
||||
|
||||
-- Create the aggregation function
|
||||
CREATE OR REPLACE FUNCTION st_intersection_agg(g1 geometry, g2 geometry)
|
||||
RETURNS geometry AS
|
||||
$$
|
||||
SELECT CASE WHEN g1 IS NULL THEN g2
|
||||
WHEN g2 IS NULL THEN g1
|
||||
ELSE ST_Intersection(g1, g2) END;
|
||||
$$
|
||||
LANGUAGE SQL;
|
||||
|
||||
COMMENT ON FUNCTION st_intersection_agg(geometry, geometry) IS
|
||||
'Aggregates a set of geometries by computing the intersection pairwise. Returns NULL if input is empty.';
|
||||
|
||||
-- Create aggregate wrapper for single-argument usage in SQL queries
|
||||
CREATE AGGREGATE st_intersection_agg(geometry) (
|
||||
SFUNC = st_intersection_agg,
|
||||
STYPE = geometry
|
||||
);
|
||||
|
||||
COMMENT ON AGGREGATE st_intersection_agg(geometry) IS
|
||||
'Aggregates a set of geometries by computing the intersection pairwise.';
|
||||
@@ -0,0 +1,319 @@
|
||||
-- Migration: PostGIS helper functions and views for InSAR workflows
|
||||
-- Note: keep this file UTF-8 without BOM to avoid SQL parser issues.
|
||||
|
||||
CREATE EXTENSION IF NOT EXISTS postgis;
|
||||
|
||||
-- =====================================================
|
||||
-- Function: find_dinsar_pairs
|
||||
-- Purpose : Find candidate D-InSAR pairs with spatial/time/overlap constraints
|
||||
-- =====================================================
|
||||
CREATE OR REPLACE FUNCTION find_dinsar_pairs(
|
||||
p_time_baseline_min INTEGER,
|
||||
p_time_baseline_max INTEGER,
|
||||
p_spatial_baseline_max_meters NUMERIC,
|
||||
p_overlap_threshold NUMERIC,
|
||||
p_start_date TEXT DEFAULT NULL,
|
||||
p_aoi_geom GEOMETRY DEFAULT NULL,
|
||||
p_require_orbit_data BOOLEAN DEFAULT TRUE,
|
||||
p_require_same_imaging_mode BOOLEAN DEFAULT TRUE,
|
||||
p_require_same_polarization BOOLEAN DEFAULT TRUE,
|
||||
p_aoi_overlap_threshold NUMERIC DEFAULT NULL
|
||||
)
|
||||
RETURNS TABLE (
|
||||
master_id INTEGER,
|
||||
slave_id INTEGER,
|
||||
master_imaging_date TEXT,
|
||||
slave_imaging_date TEXT,
|
||||
time_baseline_days INTEGER,
|
||||
spatial_baseline_meters NUMERIC,
|
||||
overlap_ratio NUMERIC
|
||||
)
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
BEGIN
|
||||
RETURN QUERY
|
||||
WITH candidate_pairs AS (
|
||||
SELECT
|
||||
m.id AS master_id,
|
||||
s.id AS slave_id,
|
||||
m.imaging_date::text AS master_imaging_date,
|
||||
s.imaging_date::text AS slave_imaging_date,
|
||||
ABS(to_date(s.imaging_date, 'YYYYMMDD') - to_date(m.imaging_date, 'YYYYMMDD')) AS time_baseline_days,
|
||||
ST_DistanceSphere(ST_Centroid(m.geom), ST_Centroid(s.geom))::numeric AS spatial_baseline_meters,
|
||||
(
|
||||
ST_Area(ST_Intersection(m.geom, s.geom)::geography) /
|
||||
NULLIF(GREATEST(ST_Area(m.geom::geography), ST_Area(s.geom::geography)), 0)
|
||||
)::numeric AS overlap_ratio
|
||||
FROM radar_data m
|
||||
JOIN radar_data s ON ST_Intersects(m.geom, s.geom)
|
||||
WHERE m.id < s.id
|
||||
AND m.orbit_direction = s.orbit_direction
|
||||
AND m.satellite = s.satellite
|
||||
AND (NOT p_require_orbit_data OR (m.has_orbit_data = true AND s.has_orbit_data = true))
|
||||
AND (
|
||||
NOT p_require_same_imaging_mode OR (
|
||||
m.imaging_mode IS NOT NULL AND m.imaging_mode <> ''
|
||||
AND s.imaging_mode IS NOT NULL AND s.imaging_mode <> ''
|
||||
AND m.imaging_mode = s.imaging_mode
|
||||
)
|
||||
)
|
||||
AND (
|
||||
NOT p_require_same_polarization OR (
|
||||
m.polarization IS NOT NULL AND m.polarization <> ''
|
||||
AND s.polarization IS NOT NULL AND s.polarization <> ''
|
||||
AND m.polarization = s.polarization
|
||||
)
|
||||
)
|
||||
AND (p_start_date IS NULL OR (m.imaging_date >= p_start_date AND s.imaging_date >= p_start_date))
|
||||
AND (p_aoi_geom IS NULL OR (ST_Intersects(m.geom, p_aoi_geom) AND ST_Intersects(s.geom, p_aoi_geom)))
|
||||
AND (
|
||||
p_aoi_geom IS NULL OR p_aoi_overlap_threshold IS NULL OR (
|
||||
ST_Area(ST_Intersection(m.geom, p_aoi_geom)::geography) /
|
||||
NULLIF(ST_Area(p_aoi_geom::geography), 0) >= p_aoi_overlap_threshold
|
||||
AND ST_Area(ST_Intersection(s.geom, p_aoi_geom)::geography) /
|
||||
NULLIF(ST_Area(p_aoi_geom::geography), 0) >= p_aoi_overlap_threshold
|
||||
)
|
||||
)
|
||||
AND (m.imaging_date ~ '^[0-9]{8}$' AND s.imaging_date ~ '^[0-9]{8}$')
|
||||
)
|
||||
SELECT
|
||||
cp.master_id,
|
||||
cp.slave_id,
|
||||
cp.master_imaging_date,
|
||||
cp.slave_imaging_date,
|
||||
cp.time_baseline_days,
|
||||
cp.spatial_baseline_meters,
|
||||
cp.overlap_ratio
|
||||
FROM candidate_pairs cp
|
||||
WHERE cp.time_baseline_days BETWEEN p_time_baseline_min AND p_time_baseline_max
|
||||
AND cp.spatial_baseline_meters <= p_spatial_baseline_max_meters
|
||||
AND cp.overlap_ratio >= p_overlap_threshold
|
||||
ORDER BY cp.overlap_ratio DESC;
|
||||
END;
|
||||
$$;
|
||||
|
||||
-- =====================================================
|
||||
-- Function: calculate_coverage_overlap
|
||||
-- Purpose : Compute overlap area and ratio between two images
|
||||
-- =====================================================
|
||||
CREATE OR REPLACE FUNCTION calculate_coverage_overlap(
|
||||
p_image1_id INTEGER,
|
||||
p_image2_id INTEGER
|
||||
)
|
||||
RETURNS TABLE (
|
||||
overlap_area NUMERIC,
|
||||
overlap_ratio NUMERIC,
|
||||
intersection_geom GEOMETRY
|
||||
)
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
BEGIN
|
||||
RETURN QUERY
|
||||
SELECT
|
||||
ST_Area(ST_Intersection(r1.geom, r2.geom)::geography) AS overlap_area,
|
||||
ST_Area(ST_Intersection(r1.geom, r2.geom)::geography) /
|
||||
NULLIF(GREATEST(ST_Area(r1.geom::geography), ST_Area(r2.geom::geography)), 0) AS overlap_ratio,
|
||||
ST_Intersection(r1.geom, r2.geom) AS intersection_geom
|
||||
FROM radar_data r1
|
||||
CROSS JOIN radar_data r2
|
||||
WHERE r1.id = p_image1_id AND r2.id = p_image2_id;
|
||||
END;
|
||||
$$;
|
||||
|
||||
-- =====================================================
|
||||
-- Function: find_common_overlap_area
|
||||
-- Purpose : Find common overlap area among a set of images
|
||||
-- =====================================================
|
||||
CREATE OR REPLACE FUNCTION find_common_overlap_area(
|
||||
p_image_ids INTEGER[]
|
||||
)
|
||||
RETURNS TABLE (
|
||||
common_area NUMERIC,
|
||||
common_geom GEOMETRY
|
||||
)
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
DECLARE
|
||||
v_first_geom GEOMETRY;
|
||||
v_result_geom GEOMETRY;
|
||||
BEGIN
|
||||
IF p_image_ids IS NULL OR array_length(p_image_ids, 1) IS NULL THEN
|
||||
RETURN;
|
||||
END IF;
|
||||
|
||||
SELECT geom INTO v_first_geom
|
||||
FROM radar_data
|
||||
WHERE id = p_image_ids[1];
|
||||
|
||||
v_result_geom := v_first_geom;
|
||||
|
||||
FOR i IN 2..array_length(p_image_ids, 1) LOOP
|
||||
SELECT ST_Intersection(v_result_geom, geom) INTO v_result_geom
|
||||
FROM radar_data
|
||||
WHERE id = p_image_ids[i];
|
||||
|
||||
IF v_result_geom IS NULL OR ST_IsEmpty(v_result_geom) THEN
|
||||
EXIT;
|
||||
END IF;
|
||||
END LOOP;
|
||||
|
||||
IF v_result_geom IS NULL OR ST_IsEmpty(v_result_geom) THEN
|
||||
RETURN;
|
||||
END IF;
|
||||
|
||||
RETURN QUERY
|
||||
SELECT
|
||||
ST_Area(v_result_geom::geography) AS common_area,
|
||||
v_result_geom AS common_geom;
|
||||
END;
|
||||
$$;
|
||||
|
||||
-- =====================================================
|
||||
-- View: radar_pairs_view (helper view)
|
||||
-- =====================================================
|
||||
CREATE OR REPLACE VIEW radar_pairs_view AS
|
||||
SELECT
|
||||
row_number() OVER (ORDER BY m.id, s.id) AS id,
|
||||
m.id AS master_id,
|
||||
s.id AS slave_id,
|
||||
m.imaging_date AS master_date,
|
||||
s.imaging_date AS slave_date,
|
||||
CASE
|
||||
WHEN m.imaging_date ~ '^[0-9]{8}$' AND s.imaging_date ~ '^[0-9]{8}$'
|
||||
THEN ABS(to_date(s.imaging_date, 'YYYYMMDD') - to_date(m.imaging_date, 'YYYYMMDD'))
|
||||
ELSE NULL
|
||||
END AS time_baseline_days,
|
||||
ST_DistanceSphere(ST_Centroid(m.geom), ST_Centroid(s.geom)) AS spatial_baseline_meters,
|
||||
m.geom AS geom1,
|
||||
s.geom AS geom2,
|
||||
m.geom AS master_geom,
|
||||
s.geom AS slave_geom,
|
||||
ST_Area(ST_Intersection(m.geom, s.geom)::geography) AS overlap_area
|
||||
FROM radar_data m
|
||||
JOIN radar_data s ON ST_Intersects(m.geom, s.geom)
|
||||
WHERE m.id < s.id;
|
||||
|
||||
-- =====================================================
|
||||
-- Function: optimize_task_selection
|
||||
-- Purpose : Select pairs with spatial coverage diversity
|
||||
-- =====================================================
|
||||
CREATE OR REPLACE FUNCTION optimize_task_selection(
|
||||
p_pair_ids INTEGER[],
|
||||
p_penalty_factor NUMERIC DEFAULT 0.3
|
||||
)
|
||||
RETURNS TABLE (
|
||||
selected_pair_id INTEGER,
|
||||
score NUMERIC
|
||||
)
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
DECLARE
|
||||
v_selected_ids INTEGER[];
|
||||
v_total_geom GEOMETRY := NULL;
|
||||
v_pair RECORD;
|
||||
v_inter_geom GEOMETRY;
|
||||
v_new_area NUMERIC;
|
||||
v_overlap_area NUMERIC;
|
||||
v_score NUMERIC;
|
||||
BEGIN
|
||||
FOR v_pair IN
|
||||
SELECT id, geom1, geom2, overlap_area
|
||||
FROM radar_pairs_view
|
||||
WHERE id = ANY(p_pair_ids)
|
||||
ORDER BY overlap_area DESC
|
||||
LOOP
|
||||
v_inter_geom := ST_Intersection(v_pair.geom1, v_pair.geom2);
|
||||
|
||||
IF v_total_geom IS NULL THEN
|
||||
v_total_geom := v_inter_geom;
|
||||
v_selected_ids := array_append(v_selected_ids, v_pair.id);
|
||||
ELSE
|
||||
v_new_area := ST_Area(ST_Difference(v_inter_geom, v_total_geom));
|
||||
v_overlap_area := ST_Area(ST_Intersection(v_inter_geom, v_total_geom));
|
||||
v_score := v_new_area - (v_overlap_area * p_penalty_factor);
|
||||
|
||||
IF v_score > 0 THEN
|
||||
v_total_geom := ST_Union(v_total_geom, v_inter_geom);
|
||||
v_selected_ids := array_append(v_selected_ids, v_pair.id);
|
||||
END IF;
|
||||
END IF;
|
||||
END LOOP;
|
||||
|
||||
RETURN QUERY
|
||||
SELECT unnest(v_selected_ids)::INTEGER AS selected_pair_id, 0 AS score;
|
||||
END;
|
||||
$$;
|
||||
|
||||
-- =====================================================
|
||||
-- Function: find_hazard_points_in_area
|
||||
-- Purpose : Find hazard points within a geometry
|
||||
-- =====================================================
|
||||
CREATE OR REPLACE FUNCTION find_hazard_points_in_area(
|
||||
p_area_geom GEOMETRY
|
||||
)
|
||||
RETURNS TABLE (
|
||||
id INTEGER,
|
||||
tybh TEXT,
|
||||
hazard_type TEXT,
|
||||
hazard_name TEXT,
|
||||
city TEXT,
|
||||
county TEXT,
|
||||
longitude NUMERIC,
|
||||
latitude NUMERIC
|
||||
)
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
BEGIN
|
||||
RETURN QUERY
|
||||
SELECT
|
||||
hp.id,
|
||||
hp.tybh,
|
||||
hp.hazard_type,
|
||||
hp.hazard_name,
|
||||
hp.city,
|
||||
hp.county,
|
||||
hp.longitude,
|
||||
hp.latitude
|
||||
FROM hazard_points hp
|
||||
WHERE ST_Covers(p_area_geom, hp.geom);
|
||||
END;
|
||||
$$;
|
||||
|
||||
-- =====================================================
|
||||
-- Table + Function: spatial query logs
|
||||
-- =====================================================
|
||||
CREATE TABLE IF NOT EXISTS spatial_query_logs (
|
||||
id SERIAL PRIMARY KEY,
|
||||
query_type TEXT,
|
||||
execution_time_ms NUMERIC,
|
||||
result_count INTEGER,
|
||||
created_at TIMESTAMP DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE OR REPLACE FUNCTION log_spatial_query(
|
||||
p_query_type TEXT,
|
||||
p_execution_time_ms NUMERIC,
|
||||
p_result_count INTEGER
|
||||
)
|
||||
RETURNS void
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
BEGIN
|
||||
INSERT INTO spatial_query_logs (query_type, execution_time_ms, result_count)
|
||||
VALUES (p_query_type, p_execution_time_ms, p_result_count);
|
||||
END;
|
||||
$$;
|
||||
|
||||
-- =====================================================
|
||||
-- Indexes (redundant with ORM, kept for reference)
|
||||
-- =====================================================
|
||||
CREATE INDEX IF NOT EXISTS idx_radar_data_geom ON radar_data USING GIST (geom);
|
||||
CREATE INDEX IF NOT EXISTS idx_dinsar_results_geom ON dinsar_results USING GIST (geom);
|
||||
CREATE INDEX IF NOT EXISTS idx_hazard_points_geom ON hazard_points USING GIST (geom);
|
||||
|
||||
-- =====================================================
|
||||
-- Optional grants (adjust to your app user)
|
||||
-- =====================================================
|
||||
-- GRANT EXECUTE ON FUNCTION find_dinsar_pairs TO your_app_user;
|
||||
-- GRANT EXECUTE ON FUNCTION calculate_coverage_overlap TO your_app_user;
|
||||
-- GRANT SELECT ON radar_pairs_view TO your_app_user;
|
||||
@@ -0,0 +1,143 @@
|
||||
-- Migration: D-InSAR Pairing Enhancement
|
||||
-- Version: 2.0
|
||||
-- Date: 2026-03-08
|
||||
-- Purpose: Add dual-pool pairing, multiple strategies, and multi-satellite support
|
||||
|
||||
-- =====================================================
|
||||
-- Function: find_dinsar_pairs_v2 (Enhanced Version)
|
||||
-- Purpose: Find D-InSAR pairs with dual-pool and multi-satellite support
|
||||
-- =====================================================
|
||||
CREATE OR REPLACE FUNCTION find_dinsar_pairs_v2(
|
||||
-- Time/Space constraints (existing)
|
||||
p_time_baseline_min INTEGER,
|
||||
p_time_baseline_max INTEGER,
|
||||
p_spatial_baseline_max_meters NUMERIC,
|
||||
p_overlap_threshold NUMERIC,
|
||||
p_aoi_geom GEOMETRY DEFAULT NULL,
|
||||
p_require_orbit_data BOOLEAN DEFAULT TRUE,
|
||||
p_require_same_imaging_mode BOOLEAN DEFAULT TRUE,
|
||||
p_require_same_polarization BOOLEAN DEFAULT TRUE,
|
||||
p_aoi_overlap_threshold NUMERIC DEFAULT NULL,
|
||||
|
||||
-- Dual-pool date ranges (new)
|
||||
p_master_date_from TEXT DEFAULT NULL,
|
||||
p_master_date_to TEXT DEFAULT NULL,
|
||||
p_slave_date_from TEXT DEFAULT NULL,
|
||||
p_slave_date_to TEXT DEFAULT NULL,
|
||||
|
||||
-- Multi-satellite support (new)
|
||||
p_allowed_satellites TEXT[] DEFAULT NULL,
|
||||
p_cross_satellite_pairing BOOLEAN DEFAULT FALSE
|
||||
)
|
||||
RETURNS TABLE (
|
||||
master_id INTEGER,
|
||||
slave_id INTEGER,
|
||||
master_imaging_date TEXT,
|
||||
slave_imaging_date TEXT,
|
||||
time_baseline_days INTEGER,
|
||||
spatial_baseline_meters NUMERIC,
|
||||
overlap_ratio NUMERIC
|
||||
)
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
BEGIN
|
||||
RETURN QUERY
|
||||
WITH candidate_pairs AS (
|
||||
SELECT
|
||||
m.id AS master_id,
|
||||
s.id AS slave_id,
|
||||
m.imaging_date::text AS master_imaging_date,
|
||||
s.imaging_date::text AS slave_imaging_date,
|
||||
ABS(to_date(s.imaging_date, 'YYYYMMDD') - to_date(m.imaging_date, 'YYYYMMDD')) AS time_baseline_days,
|
||||
ST_DistanceSphere(ST_Centroid(m.geom), ST_Centroid(s.geom))::numeric AS spatial_baseline_meters,
|
||||
(
|
||||
ST_Area(ST_Intersection(m.geom, s.geom)::geography) /
|
||||
NULLIF(GREATEST(ST_Area(m.geom::geography), ST_Area(s.geom::geography)), 0)
|
||||
)::numeric AS overlap_ratio
|
||||
FROM radar_data m
|
||||
JOIN radar_data s ON ST_Intersects(m.geom, s.geom)
|
||||
WHERE m.id <> s.id
|
||||
-- Master must be earlier than or equal to slave
|
||||
AND m.imaging_date <= s.imaging_date
|
||||
|
||||
-- Same orbit direction and satellite (unless cross-satellite allowed)
|
||||
AND m.orbit_direction = s.orbit_direction
|
||||
AND (p_cross_satellite_pairing OR m.satellite = s.satellite)
|
||||
|
||||
-- Master pool date constraints
|
||||
AND (p_master_date_from IS NULL OR m.imaging_date >= p_master_date_from)
|
||||
AND (p_master_date_to IS NULL OR m.imaging_date <= p_master_date_to)
|
||||
|
||||
-- Slave pool date constraints
|
||||
AND (p_slave_date_from IS NULL OR s.imaging_date >= p_slave_date_from)
|
||||
AND (p_slave_date_to IS NULL OR s.imaging_date <= p_slave_date_to)
|
||||
|
||||
-- Satellite filter
|
||||
AND (p_allowed_satellites IS NULL OR m.satellite = ANY(p_allowed_satellites))
|
||||
AND (p_allowed_satellites IS NULL OR s.satellite = ANY(p_allowed_satellites))
|
||||
|
||||
-- Orbit data requirement
|
||||
AND (NOT p_require_orbit_data OR (m.has_orbit_data = true AND s.has_orbit_data = true))
|
||||
|
||||
-- Imaging mode consistency
|
||||
AND (
|
||||
NOT p_require_same_imaging_mode OR (
|
||||
m.imaging_mode IS NOT NULL AND m.imaging_mode <> ''
|
||||
AND s.imaging_mode IS NOT NULL AND s.imaging_mode <> ''
|
||||
AND m.imaging_mode = s.imaging_mode
|
||||
)
|
||||
)
|
||||
|
||||
-- Polarization consistency
|
||||
AND (
|
||||
NOT p_require_same_polarization OR (
|
||||
m.polarization IS NOT NULL AND m.polarization <> ''
|
||||
AND s.polarization IS NOT NULL AND s.polarization <> ''
|
||||
AND m.polarization = s.polarization
|
||||
)
|
||||
)
|
||||
|
||||
-- AOI intersection
|
||||
AND (p_aoi_geom IS NULL OR (ST_Intersects(m.geom, p_aoi_geom) AND ST_Intersects(s.geom, p_aoi_geom)))
|
||||
|
||||
-- AOI overlap threshold
|
||||
AND (
|
||||
p_aoi_geom IS NULL OR p_aoi_overlap_threshold IS NULL OR (
|
||||
ST_Area(ST_Intersection(m.geom, p_aoi_geom)::geography) /
|
||||
NULLIF(ST_Area(p_aoi_geom::geography), 0) >= p_aoi_overlap_threshold
|
||||
AND ST_Area(ST_Intersection(s.geom, p_aoi_geom)::geography) /
|
||||
NULLIF(ST_Area(p_aoi_geom::geography), 0) >= p_aoi_overlap_threshold
|
||||
)
|
||||
)
|
||||
|
||||
-- Valid date format
|
||||
AND (m.imaging_date ~ '^[0-9]{8}$' AND s.imaging_date ~ '^[0-9]{8}$')
|
||||
)
|
||||
SELECT
|
||||
cp.master_id,
|
||||
cp.slave_id,
|
||||
cp.master_imaging_date,
|
||||
cp.slave_imaging_date,
|
||||
cp.time_baseline_days,
|
||||
cp.spatial_baseline_meters,
|
||||
cp.overlap_ratio
|
||||
FROM candidate_pairs cp
|
||||
WHERE cp.time_baseline_days BETWEEN p_time_baseline_min AND p_time_baseline_max
|
||||
AND cp.spatial_baseline_meters <= p_spatial_baseline_max_meters
|
||||
AND cp.overlap_ratio >= p_overlap_threshold
|
||||
ORDER BY cp.overlap_ratio DESC;
|
||||
END;
|
||||
$$;
|
||||
|
||||
COMMENT ON FUNCTION find_dinsar_pairs_v2 IS
|
||||
'Enhanced D-InSAR pairing function with dual-pool support, multiple strategies, and multi-satellite capability.
|
||||
Backward compatible: when all date parameters are NULL, behaves like find_dinsar_pairs.';
|
||||
|
||||
-- =====================================================
|
||||
-- Backward Compatibility Note
|
||||
-- =====================================================
|
||||
-- The original find_dinsar_pairs function is preserved unchanged.
|
||||
-- Applications should migrate to find_dinsar_pairs_v2 for new features.
|
||||
-- When all new parameters (master_date_from/to, slave_date_from/to,
|
||||
-- allowed_satellites, cross_satellite_pairing) are NULL/default,
|
||||
-- the behavior is equivalent to the original function.
|
||||
@@ -0,0 +1,153 @@
|
||||
-- Migration: Pairing Refactor Foundation
|
||||
-- Version: 4.0
|
||||
-- Date: 2026-04-14
|
||||
-- Purpose: Introduce durable pairing cache/state tables for the pairing refactor.
|
||||
|
||||
CREATE TABLE IF NOT EXISTS pairing_cache_state (
|
||||
id SERIAL PRIMARY KEY,
|
||||
cache_scope VARCHAR(32) NOT NULL,
|
||||
metric_version VARCHAR(32) NOT NULL DEFAULT '2026.04.v1',
|
||||
status VARCHAR(16) NOT NULL DEFAULT 'DIRTY',
|
||||
scene_count INTEGER NOT NULL DEFAULT 0,
|
||||
pair_count INTEGER NOT NULL DEFAULT 0,
|
||||
dirty_scene_count INTEGER NOT NULL DEFAULT 0,
|
||||
last_full_rebuild_at TIMESTAMP NULL,
|
||||
last_incremental_reconcile_at TIMESTAMP NULL,
|
||||
last_error TEXT NULL,
|
||||
updated_at TIMESTAMP NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_pairing_cache_state_scope
|
||||
ON pairing_cache_state (cache_scope);
|
||||
|
||||
INSERT INTO pairing_cache_state (
|
||||
cache_scope,
|
||||
metric_version,
|
||||
status,
|
||||
scene_count,
|
||||
pair_count,
|
||||
dirty_scene_count
|
||||
)
|
||||
SELECT
|
||||
'global',
|
||||
'2026.04.v1',
|
||||
'DIRTY',
|
||||
0,
|
||||
0,
|
||||
0
|
||||
WHERE NOT EXISTS (
|
||||
SELECT 1 FROM pairing_cache_state WHERE cache_scope = 'global'
|
||||
);
|
||||
|
||||
|
||||
CREATE TABLE IF NOT EXISTS pairing_dirty_scenes (
|
||||
id SERIAL PRIMARY KEY,
|
||||
scene_ref_id INTEGER NOT NULL REFERENCES radar_data(id) ON DELETE CASCADE,
|
||||
scene_uid VARCHAR NOT NULL,
|
||||
reason VARCHAR(64) NOT NULL DEFAULT 'scan',
|
||||
status VARCHAR(16) NOT NULL DEFAULT 'PENDING',
|
||||
marked_at TIMESTAMP NOT NULL DEFAULT NOW(),
|
||||
resolved_at TIMESTAMP NULL
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_pairing_dirty_scenes_scene_status
|
||||
ON pairing_dirty_scenes (scene_ref_id, status);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_pairing_dirty_scenes_uid_status
|
||||
ON pairing_dirty_scenes (scene_uid, status);
|
||||
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_pairing_dirty_scenes_pending_unique
|
||||
ON pairing_dirty_scenes (scene_ref_id)
|
||||
WHERE status = 'PENDING';
|
||||
|
||||
|
||||
CREATE TABLE IF NOT EXISTS pairing_metric_cache (
|
||||
id SERIAL PRIMARY KEY,
|
||||
master_scene_ref_id INTEGER NOT NULL REFERENCES radar_data(id) ON DELETE CASCADE,
|
||||
slave_scene_ref_id INTEGER NOT NULL REFERENCES radar_data(id) ON DELETE CASCADE,
|
||||
master_scene_uid VARCHAR NOT NULL,
|
||||
slave_scene_uid VARCHAR NOT NULL,
|
||||
pair_uid VARCHAR NOT NULL,
|
||||
metric_version VARCHAR(32) NOT NULL DEFAULT '2026.04.v1',
|
||||
orientation_rule_version VARCHAR(32) NOT NULL DEFAULT 'date_then_scene_uid_v1',
|
||||
time_baseline_days INTEGER NULL,
|
||||
spatial_baseline_meters DOUBLE PRECISION NULL,
|
||||
scene_overlap_ratio DOUBLE PRECISION NULL,
|
||||
orbit_direction VARCHAR NULL,
|
||||
same_satellite BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
same_imaging_mode BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
same_polarization BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
master_imaging_date VARCHAR(8) NULL,
|
||||
slave_imaging_date VARCHAR(8) NULL,
|
||||
master_satellite VARCHAR NULL,
|
||||
slave_satellite VARCHAR NULL,
|
||||
master_imaging_mode VARCHAR NULL,
|
||||
slave_imaging_mode VARCHAR NULL,
|
||||
master_polarization VARCHAR NULL,
|
||||
slave_polarization VARCHAR NULL,
|
||||
master_file_path VARCHAR NULL,
|
||||
slave_file_path VARCHAR NULL,
|
||||
status VARCHAR(16) NOT NULL DEFAULT 'READY',
|
||||
computed_at TIMESTAMP NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_pairing_metric_cache_pair_version
|
||||
ON pairing_metric_cache (master_scene_ref_id, slave_scene_ref_id, metric_version);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_pairing_metric_cache_pair_uid_version
|
||||
ON pairing_metric_cache (pair_uid, metric_version);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_pairing_metric_cache_metric_dates
|
||||
ON pairing_metric_cache (metric_version, master_imaging_date, slave_imaging_date);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_pairing_metric_cache_orbit_direction
|
||||
ON pairing_metric_cache (orbit_direction);
|
||||
|
||||
|
||||
CREATE TABLE IF NOT EXISTS pairing_network_runs (
|
||||
id SERIAL PRIMARY KEY,
|
||||
network_run_id VARCHAR(64) NOT NULL,
|
||||
strategy VARCHAR(32) NOT NULL,
|
||||
policy_version VARCHAR(32) NOT NULL,
|
||||
request_hash VARCHAR(64) NULL,
|
||||
request_params_json JSON NULL,
|
||||
aoi_source VARCHAR(32) NULL,
|
||||
aoi_hash VARCHAR(64) NULL,
|
||||
aoi_summary_json JSON NULL,
|
||||
candidate_count INTEGER NOT NULL DEFAULT 0,
|
||||
selected_edge_count INTEGER NOT NULL DEFAULT 0,
|
||||
warning_count INTEGER NOT NULL DEFAULT 0,
|
||||
status VARCHAR(16) NOT NULL DEFAULT 'PENDING',
|
||||
fallback_used BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
created_by VARCHAR(64) NULL,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMP NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_pairing_network_runs_run_id
|
||||
ON pairing_network_runs (network_run_id);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_pairing_network_runs_strategy_status
|
||||
ON pairing_network_runs (strategy, status);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_pairing_network_runs_request_hash
|
||||
ON pairing_network_runs (request_hash);
|
||||
|
||||
|
||||
CREATE TABLE IF NOT EXISTS pairing_network_edges (
|
||||
id SERIAL PRIMARY KEY,
|
||||
network_run_ref_id INTEGER NOT NULL REFERENCES pairing_network_runs(id) ON DELETE CASCADE,
|
||||
metric_cache_ref_id INTEGER NOT NULL REFERENCES pairing_metric_cache(id) ON DELETE CASCADE,
|
||||
edge_rank INTEGER NOT NULL DEFAULT 0,
|
||||
selection_reason VARCHAR(64) NULL,
|
||||
selection_score DOUBLE PRECISION NULL,
|
||||
selection_meta_json JSON NULL,
|
||||
is_reference_edge BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_pairing_network_edges_run_metric
|
||||
ON pairing_network_edges (network_run_ref_id, metric_cache_ref_id);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_pairing_network_edges_run_rank
|
||||
ON pairing_network_edges (network_run_ref_id, edge_rank);
|
||||
@@ -0,0 +1,31 @@
|
||||
-- Migration: Pairing Task Trace Fields
|
||||
-- Version: 5.0
|
||||
-- Date: 2026-04-14
|
||||
-- Purpose: Add pairing network trace fields to D-InSAR task items.
|
||||
|
||||
ALTER TABLE IF EXISTS dinsar_task_items
|
||||
ADD COLUMN IF NOT EXISTS scene_pair_uid VARCHAR(64) NULL;
|
||||
|
||||
ALTER TABLE IF EXISTS dinsar_task_items
|
||||
ADD COLUMN IF NOT EXISTS network_run_id VARCHAR(64) NULL;
|
||||
|
||||
ALTER TABLE IF EXISTS dinsar_task_items
|
||||
ADD COLUMN IF NOT EXISTS network_edge_id INTEGER NULL;
|
||||
|
||||
ALTER TABLE IF EXISTS dinsar_task_items
|
||||
ADD COLUMN IF NOT EXISTS policy_version VARCHAR(32) NULL;
|
||||
|
||||
ALTER TABLE IF EXISTS dinsar_task_items
|
||||
ADD COLUMN IF NOT EXISTS selection_strategy VARCHAR(32) NULL;
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_dinsar_task_items_scene_pair_uid
|
||||
ON dinsar_task_items (scene_pair_uid);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_dinsar_task_items_network_run_id
|
||||
ON dinsar_task_items (network_run_id);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_dinsar_task_items_policy_version
|
||||
ON dinsar_task_items (policy_version);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_dinsar_task_items_selection_strategy
|
||||
ON dinsar_task_items (selection_strategy);
|
||||
@@ -0,0 +1,31 @@
|
||||
-- Migration: Result Product Pairing Trace Fields
|
||||
-- Version: 6.0
|
||||
-- Date: 2026-04-14
|
||||
-- Purpose: Persist pairing trace on result catalog products.
|
||||
|
||||
ALTER TABLE IF EXISTS result_products
|
||||
ADD COLUMN IF NOT EXISTS pair_uid VARCHAR(64) NULL;
|
||||
|
||||
ALTER TABLE IF EXISTS result_products
|
||||
ADD COLUMN IF NOT EXISTS network_run_id VARCHAR(64) NULL;
|
||||
|
||||
ALTER TABLE IF EXISTS result_products
|
||||
ADD COLUMN IF NOT EXISTS network_edge_id INTEGER NULL;
|
||||
|
||||
ALTER TABLE IF EXISTS result_products
|
||||
ADD COLUMN IF NOT EXISTS policy_version VARCHAR(32) NULL;
|
||||
|
||||
ALTER TABLE IF EXISTS result_products
|
||||
ADD COLUMN IF NOT EXISTS selection_strategy VARCHAR(32) NULL;
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_result_products_pair_uid
|
||||
ON result_products (pair_uid);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_result_products_network_run_id
|
||||
ON result_products (network_run_id);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_result_products_policy_version
|
||||
ON result_products (policy_version);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_result_products_selection_strategy
|
||||
ON result_products (selection_strategy);
|
||||
Reference in New Issue
Block a user