Skip to content

Commit

Permalink
pyproj.crs updates/fixes (is_proj/to_proj4/to_dict/from_string) (#359)
Browse files Browse the repository at this point in the history
- added 'is_proj';
fixed case in 'to_dict' with 'to_proj4' returning None
keep no_defs as it does not hurt anything in current code and it preserves backwards compatibility with older versions
made public properties on C classes readonly
  • Loading branch information
snowman2 authored Jul 3, 2019
1 parent d7978fa commit 1d0a9b4
Show file tree
Hide file tree
Showing 7 changed files with 108 additions and 81 deletions.
6 changes: 6 additions & 0 deletions docs/api/crs.rst
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@ pyproj.crs.is_wkt
.. autofunction:: pyproj.crs.is_wkt


pyproj.crs.is_proj
------------------

.. autofunction:: pyproj.crs.is_proj


Area Of Use
-----------

Expand Down
8 changes: 6 additions & 2 deletions docs/history.rst
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
Change Log
==========

2.2.1
2.2.2
~~~~~
* Added custom warnings for pyproj (pull #358)
* Add deprecation warning when using +init= syntax (pull #358)
* Added :meth:`~pyproj.crs.is_proj` (pull #359)
* Fixed case in :meth:`~pyproj.crs.CRS.to_dict` with :meth:`~pyproj.crs.CRS.to_proj4` returning None (pull #359)
* Keep `no_defs` in input PROJ string as it does not hurt/help anything in current code (pull #359)
* Made public properties on C classes readonly (pull #359)

2.2.1
~~~~~
* Added :meth:`~pyproj.show_versions()` (issue #334)
* Added :meth:`~pyproj.show_versions` (issue #334)
* Added fix for whitepace around '=' in PROJ strings (issue #345)
* Update version check in `setup.py` (issue #323)
* Add "stable" doc site pointing to latest release (issue #347, pull #348)
Expand Down
96 changes: 48 additions & 48 deletions pyproj/_crs.pxd
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
include "proj.pxi"

cdef class Axis:
cdef public object name
cdef public object abbrev
cdef public object direction
cdef public double unit_conversion_factor
cdef public object unit_name
cdef public object unit_auth_code
cdef public object unit_code
cdef readonly object name
cdef readonly object abbrev
cdef readonly object direction
cdef readonly double unit_conversion_factor
cdef readonly object unit_name
cdef readonly object unit_auth_code
cdef readonly object unit_code

@staticmethod
cdef create(PJ_CONTEXT* projcontext, PJ* projobj, int index)

cdef class AreaOfUse:
cdef public double west
cdef public double south
cdef public double east
cdef public double north
cdef public object name
cdef readonly double west
cdef readonly double south
cdef readonly double east
cdef readonly double north
cdef readonly object name

@staticmethod
cdef create(PJ_CONTEXT* projcontext, PJ* projobj)
Expand All @@ -26,81 +26,81 @@ cdef class AreaOfUse:
cdef class Base:
cdef PJ *projobj
cdef PJ_CONTEXT *projctx
cdef public object name
cdef readonly object name


cdef class Ellipsoid(Base):
cdef double _semi_major_metre
cdef double _semi_minor_metre
cdef public object is_semi_minor_computed
cdef readonly object is_semi_minor_computed
cdef double _inv_flattening
cdef public object ellipsoid_loaded
cdef readonly object ellipsoid_loaded

@staticmethod
cdef create(PJ* ellipsoid_pj)

cdef class PrimeMeridian(Base):
cdef public double longitude
cdef public double unit_conversion_factor
cdef public object unit_name
cdef readonly double longitude
cdef readonly double unit_conversion_factor
cdef readonly object unit_name

@staticmethod
cdef create(PJ* prime_meridian_pj)


cdef class Datum(Base):
cdef public object _ellipsoid
cdef public object _prime_meridian
cdef readonly object _ellipsoid
cdef readonly object _prime_meridian

@staticmethod
cdef create(PJ* datum_pj)


cdef class CoordinateSystem(Base):
cdef public object _axis_list
cdef readonly object _axis_list

@staticmethod
cdef create(PJ* coordinate_system_pj)


cdef class Param:
cdef public object name
cdef public object auth_name
cdef public object code
cdef public object value
cdef public double unit_conversion_factor
cdef public object unit_name
cdef public object unit_auth_name
cdef public object unit_code
cdef public object unit_category
cdef readonly object name
cdef readonly object auth_name
cdef readonly object code
cdef readonly object value
cdef readonly double unit_conversion_factor
cdef readonly object unit_name
cdef readonly object unit_auth_name
cdef readonly object unit_code
cdef readonly object unit_category

@staticmethod
cdef create(PJ_CONTEXT* projcontext, PJ* projobj, int param_idx)


cdef class Grid:
cdef public object short_name
cdef public object full_name
cdef public object package_name
cdef public object url
cdef public object direct_download
cdef public object open_license
cdef public object available
cdef readonly object short_name
cdef readonly object full_name
cdef readonly object package_name
cdef readonly object url
cdef readonly object direct_download
cdef readonly object open_license
cdef readonly object available

@staticmethod
cdef create(PJ_CONTEXT* projcontext, PJ* projobj, int grid_idx)


cdef class CoordinateOperation(Base):
cdef public object _params
cdef public object _grids
cdef public object method_name
cdef public object method_auth_name
cdef public object method_code
cdef public double accuracy
cdef public object is_instantiable
cdef public object has_ballpark_transformation
cdef public object _towgs84
cdef readonly object _params
cdef readonly object _grids
cdef readonly object method_name
cdef readonly object method_auth_name
cdef readonly object method_code
cdef readonly double accuracy
cdef readonly object is_instantiable
cdef readonly object has_ballpark_transformation
cdef readonly object _towgs84

@staticmethod
cdef create(PJ* coordinate_operation_pj)
Expand All @@ -110,8 +110,8 @@ cdef class _CRS(Base):
cdef PJ_TYPE _type
cdef PJ_PROJ_INFO projpj_info
cdef char *pjinitstring
cdef public object srs
cdef public object type_name
cdef readonly object srs
cdef readonly object type_name
cdef object _ellipsoid
cdef object _area_of_use
cdef object _prime_meridian
Expand Down
16 changes: 16 additions & 0 deletions pyproj/_crs.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,22 @@ def is_wkt(proj_string):
return proj_context_guess_wkt_dialect(NULL, tmp_string) != PJ_GUESSED_NOT_WKT


def is_proj(proj_string):
"""
Check if the input projection string is in the PROJ format.
Parameters
----------
proj_string: str
The projection string.
Returns
-------
bool: True if the string is in the PROJ format
"""
return not is_wkt(proj_string) and "=" in proj_string


cdef _to_wkt(PJ_CONTEXT* projctx, PJ* projobj, version=WktVersion.WKT2_2018, pretty=False):
"""
Convert a PJ object to a wkt string.
Expand Down
12 changes: 6 additions & 6 deletions pyproj/_geod.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@ cdef extern from "geodesic.h":

cdef class Geod:
cdef geod_geodesic _geod_geodesic
cdef public object initstring
cdef public object a
cdef public object b
cdef public object f
cdef public object es
cdef public object sphere
cdef readonly object initstring
cdef readonly object a
cdef readonly object b
cdef readonly object f
cdef readonly object es
cdef readonly object sphere
19 changes: 9 additions & 10 deletions pyproj/crs.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
"Ellipsoid",
"PrimeMeridian",
"is_wkt",
"is_proj",
]

import json
Expand All @@ -36,7 +37,7 @@
from pyproj._crs import Datum # noqa
from pyproj._crs import Ellipsoid # noqa
from pyproj._crs import PrimeMeridian # noqa
from pyproj._crs import _CRS, is_wkt
from pyproj._crs import _CRS, is_proj, is_wkt
from pyproj.cf1x8 import (
GRID_MAPPING_NAME_MAP,
INVERSE_GRID_MAPPING_NAME_MAP,
Expand Down Expand Up @@ -81,7 +82,7 @@ def _prepare_from_string(in_crs_string):
if not crs_dict:
raise CRSError("CRS is empty JSON")
return _prepare_from_dict(crs_dict)
elif not is_wkt(in_crs_string) and "=" in in_crs_string:
elif is_proj(in_crs_string):
in_crs_string = re.sub(r"[\s+]?=[\s+]?", "=", in_crs_string.lstrip())
# make sure the projection starts with +proj or +init
starting_params = ("+init", "+proj", "init", "proj")
Expand All @@ -106,9 +107,6 @@ def _prepare_from_string(in_crs_string):
# look for EPSG, replace with epsg (EPSG only works
# on case-insensitive filesystems).
in_crs_string = in_crs_string.replace("+init=EPSG", "+init=epsg").strip()
# remove no_defs as it does nothing as of PROJ 6.0.0 and breaks
# initialization with +init=epsg:...
in_crs_string = re.sub(r"\s\+?no_defs([\w=]+)?", "", in_crs_string)
if in_crs_string.startswith(("+init", "init")):
warnings.warn(
"'+init=<authority>:<code>' syntax is deprecated."
Expand Down Expand Up @@ -349,7 +347,7 @@ def from_proj4(cls, in_proj_string):
-------
CRS
"""
if is_wkt(in_proj_string) or "=" not in in_proj_string:
if not is_proj(in_proj_string):
raise CRSError("Invalid PROJ string: {}".format(in_proj_string))
return cls(_prepare_from_string(in_proj_string))

Expand Down Expand Up @@ -498,12 +496,13 @@ def parse(val):
val = [float(sval.strip()) for sval in val_split]
return val

proj_string = self.to_proj4()
if proj_string is None:
return {}

items = map(
lambda kv: len(kv) == 2 and (kv[0], parse(kv[1])) or (kv[0], None),
(
part.lstrip("+").split("=", 1)
for part in self.to_proj4().strip().split()
),
(part.lstrip("+").split("=", 1) for part in proj_string.strip().split()),
)

return {key: value for key, value in items if value is not False}
Expand Down
32 changes: 17 additions & 15 deletions test/test_crs.py
Original file line number Diff line number Diff line change
Expand Up @@ -800,24 +800,10 @@ def test_to_string__auth():
def test_srs__no_plus():
assert (
CRS("proj=longlat datum=WGS84 no_defs").srs
== "proj=longlat datum=WGS84 type=crs"
== "proj=longlat datum=WGS84 no_defs type=crs"
)


@pytest.mark.parametrize(
"init_string, expected_srs",
[
("+init=epsg:4326 +no_defs=True", "+init=epsg:4326 +type=crs"),
("init=epsg:4326 no_defs=True", "init=epsg:4326 type=crs"),
("+init=epsg:4326 +no_defs", "+init=epsg:4326 +type=crs"),
("init=epsg:4326 no_defs", "init=epsg:4326 type=crs"),
],
)
def test_removing_nodefs(init_string, expected_srs):
with pytest.warns(ProjDeprecationWarning):
assert CRS(init_string).srs == expected_srs


def test_equals_different_type():
assert CRS("epsg:4326") != ""

Expand Down Expand Up @@ -889,3 +875,19 @@ def test_whitepace_between_equals():
"+proj=lcc +lat_1=30.0 +lat_2=35.0 +lat_0=30.0 "
"+lon_0=87.0 +x_0=0 +y_0=0 +type=crs"
)


def test_to_dict_no_proj4():
crs = CRS(
{
"a": 6371229.0,
"b": 6371229.0,
"lon_0": -10.0,
"o_lat_p": 30.0,
"o_lon_p": 0.0,
"o_proj": "longlat",
"proj": "ob_tran",
}
)
assert crs.to_proj4() is None
assert crs.to_dict() == {}

0 comments on commit 1d0a9b4

Please sign in to comment.