Skip to content

Commit 66ceda0

Browse files
committed
Add query_geodetic_crs_from_datum
1 parent 28cc117 commit 66ceda0

File tree

6 files changed

+159
-1
lines changed

6 files changed

+159
-1
lines changed

docs/api/database.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,3 +49,9 @@ pyproj.database.get_database_metadata
4949
---------------------------------------
5050

5151
.. autofunction:: pyproj.database.get_database_metadata
52+
53+
54+
pyproj.database.query_geodetic_crs_from_datum
55+
---------------------------------------
56+
57+
.. autofunction:: pyproj.database.query_geodetic_crs_from_datum

docs/history.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ Latest
55
------
66
- DEP: Minimum supported Python version 3.10 (pull #1357)
77
- ENH: Add :meth:`CRS.is_deprecated` and :meth:`CRS.get_non_deprecated` (pull #1383)
8+
- ENH: Add :meth:`database.query_geodetic_crs_from_datum` (pull #1390)
89

910
3.6.1
1011
------

pyproj/database.pyi

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from typing import NamedTuple
22

33
from pyproj.aoi import AreaOfInterest, AreaOfUse
4+
from pyproj.crs import CRS
45
from pyproj.enums import PJType
56

67
class Unit(NamedTuple):
@@ -44,3 +45,9 @@ def query_utm_crs_info(
4445
contains: bool = False,
4546
) -> list[CRSInfo]: ...
4647
def get_database_metadata(key: str) -> str | None: ...
48+
def query_geodetic_crs_from_datum(
49+
crs_auth_name: str | None,
50+
datum_auth_name: str,
51+
datum_code: str,
52+
pj_type: PJType | None = None,
53+
) -> list[CRS]: ...

pyproj/database.pyx

Lines changed: 102 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,14 @@ from collections import namedtuple
66
from libc.stdlib cimport free, malloc
77

88
from pyproj._compat cimport cstrdecode, cstrencode
9-
from pyproj._datadir cimport pyproj_context_create, pyproj_context_destroy
9+
from pyproj._datadir cimport (
10+
_clear_proj_error,
11+
pyproj_context_create,
12+
pyproj_context_destroy,
13+
)
1014

1115
from pyproj.aoi import AreaOfUse
16+
from pyproj.crs import CRS
1217
from pyproj.enums import PJType
1318

1419

@@ -483,3 +488,99 @@ def get_database_metadata(str key not None):
483488
return metadata
484489
finally:
485490
pyproj_context_destroy(context)
491+
492+
493+
def query_geodetic_crs_from_datum(
494+
str crs_auth_name,
495+
str datum_auth_name not None,
496+
str datum_code not None,
497+
pj_type=None
498+
):
499+
"""
500+
.. versionadded:: 3.7.0
501+
502+
Return GeodeticCRS that use the specified datum
503+
504+
See: :c:func:`proj_query_geodetic_crs_from_datum`
505+
506+
Parameters
507+
----------
508+
crs_auth_name: str | None
509+
The authority name to filter by (e.g. EPSG, ESRI). None is all.
510+
datum_auth_name: str
511+
The authority of the datum
512+
datum_code: str
513+
Datum code
514+
pj_type: pyproj.enums.PJType | None, optional
515+
The type of object to get the CRSs. Can be PJType.GEOCENTRIC_CRS,
516+
PJType.GEOGRAPHIC_3D_CRS, PJType.GEOGRAPHIC_2D_CRS or None for all.
517+
518+
Returns
519+
-------
520+
list[CRS]
521+
"""
522+
523+
cdef const char* c_crs_type = NULL
524+
if pj_type is None:
525+
pass
526+
elif pj_type is PJType.GEOCENTRIC_CRS:
527+
c_crs_type = b"geocentric"
528+
elif pj_type is PJType.GEOGRAPHIC_2D_CRS:
529+
c_crs_type = b"geographic 2D"
530+
elif pj_type is PJType.GEOGRAPHIC_3D_CRS:
531+
c_crs_type = b"geographic 3D"
532+
else:
533+
raise ValueError("type must be GEOCENTRIC_CRS, GEOGRAPHIC_2D_CRS, GEOGRAPHIC_3D_CRS or None")
534+
535+
cdef const char* c_crs_auth_name = NULL
536+
cdef const char* c_datum_auth_name = NULL
537+
cdef const char* c_datum_code = NULL
538+
cdef bytes b_crs_auth_name
539+
cdef bytes b_datum_auth_name
540+
cdef bytes b_datum_code
541+
542+
if crs_auth_name is not None:
543+
b_crs_auth_name = cstrencode(crs_auth_name)
544+
c_crs_auth_name = b_crs_auth_name
545+
546+
if datum_auth_name is not None:
547+
b_datum_auth_name = cstrencode(datum_auth_name)
548+
c_datum_auth_name = b_datum_auth_name
549+
550+
if datum_code is not None:
551+
b_datum_code = cstrencode(datum_code)
552+
c_datum_code = b_datum_code
553+
554+
ret_list = []
555+
556+
cdef PJ_OBJ_LIST *proj_list = NULL
557+
cdef int num_proj_objects = 0
558+
559+
cdef PJ_CONTEXT* context = pyproj_context_create()
560+
proj_list = proj_query_geodetic_crs_from_datum(
561+
context,
562+
c_crs_auth_name,
563+
c_datum_auth_name,
564+
c_datum_code,
565+
c_crs_type
566+
)
567+
568+
if proj_list != NULL:
569+
num_proj_objects = proj_list_get_count(proj_list)
570+
571+
cdef PJ* proj = NULL
572+
try:
573+
for iii in range(num_proj_objects):
574+
proj = proj_list_get(context, proj_list, iii)
575+
ret_list.append(CRS(proj_as_wkt(context, proj, PJ_WKT2_2019, NULL)))
576+
proj_destroy(proj)
577+
proj = NULL
578+
finally:
579+
# If there was an error we have to call proj_destroy
580+
# If there was none, calling it on NULL does nothing
581+
proj_destroy(proj)
582+
proj_list_destroy(proj_list)
583+
pyproj_context_destroy(context)
584+
_clear_proj_error()
585+
586+
return ret_list

pyproj/proj.pxi

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -549,3 +549,5 @@ cdef extern from "proj.h" nogil:
549549

550550
int proj_is_deprecated(const PJ *obj)
551551
PJ_OBJ_LIST *proj_get_non_deprecated(PJ_CONTEXT *ctx, const PJ *obj)
552+
553+
PJ_OBJ_LIST *proj_query_geodetic_crs_from_datum(PJ_CONTEXT *ctx, const char *crs_auth_name, const char *datum_auth_name, const char *datum_code, const char *crs_type)

test/test_database.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
get_database_metadata,
99
get_units_map,
1010
query_crs_info,
11+
query_geodetic_crs_from_datum,
1112
query_utm_crs_info,
1213
)
1314
from pyproj.enums import PJType
@@ -281,3 +282,43 @@ def test_get_database_metadata():
281282

282283
def test_get_database_metadata__invalid():
283284
assert get_database_metadata("doesnotexist") is None
285+
286+
287+
def test_query_geodetic_crs_from_datum():
288+
crss = query_geodetic_crs_from_datum("EPSG", "EPSG", "1116", PJType.GEOCENTRIC_CRS)
289+
assert len(crss) == 1
290+
assert crss[0].to_authority()[1] == "6317"
291+
292+
crss = query_geodetic_crs_from_datum(None, "EPSG", "1116")
293+
assert len(crss) == 3
294+
codes = [x.to_authority()[1] for x in crss]
295+
assert "6317" in codes
296+
assert "6318" in codes
297+
assert "6319" in codes
298+
299+
crss = query_geodetic_crs_from_datum("EPSG", "EPSG", "6269", None)
300+
assert len(crss) == 1
301+
assert crss[0].to_authority()[1] == "4269"
302+
303+
crss = query_geodetic_crs_from_datum(None, "EPSG", "6269")
304+
assert len(crss) == 3 # EPSG, ESRI, OGC
305+
306+
307+
def test_query_geodetic_crs_from_datum_invalid():
308+
crss = query_geodetic_crs_from_datum(None, "EPSG", "11")
309+
assert len(crss) == 0
310+
311+
crss = query_geodetic_crs_from_datum(None, "EPSG", "32632")
312+
assert len(crss) == 0
313+
314+
crss = query_geodetic_crs_from_datum("foo-bar", "EPSG", "6269", None)
315+
assert len(crss) == 0
316+
317+
with pytest.raises(ValueError):
318+
query_geodetic_crs_from_datum("EPSG", "EPSG", "1116", PJType.PROJECTED_CRS)
319+
320+
with pytest.raises(TypeError):
321+
query_geodetic_crs_from_datum("EPSG", "EPSG", None)
322+
323+
with pytest.raises(TypeError):
324+
query_geodetic_crs_from_datum("EPSG", None, "1116")

0 commit comments

Comments
 (0)