Files
osm2pgsql/tests/test-expire-from-geometry.cpp
2024-01-26 16:45:53 +01:00

503 lines
16 KiB
C++

/**
* SPDX-License-Identifier: GPL-2.0-or-later
*
* This file is part of osm2pgsql (https://osm2pgsql.org/).
*
* Copyright (C) 2006-2024 by the osm2pgsql developer community.
* For a full list of authors see the git log.
*/
#include <catch.hpp>
#include <random>
#include <set>
#include "expire-tiles.hpp"
#include "reprojection.hpp"
#include "tile-output.hpp"
#include "tile.hpp"
static std::shared_ptr<reprojection> defproj{
reprojection::create_projection(PROJ_SPHERE_MERC)};
// We are using zoom level 12 here, because at that level a tile is about
// 10,000 units wide/high which gives us easy numbers to work with.
static constexpr uint32_t const zoom = 12;
TEST_CASE("expire null geometry does nothing", "[NoDB]")
{
expire_config_t const expire_config;
expire_tiles et{zoom, defproj};
SECTION("geom")
{
geom::geometry_t const geom{};
et.from_geometry(geom, expire_config);
}
SECTION("geom with check")
{
geom::geometry_t geom{};
geom.set_srid(3857);
et.from_geometry_if_3857(geom, expire_config);
}
REQUIRE(et.get_tiles().empty());
}
TEST_CASE("expire point at tile boundary", "[NoDB]")
{
expire_config_t const expire_config;
expire_tiles et{zoom, defproj};
geom::point_t const pt{0.0, 0.0};
SECTION("point") { et.from_geometry(pt, expire_config); }
SECTION("geom")
{
geom::geometry_t const geom{pt};
et.from_geometry(geom, expire_config);
}
SECTION("geom with check")
{
geom::geometry_t geom{pt};
geom.set_srid(3857);
et.from_geometry_if_3857(geom, expire_config);
}
auto const tiles = et.get_tiles();
REQUIRE(tiles.size() == 4);
CHECK(tile_t::from_quadkey(tiles[0], zoom) == tile_t{zoom, 2047, 2047});
CHECK(tile_t::from_quadkey(tiles[1], zoom) == tile_t{zoom, 2048, 2047});
CHECK(tile_t::from_quadkey(tiles[2], zoom) == tile_t{zoom, 2047, 2048});
CHECK(tile_t::from_quadkey(tiles[3], zoom) == tile_t{zoom, 2048, 2048});
}
TEST_CASE("expire point away from tile boundary", "[NoDB]")
{
expire_config_t const expire_config;
expire_tiles et{zoom, defproj};
geom::point_t const pt{5000.0, 5000.0};
SECTION("point") { et.from_geometry(pt, expire_config); }
SECTION("geom")
{
geom::geometry_t const geom{pt};
et.from_geometry(geom, expire_config);
}
SECTION("geom with check")
{
geom::geometry_t geom{pt};
geom.set_srid(3857);
et.from_geometry_if_3857(geom, expire_config);
}
auto const tiles = et.get_tiles();
REQUIRE(tiles.size() == 1);
CHECK(tile_t::from_quadkey(tiles[0], zoom) == tile_t{zoom, 2048, 2047});
}
TEST_CASE("expire linestring away from tile boundary", "[NoDB]")
{
expire_config_t const expire_config;
expire_tiles et{zoom, defproj};
SECTION("line")
{
geom::linestring_t const line{{5000.0, 4000.0}, {5100.0, 4200.0}};
et.from_geometry(line, expire_config);
}
SECTION("geom")
{
geom::linestring_t line{{5000.0, 4000.0}, {5100.0, 4200.0}};
geom::geometry_t const geom{std::move(line)};
et.from_geometry(geom, expire_config);
}
SECTION("geom with check")
{
geom::linestring_t line{{5000.0, 4000.0}, {5100.0, 4200.0}};
geom::geometry_t geom{std::move(line)};
geom.set_srid(3857);
et.from_geometry_if_3857(geom, expire_config);
}
auto const tiles = et.get_tiles();
REQUIRE(tiles.size() == 1);
CHECK(tile_t::from_quadkey(tiles[0], zoom) == tile_t{zoom, 2048, 2047});
}
TEST_CASE("expire linestring crossing tile boundary", "[NoDB]")
{
expire_config_t const expire_config;
expire_tiles et{zoom, defproj};
SECTION("line")
{
geom::linestring_t const line{{5000.0, 5000.0}, {5000.0, 15000.0}};
et.from_geometry(line, expire_config);
}
SECTION("geom")
{
geom::linestring_t line{{5000.0, 5000.0}, {5000.0, 15000.0}};
geom::geometry_t const geom{std::move(line)};
et.from_geometry(geom, expire_config);
}
SECTION("geom with check")
{
geom::linestring_t line{{5000.0, 5000.0}, {5000.0, 15000.0}};
geom::geometry_t geom{std::move(line)};
geom.set_srid(3857);
et.from_geometry_if_3857(geom, expire_config);
}
auto const tiles = et.get_tiles();
REQUIRE(tiles.size() == 2);
CHECK(tile_t::from_quadkey(tiles[0], zoom) == tile_t{zoom, 2048, 2046});
CHECK(tile_t::from_quadkey(tiles[1], zoom) == tile_t{zoom, 2048, 2047});
}
TEST_CASE("expire small polygon", "[NoDB]")
{
expire_config_t const expire_config;
expire_tiles et{zoom, defproj};
SECTION("polygon")
{
geom::polygon_t const poly{{{5000.0, 5000.0},
{5100.0, 5000.0},
{5100.0, 5100.0},
{5000.0, 5100.0},
{5000.0, 5000.0}}};
et.from_geometry(poly, expire_config);
}
SECTION("geom")
{
geom::polygon_t poly{{{5000.0, 5000.0},
{5100.0, 5000.0},
{5100.0, 5100.0},
{5000.0, 5100.0},
{5000.0, 5000.0}}};
geom::geometry_t const geom{std::move(poly)};
et.from_geometry(geom, expire_config);
}
SECTION("geom with check")
{
geom::polygon_t poly{{{5000.0, 5000.0},
{5100.0, 5000.0},
{5100.0, 5100.0},
{5000.0, 5100.0},
{5000.0, 5000.0}}};
geom::geometry_t geom{std::move(poly)};
geom.set_srid(3857);
et.from_geometry_if_3857(geom, expire_config);
}
auto const tiles = et.get_tiles();
REQUIRE(tiles.size() == 1);
CHECK(tile_t::from_quadkey(tiles[0], zoom) == tile_t{zoom, 2048, 2047});
}
TEST_CASE("expire large polygon as bbox", "[NoDB]")
{
expire_config_t expire_config;
expire_config.mode = expire_mode::hybrid;
expire_config.full_area_limit = 40000;
expire_tiles et{zoom, defproj};
SECTION("polygon")
{
geom::polygon_t const poly{{{5000.0, 5000.0},
{25000.0, 5000.0},
{25000.0, 25000.0},
{5000.0, 25000.0},
{5000.0, 5000.0}}};
et.from_geometry(poly, expire_config);
}
SECTION("geom")
{
geom::polygon_t poly{{{5000.0, 5000.0},
{25000.0, 5000.0},
{25000.0, 25000.0},
{5000.0, 25000.0},
{5000.0, 5000.0}}};
geom::geometry_t const geom{std::move(poly)};
et.from_geometry(geom, expire_config);
}
SECTION("geom with check")
{
geom::polygon_t poly{{{5000.0, 5000.0},
{25000.0, 5000.0},
{25000.0, 25000.0},
{5000.0, 25000.0},
{5000.0, 5000.0}}};
geom::geometry_t geom{std::move(poly)};
geom.set_srid(3857);
et.from_geometry_if_3857(geom, expire_config);
}
auto const tiles = et.get_tiles();
REQUIRE(tiles.size() == 9);
CHECK(tile_t::from_quadkey(tiles[0], zoom) == tile_t{zoom, 2048, 2045});
CHECK(tile_t::from_quadkey(tiles[1], zoom) == tile_t{zoom, 2049, 2045});
CHECK(tile_t::from_quadkey(tiles[2], zoom) == tile_t{zoom, 2050, 2045});
CHECK(tile_t::from_quadkey(tiles[3], zoom) == tile_t{zoom, 2048, 2046});
CHECK(tile_t::from_quadkey(tiles[4], zoom) == tile_t{zoom, 2049, 2046});
CHECK(tile_t::from_quadkey(tiles[7], zoom) == tile_t{zoom, 2050, 2046});
CHECK(tile_t::from_quadkey(tiles[5], zoom) == tile_t{zoom, 2048, 2047});
CHECK(tile_t::from_quadkey(tiles[6], zoom) == tile_t{zoom, 2049, 2047});
CHECK(tile_t::from_quadkey(tiles[8], zoom) == tile_t{zoom, 2050, 2047});
}
TEST_CASE("expire large polygon as boundary", "[NoDB]")
{
expire_config_t expire_config;
expire_config.mode = expire_mode::hybrid;
expire_config.full_area_limit = 10000;
expire_tiles et{zoom, defproj};
SECTION("polygon")
{
geom::polygon_t const poly{{{5000.0, 5000.0},
{25000.0, 5000.0},
{25000.0, 25000.0},
{5000.0, 25000.0},
{5000.0, 5000.0}}};
et.from_geometry(poly, expire_config);
}
SECTION("polygon boundary")
{
geom::polygon_t const poly{{{5000.0, 5000.0},
{25000.0, 5000.0},
{25000.0, 25000.0},
{5000.0, 25000.0},
{5000.0, 5000.0}}};
et.from_polygon_boundary(poly, expire_config);
}
SECTION("geom")
{
geom::polygon_t poly{{{5000.0, 5000.0},
{25000.0, 5000.0},
{25000.0, 25000.0},
{5000.0, 25000.0},
{5000.0, 5000.0}}};
geom::geometry_t const geom{std::move(poly)};
et.from_geometry(geom, expire_config);
}
SECTION("geom with check")
{
geom::polygon_t poly{{{5000.0, 5000.0},
{25000.0, 5000.0},
{25000.0, 25000.0},
{5000.0, 25000.0},
{5000.0, 5000.0}}};
geom::geometry_t geom{std::move(poly)};
geom.set_srid(3857);
et.from_geometry_if_3857(geom, expire_config);
}
auto const tiles = et.get_tiles();
REQUIRE(tiles.size() == 8);
CHECK(tile_t::from_quadkey(tiles[0], zoom) == tile_t{zoom, 2048, 2045});
CHECK(tile_t::from_quadkey(tiles[1], zoom) == tile_t{zoom, 2049, 2045});
CHECK(tile_t::from_quadkey(tiles[2], zoom) == tile_t{zoom, 2050, 2045});
CHECK(tile_t::from_quadkey(tiles[3], zoom) == tile_t{zoom, 2048, 2046});
CHECK(tile_t::from_quadkey(tiles[6], zoom) == tile_t{zoom, 2050, 2046});
CHECK(tile_t::from_quadkey(tiles[4], zoom) == tile_t{zoom, 2048, 2047});
CHECK(tile_t::from_quadkey(tiles[5], zoom) == tile_t{zoom, 2049, 2047});
CHECK(tile_t::from_quadkey(tiles[7], zoom) == tile_t{zoom, 2050, 2047});
}
TEST_CASE("expire multipoint geometry", "[NoDB]")
{
expire_config_t const expire_config;
expire_tiles et{zoom, defproj};
geom::point_t const p1{0.0, 0.0};
geom::point_t const p2{15000.0, 15000.0};
SECTION("multipoint")
{
geom::multipoint_t mpt;
mpt.add_geometry(p1);
mpt.add_geometry(p2);
et.from_geometry(mpt, expire_config);
}
SECTION("geom")
{
geom::multipoint_t mpt;
mpt.add_geometry(p1);
mpt.add_geometry(p2);
geom::geometry_t const geom{std::move(mpt)};
et.from_geometry(geom, expire_config);
}
SECTION("geom with check")
{
geom::multipoint_t mpt;
mpt.add_geometry(p1);
mpt.add_geometry(p2);
geom::geometry_t geom{std::move(mpt)};
geom.set_srid(3857);
et.from_geometry_if_3857(geom, expire_config);
}
auto const tiles = et.get_tiles();
REQUIRE(tiles.size() == 5);
CHECK(tile_t::from_quadkey(tiles[0], zoom) == tile_t{zoom, 2047, 2047});
CHECK(tile_t::from_quadkey(tiles[1], zoom) == tile_t{zoom, 2049, 2046});
CHECK(tile_t::from_quadkey(tiles[2], zoom) == tile_t{zoom, 2048, 2047});
CHECK(tile_t::from_quadkey(tiles[3], zoom) == tile_t{zoom, 2047, 2048});
CHECK(tile_t::from_quadkey(tiles[4], zoom) == tile_t{zoom, 2048, 2048});
}
TEST_CASE("expire multilinestring geometry", "[NoDB]")
{
expire_config_t const expire_config;
expire_tiles et{zoom, defproj};
geom::linestring_t l1{{2000.0, 2000.0}, {3000.0, 3000.0}};
geom::linestring_t l2{{15000.0, 15000.0}, {25000.0, 15000.0}};
geom::multilinestring_t ml;
ml.add_geometry(std::move(l1));
ml.add_geometry(std::move(l2));
SECTION("multilinestring") { et.from_geometry(ml, expire_config); }
SECTION("geom")
{
geom::geometry_t const geom{std::move(ml)};
et.from_geometry(geom, expire_config);
}
auto const tiles = et.get_tiles();
REQUIRE(tiles.size() == 3);
CHECK(tile_t::from_quadkey(tiles[0], zoom) == tile_t{zoom, 2049, 2046});
CHECK(tile_t::from_quadkey(tiles[1], zoom) == tile_t{zoom, 2048, 2047});
CHECK(tile_t::from_quadkey(tiles[2], zoom) == tile_t{zoom, 2050, 2046});
}
TEST_CASE("expire multipolygon geometry", "[NoDB]")
{
expire_config_t expire_config;
expire_config.mode = expire_mode::hybrid;
expire_config.full_area_limit = 10000;
expire_tiles et{zoom, defproj};
geom::polygon_t p1{{{2000.0, 2000.0},
{2000.0, 3000.0},
{3000.0, 3000.0},
{3000.0, 2000.0},
{2000.0, 2000.0}}};
geom::polygon_t p2{{{15000.0, 15000.0},
{45000.0, 15000.0},
{45000.0, 45000.0},
{15000.0, 45000.0},
{15000.0, 15000.0}}};
p2.add_inner_ring({{25000.0, 25000.0},
{25000.0, 35000.0},
{35000.0, 35000.0},
{35000.0, 25000.0},
{25000.0, 25000.0}});
geom::multipolygon_t mp;
mp.add_geometry(std::move(p1));
mp.add_geometry(std::move(p2));
SECTION("multilinestring") { et.from_geometry(mp, expire_config); }
SECTION("geom")
{
geom::geometry_t const geom{std::move(mp)};
et.from_geometry(geom, expire_config);
}
auto const tiles = et.get_tiles();
REQUIRE(tiles.size() == 17);
std::set<quadkey_t> result;
for (auto const &tile : tiles) {
result.insert(tile);
}
std::set<quadkey_t> expected;
expected.insert(tile_t{zoom, 2048, 2047}.quadkey()); // p1
for (uint32_t x = 2049; x <= 2052; ++x) {
for (uint32_t y = 2043; y <= 2046; ++y) {
expected.insert(tile_t{zoom, x, y}.quadkey()); // p2
}
}
REQUIRE(expected == result);
}
TEST_CASE("expire geometry collection", "[NoDB]")
{
expire_config_t const expire_config;
expire_tiles et{zoom, defproj};
geom::collection_t collection;
collection.add_geometry(geom::geometry_t{geom::point_t{0.0, 0.0}});
collection.add_geometry(geom::geometry_t{
geom::linestring_t{{15000.0, 15000.0}, {25000.0, 15000.0}}});
geom::geometry_t const geom{std::move(collection)};
et.from_geometry(geom, expire_config);
auto const tiles = et.get_tiles();
REQUIRE(tiles.size() == 6);
CHECK(tile_t::from_quadkey(tiles[0], zoom) == tile_t{zoom, 2047, 2047});
CHECK(tile_t::from_quadkey(tiles[1], zoom) == tile_t{zoom, 2049, 2046});
CHECK(tile_t::from_quadkey(tiles[2], zoom) == tile_t{zoom, 2048, 2047});
CHECK(tile_t::from_quadkey(tiles[3], zoom) == tile_t{zoom, 2050, 2046});
CHECK(tile_t::from_quadkey(tiles[4], zoom) == tile_t{zoom, 2047, 2048});
CHECK(tile_t::from_quadkey(tiles[5], zoom) == tile_t{zoom, 2048, 2048});
}
TEST_CASE("expire works if in 3857", "[NoDB]")
{
expire_config_t const expire_config;
expire_tiles et{zoom, defproj};
geom::geometry_t geom{geom::point_t{0.0, 0.0}};
geom.set_srid(3857);
et.from_geometry_if_3857(geom, expire_config);
auto const tiles = et.get_tiles();
REQUIRE(tiles.size() == 4);
}
TEST_CASE("expire doesn't do anything if not in 3857", "[NoDB]")
{
expire_config_t const expire_config;
expire_tiles et{zoom, defproj};
geom::geometry_t geom{geom::point_t{0.0, 0.0}};
geom.set_srid(1234);
et.from_geometry_if_3857(geom, expire_config);
auto const tiles = et.get_tiles();
REQUIRE(tiles.empty());
}