Skip to content

Commit

Permalink
ENH: Add support for datum ensemble
Browse files Browse the repository at this point in the history
  • Loading branch information
snowman2 committed Oct 30, 2020
1 parent 340971c commit 901da99
Show file tree
Hide file tree
Showing 6 changed files with 90 additions and 34 deletions.
27 changes: 24 additions & 3 deletions pyproj/_crs.pyx
Expand Up @@ -1288,13 +1288,13 @@ _DATUM_TYPE_MAP = {
}

_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,
DatumType.DATUM_ENSEMBLE: PJ_TYPE_DATUM_ENSEMBLE,
}


Expand Down Expand Up @@ -1329,7 +1329,7 @@ cdef class Datum(_CRSParts):
return datum

@staticmethod
def from_authority(auth_name, code):
def _from_authority(auth_name, code, PJ_CATEGORY category):
"""
Create a Datum from an authority code.
Expand All @@ -1350,7 +1350,7 @@ cdef class Datum(_CRSParts):
context,
cstrencode(str(auth_name)),
cstrencode(str(code)),
PJ_CATEGORY_DATUM,
category,
False,
NULL,
)
Expand All @@ -1361,6 +1361,27 @@ cdef class Datum(_CRSParts):
CRSError.clear()
return Datum.create(context, datum_pj)

@staticmethod
def from_authority(auth_name, code):
"""
Create a Datum from an authority code.
Parameters
----------
auth_name: str
Name ot 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):
"""
Expand Down
31 changes: 22 additions & 9 deletions pyproj/proj.pxi
@@ -1,4 +1,25 @@
# PROJ.4 API Defnition
# PROJ API Definition

IF CTE_PROJ_VERSION_MAJOR >= 8:
cdef extern from "proj.h":
ctypedef enum PJ_CATEGORY:
PJ_CATEGORY_ELLIPSOID
PJ_CATEGORY_PRIME_MERIDIAN
PJ_CATEGORY_DATUM
PJ_CATEGORY_CRS
PJ_CATEGORY_COORDINATE_OPERATION
PJ_CATEGORY_DATUM_ENSEMBLE
ELSE:
cdef extern from "proj.h":
ctypedef enum PJ_CATEGORY:
PJ_CATEGORY_ELLIPSOID
PJ_CATEGORY_PRIME_MERIDIAN
PJ_CATEGORY_DATUM
PJ_CATEGORY_CRS
PJ_CATEGORY_COORDINATE_OPERATION
cdef int PJ_CATEGORY_DATUM_ENSEMBLE = PJ_CATEGORY_DATUM


cdef extern from "proj.h":
cdef int PROJ_VERSION_MAJOR
cdef int PROJ_VERSION_MINOR
Expand Down Expand Up @@ -397,20 +418,12 @@ cdef extern from "proj.h":
PJ *proj_concatoperation_get_step(PJ_CONTEXT *ctx,
const PJ *concatoperation,
int i_step)

ctypedef enum PJ_CATEGORY:
PJ_CATEGORY_ELLIPSOID
PJ_CATEGORY_PRIME_MERIDIAN
PJ_CATEGORY_DATUM
PJ_CATEGORY_CRS
PJ_CATEGORY_COORDINATE_OPERATION
PJ *proj_create_from_database(PJ_CONTEXT *ctx,
const char *auth_name,
const char *code,
PJ_CATEGORY category,
int usePROJAlternativeGridNames,
const char* const *options)

PJ_OBJ_LIST *proj_create_from_name(PJ_CONTEXT *ctx,
const char *auth_name,
const char *searchedName,
Expand Down
35 changes: 22 additions & 13 deletions setup.py
Expand Up @@ -14,23 +14,26 @@
INTERNAL_PROJ_DIR = CURRENT_FILE_PATH / "pyproj" / BASE_INTERNAL_PROJ_DIR


def check_proj_version(proj_dir: Path):
"""checks that the PROJ library meets the minimum version"""
def get_proj_version(proj_dir: Path) -> str:
proj_version = os.environ.get("PROJ_VERSION")
if proj_version:
return proj_version
proj = proj_dir / "bin" / "proj"
proj_ver_bytes = subprocess.check_output(
str(proj), stderr=subprocess.STDOUT
).decode("ascii")
proj_ver_bytes = (proj_ver_bytes.split()[1]).strip(",")
proj_version = parse_version(proj_ver_bytes)
if proj_version < PROJ_MIN_VERSION:
proj_ver = subprocess.check_output(str(proj), stderr=subprocess.STDOUT).decode(
"ascii"
)
return (proj_ver.split()[1]).strip(",")


def check_proj_version(proj_version: str) -> None:
"""checks that the PROJ library meets the minimum version"""
if parse_version(proj_version) < PROJ_MIN_VERSION:
raise SystemExit(
f"ERROR: Minimum supported proj version is {PROJ_MIN_VERSION}, installed "
f"version is {proj_version}. For more information see: "
"https://pyproj4.github.io/pyproj/stable/installation.html"
)

return proj_version


def get_proj_dir() -> Path:
"""
Expand Down Expand Up @@ -58,9 +61,6 @@ def get_proj_dir() -> Path:
print("PROJ_DIR is set, using existing proj4 installation..\n")
else:
raise SystemExit(f"ERROR: Invalid path for PROJ_DIR {proj_dir}")

# check_proj_version
check_proj_version(proj_dir)
return proj_dir


Expand Down Expand Up @@ -152,6 +152,10 @@ def get_extension_modules():
library_dirs = get_proj_libdirs(proj_dir)
include_dirs = get_proj_incdirs(proj_dir)

proj_version = get_proj_version(proj_dir)
check_proj_version(proj_version)
proj_version_major, proj_version_minor, proj_version_patch = proj_version.split(".")

# setup extension options
ext_options = {
"include_dirs": include_dirs,
Expand All @@ -175,6 +179,11 @@ def get_extension_modules():
Extension("pyproj._sync", ["pyproj/_sync.pyx"], **ext_options),
],
quiet=True,
compile_time_env={
"CTE_PROJ_VERSION_MAJOR": int(proj_version_major),
"CTE_PROJ_VERSION_MINOR": int(proj_version_minor),
"CTE_PROJ_VERSION_PATCH": int(proj_version_patch),
},
**get_cythonize_options(),
)

Expand Down
7 changes: 7 additions & 0 deletions test/conftest.py
@@ -1,5 +1,6 @@
import os
from contextlib import contextmanager
from distutils.version import LooseVersion
from pathlib import Path

import pyproj
Expand Down Expand Up @@ -70,3 +71,9 @@ def grids_available(*grid_names, check_network=True, check_all=False):
if check_all:
return all(available)
return any(available)


def get_wgs84_datum_name():
if LooseVersion(pyproj.__proj_version__) < LooseVersion("8.0"):
return "World Geodetic System 1984"
return "World Geodetic System 1984 ensemble"
21 changes: 13 additions & 8 deletions test/crs/test_crs.py
@@ -1,9 +1,10 @@
import json
from distutils.version import LooseVersion

import numpy
import pytest

from pyproj import CRS
from pyproj import CRS, __proj_version__
from pyproj.crs import (
CoordinateOperation,
CoordinateSystem,
Expand All @@ -15,7 +16,7 @@
from pyproj.enums import ProjVersion, WktVersion
from pyproj.exceptions import CRSError
from pyproj.transformer import TransformerGroup
from test.conftest import grids_available
from test.conftest import get_wgs84_datum_name, grids_available


class CustomCRS(object):
Expand Down Expand Up @@ -201,7 +202,7 @@ def test_repr():
"Area of Use:\n"
"- name: World.\n"
"- bounds: (-180.0, -90.0, 180.0, 90.0)\n"
"Datum: World Geodetic System 1984\n"
f"Datum: {get_wgs84_datum_name()}\n"
"- Ellipsoid: WGS 84\n"
"- Prime Meridian: Greenwich\n"
)
Expand All @@ -219,7 +220,7 @@ def test_repr__long():
"Area of Use:\n"
"- name: World.\n"
"- bounds: (-180.0, -90.0, 180.0, 90.0)\n"
"Datum: World Geodetic System 1984\n"
f"Datum: {get_wgs84_datum_name()}\n"
"- Ellipsoid: WGS 84\n"
"- Prime Meridian: Greenwich\n"
)
Expand All @@ -235,7 +236,7 @@ def test_repr_epsg():
"Area of Use:\n"
"- name: World.\n"
"- bounds: (-180.0, -90.0, 180.0, 90.0)\n"
"Datum: World Geodetic System 1984\n"
f"Datum: {get_wgs84_datum_name()}\n"
"- Ellipsoid: WGS 84\n"
"- Prime Meridian: Greenwich\n"
)
Expand Down Expand Up @@ -309,9 +310,13 @@ def test_epsg():

def test_datum():
datum = CRS.from_epsg(4326).datum
assert repr(datum).startswith('DATUM["World Geodetic System 1984"')
assert "\n" in repr(datum)
assert datum.to_wkt().startswith('DATUM["World Geodetic System 1984"')
if LooseVersion(__proj_version__) < LooseVersion("8.0"):
datum_wkt = 'DATUM["World Geodetic System 1984"'
else:
datum_wkt = 'ENSEMBLE["World Geodetic System 1984"'
assert repr(datum).startswith(datum_wkt)
assert datum.to_wkt().startswith(datum_wkt)
assert datum == datum
assert datum.is_exact_same(datum)

Expand Down Expand Up @@ -871,7 +876,7 @@ def test_datum_equals():
)
def test_datum__from_string(input_str):
dd = Datum.from_string(input_str)
assert dd.name == "World Geodetic System 1984"
assert dd.name == get_wgs84_datum_name()
assert dd.type_name == "Geodetic Reference Frame"


Expand Down
3 changes: 2 additions & 1 deletion test/crs/test_crs_cf.py
Expand Up @@ -16,6 +16,7 @@
VerticalPerspectiveConversion,
)
from pyproj.exceptions import CRSError
from test.conftest import get_wgs84_datum_name


def _to_dict(operation):
Expand Down Expand Up @@ -240,7 +241,7 @@ def test_cf_from_utm():
"longitude_of_prime_meridian": 0.0,
"prime_meridian_name": "Greenwich",
"geographic_crs_name": "WGS 84",
"horizontal_datum_name": "World Geodetic System 1984",
"horizontal_datum_name": get_wgs84_datum_name(),
"projected_crs_name": "WGS 84 / UTM zone 15N",
"grid_mapping_name": "transverse_mercator",
"latitude_of_projection_origin": 0.0,
Expand Down

0 comments on commit 901da99

Please sign in to comment.