3206 lines
91 KiB
Cython
3206 lines
91 KiB
Cython
|
import json
|
||
|
import re
|
||
|
import warnings
|
||
|
from collections import OrderedDict, namedtuple
|
||
|
|
||
|
from pyproj._compat cimport cstrdecode, cstrencode
|
||
|
from pyproj._datadir cimport (
|
||
|
_clear_proj_error,
|
||
|
pyproj_context_create,
|
||
|
pyproj_context_destroy,
|
||
|
)
|
||
|
|
||
|
from pyproj.aoi import AreaOfUse
|
||
|
from pyproj.crs.datum import CustomEllipsoid
|
||
|
from pyproj.crs.enums import CoordinateOperationType, DatumType
|
||
|
from pyproj.enums import ProjVersion, WktVersion
|
||
|
from pyproj.exceptions import CRSError
|
||
|
from pyproj.geod import pj_ellps
|
||
|
from pyproj.utils import NumpyEncoder
|
||
|
|
||
|
|
||
|
# This is for looking up the ellipsoid parameters
|
||
|
# based on the long name
|
||
|
cdef dict _PJ_ELLPS_NAME_MAP = {
|
||
|
ellps["description"]: ellps_id for ellps_id, ellps in pj_ellps.items()
|
||
|
}
|
||
|
|
||
|
|
||
|
cdef str decode_or_undefined(const char* instring):
|
||
|
pystr = cstrdecode(instring)
|
||
|
if pystr is None:
|
||
|
return "undefined"
|
||
|
return pystr
|
||
|
|
||
|
|
||
|
def is_wkt(str proj_string not None):
|
||
|
"""
|
||
|
.. versionadded:: 2.0.0
|
||
|
|
||
|
Check if the input projection string is in the Well-Known Text format.
|
||
|
|
||
|
Parameters
|
||
|
----------
|
||
|
proj_string: str
|
||
|
The projection string.
|
||
|
|
||
|
Returns
|
||
|
-------
|
||
|
bool: True if the string is in the Well-Known Text format
|
||
|
"""
|
||
|
cdef bytes b_proj_string = cstrencode(proj_string)
|
||
|
return proj_context_guess_wkt_dialect(NULL, b_proj_string) != PJ_GUESSED_NOT_WKT
|
||
|
|
||
|
|
||
|
def is_proj(str proj_string not None):
|
||
|
"""
|
||
|
.. versionadded:: 2.2.2
|
||
|
|
||
|
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* context,
|
||
|
PJ* projobj,
|
||
|
object version,
|
||
|
bint pretty,
|
||
|
bool output_axis_rule=None,
|
||
|
):
|
||
|
"""
|
||
|
Convert a PJ object to a wkt string.
|
||
|
|
||
|
Parameters
|
||
|
----------
|
||
|
context: PJ_CONTEXT*
|
||
|
projobj: PJ*
|
||
|
wkt_out_type: PJ_WKT_TYPE
|
||
|
pretty: bool
|
||
|
output_axis_rule: bool or None
|
||
|
|
||
|
Return
|
||
|
------
|
||
|
str or None
|
||
|
"""
|
||
|
# get the output WKT format
|
||
|
supported_wkt_types = {
|
||
|
WktVersion.WKT2_2015: PJ_WKT2_2015,
|
||
|
WktVersion.WKT2_2015_SIMPLIFIED: PJ_WKT2_2015_SIMPLIFIED,
|
||
|
WktVersion.WKT2_2018: PJ_WKT2_2019,
|
||
|
WktVersion.WKT2_2018_SIMPLIFIED: PJ_WKT2_2019_SIMPLIFIED,
|
||
|
WktVersion.WKT2_2019: PJ_WKT2_2019,
|
||
|
WktVersion.WKT2_2019_SIMPLIFIED: PJ_WKT2_2019_SIMPLIFIED,
|
||
|
WktVersion.WKT1_GDAL: PJ_WKT1_GDAL,
|
||
|
WktVersion.WKT1_ESRI: PJ_WKT1_ESRI
|
||
|
}
|
||
|
cdef PJ_WKT_TYPE wkt_out_type
|
||
|
wkt_out_type = supported_wkt_types[WktVersion.create(version)]
|
||
|
|
||
|
cdef const char* options_wkt[3]
|
||
|
cdef bytes multiline = b"MULTILINE=NO"
|
||
|
if pretty:
|
||
|
multiline = b"MULTILINE=YES"
|
||
|
cdef bytes output_axis = b"OUTPUT_AXIS=AUTO"
|
||
|
if output_axis_rule is False:
|
||
|
output_axis = b"OUTPUT_AXIS=NO"
|
||
|
elif output_axis_rule is True:
|
||
|
output_axis = b"OUTPUT_AXIS=YES"
|
||
|
options_wkt[0] = multiline
|
||
|
options_wkt[1] = output_axis
|
||
|
options_wkt[2] = NULL
|
||
|
cdef const char* proj_string
|
||
|
proj_string = proj_as_wkt(
|
||
|
context,
|
||
|
projobj,
|
||
|
wkt_out_type,
|
||
|
options_wkt,
|
||
|
)
|
||
|
_clear_proj_error()
|
||
|
return cstrdecode(proj_string)
|
||
|
|
||
|
|
||
|
cdef _to_proj4(
|
||
|
PJ_CONTEXT* context,
|
||
|
PJ* projobj,
|
||
|
object version,
|
||
|
bint pretty,
|
||
|
):
|
||
|
"""
|
||
|
Convert the projection to a PROJ string.
|
||
|
|
||
|
Parameters
|
||
|
----------
|
||
|
context: PJ_CONTEXT*
|
||
|
projobj: PJ*
|
||
|
version: pyproj.enums.ProjVersion
|
||
|
The version of the PROJ string output.
|
||
|
pretty: bool
|
||
|
|
||
|
Returns
|
||
|
-------
|
||
|
str: The PROJ string.
|
||
|
"""
|
||
|
# get the output PROJ string format
|
||
|
supported_prj_types = {
|
||
|
ProjVersion.PROJ_4: PJ_PROJ_4,
|
||
|
ProjVersion.PROJ_5: PJ_PROJ_5,
|
||
|
}
|
||
|
cdef PJ_PROJ_STRING_TYPE proj_out_type
|
||
|
proj_out_type = supported_prj_types[ProjVersion.create(version)]
|
||
|
|
||
|
cdef const char* options[2]
|
||
|
cdef bytes multiline = b"MULTILINE=NO"
|
||
|
if pretty:
|
||
|
multiline = b"MULTILINE=YES"
|
||
|
options[0] = multiline
|
||
|
options[1] = NULL
|
||
|
|
||
|
# convert projection to string
|
||
|
cdef const char* proj_string
|
||
|
proj_string = proj_as_proj_string(
|
||
|
context,
|
||
|
projobj,
|
||
|
proj_out_type,
|
||
|
options,
|
||
|
)
|
||
|
_clear_proj_error()
|
||
|
return cstrdecode(proj_string)
|
||
|
|
||
|
|
||
|
cdef tuple _get_concatenated_operations(
|
||
|
PJ_CONTEXT* context, PJ* concatenated_operation
|
||
|
):
|
||
|
"""
|
||
|
For a PJ* of type concatenated operation, get the operations
|
||
|
"""
|
||
|
cdef int step_count = proj_concatoperation_get_step_count(
|
||
|
context,
|
||
|
concatenated_operation,
|
||
|
)
|
||
|
cdef PJ* operation = NULL
|
||
|
cdef PJ_CONTEXT* sub_context = NULL
|
||
|
cdef int iii = 0
|
||
|
operations = []
|
||
|
for iii in range(step_count):
|
||
|
sub_context = pyproj_context_create()
|
||
|
operation = proj_concatoperation_get_step(
|
||
|
sub_context,
|
||
|
concatenated_operation,
|
||
|
iii,
|
||
|
)
|
||
|
operations.append(CoordinateOperation.create(sub_context, operation))
|
||
|
_clear_proj_error()
|
||
|
return tuple(operations)
|
||
|
|
||
|
cdef PJ * _from_name(
|
||
|
PJ_CONTEXT* context,
|
||
|
str name_string,
|
||
|
str auth_name,
|
||
|
PJ_TYPE pj_type,
|
||
|
):
|
||
|
"""
|
||
|
Create an object from a name.
|
||
|
|
||
|
Parameters
|
||
|
----------
|
||
|
context: PJ_CONTEXT*
|
||
|
The context to use to create the object.
|
||
|
name_string: str
|
||
|
Name of object to create.
|
||
|
auth_name: str
|
||
|
The authority name to refine search.
|
||
|
If None, will search all authorities.
|
||
|
pj_type: PJ_TYPE
|
||
|
The type of PJ * to create.
|
||
|
|
||
|
Returns
|
||
|
-------
|
||
|
PJ *
|
||
|
"""
|
||
|
cdef PJ_TYPE[1] pj_types = [pj_type]
|
||
|
cdef char* c_auth_name = NULL
|
||
|
cdef bytes b_auth_name
|
||
|
if auth_name is not None:
|
||
|
b_auth_name = cstrencode(auth_name)
|
||
|
c_auth_name = b_auth_name
|
||
|
|
||
|
cdef PJ_OBJ_LIST *pj_list = proj_create_from_name(
|
||
|
context,
|
||
|
c_auth_name,
|
||
|
cstrencode(name_string),
|
||
|
<PJ_TYPE*>&pj_types,
|
||
|
1,
|
||
|
False,
|
||
|
1,
|
||
|
NULL,
|
||
|
)
|
||
|
if pj_list == NULL or proj_list_get_count(pj_list) <= 0:
|
||
|
proj_list_destroy(pj_list)
|
||
|
return NULL
|
||
|
cdef PJ* datum_pj = proj_list_get(context, pj_list, 0)
|
||
|
proj_list_destroy(pj_list)
|
||
|
return datum_pj
|
||
|
|
||
|
|
||
|
def _load_proj_json(str in_proj_json):
|
||
|
try:
|
||
|
return json.loads(in_proj_json)
|
||
|
except ValueError:
|
||
|
raise CRSError("Invalid JSON")
|
||
|
|
||
|
|
||
|
cdef class Axis:
|
||
|
"""
|
||
|
.. versionadded:: 2.0.0
|
||
|
|
||
|
Coordinate System Axis
|
||
|
|
||
|
Attributes
|
||
|
----------
|
||
|
name: str
|
||
|
abbrev: str
|
||
|
direction: str
|
||
|
unit_conversion_factor: float
|
||
|
unit_name: str
|
||
|
unit_auth_code: str
|
||
|
unit_code: str
|
||
|
|
||
|
"""
|
||
|
def __cinit__(self):
|
||
|
self.name = "undefined"
|
||
|
self.abbrev = "undefined"
|
||
|
self.direction = "undefined"
|
||
|
self.unit_conversion_factor = float("NaN")
|
||
|
self.unit_name = "undefined"
|
||
|
self.unit_auth_code = "undefined"
|
||
|
self.unit_code = "undefined"
|
||
|
|
||
|
def __str__(self):
|
||
|
return f"{self.abbrev}[{self.direction}]: {self.name} ({self.unit_name})"
|
||
|
|
||
|
def __repr__(self):
|
||
|
return (
|
||
|
f"Axis(name={self.name}, abbrev={self.abbrev}, "
|
||
|
f"direction={self.direction}, unit_auth_code={self.unit_auth_code}, "
|
||
|
f"unit_code={self.unit_code}, unit_name={self.unit_name})"
|
||
|
)
|
||
|
|
||
|
@staticmethod
|
||
|
cdef Axis create(PJ_CONTEXT* context, PJ* projobj, int index):
|
||
|
cdef:
|
||
|
Axis axis_info = Axis()
|
||
|
const char * name = NULL
|
||
|
const char * abbrev = NULL
|
||
|
const char * direction = NULL
|
||
|
const char * unit_name = NULL
|
||
|
const char * unit_auth_code = NULL
|
||
|
const char * unit_code = NULL
|
||
|
|
||
|
if not proj_cs_get_axis_info(
|
||
|
context,
|
||
|
projobj,
|
||
|
index,
|
||
|
&name,
|
||
|
&abbrev,
|
||
|
&direction,
|
||
|
&axis_info.unit_conversion_factor,
|
||
|
&unit_name,
|
||
|
&unit_auth_code,
|
||
|
&unit_code):
|
||
|
return None
|
||
|
axis_info.name = decode_or_undefined(name)
|
||
|
axis_info.abbrev = decode_or_undefined(abbrev)
|
||
|
axis_info.direction = decode_or_undefined(direction)
|
||
|
axis_info.unit_name = decode_or_undefined(unit_name)
|
||
|
axis_info.unit_auth_code = decode_or_undefined(unit_auth_code)
|
||
|
axis_info.unit_code = decode_or_undefined(unit_code)
|
||
|
return axis_info
|
||
|
|
||
|
|
||
|
cdef create_area_of_use(PJ_CONTEXT* context, PJ* projobj):
|
||
|
cdef:
|
||
|
double west = float("nan")
|
||
|
double south = float("nan")
|
||
|
double east = float("nan")
|
||
|
double north = float("nan")
|
||
|
const char * area_name = NULL
|
||
|
|
||
|
if not proj_get_area_of_use(
|
||
|
context,
|
||
|
projobj,
|
||
|
&west,
|
||
|
&south,
|
||
|
&east,
|
||
|
&north,
|
||
|
&area_name):
|
||
|
return None
|
||
|
return AreaOfUse(
|
||
|
west=west,
|
||
|
south=south,
|
||
|
east=east,
|
||
|
north=north,
|
||
|
name=decode_or_undefined(area_name),
|
||
|
)
|
||
|
|
||
|
|
||
|
cdef class Base:
|
||
|
def __cinit__(self):
|
||
|
self.projobj = NULL
|
||
|
self.context = NULL
|
||
|
self.name = "undefined"
|
||
|
self._scope = None
|
||
|
self._remarks = None
|
||
|
|
||
|
def __dealloc__(self):
|
||
|
"""destroy projection definition"""
|
||
|
if self.projobj != NULL:
|
||
|
proj_destroy(self.projobj)
|
||
|
if self.context != NULL:
|
||
|
pyproj_context_destroy(self.context)
|
||
|
|
||
|
cdef _set_base_info(self):
|
||
|
"""
|
||
|
Set the name of the PJ
|
||
|
"""
|
||
|
# get proj information
|
||
|
cdef const char* proj_name = proj_get_name(self.projobj)
|
||
|
self.name = decode_or_undefined(proj_name)
|
||
|
cdef const char* scope = proj_get_scope(self.projobj)
|
||
|
if scope != NULL and scope != "":
|
||
|
self._scope = scope
|
||
|
cdef const char* remarks = proj_get_remarks(self.projobj)
|
||
|
if remarks != NULL and remarks != "":
|
||
|
self._remarks = remarks
|
||
|
|
||
|
@property
|
||
|
def remarks(self):
|
||
|
"""
|
||
|
.. versionadded:: 2.4.0
|
||
|
|
||
|
Returns
|
||
|
-------
|
||
|
str:
|
||
|
Remarks about object.
|
||
|
"""
|
||
|
return self._remarks
|
||
|
|
||
|
@property
|
||
|
def scope(self):
|
||
|
"""
|
||
|
.. versionadded:: 2.4.0
|
||
|
|
||
|
Returns
|
||
|
-------
|
||
|
str:
|
||
|
Scope of object.
|
||
|
"""
|
||
|
return self._scope
|
||
|
|
||
|
def to_wkt(self, version=WktVersion.WKT2_2019, pretty=False, output_axis_rule=None):
|
||
|
"""
|
||
|
Convert the projection to a WKT string.
|
||
|
|
||
|
Version options:
|
||
|
- WKT2_2015
|
||
|
- WKT2_2015_SIMPLIFIED
|
||
|
- WKT2_2019
|
||
|
- WKT2_2019_SIMPLIFIED
|
||
|
- WKT1_GDAL
|
||
|
- WKT1_ESRI
|
||
|
|
||
|
.. versionadded:: 3.6.0 output_axis_rule
|
||
|
|
||
|
Parameters
|
||
|
----------
|
||
|
version: pyproj.enums.WktVersion, default=pyproj.enums.WktVersion.WKT2_2019
|
||
|
The version of the WKT output.
|
||
|
pretty: bool, default=False
|
||
|
If True, it will set the output to be a multiline string.
|
||
|
output_axis_rule: bool, optional, default=None
|
||
|
If True, it will set the axis rule on any case. If false, never.
|
||
|
None for AUTO, that depends on the CRS and version.
|
||
|
|
||
|
Returns
|
||
|
-------
|
||
|
str
|
||
|
"""
|
||
|
return _to_wkt(self.context, self.projobj, version, pretty=pretty, output_axis_rule=output_axis_rule)
|
||
|
|
||
|
def to_json(self, bint pretty=False, int indentation=2):
|
||
|
"""
|
||
|
.. versionadded:: 2.4.0
|
||
|
|
||
|
Convert the object to a JSON string.
|
||
|
|
||
|
Parameters
|
||
|
----------
|
||
|
pretty: bool, default=False
|
||
|
If True, it will set the output to be a multiline string.
|
||
|
indentation: int, default=2
|
||
|
If pretty is True, it will set the width of the indentation.
|
||
|
|
||
|
Returns
|
||
|
-------
|
||
|
str
|
||
|
"""
|
||
|
cdef const char* options[3]
|
||
|
multiline = b"MULTILINE=NO"
|
||
|
if pretty:
|
||
|
multiline = b"MULTILINE=YES"
|
||
|
indentation_width = cstrencode(f"INDENTATION_WIDTH={indentation:.0f}")
|
||
|
options[0] = multiline
|
||
|
options[1] = indentation_width
|
||
|
options[2] = NULL
|
||
|
|
||
|
cdef const char* proj_json_string = proj_as_projjson(
|
||
|
self.context,
|
||
|
self.projobj,
|
||
|
options,
|
||
|
)
|
||
|
return cstrdecode(proj_json_string)
|
||
|
|
||
|
def to_json_dict(self):
|
||
|
"""
|
||
|
.. versionadded:: 2.4.0
|
||
|
|
||
|
Convert the object to a JSON dictionary.
|
||
|
|
||
|
Returns
|
||
|
-------
|
||
|
dict
|
||
|
"""
|
||
|
return json.loads(self.to_json())
|
||
|
|
||
|
def __str__(self):
|
||
|
return self.name
|
||
|
|
||
|
def __repr__(self):
|
||
|
return self.to_wkt(pretty=True)
|
||
|
|
||
|
def _is_exact_same(self, Base other):
|
||
|
return proj_is_equivalent_to_with_ctx(
|
||
|
self.context, self.projobj, other.projobj, PJ_COMP_STRICT) == 1
|
||
|
|
||
|
def _is_equivalent(self, Base other):
|
||
|
return proj_is_equivalent_to_with_ctx(
|
||
|
self.context, self.projobj, other.projobj, PJ_COMP_EQUIVALENT) == 1
|
||
|
|
||
|
def __eq__(self, other):
|
||
|
if not isinstance(other, Base):
|
||
|
return False
|
||
|
return self._is_equivalent(other)
|
||
|
|
||
|
def is_exact_same(self, other):
|
||
|
"""Compares projection objects to see if they are exactly the same."""
|
||
|
if not isinstance(other, Base):
|
||
|
return False
|
||
|
return self._is_exact_same(other)
|
||
|
|
||
|
|
||
|
cdef class _CRSParts(Base):
|
||
|
@classmethod
|
||
|
def from_user_input(cls, user_input):
|
||
|
"""
|
||
|
.. versionadded:: 2.5.0
|
||
|
|
||
|
Create cls from user input:
|
||
|
- PROJ JSON string
|
||
|
- PROJ JSON dict
|
||
|
- WKT string
|
||
|
- An authority string
|
||
|
- An EPSG integer code
|
||
|
- An iterable of ("auth_name", "auth_code")
|
||
|
- An object with a `to_json` method.
|
||
|
|
||
|
Parameters
|
||
|
----------
|
||
|
user_input: str, dict, int, Iterable[str, str]
|
||
|
Input to create cls.
|
||
|
|
||
|
Returns
|
||
|
-------
|
||
|
cls
|
||
|
"""
|
||
|
if isinstance(user_input, str):
|
||
|
prepared = cls.from_string(user_input)
|
||
|
elif isinstance(user_input, dict):
|
||
|
prepared = cls.from_json_dict(user_input)
|
||
|
elif isinstance(user_input, int) and hasattr(cls, "from_epsg"):
|
||
|
prepared = cls.from_epsg(user_input)
|
||
|
elif (
|
||
|
isinstance(user_input, (list, tuple))
|
||
|
and len(user_input) == 2
|
||
|
and hasattr(cls, "from_authority")
|
||
|
):
|
||
|
prepared = cls.from_authority(*user_input)
|
||
|
elif hasattr(user_input, "to_json"):
|
||
|
prepared = cls.from_json(user_input.to_json())
|
||
|
else:
|
||
|
raise CRSError(f"Invalid {cls.__name__} input: {user_input!r}")
|
||
|
return prepared
|
||
|
|
||
|
def __eq__(self, other):
|
||
|
try:
|
||
|
other = self.from_user_input(other)
|
||
|
except CRSError:
|
||
|
return False
|
||
|
return self._is_equivalent(other)
|
||
|
|
||
|
|
||
|
cdef dict _COORD_SYSTEM_TYPE_MAP = {
|
||
|
PJ_CS_TYPE_UNKNOWN: "unknown",
|
||
|
PJ_CS_TYPE_CARTESIAN: "cartesian",
|
||
|
PJ_CS_TYPE_ELLIPSOIDAL: "ellipsoidal",
|
||
|
PJ_CS_TYPE_VERTICAL: "vertical",
|
||
|
PJ_CS_TYPE_SPHERICAL: "spherical",
|
||
|
PJ_CS_TYPE_ORDINAL: "ordinal",
|
||
|
PJ_CS_TYPE_PARAMETRIC: "parametric",
|
||
|
PJ_CS_TYPE_DATETIMETEMPORAL: "datetimetemporal",
|
||
|
PJ_CS_TYPE_TEMPORALCOUNT: "temporalcount",
|
||
|
PJ_CS_TYPE_TEMPORALMEASURE: "temporalmeasure",
|
||
|
}
|
||
|
|
||
|
cdef class CoordinateSystem(_CRSParts):
|
||
|
"""
|
||
|
.. versionadded:: 2.2.0
|
||
|
|
||
|
Coordinate System for CRS
|
||
|
|
||
|
Attributes
|
||
|
----------
|
||
|
name: str
|
||
|
The name of the coordinate system.
|
||
|
|
||
|
"""
|
||
|
def __cinit__(self):
|
||
|
self._axis_list = None
|
||
|
|
||
|
def __init__(self):
|
||
|
raise RuntimeError("CoordinateSystem is not initializable.")
|
||
|
|
||
|
@staticmethod
|
||
|
cdef CoordinateSystem create(PJ_CONTEXT* context, PJ* coord_system_pj):
|
||
|
cdef CoordinateSystem coord_system = CoordinateSystem.__new__(CoordinateSystem)
|
||
|
coord_system.context = context
|
||
|
coord_system.projobj = coord_system_pj
|
||
|
|
||
|
cdef PJ_COORDINATE_SYSTEM_TYPE cs_type = proj_cs_get_type(
|
||
|
coord_system.context,
|
||
|
coord_system.projobj,
|
||
|
)
|
||
|
coord_system.name = _COORD_SYSTEM_TYPE_MAP[cs_type]
|
||
|
return coord_system
|
||
|
|
||
|
@property
|
||
|
def axis_list(self):
|
||
|
"""
|
||
|
Returns
|
||
|
-------
|
||
|
list[Axis]:
|
||
|
The Axis list for the coordinate system.
|
||
|
"""
|
||
|
if self._axis_list is not None:
|
||
|
return self._axis_list
|
||
|
self._axis_list = []
|
||
|
cdef int num_axes = 0
|
||
|
num_axes = proj_cs_get_axis_count(
|
||
|
self.context,
|
||
|
self.projobj
|
||
|
)
|
||
|
for axis_idx from 0 <= axis_idx < num_axes:
|
||
|
self._axis_list.append(
|
||
|
Axis.create(
|
||
|
self.context,
|
||
|
self.projobj,
|
||
|
axis_idx
|
||
|
)
|
||
|
)
|
||
|
return self._axis_list
|
||
|
|
||
|
@staticmethod
|
||
|
def from_string(str coordinate_system_string not None):
|
||
|
"""
|
||
|
.. versionadded:: 2.5.0
|
||
|
|
||
|
.. note:: Only works with PROJ JSON.
|
||
|
|
||
|
Create a Coordinate System from a string.
|
||
|
|
||
|
Parameters
|
||
|
----------
|
||
|
coordinate_system_string: str
|
||
|
Coordinate System string.
|
||
|
|
||
|
Returns
|
||
|
-------
|
||
|
CoordinateSystem
|
||
|
"""
|
||
|
cdef PJ_CONTEXT* context = pyproj_context_create()
|
||
|
cdef PJ* coordinate_system_pj = proj_create(
|
||
|
context,
|
||
|
cstrencode(coordinate_system_string)
|
||
|
)
|
||
|
if coordinate_system_pj == NULL or proj_cs_get_type(
|
||
|
context,
|
||
|
coordinate_system_pj,
|
||
|
) == PJ_CS_TYPE_UNKNOWN:
|
||
|
proj_destroy(coordinate_system_pj)
|
||
|
pyproj_context_destroy(context)
|
||
|
raise CRSError(
|
||
|
"Invalid coordinate system string: "
|
||
|
f"{coordinate_system_string}"
|
||
|
)
|
||
|
_clear_proj_error()
|
||
|
return CoordinateSystem.create(context, coordinate_system_pj)
|
||
|
|
||
|
@staticmethod
|
||
|
def from_json_dict(dict coordinate_system_dict not None):
|
||
|
"""
|
||
|
.. versionadded:: 2.5.0
|
||
|
|
||
|
Create Coordinate System from a JSON dictionary.
|
||
|
|
||
|
Parameters
|
||
|
----------
|
||
|
coordinate_system_dict: str
|
||
|
Coordinate System dictionary.
|
||
|
|
||
|
Returns
|
||
|
-------
|
||
|
CoordinateSystem
|
||
|
"""
|
||
|
return CoordinateSystem.from_string(
|
||
|
json.dumps(coordinate_system_dict, cls=NumpyEncoder)
|
||
|
)
|
||
|
|
||
|
@staticmethod
|
||
|
def from_json(str coordinate_system_json_str not None):
|
||
|
"""
|
||
|
.. versionadded:: 2.5.0
|
||
|
|
||
|
Create Coordinate System from a JSON string.
|
||
|
|
||
|
Parameters
|
||
|
----------
|
||
|
coordinate_system_json_str: str
|
||
|
Coordinate System JSON string.
|
||
|
|
||
|
Returns
|
||
|
-------
|
||
|
CoordinateSystem
|
||
|
"""
|
||
|
return CoordinateSystem.from_json_dict(
|
||
|
_load_proj_json(coordinate_system_json_str)
|
||
|
)
|
||
|
|
||
|
def to_cf(self, bint rotated_pole=False):
|
||
|
"""
|
||
|
.. versionadded:: 3.0.0
|
||
|
|
||
|
This converts a :obj:`pyproj.crs.CoordinateSystem` axis
|
||
|
to a list of Climate and Forecast (CF) Version 1.8 dicts.
|
||
|
|
||
|
Parameters
|
||
|
----------
|
||
|
rotated_pole: bool, default=False
|
||
|
If True, the geographic coordinates are on a rotated pole grid.
|
||
|
This corresponds to the rotated_latitude_longitude grid_mapping_name.
|
||
|
|
||
|
Returns
|
||
|
-------
|
||
|
list[dict]:
|
||
|
CF-1.8 version of the CoordinateSystem.
|
||
|
"""
|
||
|
axis_list = self.to_json_dict()["axis"]
|
||
|
cf_params = []
|
||
|
def get_linear_unit(axis):
|
||
|
try:
|
||
|
return f'{axis["unit"]["conversion_factor"]} metre'
|
||
|
except TypeError:
|
||
|
return axis["unit"]
|
||
|
|
||
|
if self.name == "cartesian":
|
||
|
for axis in axis_list:
|
||
|
if axis["name"].lower() == "easting":
|
||
|
cf_axis = "X"
|
||
|
else:
|
||
|
cf_axis = "Y"
|
||
|
cf_params.append(dict(
|
||
|
axis=cf_axis,
|
||
|
long_name=axis["name"],
|
||
|
standard_name=f"projection_{cf_axis.lower()}_coordinate",
|
||
|
units=get_linear_unit(axis),
|
||
|
))
|
||
|
elif self.name == "ellipsoidal":
|
||
|
for axis in axis_list:
|
||
|
if axis["abbreviation"].upper() in ("D", "H"):
|
||
|
cf_params.append(dict(
|
||
|
standard_name="height_above_reference_ellipsoid",
|
||
|
long_name=axis["name"],
|
||
|
units=axis["unit"],
|
||
|
positive=axis["direction"],
|
||
|
axis="Z",
|
||
|
))
|
||
|
else:
|
||
|
if "longitude" in axis["name"].lower():
|
||
|
cf_axis = "X"
|
||
|
name = "longitude"
|
||
|
else:
|
||
|
cf_axis = "Y"
|
||
|
name = "latitude"
|
||
|
if rotated_pole:
|
||
|
cf_params.append(dict(
|
||
|
standard_name=f"grid_{name}",
|
||
|
long_name=f"{name} in rotated pole grid",
|
||
|
units="degrees",
|
||
|
axis=cf_axis,
|
||
|
))
|
||
|
else:
|
||
|
cf_params.append(dict(
|
||
|
standard_name=name,
|
||
|
long_name=f"{name} coordinate",
|
||
|
units=f'degrees_{axis["direction"]}',
|
||
|
axis=cf_axis,
|
||
|
))
|
||
|
elif self.name == "vertical":
|
||
|
for axis in axis_list:
|
||
|
cf_params.append(dict(
|
||
|
standard_name="height_above_reference_ellipsoid",
|
||
|
long_name=axis["name"],
|
||
|
units=get_linear_unit(axis),
|
||
|
positive=axis["direction"],
|
||
|
axis="Z",
|
||
|
))
|
||
|
|
||
|
return cf_params
|
||
|
|
||
|
|
||
|
cdef class Ellipsoid(_CRSParts):
|
||
|
"""
|
||
|
.. versionadded:: 2.0.0
|
||
|
|
||
|
Ellipsoid for CRS
|
||
|
|
||
|
Attributes
|
||
|
----------
|
||
|
name: str
|
||
|
The name of the ellipsoid.
|
||
|
is_semi_minor_computed: int
|
||
|
1 if True, 0 if False
|
||
|
semi_major_metre: float
|
||
|
The semi major axis in meters of the ellipsoid.
|
||
|
semi_minor_metre: float
|
||
|
The semi minor axis in meters of the ellipsoid.
|
||
|
inverse_flattening: float
|
||
|
The inverse flattening of the ellipsoid.
|
||
|
|
||
|
"""
|
||
|
def __cinit__(self):
|
||
|
# load in ellipsoid information if applicable
|
||
|
self.semi_major_metre = float("NaN")
|
||
|
self.semi_minor_metre = float("NaN")
|
||
|
self.is_semi_minor_computed = False
|
||
|
self.inverse_flattening = float("NaN")
|
||
|
|
||
|
def __init__(self):
|
||
|
raise RuntimeError(
|
||
|
"Ellipsoid can only be initialized like 'Ellipsoid.from_*()'."
|
||
|
)
|
||
|
|
||
|
@staticmethod
|
||
|
cdef Ellipsoid create(PJ_CONTEXT* context, PJ* ellipsoid_pj):
|
||
|
cdef Ellipsoid ellips = Ellipsoid.__new__(Ellipsoid)
|
||
|
ellips.context = context
|
||
|
ellips.projobj = ellipsoid_pj
|
||
|
cdef int is_semi_minor_computed = 0
|
||
|
proj_ellipsoid_get_parameters(
|
||
|
context,
|
||
|
ellipsoid_pj,
|
||
|
&ellips.semi_major_metre,
|
||
|
&ellips.semi_minor_metre,
|
||
|
&is_semi_minor_computed,
|
||
|
&ellips.inverse_flattening,
|
||
|
)
|
||
|
ellips.is_semi_minor_computed = is_semi_minor_computed == 1
|
||
|
ellips._set_base_info()
|
||
|
_clear_proj_error()
|
||
|
return ellips
|
||
|
|
||
|
@staticmethod
|
||
|
def from_authority(str auth_name not None, code not None):
|
||
|
"""
|
||
|
.. versionadded:: 2.2.0
|
||
|
|
||
|
Create an Ellipsoid from an authority code.
|
||
|
|
||
|
Parameters
|
||
|
----------
|
||
|
auth_name: str
|
||
|
Name of the authority.
|
||
|
code: str or int
|
||
|
The code used by the authority.
|
||
|
|
||
|
Returns
|
||
|
-------
|
||
|
Ellipsoid
|
||
|
"""
|
||
|
cdef PJ_CONTEXT* context = pyproj_context_create()
|
||
|
cdef PJ* ellipsoid_pj = proj_create_from_database(
|
||
|
context,
|
||
|
cstrencode(auth_name),
|
||
|
cstrencode(str(code)),
|
||
|
PJ_CATEGORY_ELLIPSOID,
|
||
|
False,
|
||
|
NULL,
|
||
|
)
|
||
|
|
||
|
if ellipsoid_pj == NULL:
|
||
|
pyproj_context_destroy(context)
|
||
|
raise CRSError(f"Invalid authority or code ({auth_name}, {code})")
|
||
|
_clear_proj_error()
|
||
|
return Ellipsoid.create(context, ellipsoid_pj)
|
||
|
|
||
|
@staticmethod
|
||
|
def from_epsg(code not None):
|
||
|
"""
|
||
|
.. versionadded:: 2.2.0
|
||
|
|
||
|
Create an Ellipsoid from an EPSG code.
|
||
|
|
||
|
Parameters
|
||
|
----------
|
||
|
code: str or int
|
||
|
The code used by the EPSG.
|
||
|
|
||
|
Returns
|
||
|
-------
|
||
|
Ellipsoid
|
||
|
"""
|
||
|
return Ellipsoid.from_authority("EPSG", code)
|
||
|
|
||
|
@staticmethod
|
||
|
def _from_string(str ellipsoid_string not None):
|
||
|
"""
|
||
|
Create an Ellipsoid from a string.
|
||
|
|
||
|
Examples:
|
||
|
- urn:ogc:def:ellipsoid:EPSG::7001
|
||
|
- ELLIPSOID["Airy 1830",6377563.396,299.3249646,
|
||
|
LENGTHUNIT["metre",1],
|
||
|
ID["EPSG",7001]]
|
||
|
|
||
|
Parameters
|
||
|
----------
|
||
|
ellipsoid_string: str
|
||
|
Ellipsoid string.
|
||
|
|
||
|
Returns
|
||
|
-------
|
||
|
Ellipsoid
|
||
|
"""
|
||
|
cdef PJ_CONTEXT* context = pyproj_context_create()
|
||
|
cdef PJ* ellipsoid_pj = proj_create(
|
||
|
context,
|
||
|
cstrencode(ellipsoid_string)
|
||
|
)
|
||
|
if ellipsoid_pj == NULL or proj_get_type(ellipsoid_pj) != PJ_TYPE_ELLIPSOID:
|
||
|
proj_destroy(ellipsoid_pj)
|
||
|
pyproj_context_destroy(context)
|
||
|
raise CRSError(
|
||
|
f"Invalid ellipsoid string: {ellipsoid_string}"
|
||
|
)
|
||
|
_clear_proj_error()
|
||
|
return Ellipsoid.create(context, ellipsoid_pj)
|
||
|
|
||
|
@staticmethod
|
||
|
def from_string(str ellipsoid_string not None):
|
||
|
"""
|
||
|
.. versionadded:: 2.2.0
|
||
|
|
||
|
Create an Ellipsoid from a string.
|
||
|
|
||
|
Examples:
|
||
|
- urn:ogc:def:ellipsoid:EPSG::7001
|
||
|
- ELLIPSOID["Airy 1830",6377563.396,299.3249646,
|
||
|
LENGTHUNIT["metre",1],
|
||
|
ID["EPSG",7001]]
|
||
|
- WGS 84
|
||
|
|
||
|
Parameters
|
||
|
----------
|
||
|
ellipsoid_string: str
|
||
|
Ellipsoid string.
|
||
|
|
||
|
Returns
|
||
|
-------
|
||
|
Ellipsoid
|
||
|
"""
|
||
|
try:
|
||
|
return Ellipsoid._from_string(ellipsoid_string)
|
||
|
except CRSError as crs_err:
|
||
|
try:
|
||
|
return Ellipsoid.from_name(ellipsoid_string)
|
||
|
except CRSError:
|
||
|
raise crs_err
|
||
|
|
||
|
@staticmethod
|
||
|
def from_json_dict(dict ellipsoid_dict not None):
|
||
|
"""
|
||
|
.. versionadded:: 2.4.0
|
||
|
|
||
|
Create Ellipsoid from a JSON dictionary.
|
||
|
|
||
|
Parameters
|
||
|
----------
|
||
|
ellipsoid_dict: str
|
||
|
Ellipsoid dictionary.
|
||
|
|
||
|
Returns
|
||
|
-------
|
||
|
Ellipsoid
|
||
|
"""
|
||
|
return Ellipsoid._from_string(json.dumps(ellipsoid_dict, cls=NumpyEncoder))
|
||
|
|
||
|
@staticmethod
|
||
|
def from_json(str ellipsoid_json_str not None):
|
||
|
"""
|
||
|
.. versionadded:: 2.4.0
|
||
|
|
||
|
Create Ellipsoid from a JSON string.
|
||
|
|
||
|
Parameters
|
||
|
----------
|
||
|
ellipsoid_json_str: str
|
||
|
Ellipsoid JSON string.
|
||
|
|
||
|
Returns
|
||
|
-------
|
||
|
Ellipsoid
|
||
|
"""
|
||
|
return Ellipsoid.from_json_dict(_load_proj_json(ellipsoid_json_str))
|
||
|
|
||
|
@staticmethod
|
||
|
def _from_name(
|
||
|
str ellipsoid_name,
|
||
|
str auth_name,
|
||
|
):
|
||
|
"""
|
||
|
.. versionadded:: 2.5.0
|
||
|
|
||
|
Create a Ellipsoid from a name.
|
||
|
|
||
|
Parameters
|
||
|
----------
|
||
|
ellipsoid_name: str
|
||
|
Ellipsoid name.
|
||
|
auth_name: str
|
||
|
The authority name to refine search (e.g. 'EPSG').
|
||
|
If None, will search all authorities.
|
||
|
|
||
|
Returns
|
||
|
-------
|
||
|
Ellipsoid
|
||
|
"""
|
||
|
cdef PJ_CONTEXT* context = pyproj_context_create()
|
||
|
cdef PJ* ellipsoid_pj = _from_name(
|
||
|
context,
|
||
|
ellipsoid_name,
|
||
|
auth_name,
|
||
|
PJ_TYPE_ELLIPSOID,
|
||
|
)
|
||
|
if ellipsoid_pj == NULL:
|
||
|
pyproj_context_destroy(context)
|
||
|
raise CRSError(f"Invalid ellipsoid name: {ellipsoid_name}")
|
||
|
_clear_proj_error()
|
||
|
return Ellipsoid.create(context, ellipsoid_pj)
|
||
|
|
||
|
@staticmethod
|
||
|
def from_name(
|
||
|
str ellipsoid_name not None,
|
||
|
str auth_name=None,
|
||
|
):
|
||
|
"""
|
||
|
.. versionadded:: 2.5.0
|
||
|
|
||
|
Create a Ellipsoid from a name.
|
||
|
|
||
|
Examples:
|
||
|
- WGS 84
|
||
|
|
||
|
Parameters
|
||
|
----------
|
||
|
ellipsoid_name: str
|
||
|
Ellipsoid name.
|
||
|
auth_name: str, optional
|
||
|
The authority name to refine search (e.g. 'EPSG').
|
||
|
If None, will search all authorities.
|
||
|
|
||
|
Returns
|
||
|
-------
|
||
|
Ellipsoid
|
||
|
"""
|
||
|
try:
|
||
|
return Ellipsoid._from_name(
|
||
|
ellipsoid_name=ellipsoid_name,
|
||
|
auth_name=auth_name,
|
||
|
)
|
||
|
except CRSError:
|
||
|
if auth_name not in ("PROJ", None):
|
||
|
raise
|
||
|
pass
|
||
|
|
||
|
# add support for past names for PROJ ellipsoids
|
||
|
try:
|
||
|
ellipsoid_params = pj_ellps[
|
||
|
_PJ_ELLPS_NAME_MAP.get(ellipsoid_name, ellipsoid_name)
|
||
|
]
|
||
|
except KeyError:
|
||
|
raise CRSError(f"Invalid ellipsoid name: {ellipsoid_name}")
|
||
|
return CustomEllipsoid(
|
||
|
name=ellipsoid_params["description"],
|
||
|
semi_major_axis=ellipsoid_params["a"],
|
||
|
semi_minor_axis=ellipsoid_params.get("b"),
|
||
|
inverse_flattening=ellipsoid_params.get("rf"),
|
||
|
)
|
||
|
|
||
|
|
||
|
cdef class PrimeMeridian(_CRSParts):
|
||
|
"""
|
||
|
.. versionadded:: 2.0.0
|
||
|
|
||
|
Prime Meridian for CRS
|
||
|
|
||
|
Attributes
|
||
|
----------
|
||
|
name: str
|
||
|
The name of the prime meridian.
|
||
|
unit_name: str
|
||
|
The unit name for the prime meridian.
|
||
|
|
||
|
"""
|
||
|
def __cinit__(self):
|
||
|
self.unit_name = None
|
||
|
|
||
|
def __init__(self):
|
||
|
raise RuntimeError(
|
||
|
"PrimeMeridian can only be initialized like 'PrimeMeridian.from_*()'."
|
||
|
)
|
||
|
|
||
|
@staticmethod
|
||
|
cdef PrimeMeridian create(PJ_CONTEXT* context, PJ* prime_meridian_pj):
|
||
|
cdef PrimeMeridian prime_meridian = PrimeMeridian.__new__(PrimeMeridian)
|
||
|
prime_meridian.context = context
|
||
|
prime_meridian.projobj = prime_meridian_pj
|
||
|
cdef const char * unit_name
|
||
|
proj_prime_meridian_get_parameters(
|
||
|
prime_meridian.context,
|
||
|
prime_meridian.projobj,
|
||
|
&prime_meridian.longitude,
|
||
|
&prime_meridian.unit_conversion_factor,
|
||
|
&unit_name,
|
||
|
)
|
||
|
prime_meridian.unit_name = decode_or_undefined(unit_name)
|
||
|
prime_meridian._set_base_info()
|
||
|
_clear_proj_error()
|
||
|
return prime_meridian
|
||
|
|
||
|
@staticmethod
|
||
|
def from_authority(str auth_name not None, code not None):
|
||
|
"""
|
||
|
.. versionadded:: 2.2.0
|
||
|
|
||
|
Create a PrimeMeridian from an authority code.
|
||
|
|
||
|
Parameters
|
||
|
----------
|
||
|
auth_name: str
|
||
|
Name of the authority.
|
||
|
code: str or int
|
||
|
The code used by the authority.
|
||
|
|
||
|
Returns
|
||
|
-------
|
||
|
PrimeMeridian
|
||
|
"""
|
||
|
cdef PJ_CONTEXT* context = pyproj_context_create()
|
||
|
cdef PJ* prime_meridian_pj = proj_create_from_database(
|
||
|
context,
|
||
|
cstrencode(auth_name),
|
||
|
cstrencode(str(code)),
|
||
|
PJ_CATEGORY_PRIME_MERIDIAN,
|
||
|
False,
|
||
|
NULL,
|
||
|
)
|
||
|
|
||
|
if prime_meridian_pj == NULL:
|
||
|
pyproj_context_destroy(context)
|
||
|
raise CRSError(f"Invalid authority or code ({auth_name}, {code})")
|
||
|
_clear_proj_error()
|
||
|
return PrimeMeridian.create(context, prime_meridian_pj)
|
||
|
|
||
|
@staticmethod
|
||
|
def from_epsg(code not None):
|
||
|
"""
|
||
|
.. versionadded:: 2.2.0
|
||
|
|
||
|
Create a PrimeMeridian from an EPSG code.
|
||
|
|
||
|
Parameters
|
||
|
----------
|
||
|
code: str or int
|
||
|
The code used by EPSG.
|
||
|
|
||
|
Returns
|
||
|
-------
|
||
|
PrimeMeridian
|
||
|
"""
|
||
|
return PrimeMeridian.from_authority("EPSG", code)
|
||
|
|
||
|
@staticmethod
|
||
|
def _from_string(str prime_meridian_string not None):
|
||
|
"""
|
||
|
Create an PrimeMeridian from a string.
|
||
|
|
||
|
Examples:
|
||
|
- urn:ogc:def:meridian:EPSG::8901
|
||
|
- PRIMEM["Greenwich",0,
|
||
|
ANGLEUNIT["degree",0.0174532925199433],
|
||
|
ID["EPSG",8901]]
|
||
|
|
||
|
Parameters
|
||
|
----------
|
||
|
prime_meridian_string: str
|
||
|
prime meridian string.
|
||
|
|
||
|
Returns
|
||
|
-------
|
||
|
PrimeMeridian
|
||
|
"""
|
||
|
cdef PJ_CONTEXT* context = pyproj_context_create()
|
||
|
cdef PJ* prime_meridian_pj = proj_create(
|
||
|
context,
|
||
|
cstrencode(prime_meridian_string)
|
||
|
)
|
||
|
if (
|
||
|
prime_meridian_pj == NULL or
|
||
|
proj_get_type(prime_meridian_pj) != PJ_TYPE_PRIME_MERIDIAN
|
||
|
):
|
||
|
proj_destroy(prime_meridian_pj)
|
||
|
pyproj_context_destroy(context)
|
||
|
raise CRSError(
|
||
|
f"Invalid prime meridian string: {prime_meridian_string}"
|
||
|
)
|
||
|
_clear_proj_error()
|
||
|
return PrimeMeridian.create(context, prime_meridian_pj)
|
||
|
|
||
|
@staticmethod
|
||
|
def from_string(str prime_meridian_string not None):
|
||
|
"""
|
||
|
.. versionadded:: 2.2.0
|
||
|
|
||
|
Create an PrimeMeridian from a string.
|
||
|
|
||
|
Examples:
|
||
|
- urn:ogc:def:meridian:EPSG::8901
|
||
|
- PRIMEM["Greenwich",0,
|
||
|
ANGLEUNIT["degree",0.0174532925199433],
|
||
|
ID["EPSG",8901]]
|
||
|
- Greenwich
|
||
|
|
||
|
Parameters
|
||
|
----------
|
||
|
prime_meridian_string: str
|
||
|
prime meridian string.
|
||
|
|
||
|
Returns
|
||
|
-------
|
||
|
PrimeMeridian
|
||
|
"""
|
||
|
try:
|
||
|
return PrimeMeridian._from_string(prime_meridian_string)
|
||
|
except CRSError as crs_err:
|
||
|
try:
|
||
|
return PrimeMeridian.from_name(prime_meridian_string)
|
||
|
except CRSError:
|
||
|
raise crs_err
|
||
|
|
||
|
@staticmethod
|
||
|
def from_json_dict(dict prime_meridian_dict not None):
|
||
|
"""
|
||
|
.. versionadded:: 2.4.0
|
||
|
|
||
|
Create PrimeMeridian from a JSON dictionary.
|
||
|
|
||
|
Parameters
|
||
|
----------
|
||
|
prime_meridian_dict: str
|
||
|
PrimeMeridian dictionary.
|
||
|
|
||
|
Returns
|
||
|
-------
|
||
|
PrimeMeridian
|
||
|
"""
|
||
|
return PrimeMeridian._from_string(
|
||
|
json.dumps(prime_meridian_dict, cls=NumpyEncoder)
|
||
|
)
|
||
|
|
||
|
@staticmethod
|
||
|
def from_json(str prime_meridian_json_str not None):
|
||
|
"""
|
||
|
.. versionadded:: 2.4.0
|
||
|
|
||
|
Create PrimeMeridian from a JSON string.
|
||
|
|
||
|
Parameters
|
||
|
----------
|
||
|
prime_meridian_json_str: str
|
||
|
PrimeMeridian JSON string.
|
||
|
|
||
|
Returns
|
||
|
-------
|
||
|
PrimeMeridian
|
||
|
"""
|
||
|
return PrimeMeridian.from_json_dict(_load_proj_json(prime_meridian_json_str))
|
||
|
|
||
|
@staticmethod
|
||
|
def from_name(
|
||
|
str prime_meridian_name not None,
|
||
|
str auth_name=None,
|
||
|
):
|
||
|
"""
|
||
|
.. versionadded:: 2.5.0
|
||
|
|
||
|
Create a Prime Meridian from a name.
|
||
|
|
||
|
Examples:
|
||
|
- Greenwich
|
||
|
|
||
|
Parameters
|
||
|
----------
|
||
|
prime_meridian_name: str
|
||
|
Prime Meridian name.
|
||
|
auth_name: str, optional
|
||
|
The authority name to refine search (e.g. 'EPSG').
|
||
|
If None, will search all authorities.
|
||
|
|
||
|
Returns
|
||
|
-------
|
||
|
PrimeMeridian
|
||
|
"""
|
||
|
cdef PJ_CONTEXT* context = pyproj_context_create()
|
||
|
cdef PJ* prime_meridian_pj = _from_name(
|
||
|
context,
|
||
|
prime_meridian_name,
|
||
|
auth_name,
|
||
|
PJ_TYPE_PRIME_MERIDIAN,
|
||
|
)
|
||
|
if prime_meridian_pj == NULL:
|
||
|
pyproj_context_destroy(context)
|
||
|
raise CRSError(
|
||
|
f"Invalid prime meridian name: {prime_meridian_name}"
|
||
|
)
|
||
|
_clear_proj_error()
|
||
|
return PrimeMeridian.create(context, prime_meridian_pj)
|
||
|
|
||
|
|
||
|
cdef dict _DATUM_TYPE_MAP = {
|
||
|
PJ_TYPE_GEODETIC_REFERENCE_FRAME: "Geodetic Reference Frame",
|
||
|
PJ_TYPE_DYNAMIC_GEODETIC_REFERENCE_FRAME: "Dynamic Geodetic Reference Frame",
|
||
|
PJ_TYPE_VERTICAL_REFERENCE_FRAME: "Vertical Reference Frame",
|
||
|
PJ_TYPE_DYNAMIC_VERTICAL_REFERENCE_FRAME: "Dynamic Vertical Reference Frame",
|
||
|
PJ_TYPE_DATUM_ENSEMBLE: "Datum Ensemble",
|
||
|
PJ_TYPE_TEMPORAL_DATUM: "Temporal Datum",
|
||
|
PJ_TYPE_ENGINEERING_DATUM: "Engineering Datum",
|
||
|
PJ_TYPE_PARAMETRIC_DATUM: "Parametric Datum",
|
||
|
}
|
||
|
|
||
|
cdef dict _PJ_DATUM_TYPE_MAP = {
|
||
|
DatumType.DATUM_ENSEMBLE: PJ_TYPE_DATUM_ENSEMBLE,
|
||
|
DatumType.GEODETIC_REFERENCE_FRAME: PJ_TYPE_GEODETIC_REFERENCE_FRAME,
|
||
|
DatumType.DYNAMIC_GEODETIC_REFERENCE_FRAME:
|
||
|
PJ_TYPE_DYNAMIC_GEODETIC_REFERENCE_FRAME,
|
||
|
DatumType.VERTICAL_REFERENCE_FRAME: PJ_TYPE_VERTICAL_REFERENCE_FRAME,
|
||
|
DatumType.DYNAMIC_VERTICAL_REFERENCE_FRAME:
|
||
|
PJ_TYPE_DYNAMIC_VERTICAL_REFERENCE_FRAME,
|
||
|
}
|
||
|
|
||
|
|
||
|
cdef class Datum(_CRSParts):
|
||
|
"""
|
||
|
.. versionadded:: 2.2.0
|
||
|
|
||
|
Datum for CRS. If it is a compound CRS it is the horizontal datum.
|
||
|
|
||
|
Attributes
|
||
|
----------
|
||
|
name: str
|
||
|
The name of the datum.
|
||
|
|
||
|
"""
|
||
|
def __cinit__(self):
|
||
|
self._ellipsoid = None
|
||
|
self._prime_meridian = None
|
||
|
|
||
|
def __init__(self):
|
||
|
raise RuntimeError(
|
||
|
"Datum can only be initialized like 'Datum.from_*()'."
|
||
|
)
|
||
|
|
||
|
@staticmethod
|
||
|
cdef Datum create(PJ_CONTEXT* context, PJ* datum_pj):
|
||
|
cdef Datum datum = Datum.__new__(Datum)
|
||
|
datum.context = context
|
||
|
datum.projobj = datum_pj
|
||
|
datum._set_base_info()
|
||
|
datum.type_name = _DATUM_TYPE_MAP[proj_get_type(datum.projobj)]
|
||
|
return datum
|
||
|
|
||
|
@staticmethod
|
||
|
def _from_authority(str auth_name not None, code not None, PJ_CATEGORY category):
|
||
|
"""
|
||
|
Create a Datum from an authority code.
|
||
|
|
||
|
Parameters
|
||
|
----------
|
||
|
auth_name: str
|
||
|
Name of the authority.
|
||
|
code: str or int
|
||
|
The code used by the authority.
|
||
|
|
||
|
Returns
|
||
|
-------
|
||
|
Datum
|
||
|
"""
|
||
|
cdef PJ_CONTEXT* context = pyproj_context_create()
|
||
|
|
||
|
cdef PJ* datum_pj = proj_create_from_database(
|
||
|
context,
|
||
|
cstrencode(auth_name),
|
||
|
cstrencode(str(code)),
|
||
|
category,
|
||
|
False,
|
||
|
NULL,
|
||
|
)
|
||
|
|
||
|
if datum_pj == NULL:
|
||
|
pyproj_context_destroy(context)
|
||
|
raise CRSError(f"Invalid authority or code ({auth_name}, {code})")
|
||
|
_clear_proj_error()
|
||
|
return Datum.create(context, datum_pj)
|
||
|
|
||
|
@staticmethod
|
||
|
def from_authority(str auth_name not None, code not None):
|
||
|
"""
|
||
|
Create a Datum from an authority code.
|
||
|
|
||
|
Parameters
|
||
|
----------
|
||
|
auth_name: str
|
||
|
Name of the authority.
|
||
|
code: str or int
|
||
|
The code used by the authority.
|
||
|
|
||
|
Returns
|
||
|
-------
|
||
|
Datum
|
||
|
"""
|
||
|
try:
|
||
|
return Datum._from_authority(auth_name, code, PJ_CATEGORY_DATUM_ENSEMBLE)
|
||
|
except CRSError:
|
||
|
return Datum._from_authority(auth_name, code, PJ_CATEGORY_DATUM)
|
||
|
|
||
|
@staticmethod
|
||
|
def from_epsg(code not None):
|
||
|
"""
|
||
|
Create a Datum from an EPSG code.
|
||
|
|
||
|
Parameters
|
||
|
----------
|
||
|
code: str or int
|
||
|
The code used by EPSG.
|
||
|
|
||
|
Returns
|
||
|
-------
|
||
|
Datum
|
||
|
"""
|
||
|
return Datum.from_authority("EPSG", code)
|
||
|
|
||
|
@staticmethod
|
||
|
def _from_string(str datum_string not None):
|
||
|
"""
|
||
|
Create a Datum from a string.
|
||
|
|
||
|
Examples:
|
||
|
- urn:ogc:def:datum:EPSG::6326
|
||
|
- DATUM["World Geodetic System 1984",
|
||
|
ELLIPSOID["WGS 84",6378137,298.257223563,
|
||
|
LENGTHUNIT["metre",1]],
|
||
|
ID["EPSG",6326]]
|
||
|
|
||
|
Parameters
|
||
|
----------
|
||
|
datum_string: str
|
||
|
Datum string.
|
||
|
|
||
|
Returns
|
||
|
-------
|
||
|
Datum
|
||
|
"""
|
||
|
cdef PJ_CONTEXT* context = pyproj_context_create()
|
||
|
cdef PJ* datum_pj = proj_create(
|
||
|
context,
|
||
|
cstrencode(datum_string)
|
||
|
)
|
||
|
if (
|
||
|
datum_pj == NULL or
|
||
|
proj_get_type(datum_pj) not in _DATUM_TYPE_MAP
|
||
|
):
|
||
|
proj_destroy(datum_pj)
|
||
|
pyproj_context_destroy(context)
|
||
|
raise CRSError(f"Invalid datum string: {datum_string}")
|
||
|
_clear_proj_error()
|
||
|
return Datum.create(context, datum_pj)
|
||
|
|
||
|
@staticmethod
|
||
|
def from_string(str datum_string not None):
|
||
|
"""
|
||
|
Create a Datum from a string.
|
||
|
|
||
|
Examples:
|
||
|
- urn:ogc:def:datum:EPSG::6326
|
||
|
- DATUM["World Geodetic System 1984",
|
||
|
ELLIPSOID["WGS 84",6378137,298.257223563,
|
||
|
LENGTHUNIT["metre",1]],
|
||
|
ID["EPSG",6326]]
|
||
|
- World Geodetic System 1984
|
||
|
|
||
|
Parameters
|
||
|
----------
|
||
|
datum_string: str
|
||
|
Datum string.
|
||
|
|
||
|
Returns
|
||
|
-------
|
||
|
Datum
|
||
|
"""
|
||
|
try:
|
||
|
return Datum._from_string(datum_string)
|
||
|
except CRSError as crs_err:
|
||
|
try:
|
||
|
return Datum.from_name(datum_string)
|
||
|
except CRSError:
|
||
|
raise crs_err
|
||
|
|
||
|
@staticmethod
|
||
|
def _from_name(
|
||
|
str datum_name,
|
||
|
str auth_name,
|
||
|
object datum_type,
|
||
|
):
|
||
|
"""
|
||
|
.. versionadded:: 2.5.0
|
||
|
|
||
|
Create a Datum from a name.
|
||
|
|
||
|
Parameters
|
||
|
----------
|
||
|
datum_name: str
|
||
|
Datum name.
|
||
|
auth_name: str
|
||
|
The authority name to refine search (e.g. 'EPSG').
|
||
|
If None, will search all authorities.
|
||
|
datum_type: DatumType
|
||
|
The datum type to create.
|
||
|
|
||
|
Returns
|
||
|
-------
|
||
|
Datum
|
||
|
"""
|
||
|
pj_datum_type = _PJ_DATUM_TYPE_MAP[datum_type]
|
||
|
cdef PJ_CONTEXT* context = pyproj_context_create()
|
||
|
cdef PJ* datum_pj = _from_name(
|
||
|
context,
|
||
|
datum_name,
|
||
|
auth_name,
|
||
|
<PJ_TYPE>pj_datum_type,
|
||
|
)
|
||
|
if datum_pj == NULL:
|
||
|
pyproj_context_destroy(context)
|
||
|
raise CRSError(f"Invalid datum name: {datum_name}")
|
||
|
_clear_proj_error()
|
||
|
return Datum.create(context, datum_pj)
|
||
|
|
||
|
@staticmethod
|
||
|
def from_name(
|
||
|
str datum_name not None,
|
||
|
str auth_name=None,
|
||
|
datum_type=None,
|
||
|
):
|
||
|
"""
|
||
|
.. versionadded:: 2.5.0
|
||
|
|
||
|
Create a Datum from a name.
|
||
|
|
||
|
Examples:
|
||
|
- WGS 84
|
||
|
- World Geodetic System 1984
|
||
|
|
||
|
Parameters
|
||
|
----------
|
||
|
datum_name: str
|
||
|
Datum name.
|
||
|
auth_name: str, optional
|
||
|
The authority name to refine search (e.g. 'EPSG').
|
||
|
If None, will search all authorities.
|
||
|
datum_type: DatumType, optional
|
||
|
The datum type to create. If it is None, it uses any datum type.
|
||
|
|
||
|
Returns
|
||
|
-------
|
||
|
Datum
|
||
|
"""
|
||
|
if datum_type is None:
|
||
|
# try creating name from all datum types
|
||
|
first_error = None
|
||
|
for datum_type in _PJ_DATUM_TYPE_MAP:
|
||
|
try:
|
||
|
return Datum.from_name(
|
||
|
datum_name=datum_name,
|
||
|
auth_name=auth_name,
|
||
|
datum_type=datum_type,
|
||
|
)
|
||
|
except CRSError as err:
|
||
|
if first_error is None:
|
||
|
first_error = err
|
||
|
raise first_error
|
||
|
|
||
|
datum_type = DatumType.create(datum_type)
|
||
|
return Datum._from_name(
|
||
|
datum_name=datum_name,
|
||
|
auth_name=auth_name,
|
||
|
datum_type=datum_type,
|
||
|
)
|
||
|
|
||
|
@staticmethod
|
||
|
def from_json_dict(dict datum_dict not None):
|
||
|
"""
|
||
|
.. versionadded:: 2.4.0
|
||
|
|
||
|
Create Datum from a JSON dictionary.
|
||
|
|
||
|
Parameters
|
||
|
----------
|
||
|
datum_dict: str
|
||
|
Datum dictionary.
|
||
|
|
||
|
Returns
|
||
|
-------
|
||
|
Datum
|
||
|
"""
|
||
|
return Datum._from_string(json.dumps(datum_dict, cls=NumpyEncoder))
|
||
|
|
||
|
@staticmethod
|
||
|
def from_json(str datum_json_str not None):
|
||
|
"""
|
||
|
.. versionadded:: 2.4.0
|
||
|
|
||
|
Create Datum from a JSON string.
|
||
|
|
||
|
Parameters
|
||
|
----------
|
||
|
datum_json_str: str
|
||
|
Datum JSON string.
|
||
|
|
||
|
Returns
|
||
|
-------
|
||
|
Datum
|
||
|
"""
|
||
|
return Datum.from_json_dict(_load_proj_json(datum_json_str))
|
||
|
|
||
|
@property
|
||
|
def ellipsoid(self):
|
||
|
"""
|
||
|
Returns
|
||
|
-------
|
||
|
Ellipsoid:
|
||
|
The ellipsoid object with associated attributes.
|
||
|
"""
|
||
|
if self._ellipsoid is not None:
|
||
|
return None if self._ellipsoid is False else self._ellipsoid
|
||
|
cdef PJ_CONTEXT* context = pyproj_context_create()
|
||
|
cdef PJ* ellipsoid_pj = proj_get_ellipsoid(
|
||
|
context,
|
||
|
self.projobj,
|
||
|
)
|
||
|
_clear_proj_error()
|
||
|
if ellipsoid_pj == NULL:
|
||
|
pyproj_context_destroy(context)
|
||
|
self._ellipsoid = False
|
||
|
return None
|
||
|
self._ellipsoid = Ellipsoid.create(context, ellipsoid_pj)
|
||
|
return self._ellipsoid
|
||
|
|
||
|
@property
|
||
|
def prime_meridian(self):
|
||
|
"""
|
||
|
Returns
|
||
|
-------
|
||
|
PrimeMeridian:
|
||
|
The CRS prime meridian object with associated attributes.
|
||
|
"""
|
||
|
if self._prime_meridian is not None:
|
||
|
return None if self._prime_meridian is False else self._prime_meridian
|
||
|
cdef PJ_CONTEXT* context = pyproj_context_create()
|
||
|
cdef PJ* prime_meridian_pj = proj_get_prime_meridian(
|
||
|
context,
|
||
|
self.projobj,
|
||
|
)
|
||
|
_clear_proj_error()
|
||
|
if prime_meridian_pj == NULL:
|
||
|
pyproj_context_destroy(context)
|
||
|
self._prime_meridian = False
|
||
|
return None
|
||
|
self._prime_meridian = PrimeMeridian.create(
|
||
|
context,
|
||
|
prime_meridian_pj,
|
||
|
)
|
||
|
return self._prime_meridian
|
||
|
|
||
|
|
||
|
cdef class Param:
|
||
|
"""
|
||
|
.. versionadded:: 2.2.0
|
||
|
|
||
|
Coordinate operation parameter.
|
||
|
|
||
|
Attributes
|
||
|
----------
|
||
|
name: str
|
||
|
The name of the parameter.
|
||
|
auth_name: str
|
||
|
The authority name of the parameter (i.e. EPSG).
|
||
|
code: str
|
||
|
The code of the parameter (i.e. 9807).
|
||
|
value: str or double
|
||
|
The value of the parameter.
|
||
|
unit_conversion_factor: double
|
||
|
The factor to convert to meters.
|
||
|
unit_name: str
|
||
|
The name of the unit.
|
||
|
unit_auth_name: str
|
||
|
The authority name of the unit (i.e. EPSG).
|
||
|
unit_code: str
|
||
|
The code of the unit (i.e. 9807).
|
||
|
unit_category: str
|
||
|
The category of the unit (“unknown”, “none”, “linear”,
|
||
|
“angular”, “scale”, “time” or “parametric”).
|
||
|
|
||
|
"""
|
||
|
def __cinit__(self):
|
||
|
self.name = "undefined"
|
||
|
self.auth_name = "undefined"
|
||
|
self.code = "undefined"
|
||
|
self.value = "undefined"
|
||
|
self.unit_conversion_factor = float("nan")
|
||
|
self.unit_name = "undefined"
|
||
|
self.unit_auth_name = "undefined"
|
||
|
self.unit_code = "undefined"
|
||
|
self.unit_category = "undefined"
|
||
|
|
||
|
@staticmethod
|
||
|
cdef Param create(PJ_CONTEXT* context, PJ* projobj, int param_idx):
|
||
|
cdef:
|
||
|
Param param = Param()
|
||
|
const char *out_name
|
||
|
const char *out_auth_name
|
||
|
const char *out_code
|
||
|
const char *out_value
|
||
|
const char *out_value_string
|
||
|
const char *out_unit_name
|
||
|
const char *out_unit_auth_name
|
||
|
const char *out_unit_code
|
||
|
const char *out_unit_category
|
||
|
double value_double
|
||
|
|
||
|
proj_coordoperation_get_param(
|
||
|
context,
|
||
|
projobj,
|
||
|
param_idx,
|
||
|
&out_name,
|
||
|
&out_auth_name,
|
||
|
&out_code,
|
||
|
&value_double,
|
||
|
&out_value_string,
|
||
|
¶m.unit_conversion_factor,
|
||
|
&out_unit_name,
|
||
|
&out_unit_auth_name,
|
||
|
&out_unit_code,
|
||
|
&out_unit_category
|
||
|
)
|
||
|
param.name = decode_or_undefined(out_name)
|
||
|
param.auth_name = decode_or_undefined(out_auth_name)
|
||
|
param.code = decode_or_undefined(out_code)
|
||
|
param.unit_name = decode_or_undefined(out_unit_name)
|
||
|
param.unit_auth_name = decode_or_undefined(out_unit_auth_name)
|
||
|
param.unit_code = decode_or_undefined(out_unit_code)
|
||
|
param.unit_category = decode_or_undefined(out_unit_category)
|
||
|
value_string = cstrdecode(out_value_string)
|
||
|
param.value = value_double if value_string is None else value_string
|
||
|
return param
|
||
|
|
||
|
def __str__(self):
|
||
|
return f"{self.auth_name}:{self.auth_code}"
|
||
|
|
||
|
def __repr__(self):
|
||
|
return (
|
||
|
f"Param(name={self.name}, auth_name={self.auth_name}, code={self.code}, "
|
||
|
f"value={self.value}, unit_name={self.unit_name}, "
|
||
|
f"unit_auth_name={self.unit_auth_name}, unit_code={self.unit_code}, "
|
||
|
f"unit_category={self.unit_category})"
|
||
|
)
|
||
|
|
||
|
|
||
|
cdef class Grid:
|
||
|
"""
|
||
|
.. versionadded:: 2.2.0
|
||
|
|
||
|
Coordinate operation grid.
|
||
|
|
||
|
Attributes
|
||
|
----------
|
||
|
short_name: str
|
||
|
The short name of the grid.
|
||
|
full_name: str
|
||
|
The full name of the grid.
|
||
|
package_name: str
|
||
|
The package name where the grid might be found.
|
||
|
url: str
|
||
|
The grid URL or the package URL where the grid might be found.
|
||
|
direct_download: int
|
||
|
If 1, *url* can be downloaded directly.
|
||
|
open_license: int
|
||
|
If 1, the grid is released with an open license.
|
||
|
available: int
|
||
|
If 1, the grid is available at runtime.
|
||
|
|
||
|
"""
|
||
|
def __cinit__(self):
|
||
|
self.short_name = "undefined"
|
||
|
self.full_name = "undefined"
|
||
|
self.package_name = "undefined"
|
||
|
self.url = "undefined"
|
||
|
self.direct_download = False
|
||
|
self.open_license = False
|
||
|
self.available = False
|
||
|
|
||
|
@staticmethod
|
||
|
cdef Grid create(PJ_CONTEXT* context, PJ* projobj, int grid_idx):
|
||
|
cdef:
|
||
|
Grid grid = Grid()
|
||
|
const char *out_short_name
|
||
|
const char *out_full_name
|
||
|
const char *out_package_name
|
||
|
const char *out_url
|
||
|
int direct_download = 0
|
||
|
int open_license = 0
|
||
|
int available = 0
|
||
|
|
||
|
proj_coordoperation_get_grid_used(
|
||
|
context,
|
||
|
projobj,
|
||
|
grid_idx,
|
||
|
&out_short_name,
|
||
|
&out_full_name,
|
||
|
&out_package_name,
|
||
|
&out_url,
|
||
|
&direct_download,
|
||
|
&open_license,
|
||
|
&available
|
||
|
)
|
||
|
grid.short_name = decode_or_undefined(out_short_name)
|
||
|
grid.full_name = decode_or_undefined(out_full_name)
|
||
|
grid.package_name = decode_or_undefined(out_package_name)
|
||
|
grid.url = decode_or_undefined(out_url)
|
||
|
grid.direct_download = direct_download == 1
|
||
|
grid.open_license = open_license == 1
|
||
|
grid.available = available == 1
|
||
|
_clear_proj_error()
|
||
|
return grid
|
||
|
|
||
|
def __str__(self):
|
||
|
return self.full_name
|
||
|
|
||
|
def __repr__(self):
|
||
|
return (
|
||
|
f"Grid(short_name={self.short_name}, full_name={self.full_name}, "
|
||
|
f"package_name={self.package_name}, url={self.url}, "
|
||
|
f"direct_download={self.direct_download}, "
|
||
|
f"open_license={self.open_license}, available={self.available})"
|
||
|
)
|
||
|
|
||
|
|
||
|
cdef dict _COORDINATE_OPERATION_TYPE_MAP = {
|
||
|
PJ_TYPE_UNKNOWN: "Unknown",
|
||
|
PJ_TYPE_CONVERSION: "Conversion",
|
||
|
PJ_TYPE_TRANSFORMATION: "Transformation",
|
||
|
PJ_TYPE_CONCATENATED_OPERATION: "Concatenated Operation",
|
||
|
PJ_TYPE_OTHER_COORDINATE_OPERATION: "Other Coordinate Operation",
|
||
|
}
|
||
|
|
||
|
cdef dict _PJ_COORDINATE_OPERATION_TYPE_MAP = {
|
||
|
CoordinateOperationType.CONVERSION: PJ_TYPE_CONVERSION,
|
||
|
CoordinateOperationType.TRANSFORMATION: PJ_TYPE_TRANSFORMATION,
|
||
|
CoordinateOperationType.CONCATENATED_OPERATION: PJ_TYPE_CONCATENATED_OPERATION,
|
||
|
CoordinateOperationType.OTHER_COORDINATE_OPERATION:
|
||
|
PJ_TYPE_OTHER_COORDINATE_OPERATION,
|
||
|
}
|
||
|
|
||
|
cdef class CoordinateOperation(_CRSParts):
|
||
|
"""
|
||
|
.. versionadded:: 2.2.0
|
||
|
|
||
|
Coordinate operation for CRS.
|
||
|
|
||
|
Attributes
|
||
|
----------
|
||
|
name: str
|
||
|
The name of the method(projection) with authority information.
|
||
|
method_name: str
|
||
|
The method (projection) name.
|
||
|
method_auth_name: str
|
||
|
The method authority name.
|
||
|
method_code: str
|
||
|
The method code.
|
||
|
is_instantiable: int
|
||
|
If 1, a coordinate operation can be instantiated as a PROJ pipeline.
|
||
|
This also checks that referenced grids are available.
|
||
|
has_ballpark_transformation: int
|
||
|
If 1, the coordinate operation has a “ballpark” transformation,
|
||
|
that is a very approximate one, due to lack of more accurate transformations.
|
||
|
accuracy: float
|
||
|
The accuracy (in metre) of a coordinate operation.
|
||
|
|
||
|
"""
|
||
|
def __cinit__(self):
|
||
|
self._params = None
|
||
|
self._grids = None
|
||
|
self._area_of_use = None
|
||
|
self.method_name = "undefined"
|
||
|
self.method_auth_name = "undefined"
|
||
|
self.method_code = "undefined"
|
||
|
self.is_instantiable = False
|
||
|
self.has_ballpark_transformation = False
|
||
|
self.accuracy = float("nan")
|
||
|
self._towgs84 = None
|
||
|
self._operations = None
|
||
|
|
||
|
def __init__(self):
|
||
|
raise RuntimeError(
|
||
|
"CoordinateOperation can only be initialized like "
|
||
|
"CoordinateOperation.from_*()'."
|
||
|
)
|
||
|
|
||
|
@staticmethod
|
||
|
cdef CoordinateOperation create(PJ_CONTEXT* context, PJ* coord_operation_pj):
|
||
|
cdef CoordinateOperation coord_operation = CoordinateOperation.__new__(
|
||
|
CoordinateOperation
|
||
|
)
|
||
|
coord_operation.context = context
|
||
|
coord_operation.projobj = coord_operation_pj
|
||
|
cdef const char *out_method_name = NULL
|
||
|
cdef const char *out_method_auth_name = NULL
|
||
|
cdef const char *out_method_code = NULL
|
||
|
proj_coordoperation_get_method_info(
|
||
|
coord_operation.context,
|
||
|
coord_operation.projobj,
|
||
|
&out_method_name,
|
||
|
&out_method_auth_name,
|
||
|
&out_method_code
|
||
|
)
|
||
|
coord_operation._set_base_info()
|
||
|
coord_operation.method_name = decode_or_undefined(out_method_name)
|
||
|
coord_operation.method_auth_name = decode_or_undefined(out_method_auth_name)
|
||
|
coord_operation.method_code = decode_or_undefined(out_method_code)
|
||
|
coord_operation.accuracy = proj_coordoperation_get_accuracy(
|
||
|
coord_operation.context,
|
||
|
coord_operation.projobj
|
||
|
)
|
||
|
coord_operation.is_instantiable = proj_coordoperation_is_instantiable(
|
||
|
coord_operation.context,
|
||
|
coord_operation.projobj
|
||
|
) == 1
|
||
|
coord_operation.has_ballpark_transformation = \
|
||
|
proj_coordoperation_has_ballpark_transformation(
|
||
|
coord_operation.context,
|
||
|
coord_operation.projobj
|
||
|
) == 1
|
||
|
cdef PJ_TYPE operation_type = proj_get_type(coord_operation.projobj)
|
||
|
coord_operation.type_name = _COORDINATE_OPERATION_TYPE_MAP[operation_type]
|
||
|
_clear_proj_error()
|
||
|
return coord_operation
|
||
|
|
||
|
@staticmethod
|
||
|
def from_authority(
|
||
|
str auth_name not None,
|
||
|
code not None,
|
||
|
bint use_proj_alternative_grid_names=False,
|
||
|
):
|
||
|
"""
|
||
|
Create a CoordinateOperation from an authority code.
|
||
|
|
||
|
Parameters
|
||
|
----------
|
||
|
auth_name: str
|
||
|
Name of the authority.
|
||
|
code: str or int
|
||
|
The code used by the authority.
|
||
|
use_proj_alternative_grid_names: bool, default=False
|
||
|
Use the PROJ alternative grid names.
|
||
|
|
||
|
Returns
|
||
|
-------
|
||
|
CoordinateOperation
|
||
|
"""
|
||
|
cdef PJ_CONTEXT* context = pyproj_context_create()
|
||
|
cdef PJ* coord_operation_pj = proj_create_from_database(
|
||
|
context,
|
||
|
cstrencode(auth_name),
|
||
|
cstrencode(str(code)),
|
||
|
PJ_CATEGORY_COORDINATE_OPERATION,
|
||
|
use_proj_alternative_grid_names,
|
||
|
NULL,
|
||
|
)
|
||
|
|
||
|
if coord_operation_pj == NULL:
|
||
|
pyproj_context_destroy(context)
|
||
|
raise CRSError(f"Invalid authority or code ({auth_name}, {code})")
|
||
|
_clear_proj_error()
|
||
|
return CoordinateOperation.create(context, coord_operation_pj)
|
||
|
|
||
|
@staticmethod
|
||
|
def from_epsg(code not None, bint use_proj_alternative_grid_names= False):
|
||
|
"""
|
||
|
Create a CoordinateOperation from an EPSG code.
|
||
|
|
||
|
Parameters
|
||
|
----------
|
||
|
code: str or int
|
||
|
The code used by EPSG.
|
||
|
use_proj_alternative_grid_names: bool, default=False
|
||
|
Use the PROJ alternative grid names.
|
||
|
|
||
|
Returns
|
||
|
-------
|
||
|
CoordinateOperation
|
||
|
"""
|
||
|
return CoordinateOperation.from_authority(
|
||
|
"EPSG", code, use_proj_alternative_grid_names
|
||
|
)
|
||
|
|
||
|
@staticmethod
|
||
|
def _from_string(str coordinate_operation_string not None):
|
||
|
"""
|
||
|
Create a CoordinateOperation from a string.
|
||
|
|
||
|
Example:
|
||
|
- urn:ogc:def:coordinateOperation:EPSG::1671
|
||
|
|
||
|
Parameters
|
||
|
----------
|
||
|
coordinate_operation_string: str
|
||
|
Coordinate operation string.
|
||
|
|
||
|
Returns
|
||
|
-------
|
||
|
CoordinateOperation
|
||
|
"""
|
||
|
cdef PJ_CONTEXT* context = pyproj_context_create()
|
||
|
cdef PJ* coord_operation_pj = proj_create(
|
||
|
context,
|
||
|
cstrencode(coordinate_operation_string)
|
||
|
)
|
||
|
if (
|
||
|
coord_operation_pj == NULL or
|
||
|
proj_get_type(coord_operation_pj) not in (
|
||
|
PJ_TYPE_CONVERSION,
|
||
|
PJ_TYPE_TRANSFORMATION,
|
||
|
PJ_TYPE_CONCATENATED_OPERATION,
|
||
|
PJ_TYPE_OTHER_COORDINATE_OPERATION,
|
||
|
)
|
||
|
):
|
||
|
proj_destroy(coord_operation_pj)
|
||
|
pyproj_context_destroy(context)
|
||
|
raise CRSError(
|
||
|
"Invalid coordinate operation string: "
|
||
|
f"{coordinate_operation_string}"
|
||
|
)
|
||
|
_clear_proj_error()
|
||
|
return CoordinateOperation.create(context, coord_operation_pj)
|
||
|
|
||
|
@staticmethod
|
||
|
def from_string(str coordinate_operation_string not None):
|
||
|
"""
|
||
|
Create a CoordinateOperation from a string.
|
||
|
|
||
|
Example:
|
||
|
- urn:ogc:def:coordinateOperation:EPSG::1671
|
||
|
- UTM zone 14N
|
||
|
- +proj=utm +zone=14
|
||
|
|
||
|
Parameters
|
||
|
----------
|
||
|
coordinate_operation_string: str
|
||
|
Coordinate operation string.
|
||
|
|
||
|
Returns
|
||
|
-------
|
||
|
CoordinateOperation
|
||
|
"""
|
||
|
try:
|
||
|
return CoordinateOperation._from_string(coordinate_operation_string)
|
||
|
except CRSError as crs_err:
|
||
|
try:
|
||
|
return CoordinateOperation.from_name(coordinate_operation_string)
|
||
|
except CRSError:
|
||
|
raise crs_err
|
||
|
|
||
|
@staticmethod
|
||
|
def from_json_dict(dict coordinate_operation_dict not None):
|
||
|
"""
|
||
|
Create CoordinateOperation from a JSON dictionary.
|
||
|
|
||
|
.. versionadded:: 2.4.0
|
||
|
|
||
|
Parameters
|
||
|
----------
|
||
|
coordinate_operation_dict: str
|
||
|
CoordinateOperation dictionary.
|
||
|
|
||
|
Returns
|
||
|
-------
|
||
|
CoordinateOperation
|
||
|
"""
|
||
|
return CoordinateOperation._from_string(
|
||
|
json.dumps(coordinate_operation_dict, cls=NumpyEncoder)
|
||
|
)
|
||
|
|
||
|
@staticmethod
|
||
|
def from_json(str coordinate_operation_json_str not None):
|
||
|
"""
|
||
|
Create CoordinateOperation from a JSON string.
|
||
|
|
||
|
.. versionadded:: 2.4.0
|
||
|
|
||
|
Parameters
|
||
|
----------
|
||
|
coordinate_operation_json_str: str
|
||
|
CoordinateOperation JSON string.
|
||
|
|
||
|
Returns
|
||
|
-------
|
||
|
CoordinateOperation
|
||
|
"""
|
||
|
return CoordinateOperation.from_json_dict(
|
||
|
_load_proj_json(coordinate_operation_json_str
|
||
|
))
|
||
|
|
||
|
@staticmethod
|
||
|
def from_name(
|
||
|
str coordinate_operation_name not None,
|
||
|
str auth_name=None,
|
||
|
coordinate_operation_type not None=CoordinateOperationType.CONVERSION,
|
||
|
):
|
||
|
"""
|
||
|
.. versionadded:: 2.5.0
|
||
|
|
||
|
Create a Coordinate Operation from a name.
|
||
|
|
||
|
Examples:
|
||
|
- UTM zone 14N
|
||
|
|
||
|
Parameters
|
||
|
----------
|
||
|
coordinate_operation_name: str
|
||
|
Coordinate Operation name.
|
||
|
auth_name: str, optional
|
||
|
The authority name to refine search (e.g. 'EPSG').
|
||
|
If None, will search all authorities.
|
||
|
coordinate_operation_type: CoordinateOperationType, optional
|
||
|
The coordinate operation type to create. Default is
|
||
|
``pyproj.crs.enums.CoordinateOperationType.CONVERSION``
|
||
|
|
||
|
Returns
|
||
|
-------
|
||
|
CoordinateOperation
|
||
|
"""
|
||
|
pj_coordinate_operation_type = _PJ_COORDINATE_OPERATION_TYPE_MAP[
|
||
|
CoordinateOperationType.create(coordinate_operation_type)
|
||
|
]
|
||
|
cdef PJ_CONTEXT* context = pyproj_context_create()
|
||
|
cdef PJ* coordinate_operation_pj = _from_name(
|
||
|
context,
|
||
|
coordinate_operation_name,
|
||
|
auth_name,
|
||
|
<PJ_TYPE>pj_coordinate_operation_type,
|
||
|
)
|
||
|
if coordinate_operation_pj == NULL:
|
||
|
pyproj_context_destroy(context)
|
||
|
raise CRSError(
|
||
|
"Invalid coordinate operation name: "
|
||
|
f"{coordinate_operation_name}"
|
||
|
)
|
||
|
_clear_proj_error()
|
||
|
return CoordinateOperation.create(context, coordinate_operation_pj)
|
||
|
|
||
|
@property
|
||
|
def params(self):
|
||
|
"""
|
||
|
Returns
|
||
|
-------
|
||
|
list[Param]:
|
||
|
The coordinate operation parameters.
|
||
|
"""
|
||
|
if self._params is not None:
|
||
|
return self._params
|
||
|
self._params = []
|
||
|
cdef int num_params = 0
|
||
|
num_params = proj_coordoperation_get_param_count(
|
||
|
self.context,
|
||
|
self.projobj
|
||
|
)
|
||
|
for param_idx from 0 <= param_idx < num_params:
|
||
|
self._params.append(
|
||
|
Param.create(
|
||
|
self.context,
|
||
|
self.projobj,
|
||
|
param_idx
|
||
|
)
|
||
|
)
|
||
|
_clear_proj_error()
|
||
|
return self._params
|
||
|
|
||
|
@property
|
||
|
def grids(self):
|
||
|
"""
|
||
|
Returns
|
||
|
-------
|
||
|
list[Grid]:
|
||
|
The coordinate operation grids.
|
||
|
"""
|
||
|
if self._grids is not None:
|
||
|
return self._grids
|
||
|
self._grids = []
|
||
|
cdef int num_grids = 0
|
||
|
num_grids = proj_coordoperation_get_grid_used_count(
|
||
|
self.context,
|
||
|
self.projobj
|
||
|
)
|
||
|
for grid_idx from 0 <= grid_idx < num_grids:
|
||
|
self._grids.append(
|
||
|
Grid.create(
|
||
|
self.context,
|
||
|
self.projobj,
|
||
|
grid_idx
|
||
|
)
|
||
|
)
|
||
|
_clear_proj_error()
|
||
|
return self._grids
|
||
|
|
||
|
@property
|
||
|
def area_of_use(self):
|
||
|
"""
|
||
|
Returns
|
||
|
-------
|
||
|
AreaOfUse:
|
||
|
The area of use object with associated attributes.
|
||
|
"""
|
||
|
if self._area_of_use is not None:
|
||
|
return self._area_of_use
|
||
|
self._area_of_use = create_area_of_use(self.context, self.projobj)
|
||
|
return self._area_of_use
|
||
|
|
||
|
def to_proj4(self, version not None=ProjVersion.PROJ_5, bint pretty=False):
|
||
|
"""
|
||
|
Convert the projection to a PROJ string.
|
||
|
|
||
|
.. versionadded:: 3.1.0 pretty
|
||
|
|
||
|
Parameters
|
||
|
----------
|
||
|
version: pyproj.enums.ProjVersion, default=pyproj.enums.ProjVersion.PROJ_5
|
||
|
The version of the PROJ string output.
|
||
|
pretty: bool, default=False
|
||
|
If True, it will set the output to be a multiline string.
|
||
|
|
||
|
Returns
|
||
|
-------
|
||
|
str:
|
||
|
The PROJ string.
|
||
|
|
||
|
"""
|
||
|
return _to_proj4(self.context, self.projobj, version=version, pretty=pretty)
|
||
|
|
||
|
@property
|
||
|
def towgs84(self):
|
||
|
"""
|
||
|
Returns
|
||
|
-------
|
||
|
list[float]:
|
||
|
A list of 3 or 7 towgs84 values if they exist.
|
||
|
|
||
|
"""
|
||
|
if self._towgs84 is not None:
|
||
|
return self._towgs84
|
||
|
towgs84_dict = OrderedDict(
|
||
|
(
|
||
|
('X-axis translation', None),
|
||
|
('Y-axis translation', None),
|
||
|
('Z-axis translation', None),
|
||
|
('X-axis rotation', None),
|
||
|
('Y-axis rotation', None),
|
||
|
('Z-axis rotation', None),
|
||
|
('Scale difference', None),
|
||
|
)
|
||
|
)
|
||
|
for param in self.params:
|
||
|
if param.name in towgs84_dict:
|
||
|
towgs84_dict[param.name] = param.value
|
||
|
self._towgs84 = [val for val in towgs84_dict.values() if val is not None]
|
||
|
return self._towgs84
|
||
|
|
||
|
@property
|
||
|
def operations(self):
|
||
|
"""
|
||
|
.. versionadded:: 2.4.0
|
||
|
|
||
|
Returns
|
||
|
-------
|
||
|
tuple[CoordinateOperation]:
|
||
|
The operations in a concatenated operation.
|
||
|
|
||
|
"""
|
||
|
if self._operations is not None:
|
||
|
return self._operations
|
||
|
self._operations = _get_concatenated_operations(self.context, self.projobj)
|
||
|
return self._operations
|
||
|
|
||
|
def __repr__(self):
|
||
|
return (
|
||
|
f"<Coordinate Operation: {self.type_name}>\n"
|
||
|
f"Name: {self.name}\n"
|
||
|
f"Method: {self.method_name}\n"
|
||
|
f"Area of Use:\n{self.area_of_use or '- undefined'}"
|
||
|
)
|
||
|
|
||
|
AuthorityMatchInfo = namedtuple(
|
||
|
"AuthorityMatchInfo",
|
||
|
[
|
||
|
"auth_name",
|
||
|
"code",
|
||
|
"confidence",
|
||
|
],
|
||
|
)
|
||
|
AuthorityMatchInfo.__doc__ = """
|
||
|
.. versionadded:: 3.2.0
|
||
|
|
||
|
CRS Authority Match Information
|
||
|
|
||
|
Parameters
|
||
|
----------
|
||
|
auth_name: str
|
||
|
Authority name.
|
||
|
code: str
|
||
|
Object code.
|
||
|
confidence: int
|
||
|
Confidence that this CRS matches
|
||
|
the authority and code.
|
||
|
"""
|
||
|
|
||
|
|
||
|
cdef dict _CRS_TYPE_MAP = {
|
||
|
PJ_TYPE_UNKNOWN: "Unknown CRS",
|
||
|
PJ_TYPE_CRS: "CRS",
|
||
|
PJ_TYPE_GEODETIC_CRS: "Geodetic CRS",
|
||
|
PJ_TYPE_GEOCENTRIC_CRS: "Geocentric CRS",
|
||
|
PJ_TYPE_GEOGRAPHIC_CRS: "Geographic CRS",
|
||
|
PJ_TYPE_GEOGRAPHIC_2D_CRS: "Geographic 2D CRS",
|
||
|
PJ_TYPE_GEOGRAPHIC_3D_CRS: "Geographic 3D CRS",
|
||
|
PJ_TYPE_VERTICAL_CRS: "Vertical CRS",
|
||
|
PJ_TYPE_PROJECTED_CRS: "Projected CRS",
|
||
|
PJ_TYPE_COMPOUND_CRS: "Compound CRS",
|
||
|
PJ_TYPE_TEMPORAL_CRS: "Temporal CRS",
|
||
|
PJ_TYPE_ENGINEERING_CRS: "Engineering CRS",
|
||
|
PJ_TYPE_BOUND_CRS: "Bound CRS",
|
||
|
PJ_TYPE_OTHER_CRS: "Other CRS",
|
||
|
}
|
||
|
|
||
|
IF (CTE_PROJ_VERSION_MAJOR, CTE_PROJ_VERSION_MINOR) >= (9, 2):
|
||
|
_CRS_TYPE_MAP[PJ_TYPE_DERIVED_PROJECTED_CRS] = "Derived Projected CRS"
|
||
|
|
||
|
cdef class _CRS(Base):
|
||
|
"""
|
||
|
.. versionadded:: 2.0.0
|
||
|
|
||
|
The cython CRS class to be used as the base for the
|
||
|
python CRS class.
|
||
|
"""
|
||
|
def __cinit__(self):
|
||
|
self._ellipsoid = None
|
||
|
self._area_of_use = None
|
||
|
self._prime_meridian = None
|
||
|
self._datum = None
|
||
|
self._sub_crs_list = None
|
||
|
self._source_crs = None
|
||
|
self._target_crs = None
|
||
|
self._geodetic_crs = None
|
||
|
self._coordinate_system = None
|
||
|
self._coordinate_operation = None
|
||
|
self._type_name = None
|
||
|
|
||
|
def __init__(self, const char *proj_string):
|
||
|
self.context = pyproj_context_create()
|
||
|
# initialize projection
|
||
|
self.projobj = proj_create(
|
||
|
self.context,
|
||
|
proj_string,
|
||
|
)
|
||
|
if self.projobj == NULL:
|
||
|
raise CRSError(f"Invalid projection: {proj_string}")
|
||
|
# make sure the input is a CRS
|
||
|
if not proj_is_crs(self.projobj):
|
||
|
raise CRSError(f"Input is not a CRS: {proj_string}")
|
||
|
# set proj information
|
||
|
self.srs = proj_string
|
||
|
self._type = proj_get_type(self.projobj)
|
||
|
self._set_base_info()
|
||
|
_clear_proj_error()
|
||
|
|
||
|
@property
|
||
|
def type_name(self):
|
||
|
"""
|
||
|
Returns
|
||
|
-------
|
||
|
str:
|
||
|
The name of the type of the CRS object.
|
||
|
"""
|
||
|
if self._type_name is not None:
|
||
|
return self._type_name
|
||
|
self._type_name = _CRS_TYPE_MAP[self._type]
|
||
|
if not self.is_derived or self._type == PJ_TYPE_PROJECTED_CRS:
|
||
|
# Projected CRS are derived by definition
|
||
|
# https://github.com/OSGeo/PROJ/issues/3525#issuecomment-1365790999
|
||
|
return self._type_name
|
||
|
|
||
|
# Handle Derived Projected CRS
|
||
|
# https://github.com/OSGeo/PROJ/issues/3525#issuecomment-1366002289
|
||
|
IF (CTE_PROJ_VERSION_MAJOR, CTE_PROJ_VERSION_MINOR) < (9, 2):
|
||
|
if self._type == PJ_TYPE_OTHER_CRS:
|
||
|
self._type_name = "Derived Projected CRS"
|
||
|
return self._type_name
|
||
|
ELSE:
|
||
|
if self._type == PJ_TYPE_DERIVED_PROJECTED_CRS:
|
||
|
return self._type_name
|
||
|
|
||
|
self._type_name = f"Derived {self._type_name}"
|
||
|
return self._type_name
|
||
|
|
||
|
@property
|
||
|
def axis_info(self):
|
||
|
"""
|
||
|
Retrieves all relevant axis information in the CRS.
|
||
|
If it is a Bound CRS, it gets the axis list from the Source CRS.
|
||
|
If it is a Compound CRS, it gets the axis list from the Sub CRS list.
|
||
|
|
||
|
Returns
|
||
|
-------
|
||
|
list[Axis]:
|
||
|
The list of axis information.
|
||
|
"""
|
||
|
axis_info_list = []
|
||
|
if self.coordinate_system:
|
||
|
axis_info_list.extend(self.coordinate_system.axis_list)
|
||
|
elif self.is_bound and self.source_crs:
|
||
|
axis_info_list.extend(self.source_crs.axis_info)
|
||
|
else:
|
||
|
for sub_crs in self.sub_crs_list:
|
||
|
axis_info_list.extend(sub_crs.axis_info)
|
||
|
return axis_info_list
|
||
|
|
||
|
@property
|
||
|
def area_of_use(self):
|
||
|
"""
|
||
|
Returns
|
||
|
-------
|
||
|
AreaOfUse:
|
||
|
The area of use object with associated attributes.
|
||
|
"""
|
||
|
if self._area_of_use is not None:
|
||
|
return self._area_of_use
|
||
|
self._area_of_use = create_area_of_use(self.context, self.projobj)
|
||
|
return self._area_of_use
|
||
|
|
||
|
@property
|
||
|
def ellipsoid(self):
|
||
|
"""
|
||
|
.. versionadded:: 2.2.0
|
||
|
|
||
|
Returns
|
||
|
-------
|
||
|
Ellipsoid:
|
||
|
The ellipsoid object with associated attributes.
|
||
|
"""
|
||
|
if self._ellipsoid is not None:
|
||
|
return None if self._ellipsoid is False else self._ellipsoid
|
||
|
cdef PJ_CONTEXT* context = pyproj_context_create()
|
||
|
cdef PJ* ellipsoid_pj = proj_get_ellipsoid(
|
||
|
context,
|
||
|
self.projobj
|
||
|
)
|
||
|
_clear_proj_error()
|
||
|
if ellipsoid_pj == NULL:
|
||
|
pyproj_context_destroy(context)
|
||
|
self._ellipsoid = False
|
||
|
return None
|
||
|
self._ellipsoid = Ellipsoid.create(context, ellipsoid_pj)
|
||
|
return self._ellipsoid
|
||
|
|
||
|
@property
|
||
|
def prime_meridian(self):
|
||
|
"""
|
||
|
.. versionadded:: 2.2.0
|
||
|
|
||
|
Returns
|
||
|
-------
|
||
|
PrimeMeridian:
|
||
|
The prime meridian object with associated attributes.
|
||
|
"""
|
||
|
if self._prime_meridian is not None:
|
||
|
return None if self._prime_meridian is True else self._prime_meridian
|
||
|
cdef PJ_CONTEXT* context = pyproj_context_create()
|
||
|
cdef PJ* prime_meridian_pj = proj_get_prime_meridian(
|
||
|
context,
|
||
|
self.projobj,
|
||
|
)
|
||
|
_clear_proj_error()
|
||
|
if prime_meridian_pj == NULL:
|
||
|
pyproj_context_destroy(context)
|
||
|
self._prime_meridian = False
|
||
|
return None
|
||
|
self._prime_meridian = PrimeMeridian.create(context, prime_meridian_pj)
|
||
|
return self._prime_meridian
|
||
|
|
||
|
@property
|
||
|
def datum(self):
|
||
|
"""
|
||
|
.. versionadded:: 2.2.0
|
||
|
|
||
|
Returns
|
||
|
-------
|
||
|
Datum
|
||
|
"""
|
||
|
if self._datum is not None:
|
||
|
return None if self._datum is False else self._datum
|
||
|
cdef PJ_CONTEXT* context = pyproj_context_create()
|
||
|
cdef PJ* datum_pj = proj_crs_get_datum(
|
||
|
context,
|
||
|
self.projobj,
|
||
|
)
|
||
|
if datum_pj == NULL:
|
||
|
datum_pj = proj_crs_get_horizontal_datum(
|
||
|
context,
|
||
|
self.projobj,
|
||
|
)
|
||
|
_clear_proj_error()
|
||
|
if datum_pj == NULL:
|
||
|
pyproj_context_destroy(context)
|
||
|
self._datum = False
|
||
|
return None
|
||
|
self._datum = Datum.create(context, datum_pj)
|
||
|
return self._datum
|
||
|
|
||
|
@property
|
||
|
def coordinate_system(self):
|
||
|
"""
|
||
|
.. versionadded:: 2.2.0
|
||
|
|
||
|
Returns
|
||
|
-------
|
||
|
CoordinateSystem
|
||
|
"""
|
||
|
if self._coordinate_system is not None:
|
||
|
return None if self._coordinate_system is False else self._coordinate_system
|
||
|
cdef PJ_CONTEXT* context = pyproj_context_create()
|
||
|
cdef PJ* coord_system_pj = proj_crs_get_coordinate_system(
|
||
|
context,
|
||
|
self.projobj
|
||
|
)
|
||
|
_clear_proj_error()
|
||
|
if coord_system_pj == NULL:
|
||
|
pyproj_context_destroy(context)
|
||
|
self._coordinate_system = False
|
||
|
return None
|
||
|
|
||
|
self._coordinate_system = CoordinateSystem.create(
|
||
|
context,
|
||
|
coord_system_pj,
|
||
|
)
|
||
|
return self._coordinate_system
|
||
|
|
||
|
@property
|
||
|
def coordinate_operation(self):
|
||
|
"""
|
||
|
.. versionadded:: 2.2.0
|
||
|
|
||
|
Returns
|
||
|
-------
|
||
|
CoordinateOperation
|
||
|
"""
|
||
|
if self._coordinate_operation is not None:
|
||
|
return (
|
||
|
None
|
||
|
if self._coordinate_operation is False
|
||
|
else self._coordinate_operation
|
||
|
)
|
||
|
|
||
|
if not (
|
||
|
self.is_bound or self.is_derived
|
||
|
):
|
||
|
self._coordinate_operation = False
|
||
|
return None
|
||
|
|
||
|
cdef PJ_CONTEXT* context = pyproj_context_create()
|
||
|
cdef PJ* coord_pj = proj_crs_get_coordoperation(
|
||
|
context,
|
||
|
self.projobj
|
||
|
)
|
||
|
_clear_proj_error()
|
||
|
if coord_pj == NULL:
|
||
|
pyproj_context_destroy(context)
|
||
|
self._coordinate_operation = False
|
||
|
return None
|
||
|
self._coordinate_operation = CoordinateOperation.create(
|
||
|
context,
|
||
|
coord_pj,
|
||
|
)
|
||
|
return self._coordinate_operation
|
||
|
|
||
|
@property
|
||
|
def source_crs(self):
|
||
|
"""
|
||
|
Returns
|
||
|
-------
|
||
|
_CRS:
|
||
|
The base CRS of a BoundCRS or a DerivedCRS/ProjectedCRS.
|
||
|
"""
|
||
|
if self._source_crs is not None:
|
||
|
return None if self._source_crs is False else self._source_crs
|
||
|
cdef PJ * projobj = proj_get_source_crs(self.context, self.projobj)
|
||
|
_clear_proj_error()
|
||
|
if projobj == NULL:
|
||
|
self._source_crs = False
|
||
|
return None
|
||
|
try:
|
||
|
self._source_crs = _CRS(_to_wkt(
|
||
|
self.context,
|
||
|
projobj,
|
||
|
version=WktVersion.WKT2_2019,
|
||
|
pretty=False,
|
||
|
))
|
||
|
finally:
|
||
|
proj_destroy(projobj)
|
||
|
return self._source_crs
|
||
|
|
||
|
@property
|
||
|
def target_crs(self):
|
||
|
"""
|
||
|
.. versionadded:: 2.2.0
|
||
|
|
||
|
Returns
|
||
|
-------
|
||
|
_CRS:
|
||
|
The hub CRS of a BoundCRS.
|
||
|
"""
|
||
|
if self._target_crs is not None:
|
||
|
return None if self._target_crs is False else self._target_crs
|
||
|
cdef PJ * projobj = proj_get_target_crs(self.context, self.projobj)
|
||
|
_clear_proj_error()
|
||
|
if projobj == NULL:
|
||
|
self._target_crs = False
|
||
|
return None
|
||
|
try:
|
||
|
self._target_crs = _CRS(_to_wkt(
|
||
|
self.context,
|
||
|
projobj,
|
||
|
version=WktVersion.WKT2_2019,
|
||
|
pretty=False,
|
||
|
))
|
||
|
finally:
|
||
|
proj_destroy(projobj)
|
||
|
return self._target_crs
|
||
|
|
||
|
@property
|
||
|
def sub_crs_list(self):
|
||
|
"""
|
||
|
If the CRS is a compound CRS, it will return a list of sub CRS objects.
|
||
|
|
||
|
Returns
|
||
|
-------
|
||
|
list[_CRS]
|
||
|
"""
|
||
|
if self._sub_crs_list is not None:
|
||
|
return self._sub_crs_list
|
||
|
if not self.is_compound:
|
||
|
self._sub_crs_list = []
|
||
|
return self._sub_crs_list
|
||
|
|
||
|
cdef int iii = 0
|
||
|
cdef PJ * projobj = proj_crs_get_sub_crs(
|
||
|
self.context,
|
||
|
self.projobj,
|
||
|
iii,
|
||
|
)
|
||
|
self._sub_crs_list = []
|
||
|
while projobj != NULL:
|
||
|
try:
|
||
|
self._sub_crs_list.append(_CRS(_to_wkt(
|
||
|
self.context,
|
||
|
projobj,
|
||
|
version=WktVersion.WKT2_2019,
|
||
|
pretty=False,
|
||
|
)))
|
||
|
finally:
|
||
|
proj_destroy(projobj) # deallocate temp proj
|
||
|
iii += 1
|
||
|
projobj = proj_crs_get_sub_crs(
|
||
|
self.context,
|
||
|
self.projobj,
|
||
|
iii,
|
||
|
)
|
||
|
_clear_proj_error()
|
||
|
return self._sub_crs_list
|
||
|
|
||
|
@property
|
||
|
def geodetic_crs(self):
|
||
|
"""
|
||
|
.. versionadded:: 2.2.0
|
||
|
|
||
|
The geodeticCRS / geographicCRS from the CRS.
|
||
|
|
||
|
Returns
|
||
|
-------
|
||
|
_CRS
|
||
|
"""
|
||
|
if self._geodetic_crs is not None:
|
||
|
return self._geodetic_crs if self. _geodetic_crs is not False else None
|
||
|
cdef PJ * projobj = proj_crs_get_geodetic_crs(self.context, self.projobj)
|
||
|
_clear_proj_error()
|
||
|
if projobj == NULL:
|
||
|
self._geodetic_crs = False
|
||
|
return None
|
||
|
try:
|
||
|
self._geodetic_crs = _CRS(_to_wkt(
|
||
|
self.context,
|
||
|
projobj,
|
||
|
version=WktVersion.WKT2_2019,
|
||
|
pretty=False,
|
||
|
))
|
||
|
finally:
|
||
|
proj_destroy(projobj) # deallocate temp proj
|
||
|
return self._geodetic_crs
|
||
|
|
||
|
def to_proj4(self, version=ProjVersion.PROJ_4):
|
||
|
"""
|
||
|
Convert the projection to a PROJ string.
|
||
|
|
||
|
.. warning:: You will likely lose important projection
|
||
|
information when converting to a PROJ string from
|
||
|
another format. See:
|
||
|
https://proj.org/faq.html#what-is-the-best-format-for-describing-coordinate-reference-systems # noqa: E501
|
||
|
|
||
|
Parameters
|
||
|
----------
|
||
|
version: pyproj.enums.ProjVersion, default=pyproj.enums.ProjVersion.PROJ_4
|
||
|
The version of the PROJ string output.
|
||
|
|
||
|
Returns
|
||
|
-------
|
||
|
str
|
||
|
"""
|
||
|
warnings.warn(
|
||
|
"You will likely lose important projection information when "
|
||
|
"converting to a PROJ string from another format. See: "
|
||
|
"https://proj.org/faq.html#what-is-the-best-format-for-describing-"
|
||
|
"coordinate-reference-systems"
|
||
|
)
|
||
|
return _to_proj4(self.context, self.projobj, version=version, pretty=False)
|
||
|
|
||
|
def to_epsg(self, int min_confidence=70):
|
||
|
"""
|
||
|
Return the EPSG code best matching the CRS
|
||
|
or None if it a match is not found.
|
||
|
|
||
|
Example:
|
||
|
|
||
|
>>> from pyproj import CRS
|
||
|
>>> ccs = CRS("EPSG:4328")
|
||
|
>>> ccs.to_epsg()
|
||
|
4328
|
||
|
|
||
|
If the CRS is bound, you can attempt to get an epsg code from
|
||
|
the source CRS:
|
||
|
|
||
|
>>> from pyproj import CRS
|
||
|
>>> ccs = CRS("+proj=geocent +datum=WGS84 +towgs84=0,0,0")
|
||
|
>>> ccs.to_epsg()
|
||
|
>>> ccs.source_crs.to_epsg()
|
||
|
4978
|
||
|
>>> ccs == CRS.from_epsg(4978)
|
||
|
False
|
||
|
|
||
|
Parameters
|
||
|
----------
|
||
|
min_confidence: int, default=70
|
||
|
A value between 0-100 where 100 is the most confident.
|
||
|
:ref:`min_confidence`
|
||
|
|
||
|
|
||
|
Returns
|
||
|
-------
|
||
|
Optional[int]:
|
||
|
The best matching EPSG code matching the confidence level.
|
||
|
"""
|
||
|
auth_info = self.to_authority(
|
||
|
auth_name="EPSG",
|
||
|
min_confidence=min_confidence
|
||
|
)
|
||
|
if auth_info is not None and auth_info[0].upper() == "EPSG":
|
||
|
return int(auth_info[1])
|
||
|
return None
|
||
|
|
||
|
def to_authority(self, str auth_name=None, int min_confidence=70):
|
||
|
"""
|
||
|
.. versionadded:: 2.2.0
|
||
|
|
||
|
Return the authority name and code best matching the CRS
|
||
|
or None if it a match is not found.
|
||
|
|
||
|
Example:
|
||
|
|
||
|
>>> from pyproj import CRS
|
||
|
>>> ccs = CRS("EPSG:4328")
|
||
|
>>> ccs.to_authority()
|
||
|
('EPSG', '4328')
|
||
|
|
||
|
If the CRS is bound, you can get an authority from
|
||
|
the source CRS:
|
||
|
|
||
|
>>> from pyproj import CRS
|
||
|
>>> ccs = CRS("+proj=geocent +datum=WGS84 +towgs84=0,0,0")
|
||
|
>>> ccs.to_authority()
|
||
|
>>> ccs.source_crs.to_authority()
|
||
|
('EPSG', '4978')
|
||
|
>>> ccs == CRS.from_authorty('EPSG', '4978')
|
||
|
False
|
||
|
|
||
|
Parameters
|
||
|
----------
|
||
|
auth_name: str, optional
|
||
|
The name of the authority to filter by.
|
||
|
min_confidence: int, default=70
|
||
|
A value between 0-100 where 100 is the most confident.
|
||
|
:ref:`min_confidence`
|
||
|
|
||
|
Returns
|
||
|
-------
|
||
|
tuple(str, str) or None:
|
||
|
The best matching (<auth_name>, <code>) for the confidence level.
|
||
|
"""
|
||
|
try:
|
||
|
authority = self.list_authority(
|
||
|
auth_name=auth_name, min_confidence=min_confidence,
|
||
|
)[0]
|
||
|
return authority.auth_name, authority.code
|
||
|
except IndexError:
|
||
|
return None
|
||
|
|
||
|
def list_authority(self, str auth_name=None, int min_confidence=70):
|
||
|
"""
|
||
|
.. versionadded:: 3.2.0
|
||
|
|
||
|
Return the authority names and codes best matching the CRS.
|
||
|
|
||
|
Example:
|
||
|
|
||
|
>>> from pyproj import CRS
|
||
|
>>> ccs = CRS("EPSG:4328")
|
||
|
>>> ccs.list_authority()
|
||
|
[AuthorityMatchInfo(auth_name='EPSG', code='4326', confidence=100)]
|
||
|
|
||
|
If the CRS is bound, you can get an authority from
|
||
|
the source CRS:
|
||
|
|
||
|
>>> from pyproj import CRS
|
||
|
>>> ccs = CRS("+proj=geocent +datum=WGS84 +towgs84=0,0,0")
|
||
|
>>> ccs.list_authority()
|
||
|
[]
|
||
|
>>> ccs.source_crs.list_authority()
|
||
|
[AuthorityMatchInfo(auth_name='EPSG', code='4978', confidence=70)]
|
||
|
>>> ccs == CRS.from_authorty('EPSG', '4978')
|
||
|
False
|
||
|
|
||
|
Parameters
|
||
|
----------
|
||
|
auth_name: str, optional
|
||
|
The name of the authority to filter by.
|
||
|
min_confidence: int, default=70
|
||
|
A value between 0-100 where 100 is the most confident.
|
||
|
:ref:`min_confidence`
|
||
|
|
||
|
Returns
|
||
|
-------
|
||
|
list[AuthorityMatchInfo]:
|
||
|
List of authority matches for the CRS.
|
||
|
"""
|
||
|
# get list of possible matching projections
|
||
|
cdef PJ_OBJ_LIST *proj_list = NULL
|
||
|
cdef int *c_out_confidence_list = NULL
|
||
|
cdef int num_proj_objects = -9999
|
||
|
cdef bytes b_auth_name
|
||
|
cdef char *user_auth_name = NULL
|
||
|
cdef int iii = 0
|
||
|
|
||
|
if auth_name is not None:
|
||
|
b_auth_name = cstrencode(auth_name)
|
||
|
user_auth_name = b_auth_name
|
||
|
|
||
|
out_confidence_list = []
|
||
|
try:
|
||
|
proj_list = proj_identify(
|
||
|
self.context,
|
||
|
self.projobj,
|
||
|
user_auth_name,
|
||
|
NULL,
|
||
|
&c_out_confidence_list
|
||
|
)
|
||
|
if proj_list != NULL:
|
||
|
num_proj_objects = proj_list_get_count(proj_list)
|
||
|
if c_out_confidence_list != NULL and num_proj_objects > 0:
|
||
|
out_confidence_list = [
|
||
|
c_out_confidence_list[iii] for iii in range(num_proj_objects)
|
||
|
]
|
||
|
finally:
|
||
|
if c_out_confidence_list != NULL:
|
||
|
proj_int_list_destroy(c_out_confidence_list)
|
||
|
_clear_proj_error()
|
||
|
|
||
|
# retrieve the best matching projection
|
||
|
cdef PJ* proj = NULL
|
||
|
cdef const char* code
|
||
|
cdef const char* out_auth_name
|
||
|
authority_list = []
|
||
|
try:
|
||
|
for iii in range(num_proj_objects):
|
||
|
if out_confidence_list[iii] < min_confidence:
|
||
|
continue
|
||
|
proj = proj_list_get(self.context, proj_list, iii)
|
||
|
code = proj_get_id_code(proj, 0)
|
||
|
out_auth_name = proj_get_id_auth_name(proj, 0)
|
||
|
if out_auth_name != NULL and code != NULL:
|
||
|
authority_list.append(
|
||
|
AuthorityMatchInfo(
|
||
|
out_auth_name,
|
||
|
code,
|
||
|
out_confidence_list[iii]
|
||
|
)
|
||
|
)
|
||
|
# at this point, the auth name is copied and we can release the proj object
|
||
|
proj_destroy(proj)
|
||
|
proj = NULL
|
||
|
finally:
|
||
|
# If there was an error we have to call proj_destroy
|
||
|
# If there was none, calling it on NULL does nothing
|
||
|
proj_destroy(proj)
|
||
|
proj_list_destroy(proj_list)
|
||
|
_clear_proj_error()
|
||
|
return authority_list
|
||
|
|
||
|
def to_3d(self, str name=None):
|
||
|
"""
|
||
|
.. versionadded:: 3.1.0
|
||
|
|
||
|
Convert the current CRS to the 3D version if it makes sense.
|
||
|
|
||
|
New vertical axis attributes:
|
||
|
- ellipsoidal height
|
||
|
- oriented upwards
|
||
|
- metre units
|
||
|
|
||
|
Parameters
|
||
|
----------
|
||
|
name: str, optional
|
||
|
CRS name. If None, it will use the name of the original CRS.
|
||
|
|
||
|
Returns
|
||
|
-------
|
||
|
_CRS
|
||
|
"""
|
||
|
cdef char* c_name = NULL
|
||
|
cdef bytes b_name
|
||
|
if name is not None:
|
||
|
b_name = cstrencode(name)
|
||
|
c_name = b_name
|
||
|
|
||
|
cdef PJ * projobj = proj_crs_promote_to_3D(
|
||
|
self.context, c_name, self.projobj
|
||
|
)
|
||
|
_clear_proj_error()
|
||
|
if projobj == NULL:
|
||
|
return self
|
||
|
try:
|
||
|
crs_3d = _CRS(_to_wkt(
|
||
|
self.context,
|
||
|
projobj,
|
||
|
version=WktVersion.WKT2_2019,
|
||
|
pretty=False,
|
||
|
))
|
||
|
finally:
|
||
|
proj_destroy(projobj)
|
||
|
return crs_3d
|
||
|
|
||
|
def to_2d(self, str name=None):
|
||
|
"""
|
||
|
.. versionadded:: 3.6.0
|
||
|
|
||
|
Convert the current CRS to the 2D version if it makes sense.
|
||
|
|
||
|
Parameters
|
||
|
----------
|
||
|
name: str, optional
|
||
|
CRS name. If None, it will use the name of the original CRS.
|
||
|
|
||
|
Returns
|
||
|
-------
|
||
|
_CRS
|
||
|
"""
|
||
|
cdef char* c_name = NULL
|
||
|
cdef bytes b_name
|
||
|
if name is not None:
|
||
|
b_name = cstrencode(name)
|
||
|
c_name = b_name
|
||
|
|
||
|
cdef PJ * projobj = proj_crs_demote_to_2D(
|
||
|
self.context, c_name, self.projobj
|
||
|
)
|
||
|
_clear_proj_error()
|
||
|
if projobj == NULL:
|
||
|
return self
|
||
|
try:
|
||
|
crs_2d = _CRS(_to_wkt(
|
||
|
self.context,
|
||
|
projobj,
|
||
|
version=WktVersion.WKT2_2019,
|
||
|
pretty=False,
|
||
|
))
|
||
|
finally:
|
||
|
proj_destroy(projobj)
|
||
|
return crs_2d
|
||
|
|
||
|
def _is_crs_property(
|
||
|
self, str property_name, tuple property_types, int sub_crs_index=0
|
||
|
):
|
||
|
"""
|
||
|
.. versionadded:: 2.2.0
|
||
|
|
||
|
This method will check for a property on the CRS.
|
||
|
It will check if it has the property on the sub CRS
|
||
|
if it is a compound CRS and will check if the source CRS
|
||
|
has the property if it is a bound CRS.
|
||
|
|
||
|
Parameters
|
||
|
----------
|
||
|
property_name: str
|
||
|
The name of the CRS property.
|
||
|
property_types: tuple(PJ_TYPE)
|
||
|
The types to check for for the property.
|
||
|
sub_crs_index: int, default=0
|
||
|
THe index of the CRS in the sub CRS list.
|
||
|
|
||
|
Returns
|
||
|
-------
|
||
|
bool:
|
||
|
True if the CRS has this property.
|
||
|
"""
|
||
|
if self.sub_crs_list:
|
||
|
sub_crs = self.sub_crs_list[sub_crs_index]
|
||
|
if sub_crs.is_bound:
|
||
|
is_property = getattr(sub_crs.source_crs, property_name)
|
||
|
else:
|
||
|
is_property = getattr(sub_crs, property_name)
|
||
|
elif self.is_bound:
|
||
|
is_property = getattr(self.source_crs, property_name)
|
||
|
else:
|
||
|
is_property = self._type in property_types
|
||
|
return is_property
|
||
|
|
||
|
@property
|
||
|
def is_geographic(self):
|
||
|
"""
|
||
|
This checks if the CRS is geographic.
|
||
|
It will check if it has a geographic CRS
|
||
|
in the sub CRS if it is a compound CRS and will check if
|
||
|
the source CRS is geographic if it is a bound CRS.
|
||
|
|
||
|
Returns
|
||
|
-------
|
||
|
bool:
|
||
|
True if the CRS is in geographic (lon/lat) coordinates.
|
||
|
"""
|
||
|
return self._is_crs_property(
|
||
|
"is_geographic",
|
||
|
(
|
||
|
PJ_TYPE_GEOGRAPHIC_CRS,
|
||
|
PJ_TYPE_GEOGRAPHIC_2D_CRS,
|
||
|
PJ_TYPE_GEOGRAPHIC_3D_CRS
|
||
|
)
|
||
|
)
|
||
|
|
||
|
@property
|
||
|
def is_projected(self):
|
||
|
"""
|
||
|
This checks if the CRS is projected.
|
||
|
It will check if it has a projected CRS
|
||
|
in the sub CRS if it is a compound CRS and will check if
|
||
|
the source CRS is projected if it is a bound CRS.
|
||
|
|
||
|
Returns
|
||
|
-------
|
||
|
bool:
|
||
|
True if CRS is projected.
|
||
|
"""
|
||
|
return self._is_crs_property(
|
||
|
"is_projected",
|
||
|
(PJ_TYPE_PROJECTED_CRS,)
|
||
|
)
|
||
|
|
||
|
@property
|
||
|
def is_vertical(self):
|
||
|
"""
|
||
|
.. versionadded:: 2.2.0
|
||
|
|
||
|
This checks if the CRS is vertical.
|
||
|
It will check if it has a vertical CRS
|
||
|
in the sub CRS if it is a compound CRS and will check if
|
||
|
the source CRS is vertical if it is a bound CRS.
|
||
|
|
||
|
Returns
|
||
|
-------
|
||
|
bool:
|
||
|
True if CRS is vertical.
|
||
|
"""
|
||
|
return self._is_crs_property(
|
||
|
"is_vertical",
|
||
|
(PJ_TYPE_VERTICAL_CRS,),
|
||
|
sub_crs_index=1
|
||
|
)
|
||
|
|
||
|
@property
|
||
|
def is_bound(self):
|
||
|
"""
|
||
|
Returns
|
||
|
-------
|
||
|
bool:
|
||
|
True if CRS is bound.
|
||
|
"""
|
||
|
return self._type == PJ_TYPE_BOUND_CRS
|
||
|
|
||
|
@property
|
||
|
def is_compound(self):
|
||
|
"""
|
||
|
.. versionadded:: 3.1.0
|
||
|
|
||
|
Returns
|
||
|
-------
|
||
|
bool:
|
||
|
True if CRS is compound.
|
||
|
"""
|
||
|
return self._type == PJ_TYPE_COMPOUND_CRS
|
||
|
|
||
|
@property
|
||
|
def is_engineering(self):
|
||
|
"""
|
||
|
.. versionadded:: 2.2.0
|
||
|
|
||
|
Returns
|
||
|
-------
|
||
|
bool:
|
||
|
True if CRS is local/engineering.
|
||
|
"""
|
||
|
return self._type == PJ_TYPE_ENGINEERING_CRS
|
||
|
|
||
|
@property
|
||
|
def is_geocentric(self):
|
||
|
"""
|
||
|
This checks if the CRS is geocentric and
|
||
|
takes into account if the CRS is bound.
|
||
|
|
||
|
Returns
|
||
|
-------
|
||
|
bool:
|
||
|
True if CRS is in geocentric (x/y) coordinates.
|
||
|
"""
|
||
|
if self.is_bound:
|
||
|
return self.source_crs.is_geocentric
|
||
|
return self._type == PJ_TYPE_GEOCENTRIC_CRS
|
||
|
|
||
|
@property
|
||
|
def is_derived(self):
|
||
|
"""
|
||
|
.. versionadded:: 3.2.0
|
||
|
|
||
|
Returns
|
||
|
-------
|
||
|
bool:
|
||
|
True if CRS is a Derived CRS.
|
||
|
"""
|
||
|
return proj_is_derived_crs(self.context, self.projobj) == 1
|
||
|
|
||
|
def _equals(self, _CRS other, bint ignore_axis_order):
|
||
|
if ignore_axis_order:
|
||
|
# Only to be used with DerivedCRS/ProjectedCRS/GeographicCRS
|
||
|
return proj_is_equivalent_to_with_ctx(
|
||
|
self.context,
|
||
|
self.projobj,
|
||
|
other.projobj,
|
||
|
PJ_COMP_EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS,
|
||
|
) == 1
|
||
|
return self._is_equivalent(other)
|
||
|
|
||
|
def equals(self, other, ignore_axis_order=False):
|
||
|
"""
|
||
|
Check if the projection objects are equivalent.
|
||
|
|
||
|
Properties
|
||
|
----------
|
||
|
other: CRS
|
||
|
Check if the other object
|
||
|
ignore_axis_order: bool, default=False
|
||
|
If True, it will compare the CRS class and ignore the axis order.
|
||
|
|
||
|
Returns
|
||
|
-------
|
||
|
bool
|
||
|
"""
|
||
|
if not isinstance(other, _CRS):
|
||
|
return False
|
||
|
return self._equals(other, ignore_axis_order=ignore_axis_order)
|