diff --git a/docs/advanced_examples.rst b/docs/advanced_examples.rst index e4ac750a9..61ad00b3e 100644 --- a/docs/advanced_examples.rst +++ b/docs/advanced_examples.rst @@ -47,14 +47,8 @@ Results: 6.32 µs ± 49.7 ns per loop (mean ± std. dev. of 7 runs, 100000 loops Transforming with the same projections ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -pyproj will skip transformations if they are exactly the same by default. However, if you -sometimes throw in the projections that are about the same and the results being close enough -is what you want, the `skip_equivalent` option can help. - -.. note:: From PROJ code: The objects are equivalent for the purpose of coordinate operations. - They can differ by the name of their objects, identifiers, other metadata. - Parameters may be expressed in different units, provided that the value is - (with some tolerance) the same once expressed in a common unit. +pyproj skips `noop` transformations. + Transformation Group -------------------- diff --git a/docs/history.rst b/docs/history.rst index 34e4dfbfa..1e5c08f85 100644 --- a/docs/history.rst +++ b/docs/history.rst @@ -13,6 +13,7 @@ Change Log * ENH: Added :meth:`pyproj.crs.CRS.to_3d` (pull #808) * ENH: Added :meth:`pyproj.transformer.Transformer.transform_bounds` (issue #809) * ENH: Added :attr:`pyproj.crs.CRS.is_compound` (pull #823) +* REF: Skip transformations if `noop` & deprecate `skip_equivalent` (pull #824) 3.0.1 ----- diff --git a/pyproj/_transformer.pxd b/pyproj/_transformer.pxd index e2c4e476b..33b08bf7d 100644 --- a/pyproj/_transformer.pxd +++ b/pyproj/_transformer.pxd @@ -12,9 +12,6 @@ cdef class _TransformerGroup: cdef class _Transformer(Base): cdef PJ_PROJ_INFO proj_info cdef readonly _area_of_use - cdef readonly skip_equivalent - cdef readonly projections_equivalent - cdef readonly projections_exact_same cdef readonly type_name cdef readonly object _operations @@ -22,8 +19,5 @@ cdef class _Transformer(Base): cdef _Transformer _from_pj( PJ_CONTEXT* context, PJ *transform_pj, - _CRS crs_from, - _CRS crs_to, - skip_equivalent, always_xy, ) diff --git a/pyproj/_transformer.pyi b/pyproj/_transformer.pyi index 31bb5495d..78b2443a4 100644 --- a/pyproj/_transformer.pyi +++ b/pyproj/_transformer.pyi @@ -32,9 +32,8 @@ class _TransformerGroup: _best_available: bool def __init__( self, - crs_from: _CRS, - crs_to: _CRS, - skip_equivalent: bool = False, + crs_from: str, + crs_to: str, always_xy: bool = False, area_of_interest: Optional[AreaOfInterest] = None, ) -> None: ... @@ -43,9 +42,6 @@ class _Transformer(Base): input_geographic: bool output_geographic: bool is_pipeline: bool - skip_equivalent: bool - projections_equivalent: bool - projections_exact_same: bool type_name: str @property def id(self) -> str: ... @@ -70,9 +66,8 @@ class _Transformer(Base): ) -> str: ... @staticmethod def from_crs( - crs_from: _CRS, - crs_to: _CRS, - skip_equivalent: bool = False, + crs_from: str, + crs_to: str, always_xy: bool = False, area_of_interest: Optional[AreaOfInterest] = None, authority: Optional[str] = None, diff --git a/pyproj/_transformer.pyx b/pyproj/_transformer.pyx index 95da29407..4ead2724f 100644 --- a/pyproj/_transformer.pyx +++ b/pyproj/_transformer.pyx @@ -123,7 +123,6 @@ cdef class _TransformerGroup: self, _CRS crs_from, _CRS crs_to, - skip_equivalent=False, always_xy=False, area_of_interest=None, ): @@ -201,9 +200,6 @@ cdef class _TransformerGroup: _Transformer._from_pj( context, pj_transform, - crs_from, - crs_to, - skip_equivalent, always_xy, ) ) @@ -503,9 +499,6 @@ cdef double antimeridian_max(double* data, Py_ssize_t arr_len) nogil: cdef class _Transformer(Base): def __cinit__(self): self._area_of_use = None - self.skip_equivalent = False - self.projections_equivalent = False - self.projections_exact_same = False self.type_name = "Unknown Transformer" self._operations = None @@ -598,9 +591,8 @@ cdef class _Transformer(Base): @staticmethod def from_crs( - _CRS crs_from, - _CRS crs_to, - skip_equivalent=False, + const char* crs_from, + const char* crs_to, always_xy=False, area_of_interest=None, authority=None, @@ -638,8 +630,8 @@ cdef class _Transformer(Base): transformer.context = pyproj_context_create() transformer.projobj = proj_create_crs_to_crs( transformer.context, - cstrencode(crs_from.srs), - cstrencode(crs_to.srs), + crs_from, + crs_to, pj_area_of_interest, authority=authority, accuracy=accuracy, @@ -652,21 +644,13 @@ cdef class _Transformer(Base): if transformer.projobj == NULL: raise ProjError("Error creating Transformer from CRS.") - transformer._init_from_crs( - crs_from=crs_from, - crs_to=crs_to, - skip_equivalent=skip_equivalent, - always_xy=always_xy, - ) + transformer._init_from_crs(always_xy) return transformer @staticmethod cdef _Transformer _from_pj( PJ_CONTEXT* context, PJ *transform_pj, - _CRS crs_from, - _CRS crs_to, - skip_equivalent, always_xy, ): """ @@ -679,12 +663,7 @@ cdef class _Transformer(Base): if transformer.projobj == NULL: raise ProjError("Error creating Transformer.") - transformer._init_from_crs( - crs_from=crs_from, - crs_to=crs_to, - skip_equivalent=skip_equivalent, - always_xy=always_xy, - ) + transformer._init_from_crs(always_xy) return transformer @staticmethod @@ -729,22 +708,13 @@ cdef class _Transformer(Base): proj_destroy(self.projobj) self.projobj = always_xy_pj - def _init_from_crs( - self, - _CRS crs_from, - _CRS crs_to, - skip_equivalent, - always_xy, - ): + def _init_from_crs(self, always_xy): """ Finish initializing transformer properties from CRS objects """ if always_xy: self._set_always_xy() self._initialize_from_projobj() - self.projections_exact_same = crs_from.is_exact_same(crs_to) - self.projections_equivalent = crs_from == crs_to - self.skip_equivalent = skip_equivalent @cython.boundscheck(False) @cython.wraparound(False) @@ -758,11 +728,7 @@ cdef class _Transformer(Base): bint radians, bint errcheck, ): - if ( - self.projections_exact_same - or (self.projections_equivalent and self.skip_equivalent) - or self.id == "noop" - ): + if self.id == "noop": return tmp_pj_direction = _PJ_DIRECTION_MAP[TransformDirection.create(direction)] @@ -853,11 +819,7 @@ cdef class _Transformer(Base): bint radians, bint errcheck, ): - if ( - self.projections_exact_same - or (self.projections_equivalent and self.skip_equivalent) - or self.id == "noop" - ): + if self.id == "noop": return tmp_pj_direction = _PJ_DIRECTION_MAP[TransformDirection.create(direction)] cdef PJ_DIRECTION pj_direction = tmp_pj_direction @@ -956,11 +918,7 @@ cdef class _Transformer(Base): bint errcheck, object direction, ): - if ( - self.projections_exact_same - or (self.projections_equivalent and self.skip_equivalent) - or self.id == "noop" - ): + if self.id == "noop": return (left, bottom, right, top) if densify_pts < 0: diff --git a/pyproj/transformer.py b/pyproj/transformer.py index 2ce3f0192..46a550f7d 100644 --- a/pyproj/transformer.py +++ b/pyproj/transformer.py @@ -81,9 +81,8 @@ class TransformerFromCRS(TransformerMaker): Generates a Cython _Transformer class from input CRS data. """ - crs_from: CRS - crs_to: CRS - skip_equivalent: bool + crs_from: str + crs_to: str always_xy: bool area_of_interest: Optional[AreaOfInterest] authority: Optional[str] @@ -97,9 +96,8 @@ def __call__(self) -> _Transformer: _Transformer """ return _Transformer.from_crs( - self.crs_from._crs, - self.crs_to._crs, - skip_equivalent=self.skip_equivalent, + cstrencode(self.crs_from), + cstrencode(self.crs_to), always_xy=self.always_xy, area_of_interest=self.area_of_interest, authority=self.authority, @@ -157,6 +155,7 @@ def __init__( """Get all possible transformations from a :obj:`pyproj.crs.CRS` or input used to create one. + .. deprecated:: 3.1 skip_equivalent Parameters ---------- @@ -165,8 +164,8 @@ def __init__( crs_to: pyproj.crs.CRS or input used to create one Projection of output data. skip_equivalent: bool, optional - If true, will skip the transformation operation if input and output - projections are equivalent. Default is false. + DEPRECATED: If true, will skip the transformation operation + if input and output projections are equivalent. Default is false. always_xy: bool, optional If true, the transform method will accept as input and return as output coordinates using the traditional GIS order, that is longitude, latitude @@ -177,10 +176,16 @@ def __init__( best operation for the area. """ + if skip_equivalent: + warnings.warn( + "skip_equivalent is deprecated.", + DeprecationWarning, + stacklevel=2, + ) + super().__init__( CRS.from_user_input(crs_from)._crs, CRS.from_user_input(crs_to)._crs, - skip_equivalent=skip_equivalent, always_xy=always_xy, area_of_interest=area_of_interest, ) @@ -425,6 +430,7 @@ def from_proj( .. versionadded:: 2.1.2 skip_equivalent .. versionadded:: 2.2.0 always_xy .. versionadded:: 2.3.0 area_of_interest + .. deprecated:: 3.1 skip_equivalent Parameters ---------- @@ -433,8 +439,8 @@ def from_proj( proj_to: :obj:`pyproj.Proj` or input used to create one Projection of output data. skip_equivalent: bool, optional - If true, will skip the transformation operation if input and output - projections are equivalent. Default is false. + DEPRECATED: If true, will skip the transformation operation + if input and output projections are equivalent. Default is false. always_xy: bool, optional If true, the transform method will accept as input and return as output coordinates using the traditional GIS order, that is longitude, latitude @@ -480,6 +486,7 @@ def from_crs( .. versionadded:: 2.2.0 always_xy .. versionadded:: 2.3.0 area_of_interest .. versionadded:: 3.1.0 authority, accuracy, allow_ballpark + .. deprecated:: 3.1 skip_equivalent Parameters ---------- @@ -488,8 +495,8 @@ def from_crs( crs_to: pyproj.crs.CRS or input used to create one Projection of output data. skip_equivalent: bool, optional - If true, will skip the transformation operation if input and output - projections are equivalent. Default is false. + DEPRECATED: If true, will skip the transformation operation + if input and output projections are equivalent. Default is false. always_xy: bool, optional If true, the transform method will accept as input and return as output coordinates using the traditional GIS order, that is longitude, latitude @@ -519,11 +526,17 @@ def from_crs( Transformer """ + if skip_equivalent: + warnings.warn( + "skip_equivalent is deprecated.", + DeprecationWarning, + stacklevel=2, + ) + return Transformer( TransformerFromCRS( - CRS.from_user_input(crs_from), - CRS.from_user_input(crs_to), - skip_equivalent=skip_equivalent, + CRS.from_user_input(crs_from).srs, + CRS.from_user_input(crs_to).srs, always_xy=always_xy, area_of_interest=area_of_interest, authority=authority, @@ -640,7 +653,7 @@ def transform( >>> xpjr, ypjr, zpjr = transprojr.transform(xpj, ypj, zpj, radians=True) >>> f"{xpjr:.3f} {ypjr:.3f} {zpjr:.3f}" '-2704026.010 -4253051.810 3895878.820' - >>> transformer = Transformer.from_proj("epsg:4326", 4326, skip_equivalent=True) + >>> transformer = Transformer.from_proj("epsg:4326", 4326) >>> xeq, yeq = transformer.transform(33, 98) >>> f"{xeq:.0f} {yeq:.0f}" '33 98' @@ -761,7 +774,6 @@ def itransform( ... 'EPSG:4326', ... '+proj=longlat +datum=WGS84 +no_defs +type=crs', ... always_xy=True, - ... skip_equivalent=True ... ) >>> for pt in transproj_eq.itransform([(-2.137, 0.661)]): ... '{:.3f} {:.3f}'.format(*pt) @@ -992,8 +1004,10 @@ def transform( """ .. versionadded:: 2.1.2 skip_equivalent .. versionadded:: 2.2.0 always_xy + .. deprecated::3.1 skip_equivalent - .. warning:: This function is deprecated. See: :ref:`upgrade_transformer` + .. deprecated:: 2.6.1 + This function is deprecated. See: :ref:`upgrade_transformer` x2, y2, z2 = transform(p1, p2, x1, y1, z1) @@ -1012,12 +1026,10 @@ def transform( exception is raised if the transformation is invalid. By default errcheck=False and ``inf`` is returned for an invalid transformation (and no exception is raised). - If the optional kwarg skip_equivalent is true (default is False), - it will skip the transformation operation if input and output - projections are equivalent. If `always_xy` is toggled, the - transform method will accept as input and return as output - coordinates using the traditional GIS order, that is longitude, latitude - for geographic CRS and easting, northing for most projected CRS. + If `always_xy` is toggled, the transform method will accept as + input and return as output coordinates using the traditional GIS order, + that is longitude, latitude for geographic CRS and easting, + northing for most projected CRS. In addition to converting between cartographic and geographic projection coordinates, this function can take care of datum @@ -1092,8 +1104,10 @@ def itransform( """ .. versionadded:: 2.1.2 skip_equivalent .. versionadded:: 2.2.0 always_xy + .. deprecated::3.1 skip_equivalent - .. warning:: This function is deprecated. See: :ref:`upgrade_transformer` + .. deprecated:: 2.6.1 + This function is deprecated. See: :ref:`upgrade_transformer` points2 = itransform(p1, p2, points1) Iterator/generator version of the function pyproj.transform. @@ -1120,12 +1134,10 @@ def itransform( exception is raised if the transformation is invalid. By default errcheck=False and ``inf`` is returned for an invalid transformation (and no exception is raised). - If the optional kwarg skip_equivalent is true (default is False), - it will skip the transformation operation if input and output - projections are equivalent. If `always_xy` is toggled, the - transform method will accept as input and return as output - coordinates using the traditional GIS order, that is longitude, latitude - for geographic CRS and easting, northing for most projected CRS. + If `always_xy` is toggled, the transform method will accept as + input and return as output coordinates using the traditional GIS order, + that is longitude, latitude for geographic CRS and easting, northing + for most projected CRS. Example usage: @@ -1143,7 +1155,7 @@ def itransform( '411050.470 4497928.574' '399060.236 4486978.710' '458553.243 4523045.485' - >>> for pt in itransform(4326, 4326, [(30, 60)], skip_equivalent=True): + >>> for pt in itransform(4326, 4326, [(30, 60)]): ... '{:.0f} {:.0f}'.format(*pt) '30 60' diff --git a/test/test_transformer.py b/test/test_transformer.py index 2288f0671..13b6da3ff 100644 --- a/test/test_transformer.py +++ b/test/test_transformer.py @@ -78,61 +78,20 @@ def test_lambert_conformal_transform(): def test_equivalent_crs(): - transformer = Transformer.from_crs("epsg:4326", 4326, skip_equivalent=True) - assert transformer._transformer.projections_equivalent - assert transformer._transformer.projections_exact_same - assert transformer._transformer.skip_equivalent - - -def test_equivalent_crs__disabled(): - transformer = Transformer.from_crs("epsg:4326", 4326) - assert not transformer._transformer.skip_equivalent - assert transformer._transformer.projections_equivalent - assert transformer._transformer.projections_exact_same - - -def test_equivalent_crs__different(): - transformer = Transformer.from_crs("epsg:4326", 3857, skip_equivalent=True) - assert transformer._transformer.skip_equivalent - assert not transformer._transformer.projections_equivalent - assert not transformer._transformer.projections_exact_same + with pytest.warns(DeprecationWarning): + Transformer.from_crs("epsg:4326", 4326, skip_equivalent=True) def test_equivalent_proj(): - with pytest.warns(UserWarning): - proj_to = pyproj.Proj(4326).crs.to_proj4() with pytest.warns(FutureWarning): - transformer = Transformer.from_proj( - "+init=epsg:4326", proj_to, skip_equivalent=True - ) - assert transformer._transformer.skip_equivalent - assert transformer._transformer.projections_equivalent - assert not transformer._transformer.projections_exact_same - - -def test_equivalent_proj__disabled(): - with pytest.warns(UserWarning): - transformer = Transformer.from_proj(3857, pyproj.Proj(3857).crs.to_proj4()) - assert not transformer._transformer.skip_equivalent - assert transformer._transformer.projections_equivalent - assert not transformer._transformer.projections_exact_same - - -def test_equivalent_proj__different(): - transformer = Transformer.from_proj(3857, 4326, skip_equivalent=True) - assert transformer._transformer.skip_equivalent - assert not transformer._transformer.projections_equivalent - assert not transformer._transformer.projections_exact_same + proj_from = pyproj.Proj("+init=epsg:4326") + with pytest.warns(DeprecationWarning): + Transformer.from_proj(proj_from, 4326, skip_equivalent=True) -def test_equivalent_pipeline(): - transformer = Transformer.from_pipeline( - "+proj=pipeline +step +proj=longlat +ellps=WGS84 +step " - "+proj=unitconvert +xy_in=rad +xy_out=deg" - ) - assert not transformer._transformer.skip_equivalent - assert not transformer._transformer.projections_equivalent - assert not transformer._transformer.projections_exact_same +def test_equivalent_transformer_group(): + with pytest.warns(DeprecationWarning): + TransformerGroup("epsg:4326", 4326, skip_equivalent=True) def test_4d_transform():