Skip to content

Make locator also work with geometry collections #2349

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 7, 2025
Merged
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
53 changes: 36 additions & 17 deletions src/locator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -88,44 +88,63 @@ void locator_t::build_index()
m_rtree.insert(m_data.cbegin(), m_data.cend());
}

std::set<std::string> locator_t::all_intersecting(geom::geometry_t const &geom)
void locator_t::all_intersecting_visit(geom::geometry_t const &geom,
std::set<std::string> *results)
{
if (m_rtree.size() < m_regions.size()) {
build_index();
}

std::set<std::string> results;

geom.visit(overloaded{[&](geom::nullgeom_t const & /*input*/) {},
[&](geom::collection_t const & /*input*/) {}, // TODO
geom.visit(overloaded{[&](geom::nullgeom_t const & /*val*/) {},
[&](geom::collection_t const &val) {
for (auto const &sgeom : val) {
all_intersecting_visit(sgeom, results);
}
},
[&](auto const &val) {
for (auto it = begin_intersects(val);
it != end_query(); ++it) {
auto const &region = m_regions[it->second];
results.emplace(region.name());
results->emplace(region.name());
}
}});

return results;
}

std::string locator_t::first_intersecting(geom::geometry_t const &geom)
std::set<std::string> locator_t::all_intersecting(geom::geometry_t const &geom)
{
if (m_rtree.size() < m_regions.size()) {
build_index();
}

std::string result;
std::set<std::string> results;
all_intersecting_visit(geom, &results);
return results;
}

geom.visit(overloaded{[&](geom::nullgeom_t const & /*input*/) {},
[&](geom::collection_t const & /*input*/) {}, // TODO
void locator_t::first_intersecting_visit(geom::geometry_t const &geom,
std::string *result)
{
geom.visit(overloaded{[&](geom::nullgeom_t const & /*val*/) {},
[&](geom::collection_t const &val) {
for (auto const &sgeom : val) {
first_intersecting_visit(sgeom, result);
if (!result->empty()) {
return;
}
}
},
[&](auto const &val) {
auto const it = begin_intersects(val);
if (it != end_query()) {
auto const &region = m_regions[it->second];
result = region.name();
*result = region.name();
}
}});
}

std::string locator_t::first_intersecting(geom::geometry_t const &geom)
{
if (m_rtree.size() < m_regions.size()) {
build_index();
}

std::string result;
first_intersecting_visit(geom, &result);
return result;
}
6 changes: 6 additions & 0 deletions src/locator.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,12 @@ class locator_t

tree_t::const_query_iterator end_query() { return m_rtree.qend(); }

void all_intersecting_visit(geom::geometry_t const &geom,
std::set<std::string> *results);

void first_intersecting_visit(geom::geometry_t const &geom,
std::string *result);

public:
/// The name of this locator (for logging only)
std::string const &name() const noexcept { return m_name; }
Expand Down
68 changes: 67 additions & 1 deletion tests/bdd/flex/locator.feature
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ Feature: Locators
Error in 'first_intersecting': Need locator and geometry arguments
"""

Scenario: Use a all_intersecting() without geometry fails
Scenario: Use of all_intersecting() without geometry fails
Given the OSM data
"""
n10 v1 dV Tamenity=post_box x0.5 y0.5
Expand Down Expand Up @@ -231,3 +231,69 @@ Feature: Locators
| node_id | region | ST_AsText(geom) |
| 10 | P1 | 15 8 |

Scenario: Define and use a locator with relation from db
Given the 10.0 grid with origin 10.0 10.0
| 10 | 11 | 12 |
| 13 | 14 | 15 |
And the OSM data
"""
w29 v1 dV Tregion=P1 Nn10,n11,n14,n13,n10
"""
And the lua style
"""
local regions = osm2pgsql.define_way_table('osm2pgsql_test_regions', {
{ column = 'region', type = 'text' },
{ column = 'geom', type = 'polygon', projection = 4326 },
})

function osm2pgsql.process_way(object)
regions:insert({
region = object.tags.region,
geom = object:as_polygon(),
})
end
"""
When running osm2pgsql flex
Then table osm2pgsql_test_regions contains exactly
| way_id | region | ST_AsText(geom) |
| 29 | P1 | (10 0,20 0,20 10,10 10, 10 0) |

Given the 10.0 grid with origin 10.0 10.0
| 10 | 11 | 12 |
| 13 | 14 | 15 |
And the OSM data
"""
w20 v1 dV Nn10,n11,n13
w21 v1 dV Nn13,n10
w22 v1 dV Nn14,n15
w23 v1 dV Nn12,n15
r30 v1 dV Tfoo=bar Mw20@,w21@,w22@,n12@
r31 v1 dV Tfoo=bar Mn12@,n15@
r32 v1 dV Tfoo=bar Mw23@
"""
And the lua style
"""
local regions = osm2pgsql.define_locator({ name = 'regions' })
regions:add_from_db('SELECT region, geom FROM osm2pgsql_test_regions')

local points = osm2pgsql.define_relation_table('osm2pgsql_test_rels', {
{ column = 'region', type = 'text' },
{ column = 'geom', type = 'geometry', projection = 4326 },
})

function osm2pgsql.process_relation(object)
local g = object:as_geometrycollection()
local r = regions:first_intersecting(g)
if r then
points:insert({
region = r,
geom = g,
})
end
end
"""
When running osm2pgsql flex
Then table osm2pgsql_test_rels contains exactly
| relation_id | region | ST_GeometryType(geom) |
| 30 | P1 | ST_GeometryCollection |