21#include <geos/geom/Envelope.h>
23constexpr double DEFAULT_GRID_COMPAT_TOL = 1e-6;
25namespace geos::operation::grid {
28 static constexpr size_t padding = 1;
33 static constexpr size_t padding = 0;
36template<
typename extent_tag>
41 Grid(
const geom::Envelope& extent,
double dx,
double dy)
46 , m_num_rows{ 2 * extent_tag::padding + (extent.getMaxY() > extent.getMinY() ? static_cast<size_t>(std::
round(extent.getHeight() / dy)) : 0) }
47 , m_num_cols{ 2 * extent_tag::padding + (extent.getMaxX() > extent.getMinX() ? static_cast<size_t>(std::
round(extent.getWidth() / dx)) : 0) } {
48 static_assert(std::is_same_v<extent_tag, bounded_extent>);
51 Grid(
const geom::Envelope& extent,
double dx,
double dy,
const geom::Envelope& domain)
56 , m_num_rows{ 2 * extent_tag::padding + (extent.getMaxY() > extent.getMinY() ? static_cast<size_t>(std::
round(extent.getHeight() / dy)) : 0) }
57 , m_num_cols{ 2 * extent_tag::padding + (extent.getMaxX() > extent.getMinX() ? static_cast<size_t>(std::
round(extent.getWidth() / dx)) : 0) }
59 static_assert(std::is_same_v<extent_tag, infinite_extent>);
63 static Grid makeEmpty()
65 if constexpr (std::is_same_v<extent_tag, bounded_extent>) {
66 return Grid({ 0, 0, 0, 0 }, 0, 0);
68 return Grid({ 0, 0, 0, 0 }, 0, 0, geom::Envelope());
72 size_t getColumn(
double x)
const
74 if (extent_tag::padding) {
75 if (x < m_extent.getMinX())
77 if (x > m_extent.getMaxX())
78 return m_num_cols - 1;
79 if (x == m_extent.getMaxX())
80 return m_num_cols - 2;
82 if (x < m_extent.getMinX() || x > m_extent.getMaxX())
83 throw std::out_of_range(
"x");
85 if (x == m_extent.getMaxX())
86 return m_num_cols - 1;
93 extent_tag::padding +
static_cast<size_t>(std::floor((x - m_extent.getMinX()) / m_dx)),
94 getColumn(m_extent.getMaxX()));
97 size_t getRow(
double y)
const
99 if (extent_tag::padding) {
100 if (y > m_extent.getMaxY())
102 if (y < m_extent.getMinY())
103 return m_num_rows - 1;
104 if (y == m_extent.getMinY())
105 return m_num_rows - 2;
107 if (y < m_extent.getMinY() || y > m_extent.getMaxY())
108 throw std::out_of_range(
"y");
110 if (y == m_extent.getMinY())
111 return m_num_rows - 1;
118 extent_tag::padding +
static_cast<size_t>(std::floor((m_extent.getMaxY() - y) / m_dy)),
119 getRow(m_extent.getMinY()));
122 std::size_t getCell(
double x,
double y)
const
124 return getRow(y) * getNumCols() + getColumn(x);
127 bool isEmpty()
const {
return m_num_rows <= 2 * extent_tag::padding && m_num_cols <= 2 * extent_tag::padding; }
129 size_t getNumRows()
const {
return m_num_rows; }
131 size_t getNumCols()
const {
return m_num_cols; }
133 size_t getSize()
const {
return getNumRows() * getNumCols(); }
135 double xmin()
const {
return m_extent.getMinX(); }
137 double xmax()
const {
return m_extent.getMaxX(); }
139 double ymin()
const {
return m_extent.getMinY(); }
141 double ymax()
const {
return m_extent.getMaxY(); }
143 double dx()
const {
return m_dx; }
145 double dy()
const {
return m_dy; }
147 const geom::Envelope& getExtent()
const {
return m_extent; }
149 size_t getRowOffset(
const Grid& other)
const {
return static_cast<size_t>(std::round(std::abs(other.m_extent.getMaxY() - m_extent.getMaxY()) / m_dy)); }
151 size_t getColOffset(
const Grid& other)
const {
return static_cast<size_t>(std::round(std::abs(m_extent.getMinX() - other.m_extent.getMinX()) / m_dx)); }
153 double getColX(
size_t col)
const {
return m_extent.getMinX() + (
static_cast<double>(col - extent_tag::padding) + 0.5) * m_dx; }
155 double getRowY(
size_t row)
const {
return m_extent.getMaxY() - (
static_cast<double>(row - extent_tag::padding) + 0.5) * m_dy; }
157 Grid<extent_tag> shrinkToFit(
const geom::Envelope& b)
const
159 if (b.getArea() == 0) {
163 if (b.getMinX() < m_extent.getMinX() || b.getMinY() < m_extent.getMinY() || b.getMaxX() > m_extent.getMaxX() || b.getMaxY() > m_extent.getMaxY()) {
164 throw std::range_error(
"Cannot shrink extent to bounds larger than original.");
167 size_t col0 = getColumn(b.getMinX());
168 size_t row1 = getRow(b.getMaxY());
171 double snapped_xmin = m_extent.getMinX() +
static_cast<double>(col0 - extent_tag::padding) * m_dx;
172 double snapped_ymax = m_extent.getMaxY() -
static_cast<double>(row1 - extent_tag::padding) * m_dy;
176 if (b.getMinX() < snapped_xmin) {
177 snapped_xmin -= m_dx;
181 if (b.getMaxY() > snapped_ymax) {
182 snapped_ymax += m_dy;
186 size_t col1 = getColumn(b.getMaxX());
187 size_t row0 = getRow(b.getMinY());
189 size_t num_rows = 1 + (row0 - row1);
190 size_t num_cols = 1 + (col1 - col0);
197 if (num_rows > 2 && (snapped_ymax -
static_cast<double>(num_rows - 1) * m_dy <= b.getMinY())) {
200 if (num_cols > 2 && (snapped_xmin +
static_cast<double>(num_cols - 1) * m_dx >= b.getMaxX())) {
208 geom::Envelope reduced_box = {
210 std::max(snapped_xmin +
static_cast<double>(num_cols) * m_dx, b.getMaxX()),
211 std::min(snapped_ymax -
static_cast<double>(num_rows) * m_dy, b.getMinY()),
217 if (reduced_box.getMaxX() > m_extent.getMaxX()) {
218 if (std::round(reduced_box.getWidth() / m_dx) ==
219 std::round((m_extent.getMaxX() - reduced_box.getMinX()) / m_dx)) {
220 reduced_box = geom::Envelope(reduced_box.getMinX(), m_extent.getMaxX(), reduced_box.getMinY(), reduced_box.getMaxY());
222 throw std::runtime_error(
"Shrink operation failed.");
225 if (reduced_box.getMinY() < m_extent.getMinY()) {
226 if (std::round(reduced_box.getHeight() / m_dy) ==
227 std::round((reduced_box.getMaxY() - m_extent.getMinY()) / m_dy)) {
228 reduced_box = geom::Envelope(reduced_box.getMinX(), reduced_box.getMaxX(), m_extent.getMinY(), reduced_box.getMaxY());
230 throw std::runtime_error(
"Shrink operation failed.");
234 if constexpr (std::is_same_v<extent_tag, bounded_extent>) {
235 Grid<extent_tag> reduced{ reduced_box, m_dx, m_dy };
237 if (b.getMinX() < reduced.xmin() || b.getMinY() < reduced.ymin() || b.getMaxX() > reduced.xmax() || b.getMaxY() > reduced.ymax()) {
238 throw std::runtime_error(
"Shrink operation failed.");
243 Grid<extent_tag> reduced{ reduced_box, m_dx, m_dy , m_domain };
245 if (b.getMinX() < reduced.xmin() || b.getMinY() < reduced.ymin() || b.getMaxX() > reduced.xmax() || b.getMaxY() > reduced.ymax()) {
246 throw std::runtime_error(
"Shrink operation failed.");
254 bool operator==(
const Grid<extent_tag>& b)
const
256 return m_extent == b.m_extent &&
261 bool operator!=(
const Grid<extent_tag>& b)
const
263 return !(*
this == b);
266 geom::Envelope getCellEnvelope(
size_t row,
size_t col)
const;
269 geom::Envelope m_extent;
270 geom::Envelope m_domain;
280make_infinite(
const Grid<bounded_extent>& grid,
const geom::Envelope& domain);
282make_finite(
const Grid<infinite_extent>& grid);
double round(double val)
Definition math.h:36