def _get_old_and_new_crs(self, ds_path, ds_diff, context=None): from kart.crs_util import make_crs # If the CRS is changing during the diff, we extract the two CRS from the diff. if "meta" in ds_diff: meta_diff = ds_diff["meta"] old_crs_defs = [ v.old_value for k, v in meta_diff.items() if k.startswith("crs/") and v.old is not None ] new_crs_defs = [ v.new_value for k, v in meta_diff.items() if k.startswith("crs/") and v.new is not None ] if len(old_crs_defs) > 1 or len(new_crs_defs) > 1: self._raise_multi_crs_error(ds_path, context=context) old_crs, new_crs = None, None if old_crs_defs: old_crs = make_crs(old_crs_defs[0], context=ds_path) if new_crs_defs: new_crs = make_crs(new_crs_defs[0], context=ds_path) if old_crs_defs or new_crs_defs: return old_crs, new_crs # No diff - old and new CRS are the same. ds = self.base_rs.datasets().get( ds_path) or self.target_rs.datasets().get(ds_path) crs_defs = list(ds.crs_definitions().values()) if not crs_defs: return None, None if len(crs_defs) > 1: self._raise_multi_crs_error(ds_path, context=context) crs = make_crs(crs_defs[0], context=ds_path) return crs, crs
def transform_for_schema_and_crs(self, schema, crs, ds_path=None): """ Similar to transform_for_dataset above, but can also be used without a dataset object - for example, to apply the spatial filter to a working copy table which might not exactly match any dataset. schema - the dataset (or table) schema. new_crs - the crs definition of the dataset or table. The CRS should be a name eg EPSG:4326, or a full CRS definition, or an osgeo.osr.SpatialReference. """ if self.match_all: return SpatialFilter._MATCH_ALL geometry_columns = schema.geometry_columns if not geometry_columns: return SpatialFilter._MATCH_ALL new_geom_column_name = geometry_columns[0].name from osgeo import osr try: crs_spec = str(crs) if isinstance(crs, str): crs = make_crs(crs) transform = osr.CoordinateTransformation(self.crs, crs) new_filter_ogr = self.filter_ogr.Clone() new_filter_ogr.Transform(transform) return SpatialFilter(crs, new_filter_ogr, new_geom_column_name) except RuntimeError as e: crs_desc = f"CRS for {ds_path!r}" if ds_path else f"CRS:\n {crs_spec!r}" raise CrsError( f"Can't reproject spatial filter into {crs_desc}:\n{e}")
def crs_from_oid(self, crs_oid): wkt = normalise_wkt(self.repo[crs_oid].data.decode("utf-8")) result = make_crs(wkt) for prior_result in self._distinct_crs_list: if result.IsSame(prior_result): return prior_result self._distinct_crs_list.append(result) return result
def __init__(self, repo, start_commits=None, stop_commits=None): self.repo = repo self.ds_to_transforms = {} self.target_crs = make_crs("EPSG:4326") self._distinct_crs_list = [] if start_commits is not None: self.start_stop_spec = [*start_commits, "--not", *stop_commits] else: self.start_stop_spec = ["--all"]
def __init__(self, crs_spec, geometry_spec, match_all=False): if match_all: super().__init__(None, None, match_all=True) self.hexhash = None else: ctx = "spatial filter" geometry = geometry_from_string(geometry_spec, context=ctx) crs = make_crs(crs_spec, context=ctx) super().__init__(crs, geometry.to_ogr()) self.hexhash = hexhash(crs_spec.strip(), geometry.to_wkb())
def get_geometry_transform(self, target_crs): """ Find the transform to reproject this dataset into the target CRS. Returns None if the CRS for this dataset is unknown. """ crs_definition = self.get_crs_definition() if crs_definition is None: return None try: src_crs = crs_util.make_crs(crs_definition) return osr.CoordinateTransformation(src_crs, target_crs) except RuntimeError as e: raise InvalidOperation( f"Can't reproject dataset {self.path!r} into target CRS: {e}")
def envelope_wgs84(self): from osgeo import osr try: transform = osr.CoordinateTransformation(self.crs, make_crs("EPSG:4326")) geom_ogr = self.geometry.to_ogr() geom_ogr.Transform(transform) w, e, s, n = geom_ogr.GetEnvelope() return w, s, e, n except RuntimeError as e: raise CrsError( f"Can't reproject spatial filter into EPSG:4326:\n{e}")
def __init__(self, crs_spec, geometry_spec, match_all=False): super().__init__() self.match_all = match_all if not self.match_all: self.crs_spec = crs_spec self.geometry_spec = geometry_spec self.crs = make_crs(crs_spec) self.geometry = geometry_from_string( geometry_spec, allowed_types=(GeometryType.POLYGON, GeometryType.MULTIPOLYGON), allow_empty=False, context="spatial filter", )
for row in r: blob_id = row[0].hex() envelope = encoder.decode(row[1]) for i in range(len(score_funcs)): score = score_funcs[i](blob_id, envelope) if score > winning_scores[i]: winning_scores[i] = score winners[i] = Entry(blob_id, envelope) return IndexSummary(features, *winners) # This transform leaves every point exactly where it is, even points past the antimeridian eg (185, 0) # We need to test this since its a special case - no other transform will result in longitudes outside # the range [-180, 180]. EPSG_4326 = make_crs("EPSG:4326") IDENTITY_TRANSFORM = osr.CoordinateTransformation(EPSG_4326, EPSG_4326) # This transform tests the general case - like most transforms, the end result will be points with # longitudes wrapped into the range [-180, 180]. It also is valid over an area that crosses the # anti-meridian, so we can test that too. NZTM = make_crs("EPSG:2193") NZTM_TRANSFORM = osr.CoordinateTransformation(NZTM, EPSG_4326) @pytest.mark.parametrize( "input,transform,expected_result", [ ((1, 2, 3, 4), IDENTITY_TRANSFORM, (1, 2, 3, 4)), ((177, -10, 184, 10), IDENTITY_TRANSFORM, (177, -10, -176, 10)), ((185, 10, 190, 20), IDENTITY_TRANSFORM, (-175, 10, -170, 20)),