Add Sentinel-1 asset management and PyINT pipeline support

This commit is contained in:
2026-05-14 03:02:56 +08:00
parent 508ca5641a
commit 729c5c2a7f
61 changed files with 7800 additions and 885 deletions
+63 -3
View File
@@ -14,7 +14,7 @@ from typing import Any, Dict, List, Optional, Tuple
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.future import select
from sqlalchemy import and_, cast, func, or_
from sqlalchemy import and_, case, cast, func, or_
from sqlalchemy.orm import aliased
from geoalchemy2 import Geography
@@ -43,11 +43,60 @@ from .dinsar_naming import build_pair_key, build_task_alias, ensure_unique_task_
from .pairing_state_service import pairing_state_service
PAIRING_POLICY_VERSION = "2026.05.raw-source.v1"
PAIRING_POLICY_VERSION = "2026.05.raw-source.v2"
PAIRING_WARNING_CANDIDATE_THRESHOLD = 3000
logger = logging.getLogger(__name__)
def _normalized_satellite_family_expr(alias):
compact_satellite = func.upper(
func.replace(
func.replace(
func.replace(func.coalesce(alias.satellite, ""), "-", ""),
"_",
"",
),
" ",
"",
)
)
inferred_family = case(
(
compact_satellite.in_(
["LT1", "LT1A", "LT1B", "LUTAN1", "LUTAN1A", "LUTAN1B"]
),
"LT1",
),
(
compact_satellite.in_(
[
"S1",
"S1A",
"S1B",
"S1C",
"SENTINEL1",
"SENTINEL1A",
"SENTINEL1B",
"SENTINEL1C",
]
),
"S1",
),
else_=func.upper(alias.satellite),
)
return func.coalesce(func.nullif(func.upper(alias.satellite_family), ""), inferred_family)
def _same_relative_orbit_expr(left_alias, right_alias):
left_relative_orbit = func.upper(func.trim(func.coalesce(left_alias.relative_orbit, "")))
right_relative_orbit = func.upper(func.trim(func.coalesce(right_alias.relative_orbit, "")))
return and_(
left_relative_orbit != "",
right_relative_orbit != "",
left_relative_orbit == right_relative_orbit,
)
class SpatialService:
"""
纯 PostGIS 空间计算服务
@@ -155,6 +204,8 @@ class SpatialService:
) -> List[dict]:
master_alias = aliased(RadarDataORM)
slave_alias = aliased(RadarDataORM)
master_family_expr = _normalized_satellite_family_expr(master_alias)
slave_family_expr = _normalized_satellite_family_expr(slave_alias)
center_distance_expr = func.coalesce(
PairingMetricCacheORM.scene_center_distance_meters,
PairingMetricCacheORM.spatial_baseline_meters,
@@ -192,6 +243,14 @@ class SpatialService:
if params.require_same_polarization:
stmt = stmt.where(PairingMetricCacheORM.same_polarization.is_(True))
stmt = stmt.where(
or_(
master_family_expr != "S1",
slave_family_expr != "S1",
_same_relative_orbit_expr(master_alias, slave_alias),
)
)
if params.allowed_satellites:
allowed_satellites = [
str(item).strip().upper()
@@ -284,6 +343,7 @@ class SpatialService:
slave.file_path,
master.imaging_date,
slave.imaging_date,
master.satellite_family or slave.satellite_family or master.satellite or slave.satellite,
),
pair_uid=candidate.get("pair_uid"),
metric_cache_ref_id=candidate.get("metric_cache_ref_id"),
@@ -1281,7 +1341,7 @@ class SpatialService:
compact = raw_satellite.replace("-", "").replace("_", "").replace(" ", "")
if compact in {"LT1", "LT1A", "LT1B", "LUTAN1", "LUTAN1A", "LUTAN1B"}:
return "LT1"
if compact in {"S1", "S1A", "S1B", "SENTINEL1", "SENTINEL1A", "SENTINEL1B"}:
if compact in {"S1", "S1A", "S1B", "S1C", "SENTINEL1", "SENTINEL1A", "SENTINEL1B", "SENTINEL1C"}:
return "S1"
return raw_satellite or "UNKNOWN"