-
Notifications
You must be signed in to change notification settings - Fork 158
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Refactor lonlat_to_cartesian to header-only API (#514)
Following #477, implements the header-only API for `cuspatial::lonlat_to_cartesian` and implements existing C++ API on top of it. Follows the refactoring guide introduced in #477. Note this branch is based on the branch for #477 so diff includes all changes from that PR as well. Once #477 is merged this PR will simplify a lot. Authors: - Mark Harris (https://github.com/harrism) Approvers: - Vyas Ramasubramani (https://github.com/vyasr) - Michael Wang (https://github.com/isVoid) URL: #514
- v25.04.00a
- v25.02.00
- v25.02.00a
- v24.12.00
- v24.12.00a
- v24.10.00
- v24.10.00a
- v24.08.00
- v24.08.00a
- v24.06.00
- v24.06.00a
- v24.04.00
- v24.04.00a
- v24.02.00
- v24.02.00a
- v23.12.01
- v23.12.00
- v23.12.00a
- v23.10.00
- v23.10.00a
- v23.08.01
- v23.08.00
- v23.08.00a
- v23.06.00
- v23.06.00a
- v23.04.00
- v23.04.00a
- v23.02.00
- v23.02.00a
- v22.12.00
- v22.12.00a
- v22.10.00
- v22.10.00a
- v22.08.00
- v22.06.00
Showing
10 changed files
with
432 additions
and
52 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
60 changes: 60 additions & 0 deletions
60
cpp/include/cuspatial/experimental/coordinate_transform.cuh
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
/* | ||
* Copyright (c) 2022, NVIDIA CORPORATION. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
#pragma once | ||
|
||
#include <cuspatial/utility/vec_2d.hpp> | ||
|
||
#include <rmm/cuda_stream_view.hpp> | ||
|
||
#include <iterator> | ||
|
||
namespace cuspatial { | ||
|
||
/** | ||
* @brief Translate longitude/latitude relative to origin to cartesian (x/y) coordinates in km. | ||
* | ||
* @param[in] lon_lat_first beginning of range of input longitude/latitude coordinates. | ||
* @param[in] lon_lat_last end of range of input longitude/latitude coordinates. | ||
* @param[in] origin: longitude and latitude of origin. | ||
* @param[out] xy_first: beginning of range of output x/y coordinates. | ||
* @param[in] stream: The CUDA stream on which to perform computations and allocate memory. | ||
* | ||
* All input iterators must have a `value_type` of `cuspatial::lonlat_2d<T>` (Lat/Lon coordinates), | ||
* and the output iterator must be able to accept for storage values of type | ||
* `cuspatial::cartesian_2d<T>` (Cartesian coordinates). | ||
* | ||
* @tparam InputIt Iterator over longitude/latitude locations. Must meet the requirements of | ||
* [LegacyRandomAccessIterator][LinkLRAI] and be device-accessible. | ||
* @tparam OutputIt Iterator over Cartesian output points. Must meet the requirements of | ||
* [LegacyRandomAccessIterator][LinkLRAI] and be device-accessible and mutable. | ||
* @tparam T the floating-point coordinate value type of input longitude/latitude coordinates. | ||
* | ||
* @return Output iterator to the element past the last x/y coordinate computed. | ||
* | ||
* [LinkLRAI]: https://en.cppreference.com/w/cpp/named_req/RandomAccessIterator | ||
* "LegacyRandomAccessIterator" | ||
*/ | ||
template <class InputIt, class OutputIt, class T> | ||
OutputIt lonlat_to_cartesian(InputIt lon_lat_first, | ||
InputIt lon_lat_last, | ||
OutputIt xy_first, | ||
lonlat_2d<T> origin, | ||
rmm::cuda_stream_view stream = rmm::cuda_stream_default); | ||
|
||
} // namespace cuspatial | ||
|
||
#include <cuspatial/experimental/detail/coordinate_transform.cuh> |
90 changes: 90 additions & 0 deletions
90
cpp/include/cuspatial/experimental/detail/coordinate_transform.cuh
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
/* | ||
* Copyright (c) 2022, NVIDIA CORPORATION. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
#pragma once | ||
|
||
#include <cuspatial/constants.hpp> | ||
#include <cuspatial/types.hpp> | ||
|
||
#include <rmm/cuda_stream_view.hpp> | ||
#include <rmm/exec_policy.hpp> | ||
|
||
#include <thrust/transform.h> | ||
|
||
#include <iterator> | ||
#include <type_traits> | ||
|
||
namespace cuspatial { | ||
|
||
namespace detail { | ||
|
||
constexpr double EARTH_CIRCUMFERENCE_KM_PER_DEGREE = EARTH_CIRCUMFERENCE_EQUATOR_KM / 360.0; | ||
|
||
template <typename T> | ||
__device__ inline T midpoint(T a, T b) | ||
{ | ||
return (a + b) / 2; | ||
} | ||
|
||
template <typename T> | ||
__device__ inline T lon_to_x(T lon, T lat) | ||
{ | ||
return lon * EARTH_CIRCUMFERENCE_KM_PER_DEGREE * cos(lat * DEGREE_TO_RADIAN); | ||
}; | ||
|
||
template <typename T> | ||
__device__ inline T lat_to_y(T lat) | ||
{ | ||
return lat * EARTH_CIRCUMFERENCE_KM_PER_DEGREE; | ||
}; | ||
|
||
template <typename T> | ||
struct to_cartesian_functor { | ||
to_cartesian_functor(lonlat_2d<T> origin) : _origin(origin) {} | ||
|
||
cartesian_2d<T> __device__ operator()(lonlat_2d<T> loc) | ||
{ | ||
return cartesian_2d<T>{lon_to_x(_origin.x - loc.x, midpoint(loc.y, _origin.y)), | ||
lat_to_y(_origin.y - loc.y)}; | ||
} | ||
|
||
private: | ||
lonlat_2d<T> _origin{}; | ||
}; | ||
|
||
} // namespace detail | ||
|
||
template <class InputIt, class OutputIt, class T> | ||
OutputIt lonlat_to_cartesian(InputIt lon_lat_first, | ||
InputIt lon_lat_last, | ||
OutputIt xy_first, | ||
lonlat_2d<T> origin, | ||
rmm::cuda_stream_view stream) | ||
{ | ||
static_assert(std::is_floating_point_v<T>, | ||
"lonlat_to_cartesian supports only floating-point coordinates."); | ||
|
||
CUSPATIAL_EXPECTS(origin.x >= -180 && origin.x <= 180 && origin.y >= -90 && origin.y <= 90, | ||
"origin must have valid longitude [-180, 180] and latitude [-90, 90]"); | ||
|
||
return thrust::transform(rmm::exec_policy(stream), | ||
lon_lat_first, | ||
lon_lat_last, | ||
xy_first, | ||
detail::to_cartesian_functor{origin}); | ||
} | ||
|
||
} // namespace cuspatial |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
198 changes: 198 additions & 0 deletions
198
cpp/tests/experimental/spatial/coordinate_transform_test.cu
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,198 @@ | ||
/* | ||
* Copyright (c) 2022, NVIDIA CORPORATION. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
#include <cuspatial/error.hpp> | ||
#include <cuspatial/experimental/coordinate_transform.cuh> | ||
#include <cuspatial/types.hpp> | ||
|
||
#include <rmm/device_vector.hpp> | ||
|
||
#include <gtest/gtest.h> | ||
|
||
template <typename T> | ||
struct LonLatToCartesianTest : public ::testing::Test { | ||
}; | ||
|
||
// float and double are logically the same but would require seperate tests due to precision. | ||
using TestTypes = ::testing::Types<float, double>; | ||
TYPED_TEST_CASE(LonLatToCartesianTest, TestTypes); | ||
|
||
TYPED_TEST(LonLatToCartesianTest, Empty) | ||
{ | ||
using T = TypeParam; | ||
using Loc = cuspatial::lonlat_2d<T>; | ||
using Cart = cuspatial::cartesian_2d<T>; | ||
|
||
auto origin = Loc{-90.66511046, 42.49197018}; | ||
|
||
auto h_point_lonlat = std::vector<Loc>{}; | ||
auto h_expected = std::vector<Cart>{}; | ||
|
||
auto point_lonlat = rmm::device_vector<Loc>{}; | ||
auto expected = rmm::device_vector<Cart>{}; | ||
|
||
auto xy_output = rmm::device_vector<Cart>{}; | ||
|
||
auto xy_end = cuspatial::lonlat_to_cartesian( | ||
point_lonlat.begin(), point_lonlat.end(), xy_output.begin(), origin); | ||
|
||
EXPECT_EQ(expected, xy_output); | ||
EXPECT_EQ(0, std::distance(xy_output.begin(), xy_end)); | ||
} | ||
|
||
TYPED_TEST(LonLatToCartesianTest, Single) | ||
{ | ||
using T = TypeParam; | ||
using Loc = cuspatial::lonlat_2d<T>; | ||
using Cart = cuspatial::cartesian_2d<T>; | ||
|
||
auto origin = Loc{-90.66511046, 42.49197018}; | ||
|
||
auto h_point_lonlat = std::vector<Loc>({{-90.664973, 42.493894}}); | ||
auto h_expected = std::vector<Cart>({{-0.01126195531216838, -0.21375777777718794}}); | ||
|
||
auto point_lonlat = rmm::device_vector<Loc>{h_point_lonlat}; | ||
auto expected = rmm::device_vector<Cart>{h_expected}; | ||
|
||
auto xy_output = rmm::device_vector<Cart>(1); | ||
|
||
auto xy_end = cuspatial::lonlat_to_cartesian( | ||
point_lonlat.begin(), point_lonlat.end(), xy_output.begin(), origin); | ||
|
||
EXPECT_EQ(expected, xy_output); | ||
EXPECT_EQ(1, std::distance(xy_output.begin(), xy_end)); | ||
} | ||
|
||
TYPED_TEST(LonLatToCartesianTest, Extremes) | ||
{ | ||
using T = TypeParam; | ||
using Loc = cuspatial::lonlat_2d<T>; | ||
using Cart = cuspatial::cartesian_2d<T>; | ||
|
||
auto origin = Loc{0, 0}; | ||
|
||
auto h_points_lonlat = std::vector<Loc>( | ||
{{0.0, -90.0}, {0.0, 90.0}, {-180.0, 0.0}, {180.0, 0.0}, {45.0, 0.0}, {-180.0, -90.0}}); | ||
auto h_expected = std::vector<Cart>({{0.0, 10000.0}, | ||
{0.0, -10000.0}, | ||
{20000.0, 0.0}, | ||
{-20000.0, 0.0}, | ||
{-5000.0, 0.0}, | ||
{14142.13562373095192015, 10000.0}}); | ||
|
||
auto points_lonlat = rmm::device_vector<Loc>{h_points_lonlat}; | ||
auto expected = rmm::device_vector<Cart>{h_expected}; | ||
|
||
auto xy_output = rmm::device_vector<Cart>(6, Cart{-1, -1}); | ||
|
||
auto xy_end = cuspatial::lonlat_to_cartesian( | ||
points_lonlat.begin(), points_lonlat.end(), xy_output.begin(), origin); | ||
|
||
EXPECT_EQ(expected, xy_output); | ||
EXPECT_EQ(6, std::distance(xy_output.begin(), xy_end)); | ||
} | ||
|
||
TYPED_TEST(LonLatToCartesianTest, Multiple) | ||
{ | ||
using T = TypeParam; | ||
using Loc = cuspatial::lonlat_2d<T>; | ||
using Cart = cuspatial::cartesian_2d<T>; | ||
|
||
auto origin = Loc{-90.66511046, 42.49197018}; | ||
|
||
auto h_points_lonlat = std::vector<Loc>({{-90.664973, 42.493894}, | ||
{-90.665393, 42.491520}, | ||
{-90.664976, 42.491420}, | ||
{-90.664537, 42.493823}}); | ||
auto h_expected = std::vector<Cart>({ | ||
{-0.01126195531216838, -0.21375777777718794}, | ||
{0.02314864865181343, 0.05002000000015667}, | ||
{-0.01101638630252916, 0.06113111111163663}, | ||
{-0.04698301003584082, -0.20586888888847929}, | ||
}); | ||
|
||
auto points_lonlat = rmm::device_vector<Loc>{h_points_lonlat}; | ||
auto expected = rmm::device_vector<Cart>{h_expected}; | ||
|
||
auto xy_output = rmm::device_vector<Cart>(4, Cart{-1, -1}); | ||
|
||
auto xy_end = cuspatial::lonlat_to_cartesian( | ||
points_lonlat.begin(), points_lonlat.end(), xy_output.begin(), origin); | ||
|
||
EXPECT_EQ(expected, xy_output); | ||
EXPECT_EQ(4, std::distance(xy_output.begin(), xy_end)); | ||
} | ||
|
||
TYPED_TEST(LonLatToCartesianTest, OriginOutOfBounds) | ||
{ | ||
using T = TypeParam; | ||
using Loc = cuspatial::lonlat_2d<T>; | ||
using Cart = cuspatial::cartesian_2d<T>; | ||
|
||
auto origin = Loc{-181, -91}; | ||
|
||
auto h_point_lonlat = std::vector<Loc>{}; | ||
auto h_expected = std::vector<Cart>{}; | ||
|
||
auto point_lonlat = rmm::device_vector<Loc>{}; | ||
auto expected = rmm::device_vector<Cart>{}; | ||
|
||
auto xy_output = rmm::device_vector<Cart>{}; | ||
|
||
EXPECT_THROW(cuspatial::lonlat_to_cartesian( | ||
point_lonlat.begin(), point_lonlat.end(), xy_output.begin(), origin), | ||
cuspatial::logic_error); | ||
} | ||
|
||
template <typename T> | ||
struct identity_xform { | ||
using Location = cuspatial::lonlat_2d<T>; | ||
__device__ Location operator()(Location const& loc) { return loc; }; | ||
}; | ||
|
||
// This test verifies that fancy iterators can be passed by using a pass-through transform_iterator | ||
TYPED_TEST(LonLatToCartesianTest, TransformIterator) | ||
{ | ||
using T = TypeParam; | ||
using Loc = cuspatial::lonlat_2d<T>; | ||
using Cart = cuspatial::cartesian_2d<T>; | ||
|
||
auto origin = Loc{-90.66511046, 42.49197018}; | ||
|
||
auto h_points_lonlat = std::vector<Loc>({{-90.664973, 42.493894}, | ||
{-90.665393, 42.491520}, | ||
{-90.664976, 42.491420}, | ||
{-90.664537, 42.493823}}); | ||
auto h_expected = std::vector<Cart>({ | ||
{-0.01126195531216838, -0.21375777777718794}, | ||
{0.02314864865181343, 0.05002000000015667}, | ||
{-0.01101638630252916, 0.06113111111163663}, | ||
{-0.04698301003584082, -0.20586888888847929}, | ||
}); | ||
|
||
auto points_lonlat = rmm::device_vector<Loc>{h_points_lonlat}; | ||
auto expected = rmm::device_vector<Cart>{h_expected}; | ||
|
||
auto xy_output = rmm::device_vector<Cart>(4, Cart{-1, -1}); | ||
|
||
auto xform_begin = thrust::make_transform_iterator(points_lonlat.begin(), identity_xform<T>{}); | ||
auto xform_end = thrust::make_transform_iterator(points_lonlat.end(), identity_xform<T>{}); | ||
|
||
auto xy_end = cuspatial::lonlat_to_cartesian(xform_begin, xform_end, xy_output.begin(), origin); | ||
|
||
EXPECT_EQ(expected, xy_output); | ||
EXPECT_EQ(4, std::distance(xy_output.begin(), xy_end)); | ||
} |