-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Port S2Cell operations to s2geography (#35)
These previously lived only in the R package ( https://github.com/r-spatial/s2/blob/main/src/s2-cell.cpp ), but there's no reason they can't live here, too!
- Loading branch information
1 parent
1c67ddc
commit d6f3614
Showing
7 changed files
with
667 additions
and
0 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,199 @@ | ||
|
||
#include "s2geography/op/cell.h" | ||
|
||
#include <array> | ||
#include <cstdint> | ||
#include <string_view> | ||
|
||
#include "s2/s2cell.h" | ||
#include "s2/s2cell_id.h" | ||
#include "s2/s2latlng.h" | ||
|
||
namespace s2geography::op::cell { | ||
|
||
uint64_t FromToken::ExecuteScalar(const std::string_view cell_token) { | ||
// This constructor may not work for all s2 versions | ||
return S2CellId::FromToken({cell_token.data(), cell_token.size()}).id(); | ||
} | ||
|
||
uint64_t FromDebugString::ExecuteScalar(const std::string_view debug_string) { | ||
// This constructor may not work for all s2 versions | ||
return S2CellId::FromDebugString({debug_string.data(), debug_string.size()}) | ||
.id(); | ||
} | ||
|
||
uint64_t FromPoint::ExecuteScalar(Point point) { | ||
S2Point pt(point[0], point[1], point[2]); | ||
return S2CellId(pt).id(); | ||
} | ||
|
||
Point ToPoint::ExecuteScalar(const uint64_t cell_id) { | ||
S2CellId cell(cell_id); | ||
if (!cell.is_valid()) { | ||
return point::kInvalidPoint; | ||
} else { | ||
S2Point point = S2CellId(cell_id).ToPoint(); | ||
return {point.x(), point.y(), point.z()}; | ||
} | ||
} | ||
|
||
std::string_view ToToken::ExecuteScalar(const uint64_t cell_id) { | ||
last_result_ = S2CellId(cell_id).ToToken(); | ||
return last_result_; | ||
} | ||
|
||
std::string_view ToDebugString::ExecuteScalar(const uint64_t cell_id) { | ||
last_result_ = S2CellId(cell_id).ToString(); | ||
return last_result_; | ||
} | ||
|
||
bool IsValid::ExecuteScalar(const uint64_t cell_id) { | ||
return S2CellId(cell_id).is_valid(); | ||
} | ||
|
||
Point CellCenter::ExecuteScalar(const uint64_t cell_id) { | ||
S2CellId cell(cell_id); | ||
if (!cell.is_valid()) { | ||
return point::kInvalidPoint; | ||
} | ||
|
||
S2Point pt = S2Cell(cell).GetCenter(); | ||
return {pt.x(), pt.y(), pt.z()}; | ||
} | ||
|
||
Point CellVertex::ExecuteScalar(const uint64_t cell_id, | ||
const int8_t vertex_id) { | ||
S2CellId cell(cell_id); | ||
|
||
if (vertex_id < 0 || !cell.is_valid()) { | ||
return point::kInvalidPoint; | ||
} | ||
|
||
S2Point pt = S2Cell(cell).GetVertex(vertex_id); | ||
return {pt.x(), pt.y(), pt.z()}; | ||
} | ||
|
||
int8_t Level::ExecuteScalar(const uint64_t cell_id) { | ||
S2CellId cell(cell_id); | ||
if (!cell.is_valid()) { | ||
return -1; | ||
} | ||
|
||
return cell.level(); | ||
} | ||
|
||
double Area::ExecuteScalar(const uint64_t cell_id) { | ||
S2CellId cell(cell_id); | ||
if (!cell.is_valid()) { | ||
return NAN; | ||
} | ||
|
||
return S2Cell(cell).ExactArea(); | ||
} | ||
|
||
double AreaApprox::ExecuteScalar(const uint64_t cell_id) { | ||
S2CellId cell(cell_id); | ||
if (!cell.is_valid()) { | ||
return NAN; | ||
} | ||
|
||
return S2Cell(cell).ApproxArea(); | ||
} | ||
|
||
uint64_t Parent::ExecuteScalar(const uint64_t cell_id, const int8_t level) { | ||
// allow negative numbers to relate to current level | ||
S2CellId cell(cell_id); | ||
if (!cell.is_valid()) { | ||
return kCellIdSentinel; | ||
} | ||
|
||
int8_t level_final; | ||
int8_t cell_level = cell.level(); | ||
if (level < 0) { | ||
level_final = cell.level() + level; | ||
} else { | ||
level_final = level; | ||
} | ||
|
||
if (level_final > cell.level() || level_final < 0) { | ||
return kCellIdSentinel; | ||
} else { | ||
return cell.parent(level_final).id(); | ||
} | ||
} | ||
|
||
uint64_t Child::ExecuteScalar(const uint64_t cell_id, const int8_t k) { | ||
if (k < 0 || k > 3) { | ||
return kCellIdSentinel; | ||
} | ||
|
||
return S2CellId(cell_id).child(k).id(); | ||
} | ||
|
||
uint64_t EdgeNeighbor::ExecuteScalar(const uint64_t cell_id, const int8_t k) { | ||
S2CellId cell(cell_id); | ||
if (k < 0 || k > 3) { | ||
return kCellIdSentinel; | ||
} | ||
|
||
S2CellId neighbours[4]; | ||
cell.GetEdgeNeighbors(neighbours); | ||
return neighbours[k].id(); | ||
} | ||
|
||
bool Contains::ExecuteScalar(const uint64_t cell_id, | ||
const uint64_t cell_id_test) { | ||
S2CellId cell(cell_id); | ||
S2CellId cell_test(cell_id_test); | ||
if (!cell.is_valid() || !cell_test.is_valid()) { | ||
return false; | ||
} | ||
|
||
return cell.contains(cell_test); | ||
} | ||
|
||
bool MayIntersect::ExecuteScalar(const uint64_t cell_id, | ||
const uint64_t cell_id_test) { | ||
S2CellId cell(cell_id); | ||
S2CellId cell_test(cell_id_test); | ||
if (!cell.is_valid() || !cell_test.is_valid()) { | ||
return false; | ||
} | ||
|
||
return S2Cell(cell).MayIntersect(S2Cell(cell_test)); | ||
} | ||
|
||
double Distance::ExecuteScalar(const uint64_t cell_id, | ||
const uint64_t cell_id_test) { | ||
S2CellId cell(cell_id); | ||
S2CellId cell_test(cell_id_test); | ||
if (!cell.is_valid() || !cell_test.is_valid()) { | ||
return NAN; | ||
} | ||
|
||
return S2Cell(cell).GetDistance(S2Cell(cell_test)).radians(); | ||
} | ||
|
||
double MaxDistance::ExecuteScalar(const uint64_t cell_id, | ||
const uint64_t cell_id_test) { | ||
S2CellId cell(cell_id); | ||
S2CellId cell_test(cell_id_test); | ||
if (!cell.is_valid() || !cell_test.is_valid()) { | ||
return NAN; | ||
} | ||
|
||
return S2Cell(cell).GetMaxDistance(S2Cell(cell_test)).radians(); | ||
} | ||
|
||
int8_t CommonAncestorLevel::ExecuteScalar(const uint64_t cell_id, | ||
const uint64_t cell_id_test) { | ||
S2CellId cell(cell_id); | ||
S2CellId cell_test(cell_id_test); | ||
if (!cell.is_valid() || !cell_test.is_valid()) { | ||
return -1; | ||
} | ||
|
||
return cell.GetCommonAncestorLevel(cell_test); | ||
} | ||
|
||
} // namespace s2geography::op::cell |
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,162 @@ | ||
#pragma once | ||
|
||
#include <array> | ||
#include <cstdint> | ||
#include <string> | ||
#include <string_view> | ||
|
||
#include "s2geography/op/op.h" | ||
#include "s2geography/op/point.h" | ||
|
||
namespace s2geography { | ||
|
||
namespace op { | ||
|
||
namespace cell { | ||
|
||
using point::LngLat; | ||
using point::Point; | ||
|
||
/// \brief Cell identifier returned for invalid input | ||
static constexpr uint64_t kCellIdNone = 0; | ||
|
||
/// \brief Cell identifier that is greater than all other cells | ||
static constexpr uint64_t kCellIdSentinel = ~uint64_t{0}; | ||
|
||
/// \brief Create a cell identifier from a token | ||
class FromToken : public UnaryOp<uint64_t, std::string_view> { | ||
public: | ||
uint64_t ExecuteScalar(const std::string_view cell_token) override; | ||
}; | ||
|
||
/// \brief Create a cell identifier from a debug string | ||
class FromDebugString : public UnaryOp<uint64_t, std::string_view> { | ||
public: | ||
uint64_t ExecuteScalar(const std::string_view debug_string) override; | ||
}; | ||
|
||
/// \brief Create a cell identifier from an xyz unit vector | ||
class FromPoint : public UnaryOp<uint64_t, Point> { | ||
public: | ||
uint64_t ExecuteScalar(Point point) override; | ||
}; | ||
|
||
/// \brief Calculate the cell centre as an xyz vector | ||
class ToPoint : public UnaryOp<Point, uint64_t> { | ||
public: | ||
Point ExecuteScalar(const uint64_t cell_id) override; | ||
}; | ||
|
||
/// \brief Get the token string of a cell identifier | ||
class ToToken : public UnaryOp<std::string_view, uint64_t> { | ||
public: | ||
std::string_view ExecuteScalar(const uint64_t cell_id) override; | ||
|
||
private: | ||
std::string last_result_; | ||
}; | ||
|
||
/// \brief Get the debug string of a cell identifier | ||
class ToDebugString : public UnaryOp<std::string_view, uint64_t> { | ||
public: | ||
std::string_view ExecuteScalar(const uint64_t cell_id) override; | ||
|
||
private: | ||
std::string last_result_; | ||
}; | ||
|
||
/// \brief Returns true if the ID is a valid cell identifier or false otherwise | ||
class IsValid : public UnaryOp<bool, uint64_t> { | ||
public: | ||
bool ExecuteScalar(const uint64_t cell_id) override; | ||
}; | ||
|
||
/// \brief Retrieve the corners of a cell | ||
class CellCenter : public UnaryOp<Point, uint64_t> { | ||
public: | ||
Point ExecuteScalar(const uint64_t cell_id) override; | ||
}; | ||
|
||
/// \brief Retrieve the corners of a cell | ||
class CellVertex : public BinaryOp<Point, uint64_t, int8_t> { | ||
public: | ||
Point ExecuteScalar(const uint64_t cell_id, const int8_t vertex_id) override; | ||
}; | ||
|
||
/// \brief Calculate the level represented by the cell | ||
class Level : public UnaryOp<int8_t, uint64_t> { | ||
public: | ||
int8_t ExecuteScalar(const uint64_t cell_id) override; | ||
}; | ||
|
||
/// \brief Calculate the area of a given cell | ||
class Area : public UnaryOp<double, uint64_t> { | ||
public: | ||
double ExecuteScalar(const uint64_t cell_id) override; | ||
}; | ||
|
||
/// \brief Calculate the approximate area of a given cell | ||
class AreaApprox : public UnaryOp<double, uint64_t> { | ||
public: | ||
double ExecuteScalar(const uint64_t cell_id) override; | ||
}; | ||
|
||
/// \brief Calculate the parent cell at a given level | ||
class Parent : public BinaryOp<uint64_t, uint64_t, int8_t> { | ||
public: | ||
uint64_t ExecuteScalar(const uint64_t cell_id, const int8_t level) override; | ||
}; | ||
|
||
/// \brief Calculate the child cell at the next level | ||
class Child : public BinaryOp<uint64_t, uint64_t, int8_t> { | ||
public: | ||
uint64_t ExecuteScalar(const uint64_t cell_id, const int8_t k) override; | ||
}; | ||
|
||
/// \brief Get the edge neighbours of a given cell | ||
class EdgeNeighbor : public BinaryOp<uint64_t, uint64_t, int8_t> { | ||
public: | ||
uint64_t ExecuteScalar(const uint64_t cell_id, const int8_t k) override; | ||
}; | ||
|
||
/// \brief Returns true if cell_id contains cell_id_test or false otherwise | ||
class Contains : public BinaryOp<bool, uint64_t, uint64_t> { | ||
public: | ||
bool ExecuteScalar(const uint64_t cell_id, | ||
const uint64_t cell_id_test) override; | ||
}; | ||
|
||
/// \brief Returns true if cell_id might intersect cell_id_test or false | ||
/// otherwise | ||
class MayIntersect : public BinaryOp<bool, uint64_t, uint64_t> { | ||
public: | ||
bool ExecuteScalar(const uint64_t cell_id, | ||
const uint64_t cell_id_test) override; | ||
}; | ||
|
||
/// \brief Returns the minimum spherical distance (in radians) between two cells | ||
class Distance : public BinaryOp<double, uint64_t, uint64_t> { | ||
public: | ||
double ExecuteScalar(const uint64_t cell_id, | ||
const uint64_t cell_id_test) override; | ||
}; | ||
|
||
/// \brief Returns the maximum spherical distance (in radians) between two cells | ||
class MaxDistance : public BinaryOp<double, uint64_t, uint64_t> { | ||
public: | ||
double ExecuteScalar(const uint64_t cell_id, | ||
const uint64_t cell_id_test) override; | ||
}; | ||
|
||
/// \brief Returns the level at which the two cells have a common ancestor | ||
class CommonAncestorLevel : public BinaryOp<int8_t, uint64_t, uint64_t> { | ||
public: | ||
int8_t ExecuteScalar(const uint64_t cell_id, | ||
const uint64_t cell_id_test) override; | ||
}; | ||
|
||
} // namespace cell | ||
|
||
} // namespace op | ||
|
||
} // namespace s2geography |
Oops, something went wrong.