""" Coordinate transformation engine — wraps pyproj for all CRS conversions. """ import pyproj from typing import Tuple # Well-known EPSG codes WELL_KNOWN = { "WGS84": "EPSG:4326", "CGCS2000": "EPSG:4490", "Xian80": "EPSG:4610", "Beijing54": "EPSG:4214", "WebMercator": "EPSG:3857", "PseudoMercator": "EPSG:3857", } def resolve_epsg(name: str) -> str: """Resolve a well-known name or return as-is if already EPSG:xxxx.""" name = name.strip() if name.upper() in WELL_KNOWN: return WELL_KNOWN[name.upper()] if name.upper().startswith("EPSG:"): return name.upper() return f"EPSG:{name}" class CoordTransformer: """Convert coordinates between any two CRS using pyproj.""" def __init__(self, from_crs: str, to_crs: str): self.from_crs = resolve_epsg(from_crs) self.to_crs = resolve_epsg(to_crs) if self.from_crs == self.to_crs: self._is_identity = True self._transformer = None else: self._is_identity = False self._transformer = pyproj.Transformer.from_crs( self.from_crs, self.to_crs, always_xy=True ) def transform(self, x: float, y: float) -> Tuple[float, float]: if self._is_identity: return x, y return self._transformer.transform(x, y) def transform_batch(self, xs: list[float], ys: list[float]) -> Tuple[list[float], list[float]]: if self._is_identity: return xs, ys results = self._transformer.transform(xs, ys) return results[0], results[1] @property def description(self) -> str: if self._is_identity: return f"{self.from_crs} (无转换)" return f"{self.from_crs} → {self.to_crs}" @staticmethod def list_supported() -> list[dict]: return [ {"name": "WGS84", "epsg": "EPSG:4326", "desc": "GPS / Google Earth"}, {"name": "CGCS2000", "epsg": "EPSG:4490", "desc": "2000国家大地坐标系"}, {"name": "Xian80", "epsg": "EPSG:4610", "desc": "西安80坐标系"}, {"name": "Beijing54", "epsg": "EPSG:4214", "desc": "北京54坐标系"}, {"name": "Web Mercator","epsg": "EPSG:3857", "desc": "互联网地图投影"}, ]