Add and test box_t class and envelope(geom) function

HEAD
Jochen Topf 2022-01-25 17:08:55 +01:00
parent 81954f0333
commit 27ae4652af
5 changed files with 295 additions and 0 deletions

View File

@ -8,6 +8,7 @@ target_sources(osm2pgsql_lib PRIVATE
expire-tiles.cpp
gazetteer-style.cpp
geom.cpp
geom-box.cpp
geom-from-osm.cpp
geom-functions.cpp
input.cpp

112
src/geom-box.cpp Normal file
View File

@ -0,0 +1,112 @@
/**
* SPDX-License-Identifier: GPL-2.0-or-later
*
* This file is part of osm2pgsql (https://osm2pgsql.org/).
*
* Copyright (C) 2006-2022 by the osm2pgsql developer community.
* For a full list of authors see the git log.
*/
#include "geom-box.hpp"
namespace geom {
box_t &box_t::extend(point_t const &point) noexcept
{
if (point.x() < m_min_x) {
m_min_x = point.x();
}
if (point.y() < m_min_y) {
m_min_y = point.y();
}
if (point.x() > m_max_x) {
m_max_x = point.x();
}
if (point.y() > m_max_y) {
m_max_y = point.y();
}
return *this;
}
void box_t::extend(point_list_t const &list) noexcept
{
for (auto const &point : list) {
extend(point);
}
}
namespace {
class envelope_visitor
{
public:
box_t operator()(geom::nullgeom_t const & /*geom*/) const
{
return box_t{};
}
box_t operator()(geom::point_t const &geom) const
{
box_t box;
box.extend(geom);
return box;
}
box_t operator()(geom::linestring_t const &geom) const
{
box_t box;
box.extend(geom);
return box;
}
box_t operator()(geom::polygon_t const &geom) const
{
box_t box;
box.extend(geom.outer());
return box;
}
box_t operator()(geom::multipoint_t const & /*geom*/) const
{
assert(false);
return {}; // XXX not implemented
}
box_t operator()(geom::multilinestring_t const &geom) const
{
box_t box;
for (auto const &line : geom) {
box.extend(line);
}
return box;
}
box_t operator()(geom::multipolygon_t const &geom) const
{
box_t box;
for (auto const &polygon : geom) {
box.extend(polygon.outer());
}
return box;
}
box_t operator()(geom::collection_t const & /*geom*/) const
{
assert(false);
return {}; // XXX not implemented
}
}; // class envelope_visitor
} // anonymous namespace
box_t envelope(geometry_t const &geom)
{
return geom.visit(envelope_visitor{});
}
} // namespace geom

76
src/geom-box.hpp Normal file
View File

@ -0,0 +1,76 @@
#ifndef OSM2PGSQL_GEOM_BOX_HPP
#define OSM2PGSQL_GEOM_BOX_HPP
/**
* SPDX-License-Identifier: GPL-2.0-or-later
*
* This file is part of osm2pgsql (https://osm2pgsql.org/).
*
* Copyright (C) 2006-2022 by the osm2pgsql developer community.
* For a full list of authors see the git log.
*/
/**
* \file
*
* Class and functions for creating bounding boxes of geometries.
*/
#include "geom.hpp"
#include <cassert>
#include <limits>
namespace geom {
class box_t
{
public:
constexpr box_t() noexcept = default;
constexpr box_t(double min_x, double min_y, double max_x,
double max_y) noexcept
: m_min_x(min_x), m_min_y(min_y), m_max_x(max_x), m_max_y(max_y)
{
assert(min_x <= max_x);
assert(min_y <= max_y);
}
box_t &extend(point_t const &point) noexcept;
void extend(point_list_t const &list) noexcept;
constexpr double min_x() const noexcept { return m_min_x; }
constexpr double min_y() const noexcept { return m_min_y; }
constexpr double max_x() const noexcept { return m_max_x; }
constexpr double max_y() const noexcept { return m_max_y; }
constexpr double width() const noexcept { return m_max_x - m_min_x; }
constexpr double height() const noexcept { return m_max_y - m_min_y; }
constexpr friend bool operator==(box_t const &a, box_t const &b)
{
return a.min_x() == b.min_x() && a.min_y() == b.min_y() &&
a.max_x() == b.max_x() && a.max_y() == b.max_y();
}
constexpr friend bool operator!=(box_t const &a, box_t const &b)
{
return !(a == b);
}
private:
double m_min_x = std::numeric_limits<double>::max();
double m_min_y = std::numeric_limits<double>::max();
double m_max_x = std::numeric_limits<double>::lowest();
double m_max_y = std::numeric_limits<double>::lowest();
}; // class box_t
/**
* Calculate the envelope of a geometry.
*/
box_t envelope(geometry_t const &geom);
} // namespace geom
#endif // OSM2PGSQL_GEOM_BOX_HPP

View File

@ -43,6 +43,7 @@ set_test(test-db-copy-mgr)
set_test(test-domain-matcher LABELS NoDB)
set_test(test-expire-tiles LABELS NoDB)
set_test(test-geom LABELS NoDB)
set_test(test-geom-box LABELS NoDB)
set_test(test-middle)
set_test(test-node-locations LABELS NoDB)
set_test(test-options-database LABELS NoDB)

105
tests/test-geom-box.cpp Normal file
View File

@ -0,0 +1,105 @@
/**
* SPDX-License-Identifier: GPL-2.0-or-later
*
* This file is part of osm2pgsql (https://osm2pgsql.org/).
*
* Copyright (C) 2006-2022 by the osm2pgsql developer community.
* For a full list of authors see the git log.
*/
#include <catch.hpp>
#include "geom-box.hpp"
TEST_CASE("Extend box_t with points", "[NoDB]")
{
geom::box_t box;
box.extend(geom::point_t{1.0, 2.0});
REQUIRE(box.min_x() == Approx(1.0));
REQUIRE(box.max_x() == Approx(1.0));
REQUIRE(box.min_y() == Approx(2.0));
REQUIRE(box.max_y() == Approx(2.0));
REQUIRE(box.width() == Approx(0.0));
REQUIRE(box.height() == Approx(0.0));
box.extend(geom::point_t{3.0, -2.0});
REQUIRE(box.min_x() == Approx(1.0));
REQUIRE(box.max_x() == Approx(3.0));
REQUIRE(box.min_y() == Approx(-2.0));
REQUIRE(box.max_y() == Approx(2.0));
REQUIRE(box.width() == Approx(2.0));
REQUIRE(box.height() == Approx(4.0));
}
TEST_CASE("Extend box_t with linestring", "[NoDB]")
{
geom::box_t box;
geom::linestring_t const ls{{1.0, 2.0}, {2.0, 2.0}, {-5.0, 3.0}};
box.extend(ls);
REQUIRE(box.min_x() == Approx(-5.0));
REQUIRE(box.max_x() == Approx(2.0));
REQUIRE(box.min_y() == Approx(2.0));
REQUIRE(box.max_y() == Approx(3.0));
REQUIRE(box.width() == Approx(7.0));
REQUIRE(box.height() == Approx(1.0));
}
TEST_CASE("Calculate envelope of null geometry")
{
geom::geometry_t const geom{};
REQUIRE(geom::envelope(geom) == geom::box_t{});
}
TEST_CASE("Calculate envelope of point geometry")
{
geom::geometry_t const geom{geom::point_t{2.3, 1.4}};
REQUIRE(geom::envelope(geom) == geom::box_t{2.3, 1.4, 2.3, 1.4});
}
TEST_CASE("Calculate envelope of linestring geometry")
{
geom::geometry_t const geom{geom::linestring_t{{2.3, 1.4}, {2.5, 1.0}}};
REQUIRE(geom::envelope(geom) == geom::box_t{2.3, 1.0, 2.5, 1.4});
}
TEST_CASE("Calculate envelope of polygon geometry")
{
geom::geometry_t const geom{geom::polygon_t{geom::ring_t{
{0.0, 0.0}, {1.0, 0.0}, {1.0, 1.0}, {0.0, 1.0}, {0.0, 0.0}}}};
REQUIRE(geom::envelope(geom) == geom::box_t{0.0, 0.0, 1.0, 1.0});
}
TEST_CASE("Calculate envelope of multilinestring geometry")
{
geom::geometry_t geom{geom::multilinestring_t{}};
auto &mls = geom.get<geom::multilinestring_t>();
mls.add_geometry(geom::linestring_t{{2.3, 1.4}, {2.5, 1.0}});
mls.add_geometry(geom::linestring_t{{7.3, 0.4}, {2.4, 1.8}});
REQUIRE(geom::envelope(geom) == geom::box_t{2.3, 0.4, 7.3, 1.8});
}
TEST_CASE("Calculate envelope of multipolygon geometry")
{
geom::geometry_t geom{geom::multipolygon_t{}};
auto &mp = geom.get<geom::multipolygon_t>();
mp.add_geometry(geom::polygon_t{geom::ring_t{
{1.1, 1.1}, {1.1, 3.3}, {2.2, 3.3}, {2.2, 1.1}, {1.1, 1.1}}});
mp.add_geometry(geom::polygon_t{geom::ring_t{
{2.2, 2.2}, {2.2, 3.3}, {4.4, 3.3}, {4.4, 2.2}, {2.2, 2.2}}});
REQUIRE(geom::envelope(geom) == geom::box_t{1.1, 1.1, 4.4, 3.3});
}