From 94b3c6f879ecf79cdbe373bb288443afab030b59 Mon Sep 17 00:00:00 2001 From: Jochen Topf Date: Sat, 16 May 2026 17:49:46 +0200 Subject: [PATCH] Allow detecting duplicate nodes in ways when creating geometry When linestrings or polygons are created from ways, duplicate nodes are removed automatically. Generally this is what you want. But sometimes it makes sense to be able to detect this situation, for instance when creating some debug output. The information is available on the C++ side anyway, this change exposes this information in the Lua code as a second return parameter of the as_linestring and as_polygon functions which can be ignored during normal use. --- src/geom-from-osm.cpp | 12 ++++-- src/geom-from-osm.hpp | 4 +- src/output-flex.cpp | 19 ++++++++-- tests/bdd/flex/geometry-linestring.feature | 43 ++++++++++++++++++++++ 4 files changed, 68 insertions(+), 10 deletions(-) diff --git a/src/geom-from-osm.cpp b/src/geom-from-osm.cpp index 339148300..968f52d25 100644 --- a/src/geom-from-osm.cpp +++ b/src/geom-from-osm.cpp @@ -85,13 +85,15 @@ void fill_polygon(polygon_t *polygon, osmium::Area const &area, } // anonymous namespace -void create_linestring(geometry_t *geom, osmium::Way const &way) +bool create_linestring(geometry_t *geom, osmium::Way const &way) { auto &line = geom->set(); if (!fill_point_list(&line, way.nodes())) { geom->reset(); } + + return way.nodes().size() == line.size(); } geometry_t create_linestring(osmium::Way const &way) @@ -101,7 +103,7 @@ geometry_t create_linestring(osmium::Way const &way) return geom; } -void create_polygon(geometry_t *geom, osmium::Way const &way, +bool create_polygon(geometry_t *geom, osmium::Way const &way, osmium::memory::Buffer *area_buffer) { auto &polygon = geom->set(); @@ -109,20 +111,22 @@ void create_polygon(geometry_t *geom, osmium::Way const &way, // A closed way with less than 4 nodes can never be a valid polygon if (way.nodes().size() < 4U) { geom->reset(); - return; + return false; } geom::area_assembler_t assembler{area_buffer}; if (!assembler(way)) { geom->reset(); - return; + return false; } auto const &area = assembler.get_area(); auto const &ring = *area.cbegin(); fill_point_list(&polygon.outer(), ring); + + return assembler.stats().duplicate_nodes == 0; } geometry_t create_polygon(osmium::Way const &way, diff --git a/src/geom-from-osm.hpp b/src/geom-from-osm.hpp index a1068e87d..65fe6abe4 100644 --- a/src/geom-from-osm.hpp +++ b/src/geom-from-osm.hpp @@ -63,7 +63,7 @@ void create_point(geometry_t *geom, osmium::Node const &node); * \param geom Pointer to an existing geometry which will be used as output. * \param way The input way. */ -void create_linestring(geometry_t *geom, osmium::Way const &way); +bool create_linestring(geometry_t *geom, osmium::Way const &way); /** * Create a linestring geometry from a way. Nodes without location are ignored. @@ -87,7 +87,7 @@ void create_linestring(geometry_t *geom, osmium::Way const &way); * \param way The input way. * \param area_buffer Temporary buffer used to create area. */ -void create_polygon(geometry_t *geom, osmium::Way const &way, +bool create_polygon(geometry_t *geom, osmium::Way const &way, osmium::memory::Buffer *area_buffer); /** diff --git a/src/output-flex.cpp b/src/output-flex.cpp index b8a41fbed..840110544 100644 --- a/src/output-flex.cpp +++ b/src/output-flex.cpp @@ -504,9 +504,14 @@ int output_flex_t::app_as_linestring() m_way_cache.add_nodes(middle()); auto *geom = create_lua_geometry_object(lua_state()); - geom::create_linestring(geom, m_way_cache.get()); + bool const okay = geom::create_linestring(geom, m_way_cache.get()); - return 1; + if (geom->is_null()) { + return 1; + } + + lua_pushboolean(lua_state(), okay); + return 2; } int output_flex_t::app_as_polygon() @@ -517,9 +522,15 @@ int output_flex_t::app_as_polygon() m_way_cache.add_nodes(middle()); auto *geom = create_lua_geometry_object(lua_state()); - geom::create_polygon(geom, m_way_cache.get(), &m_area_buffer); + bool const okay = + geom::create_polygon(geom, m_way_cache.get(), &m_area_buffer); - return 1; + if (geom->is_null()) { + return 1; + } + + lua_pushboolean(lua_state(), okay); + return 2; } int output_flex_t::app_as_multipoint() diff --git a/tests/bdd/flex/geometry-linestring.feature b/tests/bdd/flex/geometry-linestring.feature index 6bd2f0d5e..1397aad57 100644 --- a/tests/bdd/flex/geometry-linestring.feature +++ b/tests/bdd/flex/geometry-linestring.feature @@ -66,3 +66,46 @@ Feature: Creating linestring features from way Geometry data for geometry column 'geom' has the wrong type (LINESTRING). """ + Scenario: + Given the grid + | 1 | 2 | | + | | | 3 | + And the OSM data + """ + w20 Thighway=motorway Nn1,n2,n3,n1 + w21 Thighway=motorway Nn1,n2,n2,n3,n1 + w22 Thighway=motorway Nn1,n2,n3,n1,n1 + w23 Thighway=motorway Nn1,n2,n3 + w24 Thighway=motorway Nn2,n2 + """ + And the lua style + """ + local lines = osm2pgsql.define_way_table('osm2pgsql_test_lines', { + { column = 'lgeom', type = 'linestring', projection = 4326 }, + { column = 'pgeom', type = 'polygon', projection = 4326 }, + { column = 'lclean', type = 'bool' }, + { column = 'pclean', type = 'bool' }, + }) + + function osm2pgsql.process_way(object) + lgeom, lclean = object:as_linestring() + pgeom, pclean = object:as_polygon() + lines:insert({ + lgeom = lgeom, + pgeom = pgeom, + lclean = lclean, + pclean = pclean, + }) + end + + """ + When running osm2pgsql flex + + Then table osm2pgsql_test_lines contains exactly + | way_id | lgeom!geo | lclean | pgeom!geo | pclean | + | 20 | 1, 2, 3, 1 | True | (1, 2, 3, 1) | True | + | 21 | 1, 2, 3, 1 | False | (1, 2, 3, 1) | False | + | 22 | 1, 2, 3, 1 | False | (1, 2, 3, 1) | False | + | 23 | 1, 2, 3 | True | NULL | NULL | + | 24 | NULL | NULL | NULL | NULL | +