Skip to content

Commit cfc3642

Browse files
authored
Merge pull request #2476 from joto/diff-expire
Add option for diff expire
2 parents a3d4787 + 56a5fc8 commit cfc3642

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)