From 4a37d075786b084412390b72f22535a696f0a840 Mon Sep 17 00:00:00 2001 From: Mark Harris Date: Wed, 19 Jul 2023 09:45:50 +0000 Subject: [PATCH] Python support for int epsg codes --- python/cuproj/cuproj/_lib/cpp/cuprojshim.pxd | 2 + python/cuproj/cuproj/_lib/transform.pyx | 12 ++++- .../cuproj/cuprojshim/include/cuprojshim.hpp | 5 +- .../cuproj/cuprojshim/src/cuprojshim.cu | 5 ++ python/cuproj/cuproj/tests/test_transform.py | 48 +++++++++++++++++++ python/cuproj/cuproj/transformer.py | 4 +- 6 files changed, 71 insertions(+), 5 deletions(-) diff --git a/python/cuproj/cuproj/_lib/cpp/cuprojshim.pxd b/python/cuproj/cuproj/_lib/cpp/cuprojshim.pxd index 3b4864ba5..0b9e0bc0f 100644 --- a/python/cuproj/cuproj/_lib/cpp/cuprojshim.pxd +++ b/python/cuproj/cuproj/_lib/cpp/cuprojshim.pxd @@ -13,6 +13,8 @@ cdef extern from "cuprojshim.hpp" namespace "cuproj" nogil: cdef extern from "cuprojshim.hpp" namespace "cuprojshim" nogil: projection[vec_2d[double]]* make_projection(string, string) except + + projection[vec_2d[double]]* make_projection(int, int) except + + void transform( projection[vec_2d[double]], vec_2d[double]*, diff --git a/python/cuproj/cuproj/_lib/transform.pyx b/python/cuproj/cuproj/_lib/transform.pyx index 772f04f90..f66b7a26c 100644 --- a/python/cuproj/cuproj/_lib/transform.pyx +++ b/python/cuproj/cuproj/_lib/transform.pyx @@ -1,6 +1,7 @@ import cupy as cp from libc.stdint cimport uintptr_t +from libcpp.string cimport string from cuproj._lib.cpp.cuprojshim cimport make_projection, transform, vec_2d from cuproj._lib.cpp.operation cimport direction @@ -14,8 +15,15 @@ cdef class Transformer: cdef projection[vec_2d[double]]* proj def __init__(self, crs_from, crs_to): - self.proj = make_projection( - crs_from.encode('utf-8'), crs_to.encode('utf-8')) + if (isinstance(crs_from, str) & isinstance(crs_to, str)): + crs_from_b = crs_from.encode('utf-8') + crs_to_b = crs_to.encode('utf-8') + self.proj = make_projection( crs_from_b, crs_to_b) + elif (isinstance(crs_from, int) & isinstance(crs_to, int)): + self.proj = make_projection( crs_from, crs_to) + else: + raise TypeError( + "crs_from and crs_to must be both strings or both integers") def __del__(self): del self.proj diff --git a/python/cuproj/cuproj/cuprojshim/include/cuprojshim.hpp b/python/cuproj/cuproj/cuprojshim/include/cuprojshim.hpp index 9bacf85dd..fcf833f96 100644 --- a/python/cuproj/cuproj/cuprojshim/include/cuprojshim.hpp +++ b/python/cuproj/cuproj/cuprojshim/include/cuprojshim.hpp @@ -23,9 +23,12 @@ namespace cuprojshim { -cuproj::projection>* +cuproj::projection> * make_projection(std::string const &src_epsg, std::string const &dst_epsg); +cuproj::projection> *make_projection(int src_epsg, + int dst_epsg); + void transform(cuproj::projection> const &proj, cuproj::vec_2d *xy_in, cuproj::vec_2d *xy_out, std::size_t n, cuproj::direction dir); diff --git a/python/cuproj/cuproj/cuprojshim/src/cuprojshim.cu b/python/cuproj/cuproj/cuprojshim/src/cuprojshim.cu index e6d869426..aa5a76054 100644 --- a/python/cuproj/cuproj/cuprojshim/src/cuprojshim.cu +++ b/python/cuproj/cuproj/cuprojshim/src/cuprojshim.cu @@ -125,6 +125,11 @@ make_projection(std::string const &src_epsg, std::string const &dst_epsg) { return cuproj::make_projection>(src_epsg, dst_epsg); } +cuproj::projection> *make_projection(int src_epsg, + int dst_epsg) { + return cuproj::make_projection>(src_epsg, dst_epsg); +} + void transform(cuproj::projection> const &proj, cuproj::vec_2d *xy_in, cuproj::vec_2d *xy_out, std::size_t n, cuproj::direction dir) { diff --git a/python/cuproj/cuproj/tests/test_transform.py b/python/cuproj/cuproj/tests/test_transform.py index 70d741607..cde263737 100644 --- a/python/cuproj/cuproj/tests/test_transform.py +++ b/python/cuproj/cuproj/tests/test_transform.py @@ -1,9 +1,55 @@ +import pytest from numpy.testing import assert_allclose from pyproj import Transformer from cuproj import Transformer as cuTransformer +valid_crs_combos = [ + (4326, 32756), + (32756, 4326), + (4326, 32610), + (32610, 4326)] + +invalid_crs_combos = [ + (4326, 4326), + (32756, 32756), + (4326, 756), + (756, 4326)] + + +def to_epsg_string(code): + return f"epsg:{code}" + + +@pytest.mark.parametrize("crs_from, crs_to", valid_crs_combos) +def test_valid_epsg_codes(crs_from, crs_to): + Transformer.from_crs(crs_from, crs_to) + + +@pytest.mark.parametrize("crs_from, crs_to", valid_crs_combos) +def test_valid_epsg_strings(crs_from, crs_to): + Transformer.from_crs(to_epsg_string(crs_from), to_epsg_string(crs_to)) + + +@pytest.mark.parametrize("crs_from, crs_to", valid_crs_combos) +def test_valid_uppercase_epsg_strings(crs_from, crs_to): + Transformer.from_crs( + to_epsg_string(crs_from).upper(), to_epsg_string(crs_to).upper()) + + +@pytest.mark.parametrize("crs_from, crs_to", invalid_crs_combos) +def test_invalid_epsg_codes(crs_from, crs_to): + with pytest.raises(RuntimeError): + cuTransformer.from_crs(crs_from, crs_to) + + +@pytest.mark.parametrize("crs_from, crs_to", invalid_crs_combos) +def test_invalid_epsg_strings(crs_from, crs_to): + with pytest.raises(RuntimeError): + cuTransformer.from_crs( + to_epsg_string(crs_from), to_epsg_string(crs_to)) + def test_wgs84_to_utm_one_point(): # Sydney opera house latitude and longitude @@ -22,6 +68,7 @@ def test_wgs84_to_utm_one_point(): assert_allclose(cuproj_x, pyproj_x) assert_allclose(cuproj_y, pyproj_y) + # def grid_generator(min_corner, max_corner, num_points_x, num_points_y): # spacing = ((max_corner[0] - min_corner[0]) / num_points_x, # (max_corner[1] - min_corner[1]) / num_points_y) @@ -29,6 +76,7 @@ def test_wgs84_to_utm_one_point(): # yield (min_corner[0] + (i % num_points_x) * spacing[0], # min_corner[1] + (i // num_points_x) * spacing[1]) + # # test with a grid of points around san francisco # def test_wgs84_to_utm_grid(): # # San Francisco bounding box diff --git a/python/cuproj/cuproj/transformer.py b/python/cuproj/cuproj/transformer.py index b576bd36e..f36503092 100644 --- a/python/cuproj/cuproj/transformer.py +++ b/python/cuproj/cuproj/transformer.py @@ -38,9 +38,9 @@ def from_crs(crs_from, crs_to): Parameters ---------- crs_from : CRS - The source CRS. + int or "auth:code" string. The source CRS. crs_to : CRS - The target CRS. + int or "auth:code" string. The target CRS. Returns -------