Skip to content

Commit 44f0023

Browse files
authored
Merge pull request #1783 from joto/add-create-multipoint
Add function to create multipoint geometry
2 parents 5b5685f + 3ceda46 commit 44f0023

File tree

8 files changed

+162
-2
lines changed

8 files changed

+162
-2
lines changed

src/geom-from-osm.cpp

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,37 @@ geometry_t create_polygon(osmium::Way const &way)
9595
return geom;
9696
}
9797

98+
void create_multipoint(geometry_t *geom, osmium::memory::Buffer const &buffer)
99+
{
100+
auto nodes = buffer.select<osmium::Node>();
101+
if (nodes.size() == 1) {
102+
auto const location = nodes.cbegin()->location();
103+
if (location.valid()) {
104+
geom->set<point_t>() = point_t{location};
105+
} else {
106+
geom->reset();
107+
}
108+
} else {
109+
auto &multiline = geom->set<multipoint_t>();
110+
for (auto const &node : nodes) {
111+
auto const location = node.location();
112+
if (location.valid()) {
113+
multiline.add_geometry(point_t{location});
114+
}
115+
}
116+
if (multiline.num_geometries() == 0) {
117+
geom->reset();
118+
}
119+
}
120+
}
121+
122+
geometry_t create_multipoint(osmium::memory::Buffer const &buffer)
123+
{
124+
geometry_t geom{};
125+
create_multipoint(&geom, buffer);
126+
return geom;
127+
}
128+
98129
void create_multilinestring(geometry_t *geom,
99130
osmium::memory::Buffer const &buffer,
100131
bool force_multi)

src/geom-from-osm.hpp

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,33 @@ void create_polygon(geometry_t *geom, osmium::Way const &way);
8989
*/
9090
[[nodiscard]] geometry_t create_polygon(osmium::Way const &way);
9191

92+
/**
93+
* Create a multipoint geometry from a bunch of nodes (usually this would be
94+
* used for member nodes of a relation). The result is multipoint geometry
95+
* (or a point geometry if there is only one point).
96+
*
97+
* If there are no (valid) points, a null geometry is returned.
98+
*
99+
* \param geom Pointer to an existing geometry which will be used as output.
100+
* \param ways Buffer containing all the input ways. Object types other than
101+
* ways in the buffer are ignored.
102+
*/
103+
void create_multipoint(geometry_t *geom, osmium::memory::Buffer const &buffer);
104+
105+
/**
106+
* Create a multipoint geometry from a bunch of nodes (usually this would be
107+
* used for member nodes of a relation). The result is multipoint geometry
108+
* (or a point geometry if there is only one point).
109+
*
110+
* If there are no (valid) points, a null geometry is returned.
111+
*
112+
* \param ways Buffer containing all the input nodes. Object types other than
113+
* nodes in the buffer are ignored.
114+
* \returns The created geometry.
115+
*/
116+
[[nodiscard]] geometry_t
117+
create_multipoint(osmium::memory::Buffer const &buffer);
118+
92119
/**
93120
* Create a multilinestring geometry from a bunch of ways (usually this
94121
* would be used for member ways of a relation). The result is always a

src/output-flex.cpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ TRAMPOLINE(app_get_bbox, get_bbox)
7878
TRAMPOLINE(app_as_point, as_point)
7979
TRAMPOLINE(app_as_linestring, as_linestring)
8080
TRAMPOLINE(app_as_polygon, as_polygon)
81+
TRAMPOLINE(app_as_multipoint, as_multipoint)
8182
TRAMPOLINE(app_as_multilinestring, as_multilinestring)
8283
TRAMPOLINE(app_as_multipolygon, as_multipolygon)
8384
TRAMPOLINE(app_as_geometrycollection, as_geometrycollection)
@@ -920,6 +921,25 @@ int output_flex_t::app_as_polygon()
920921
return 1;
921922
}
922923

924+
int output_flex_t::app_as_multipoint()
925+
{
926+
check_context_and_state(
927+
"as_multipoint", "process_node/relation() functions",
928+
m_calling_context != calling_context::process_node &&
929+
m_calling_context != calling_context::process_relation);
930+
931+
auto *geom = create_lua_geometry_object(lua_state());
932+
933+
if (m_calling_context == calling_context::process_node) {
934+
geom::create_point(geom, *m_context_node);
935+
} else {
936+
m_relation_cache.add_members(middle());
937+
geom::create_multipoint(geom, m_relation_cache.members_buffer());
938+
}
939+
940+
return 1;
941+
}
942+
923943
int output_flex_t::app_as_multilinestring()
924944
{
925945
check_context_and_state(
@@ -2099,6 +2119,8 @@ void output_flex_t::init_lua(std::string const &filename)
20992119
lua_trampoline_app_as_point);
21002120
luaX_add_table_func(lua_state(), "as_polygon",
21012121
lua_trampoline_app_as_polygon);
2122+
luaX_add_table_func(lua_state(), "as_multipoint",
2123+
lua_trampoline_app_as_multipoint);
21022124
luaX_add_table_func(lua_state(), "as_multilinestring",
21032125
lua_trampoline_app_as_multilinestring);
21042126
luaX_add_table_func(lua_state(), "as_multipolygon",

src/output-flex.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,7 @@ class output_flex_t : public output_t
156156
int app_as_point();
157157
int app_as_linestring();
158158
int app_as_polygon();
159+
int app_as_multipoint();
159160
int app_as_multilinestring();
160161
int app_as_multipolygon();
161162
int app_as_geometrycollection();
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
Feature: Creating (multi)point features from nodes and relations
2+
3+
Scenario:
4+
Given the grid
5+
| 1 | 2 | |
6+
| 4 | | 3 |
7+
| | 5 | 6 |
8+
And the OSM data
9+
"""
10+
n1 Thighway=bus_stop
11+
n5 Thighway=bus_stop
12+
w20 Thighway=residential Nn1,n2,n3,n4
13+
w21 Thighway=primary Nn4,n5,n6
14+
r30 Troute=bus Mn1@
15+
r31 Troute=bus Mw21@,n5@,w20@,n1@
16+
"""
17+
And the lua style
18+
"""
19+
local points = osm2pgsql.define_table({
20+
name = 'osm2pgsql_test_points',
21+
ids = { type = 'any', id_column = 'osm_id', type_column = 'osm_type' },
22+
columns = {
23+
{ column = 'geom', type = 'geometry', projection = 4326 },
24+
}
25+
})
26+
27+
function osm2pgsql.process_node(object)
28+
if object.tags.highway == 'bus_stop' then
29+
points:insert({
30+
geom = object:as_multipoint()
31+
})
32+
end
33+
end
34+
35+
function osm2pgsql.process_relation(object)
36+
if object.tags.route == 'bus' then
37+
points:insert({
38+
geom = object:as_multipoint()
39+
})
40+
end
41+
end
42+
"""
43+
When running osm2pgsql flex
44+
45+
Then table osm2pgsql_test_points contains exactly
46+
| osm_type | osm_id | ST_GeometryType(geom) | ST_NumGeometries(geom) | ST_AsText(ST_GeometryN(geom, 1)) | ST_AsText(ST_GeometryN(geom, 2)) |
47+
| N | 1 | ST_Point | 1 | 1 | NULL |
48+
| N | 5 | ST_Point | 1 | 5 | NULL |
49+
| R | 30 | ST_Point | 1 | 1 | NULL |
50+
| R | 31 | ST_MultiPoint | 2 | 5 | 1 |
51+

tests/bdd/steps/geometry_factory.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ def complete_node_list(self, nodes):
101101

102102
coords = self.grid_node(nid)
103103
assert coords is not None, f"Coordinates missing for node {node}"
104-
nodes[i] = f"{line} x{coords[0]:f.{self.grid_precision}} y{coords[1]:f.{self.grid_precision}}"
104+
nodes[i] = f"{line} x{coords[0]:.{self.grid_precision}f} y{coords[1]:.{self.grid_precision}f}"
105105

106106
todos.discard(nid)
107107

tests/bdd/steps/steps_osm_data.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,4 +38,4 @@ def osm_define_data(context, formatted):
3838
for line in data.split('\n'):
3939
if line:
4040
assert line[0] in ('n', 'w', 'r')
41-
context.import_data[line[0]].append(line)
41+
context.import_data[line[0]].append(line.strip())

tests/test-geom-multipoints.cpp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,3 +67,31 @@ TEST_CASE("multipoint_t with several points", "[NoDB]")
6767
REQUIRE(geometry_n(geom, 2) == geom::geometry_t{std::move(p1)});
6868
REQUIRE(geometry_n(geom, 3) == geom::geometry_t{std::move(p2)});
6969
}
70+
71+
TEST_CASE("create_multipoint from OSM data", "[NoDB]")
72+
{
73+
test_buffer_t buffer;
74+
buffer.add_node("n10 x1 y0");
75+
buffer.add_way("w20 Nn1x1y1,n2x2y1");
76+
buffer.add_node("n11 x1 y1");
77+
buffer.add_node("n12 x3 y2");
78+
buffer.add_way("w21 Nn3x10y10,n4x10y11");
79+
buffer.add_node("n13 x3 y1");
80+
buffer.add_relation("r30 Mw20@");
81+
82+
auto const geom = geom::create_multipoint(buffer.buffer());
83+
84+
REQUIRE(geometry_type(geom) == "MULTIPOINT");
85+
REQUIRE(dimension(geom) == 0);
86+
REQUIRE(num_geometries(geom) == 4);
87+
88+
auto const &c = geom.get<geom::multipoint_t>();
89+
REQUIRE(c[0] == geom::point_t{1, 0});
90+
REQUIRE(c[1] == geom::point_t{1, 1});
91+
REQUIRE(c[2] == geom::point_t{3, 2});
92+
REQUIRE(c[3] == geom::point_t{3, 1});
93+
94+
REQUIRE(area(geom) == Approx(0.0));
95+
REQUIRE(length(geom) == Approx(0.0));
96+
REQUIRE(centroid(geom) == geom::geometry_t{geom::point_t{2, 1}});
97+
}

0 commit comments

Comments
 (0)