Skip to content

Commit 0b9cb2e

Browse files
committed
Add query_geodetic_crs_from_datum
1 parent 24a7b7f commit 0b9cb2e

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
@@ -7,6 +7,7 @@ Latest
77
- DEP: Minimum supported Python version 3.10 (pull #1357)
88
- DEP: Minimum PROJ version 9.2 (pull #1394)
99
- ENH: Add :meth:`CRS.is_deprecated` and :meth:`CRS.get_non_deprecated` (pull #1383)
10+
- ENH: Add :meth:`database.query_geodetic_crs_from_datum` (pull #1390)
1011

1112
3.6.1
1213
------

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

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

pyproj/proj.pxi

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

551551
int proj_is_deprecated(const PJ *obj)
552552
PJ_OBJ_LIST *proj_get_non_deprecated(PJ_CONTEXT *ctx, const PJ *obj)
553+
554+
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
@@ -268,3 +269,43 @@ def test_get_database_metadata():
268269

269270
def test_get_database_metadata__invalid():
270271
assert get_database_metadata("doesnotexist") is None
272+
273+
274+
def test_query_geodetic_crs_from_datum():
275+
crss = query_geodetic_crs_from_datum("EPSG", "EPSG", "1116", PJType.GEOCENTRIC_CRS)
276+
assert len(crss) == 1
277+
assert crss[0].to_authority()[1] == "6317"
278+
279+
crss = query_geodetic_crs_from_datum(None, "EPSG", "1116")
280+
assert len(crss) == 3
281+
codes = [x.to_authority()[1] for x in crss]
282+
assert "6317" in codes
283+
assert "6318" in codes
284+
assert "6319" in codes
285+
286+
crss = query_geodetic_crs_from_datum("EPSG", "EPSG", "6269", None)
287+
assert len(crss) == 1
288+
assert crss[0].to_authority()[1] == "4269"
289+
290+
crss = query_geodetic_crs_from_datum(None, "EPSG", "6269")
291+
assert len(crss) == 3 # EPSG, ESRI, OGC
292+
293+
294+
def test_query_geodetic_crs_from_datum_invalid():
295+
crss = query_geodetic_crs_from_datum(None, "EPSG", "11")
296+
assert len(crss) == 0
297+
298+
crss = query_geodetic_crs_from_datum(None, "EPSG", "32632")
299+
assert len(crss) == 0
300+
301+
crss = query_geodetic_crs_from_datum("foo-bar", "EPSG", "6269", None)
302+
assert len(crss) == 0
303+
304+
with pytest.raises(ValueError):
305+
query_geodetic_crs_from_datum("EPSG", "EPSG", "1116", PJType.PROJECTED_CRS)
306+
307+
with pytest.raises(TypeError):
308+
query_geodetic_crs_from_datum("EPSG", "EPSG", None)
309+
310+
with pytest.raises(TypeError):
311+
query_geodetic_crs_from_datum("EPSG", None, "1116")

0 commit comments

Comments
 (0)