Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions src/expire-output.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
* For a full list of authors see the git log.
*/

#include "projection.hpp"
#include "tile.hpp"

#include <cassert>
Expand Down Expand Up @@ -59,6 +60,9 @@ class expire_output_t
uint32_t maxzoom() const noexcept { return m_maxzoom; }
void set_maxzoom(uint32_t maxzoom) noexcept { m_maxzoom = maxzoom; }

int srid() const noexcept { return m_srid; }
void set_srid(int srid) noexcept { m_srid = srid; }

std::size_t max_tiles_geometry() const noexcept
{
return m_max_tiles_geometry;
Expand Down Expand Up @@ -142,6 +146,8 @@ class expire_output_t
/// Zoom level we capture tiles on
uint32_t m_maxzoom = 0;

int m_srid = PROJ_SPHERE_MERC;

/**
* The following two settings are for protecting osm2pgsql from overload as
* well as downstream tile expiry mechanisms in case of large changes to
Expand Down
8 changes: 4 additions & 4 deletions src/expire-tiles.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -169,10 +169,10 @@ void expire_tiles_t::from_polygon_area(geom::polygon_t const &geom,

// Coordinates are numbered from bottom to top, tiles are numbered from top
// to bottom, so "min" and "max" are switched here.
auto const max_tile_y = static_cast<std::uint32_t>(
m_map_width * (0.5 - box.min().y() / tile_t::EARTH_CIRCUMFERENCE));
auto const min_tile_y = static_cast<std::uint32_t>(
m_map_width * (0.5 - box.max().y() / tile_t::EARTH_CIRCUMFERENCE));
auto const max_tile_y =
static_cast<std::uint32_t>(coords_to_tile(box.min()).y());
auto const min_tile_y =
static_cast<std::uint32_t>(coords_to_tile(box.max()).y());

std::vector<uint32_t> tile_x_list;

Expand Down
2 changes: 1 addition & 1 deletion src/flex-table-column.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ std::string flex_table_column_t::sql_create() const
void flex_table_column_t::add_expire(expire_config_t const &config)
{
assert(is_geometry_column());
assert(srid() == PROJ_SPHERE_MERC);
assert(srid() > 0);
m_expires.push_back(config);
}

Expand Down
35 changes: 24 additions & 11 deletions src/output-flex.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1315,7 +1315,7 @@ output_flex_t::output_flex_t(output_flex_t const *other,
for (auto &expire_output : *m_expire_outputs) {
m_expire_tiles.emplace_back(
expire_output.maxzoom(),
reprojection_t::create_projection(PROJ_SPHERE_MERC),
reprojection_t::create_projection(expire_output.srid()),
expire_output.max_tiles_geometry());
}
}
Expand Down Expand Up @@ -1358,17 +1358,30 @@ output_flex_t::output_flex_t(std::shared_ptr<middle_query_t> const &mid,
eo.set_minzoom(options.expire_tiles_zoom_min);
eo.set_maxzoom(options.expire_tiles_zoom);

flex_table_t const *srid_table = nullptr;
for (auto &table : *m_tables) {
if (table.has_geom_column() &&
table.geom_column().srid() == PROJ_SPHERE_MERC) {
expire_config_t config{};
config.expire_output = m_expire_outputs->size() - 1;
if (options.expire_tiles_max_bbox > 0.0) {
config.mode = expire_mode::hybrid;
config.full_area_limit = options.expire_tiles_max_bbox;
}
table.geom_column().add_expire(config);
if (!table.has_geom_column()) {
continue;
}
auto const srid = table.geom_column().srid();
if (!srid_table) {
srid_table = &table;
eo.set_srid(srid);
} else if (srid != eo.srid()) {
throw fmt_error(
"Tile expiry needs all tables with a geometry column to use"
" the same projection, but table '{}' uses SRID {} and"
" table '{}' uses SRID {}.",
srid_table->name(), eo.srid(), table.name(), srid);
}

expire_config_t config{};
config.expire_output = m_expire_outputs->size() - 1;
if (options.expire_tiles_max_bbox > 0.0) {
config.mode = expire_mode::hybrid;
config.full_area_limit = options.expire_tiles_max_bbox;
}
table.geom_column().add_expire(config);
}
}

Expand All @@ -1382,7 +1395,7 @@ output_flex_t::output_flex_t(std::shared_ptr<middle_query_t> const &mid,
for (auto const &expire_output : *m_expire_outputs) {
m_expire_tiles.emplace_back(
expire_output.maxzoom(),
reprojection_t::create_projection(PROJ_SPHERE_MERC),
reprojection_t::create_projection(expire_output.srid()),
expire_output.max_tiles_geometry());
}

Expand Down
68 changes: 68 additions & 0 deletions tests/bdd/flex/expire-non-mercator.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
Feature: Expire with command line options on geometries not in Web Mercator

Scenario: Tables in EPSG:4326 are expired
Given the lua style
"""
local points = osm2pgsql.define_node_table('osm2pgsql_test_points', {
{ column = 'geom', type = 'point', projection = 4326 },
})

function osm2pgsql.process_node(object)
points:insert({ geom = object:as_point() })
end
"""
And the OSM data
"""
n10 v1 dV Tamenity=restaurant x10.0 y10.0
"""
When running osm2pgsql flex with parameters
| --slim | -c |
Then execution is successful

Given the OSM data
"""
n10 v2 dV Tamenity=restaurant x10.5 y10.5
"""
When running osm2pgsql flex with parameters
| --slim | -a | --expire-tiles=12 | --expire-output=expire.list |
Then execution is successful
And the error output contains
"""
entries to expire output [0].
"""

Scenario: Tables in different projections can not share command line expire
Given the lua style
"""
local merc = osm2pgsql.define_node_table('osm2pgsql_test_merc', {
{ column = 'geom', type = 'point', projection = 3857 },
})

local latlon = osm2pgsql.define_node_table('osm2pgsql_test_latlon', {
{ column = 'geom', type = 'point', projection = 4326 },
})

function osm2pgsql.process_node(object)
merc:insert({ geom = object:as_point() })
latlon:insert({ geom = object:as_point() })
end
"""
And the OSM data
"""
n10 v1 dV Tamenity=restaurant x10.0 y10.0
"""
When running osm2pgsql flex with parameters
| --slim | -c |
Then execution is successful

Given the OSM data
"""
n10 v2 dV Tamenity=restaurant x10.5 y10.5
"""
When running osm2pgsql flex with parameters
| --slim | -a | --expire-tiles=12 |
Then execution fails
And the error output contains
"""
Tile expiry needs all tables with a geometry column to use the same projection
"""
95 changes: 95 additions & 0 deletions tests/test-expire-from-geometry.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@

#include <catch.hpp>

#include <osmium/geom/mercator_projection.hpp>

#include <algorithm>
#include <memory>
#include <set>
#include <utility>
Expand All @@ -24,10 +27,39 @@ namespace {
std::shared_ptr<reprojection_t> defproj{
reprojection_t::create_projection(PROJ_SPHERE_MERC)};

std::shared_ptr<reprojection_t> latlonproj{
reprojection_t::create_projection(PROJ_LATLONG)};

// 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.
constexpr uint32_t ZOOM = 12;

geom::point_t merc_to_latlon(geom::point_t const &point)
{
auto const c = osmium::geom::mercator_to_lonlat(
osmium::geom::Coordinates{point.x(), point.y()});
return {c.x, c.y};
}

template <typename TLIST>
TLIST merc_to_latlon(TLIST const &list)
{
TLIST result;
for (auto const &point : list) {
result.push_back(merc_to_latlon(point));
}
return result;
}

template <typename TGEOM>
quadkey_list_t expire(std::shared_ptr<reprojection_t> const &projection,
TGEOM const &geom, expire_config_t const &expire_config)
{
expire_tiles_t et{ZOOM, projection};
et.from_geometry(geom, expire_config);
return et.get_tiles();
}

} // anonymous namespace

TEST_CASE("expire null geometry does nothing", "[NoDB]")
Expand Down Expand Up @@ -506,3 +538,66 @@ TEST_CASE("expire doesn't do anything if not in 3857", "[NoDB]")
auto const tiles = et.get_tiles();
REQUIRE(tiles.empty());
}

TEST_CASE("expire point in 4326 matches web mercator", "[NoDB]")
{
expire_config_t const expire_config;
geom::point_t const pt{5000.0, 5000.0};

auto const merc_tiles = expire(defproj, pt, expire_config);
REQUIRE(merc_tiles.size() == 1);
REQUIRE(expire(latlonproj, merc_to_latlon(pt), expire_config) ==
merc_tiles);
}

TEST_CASE("expire linestring in 4326 matches web mercator", "[NoDB]")
{
expire_config_t const expire_config;
geom::linestring_t const line{{5000.0, 5000.0}, {5000.0, 15000.0}};

auto const merc_tiles = expire(defproj, line, expire_config);
REQUIRE(merc_tiles.size() == 2);
REQUIRE(expire(latlonproj, merc_to_latlon(line), expire_config) ==
merc_tiles);
}

TEST_CASE("expire polygon boundary in 4326 matches web mercator", "[NoDB]")
{
expire_config_t expire_config;
expire_config.mode = expire_mode::boundary_only;
geom::ring_t const ring{{5000.0, 5000.0},
{25000.0, 5000.0},
{25000.0, 25000.0},
{5000.0, 25000.0},
{5000.0, 5000.0}};

auto const merc_tiles =
expire(defproj, geom::polygon_t{geom::ring_t{ring}}, expire_config);
REQUIRE(merc_tiles.size() == 8);
REQUIRE(expire(latlonproj, geom::polygon_t{merc_to_latlon(ring)},
expire_config) == merc_tiles);
}

TEST_CASE("expire polygon interior in 4326 matches web mercator", "[NoDB]")
{
expire_config_t expire_config;
expire_config.mode = expire_mode::full_area;
geom::ring_t const ring{{5000.0, 5000.0},
{25000.0, 5000.0},
{25000.0, 25000.0},
{5000.0, 25000.0},
{5000.0, 5000.0}};

auto const merc_tiles =
expire(defproj, geom::polygon_t{geom::ring_t{ring}}, expire_config);
auto const latlon_tiles = expire(
latlonproj, geom::polygon_t{merc_to_latlon(ring)}, expire_config);

REQUIRE(merc_tiles.size() == 9);
REQUIRE(latlon_tiles == merc_tiles);

// The one tile no part of the boundary touches.
auto const interior = tile_t{ZOOM, 2049, 2046}.quadkey();
REQUIRE(std::find(latlon_tiles.cbegin(), latlon_tiles.cend(), interior) !=
latlon_tiles.cend());
}
Loading