/** * 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 #include #include #include "expire-tiles.hpp" #include "reprojection.hpp" #include "tile-output.hpp" #include "tile.hpp" static std::shared_ptr 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 result; for (auto const &tile : tiles) { result.insert(tile); } std::set 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()); }