Skip to content

Commit 56a5fc8

Browse files
committed
Add option for diff expire
The "expire" configuration for a geometry column in the table definition now has an option "diff_expire", which is set to "false" by default but can be set to "true" to get "diff expire", which means expire will be based on the symmetrical difference between old and new geometry. Note that "diff expire" will only be used for ways and relations that change based on their members. If a way or relation is changed itself, we always do a complete expire. This is needed, because we don't know what changes there are (the tags or attributes could have changed, or even the geometry type). Internally this is implemented by not calculating expire tiles first for the "delete" of the old geometry and then for the "add" of the new geometry, but by writing old and new geometries for each object in a cache and then, after we have collected all geometries, calculating the symmetrical differences between those geometries.
1 parent ee2f4b0 commit 56a5fc8

10 files changed

Lines changed: 644 additions & 38 deletions

src/expire-config.hpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@ struct expire_config_t
4343
/// Expire mode.
4444
expire_mode mode = expire_mode::full_area;
4545

46+
/// Do expire based on symmetric difference of old and new geometry
47+
bool diff_expire = false;
48+
4649
}; // struct expire_config_t
4750

4851
#endif // OSM2PGSQL_EXPIRE_CONFIG_HPP

src/flex-lua-table.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,15 @@ void parse_and_set_expire_options(lua_State *lua_state,
310310
}
311311
lua_pop(lua_state, 1); // "buffer"
312312

313+
lua_getfield(lua_state, -1, "diff_expire");
314+
if (lua_isboolean(lua_state, -1)) {
315+
config.diff_expire = lua_toboolean(lua_state, -1);
316+
} else if (!lua_isnil(lua_state, -1)) {
317+
throw std::runtime_error{
318+
"Optional expire field 'diff_expire' must contain a boolean."};
319+
}
320+
lua_pop(lua_state, 1); // "diff_expire"
321+
313322
// Actually add the expire only if we are in append mode.
314323
if (append_mode) {
315324
column->add_expire(config);

src/flex-table-column.cpp

Lines changed: 135 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
#include "flex-table-column.hpp"
1111

1212
#include "format.hpp"
13+
#include "geom-boost-adaptor.hpp"
14+
#include "overloaded.hpp"
1315
#include "pgsql-capabilities.hpp"
1416
#include "projection.hpp"
1517
#include "util.hpp"
@@ -208,18 +210,146 @@ void flex_table_column_t::add_expire(expire_config_t const &config)
208210
m_expires.push_back(config);
209211
}
210212

213+
namespace {
214+
215+
/**
216+
* This expires all geometries in "geoms" by themselves. Used when we don't
217+
* need diff expire.
218+
*/
219+
void separate_expire(std::vector<geom::geometry_t> const &geoms,
220+
expire_config_t const &expire_config,
221+
expire_tiles_t &expire_tiles,
222+
std::vector<expire_output_t> *expire_outputs)
223+
{
224+
assert(expire_outputs);
225+
226+
for (auto const &geom : geoms) {
227+
expire_tiles.from_geometry(geom, expire_config);
228+
}
229+
expire_tiles.commit_tiles(&expire_outputs->at(expire_config.expire_output));
230+
}
231+
232+
/**
233+
* When doing diff expire, we need to calculate the symmetric difference
234+
* between old and new geometries. The difference is done by type, so points
235+
* are compared with points, linestrings with linestrings, etc. This function
236+
* separates out the input geometries by the three fundamental types.
237+
*/
238+
// NOLINTBEGIN(cppcoreguidelines-rvalue-reference-param-not-moved)
239+
template <typename T>
240+
void classify_geometries(T input_geoms, geom::multipoint_t *points,
241+
geom::multilinestring_t *linestrings,
242+
geom::multipolygon_t *polygons)
243+
{
244+
assert(points);
245+
assert(linestrings);
246+
assert(polygons);
247+
248+
for (auto &&geom : *input_geoms) {
249+
visit(overloaded{
250+
[&](geom::nullgeom_t && /*input*/) {},
251+
[&](geom::point_t &&input) { points->add_geometry(input); },
252+
[&](geom::linestring_t &&input) {
253+
linestrings->add_geometry(std::move(input));
254+
},
255+
[&](geom::polygon_t &&input) {
256+
polygons->add_geometry(std::move(input));
257+
},
258+
[&](geom::multipoint_t &&input) {
259+
for (auto &&point : input) {
260+
points->add_geometry(point);
261+
}
262+
},
263+
[&](geom::multilinestring_t &&input) {
264+
for (auto &&linestring : input) {
265+
linestrings->add_geometry(std::move(linestring));
266+
}
267+
},
268+
[&](geom::multipolygon_t &&input) {
269+
for (auto &&polygon : input) {
270+
polygons->add_geometry(std::move(polygon));
271+
}
272+
},
273+
[&](geom::collection_t &&input) {
274+
classify_geometries(&input, points, linestrings,
275+
polygons);
276+
}},
277+
std::move(geom));
278+
}
279+
}
280+
// NOLINTEND(cppcoreguidelines-rvalue-reference-param-not-moved)
281+
282+
template <typename T>
283+
void diff_and_expire(geom::multigeometry_t<T> const &old_geoms,
284+
geom::multigeometry_t<T> const &new_geoms,
285+
expire_config_t const &expire_config,
286+
expire_tiles_t &expire_tiles)
287+
{
288+
std::vector<T> diffs;
289+
boost::geometry::sym_difference(old_geoms, new_geoms, diffs);
290+
for (auto const &geom : diffs) {
291+
expire_tiles.from_geometry(geom, expire_config);
292+
}
293+
}
294+
295+
void diff_expire(std::vector<geom::geometry_t> *geoms_old,
296+
std::vector<geom::geometry_t> *geoms_new,
297+
expire_config_t const &expire_config,
298+
expire_tiles_t &expire_tiles,
299+
std::vector<expire_output_t> *expire_outputs)
300+
{
301+
assert(geoms_old);
302+
assert(geoms_new);
303+
assert(expire_outputs);
304+
305+
geom::multipoint_t old_points;
306+
geom::multilinestring_t old_linestrings;
307+
geom::multipolygon_t old_polygons;
308+
309+
classify_geometries(geoms_old, &old_points, &old_linestrings,
310+
&old_polygons);
311+
312+
geom::multipoint_t new_points;
313+
geom::multilinestring_t new_linestrings;
314+
geom::multipolygon_t new_polygons;
315+
316+
classify_geometries(geoms_new, &new_points, &new_linestrings,
317+
&new_polygons);
318+
319+
diff_and_expire(old_points, new_points, expire_config, expire_tiles);
320+
diff_and_expire(old_linestrings, new_linestrings, expire_config,
321+
expire_tiles);
322+
diff_and_expire(old_polygons, new_polygons, expire_config, expire_tiles);
323+
324+
expire_tiles.commit_tiles(&expire_outputs->at(expire_config.expire_output));
325+
}
326+
327+
} // anonymous namespace
328+
211329
void flex_table_column_t::do_expire(
212-
geom::geometry_t const &geom, std::vector<expire_tiles_t> *expire,
213-
std::vector<expire_output_t> *expire_outputs) const
330+
std::vector<geom::geometry_t> *geoms_old,
331+
std::vector<geom::geometry_t> *geoms_new,
332+
std::vector<expire_tiles_t> *expire,
333+
std::vector<expire_output_t> *expire_outputs, bool enable_diff_expire) const
214334
{
335+
assert(geoms_old);
336+
assert(geoms_new);
215337
assert(expire);
216338
assert(expire_outputs);
217339

218340
for (auto const &expire_config : m_expires) {
219341
assert(expire_config.expire_output < expire->size());
220342
auto &expire_tiles = expire->at(expire_config.expire_output);
221-
expire_tiles.from_geometry(geom, expire_config);
222-
expire_tiles.commit_tiles(
223-
&expire_outputs->at(expire_config.expire_output));
343+
344+
if (!expire_config.diff_expire || !enable_diff_expire ||
345+
geoms_old->empty() || geoms_new->empty()) {
346+
separate_expire(*geoms_old, expire_config, expire_tiles,
347+
expire_outputs);
348+
separate_expire(*geoms_new, expire_config, expire_tiles,
349+
expire_outputs);
350+
} else {
351+
diff_expire(geoms_old, geoms_new, expire_config, expire_tiles,
352+
expire_outputs);
353+
}
224354
}
225355
}

src/flex-table-column.hpp

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include <cassert>
1919
#include <cstdint>
2020
#include <string>
21+
#include <unordered_map>
2122
#include <vector>
2223

2324
enum class table_column_type : uint8_t
@@ -54,6 +55,8 @@ enum class table_column_type : uint8_t
5455
id_num
5556
};
5657

58+
class geometry_cache_t;
59+
5760
/**
5861
* A column in a flex_table_t.
5962
*/
@@ -134,9 +137,11 @@ class flex_table_column_t
134137
return m_expires;
135138
}
136139

137-
void do_expire(geom::geometry_t const &geom,
140+
void do_expire(std::vector<geom::geometry_t> *geoms_old,
141+
std::vector<geom::geometry_t> *geoms_new,
138142
std::vector<expire_tiles_t> *expire,
139-
std::vector<expire_output_t> *expire_outputs) const;
143+
std::vector<expire_output_t> *expire_outputs,
144+
bool enable_diff_expire) const;
140145

141146
private:
142147
std::vector<expire_config_t> m_expires;
@@ -170,6 +175,40 @@ class flex_table_column_t
170175

171176
/// Column will be created but not filled by osm2pgsql.
172177
bool m_create_only = false;
173-
};
178+
}; // class flex_table_column_t
179+
180+
/**
181+
* While processing an OSM object, this cache is used to hold all old and all
182+
* new geometries stored in all geometry columns with expire config in a table.
183+
* Later those geometries are used to calculate the expire.
184+
*/
185+
class geometry_cache_t
186+
{
187+
public:
188+
template <typename GEOM>
189+
void add_old(flex_table_column_t const *column, GEOM &&geom)
190+
{
191+
m_geometries[column].first.push_back(std::forward<GEOM>(geom));
192+
}
193+
194+
template <typename GEOM>
195+
void add_new(flex_table_column_t const *column, GEOM &&geom)
196+
{
197+
m_geometries[column].second.push_back(std::forward<GEOM>(geom));
198+
}
199+
200+
auto begin() noexcept { return m_geometries.begin(); }
201+
202+
auto end() noexcept { return m_geometries.end(); }
203+
204+
void clear() { m_geometries.clear(); }
205+
206+
private:
207+
std::unordered_map<
208+
flex_table_column_t const *,
209+
std::pair<std::vector<geom::geometry_t>, std::vector<geom::geometry_t>>>
210+
m_geometries;
211+
212+
}; // class geometry_cache_t
174213

175214
#endif // OSM2PGSQL_FLEX_TABLE_COLUMN_HPP

src/flex-write.cpp

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
*/
99

1010
#include "flex-lua-geom.hpp"
11+
#include "flex-table-column.hpp"
1112
#include "flex-write.hpp"
1213
#include "geom-functions.hpp"
1314
#include "json-writer.hpp"
@@ -257,11 +258,9 @@ bool is_compatible(geom::geometry_t const &geom,
257258

258259
} // anonymous namespace
259260

260-
void flex_write_column(lua_State *lua_state,
261+
void flex_write_column(lua_State *lua_state, geometry_cache_t *geom_cache,
261262
db_copy_mgr_t<db_deleter_by_type_and_id_t> *copy_mgr,
262-
flex_table_column_t const &column,
263-
std::vector<expire_tiles_t> *expire,
264-
std::vector<expire_output_t> *expire_outputs)
263+
flex_table_column_t const &column)
265264
{
266265
lua_getfield(lua_state, -1, column.name().c_str());
267266
int const ltype = lua_type(lua_state, -1);
@@ -446,13 +445,13 @@ void flex_write_column(lua_State *lua_state,
446445
type == table_column_type::multilinestring ||
447446
type == table_column_type::multipolygon);
448447
if (geom->srid() == column.srid()) {
449-
column.do_expire(*geom, expire, expire_outputs);
450448
copy_mgr->add_hex_geom(geom_to_ewkb(*geom, wrap_multi));
449+
geom_cache->add_new(&column, *geom);
451450
} else {
452451
auto const &proj = get_projection(column.srid());
453-
auto const tgeom = geom::transform(*geom, proj);
454-
column.do_expire(tgeom, expire, expire_outputs);
452+
auto tgeom = geom::transform(*geom, proj);
455453
copy_mgr->add_hex_geom(geom_to_ewkb(tgeom, wrap_multi));
454+
geom_cache->add_new(&column, std::move(tgeom));
456455
}
457456
} else {
458457
write_null(copy_mgr, column);

src/flex-write.hpp

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include <vector>
2020

2121
class expire_tiles_t;
22+
class geometry_cache_t;
2223

2324
class not_null_exception_t : public std::runtime_error
2425
{
@@ -34,10 +35,8 @@ class not_null_exception_t : public std::runtime_error
3435
flex_table_column_t const *m_column;
3536
}; // class not_null_exception_t
3637

37-
void flex_write_column(lua_State *lua_state,
38+
void flex_write_column(lua_State *lua_state, geometry_cache_t *geom_cache,
3839
db_copy_mgr_t<db_deleter_by_type_and_id_t> *copy_mgr,
39-
flex_table_column_t const &column,
40-
std::vector<expire_tiles_t> *expire,
41-
std::vector<expire_output_t> *expire_outputs);
40+
flex_table_column_t const &column);
4241

4342
#endif // OSM2PGSQL_FLEX_WRITE_HPP

0 commit comments

Comments
 (0)