Skip to content

Commit c9747fd

Browse files
committed
Optionally wrap polygon geometries in multipolygons
When you have an area table in the flex output, you always had to use the "geometry" column type for the geometry column, because ways are turned into polygons and multipolygon relations are turned into polygons or multipolygons. The common type for these is the generic "geometry". With this change, declaring a geometry column as type "multipolygon" now works. All polygon geometries are automatically wrapped in a multipolygon before adding them to the database. Depending on what you are doing this might be want you want or not. It will make the geometries larger, but it also makes the data more consistent and so easier to work with and some software can only handle this consistent data. See #1316
1 parent 03a089f commit c9747fd

12 files changed

Lines changed: 281 additions & 43 deletions

src/geom-transform.cpp

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -89,46 +89,47 @@ bool geom_transform_area_t::set_param(char const *name, lua_State *lua_state)
8989
bool geom_transform_area_t::is_compatible_with(
9090
table_column_type geom_type) const noexcept
9191
{
92-
if (m_multi) {
93-
return geom_type == table_column_type::multipolygon ||
94-
geom_type == table_column_type::geometry;
95-
}
96-
9792
return geom_type == table_column_type::polygon ||
93+
geom_type == table_column_type::multipolygon ||
9894
geom_type == table_column_type::geometry;
9995
}
10096

10197
geom::osmium_builder_t::wkbs_t
10298
geom_transform_area_t::run(geom::osmium_builder_t *builder,
103-
table_column_type /*target_geom_type*/,
99+
table_column_type target_geom_type,
104100
osmium::Way *way) const
105101
{
106102
assert(builder);
107103
assert(way);
108104

105+
geom::osmium_builder_t::wkbs_t result;
106+
109107
if (!way->is_closed()) {
110-
return {};
108+
return result;
111109
}
112110

113-
geom::osmium_builder_t::wkbs_t result;
114111
result.push_back(builder->get_wkb_polygon(*way));
115112

116113
if (result.front().empty()) {
117114
result.clear();
115+
} else if (target_geom_type == table_column_type::multipolygon) {
116+
builder->wrap_in_multipolygon(&result);
118117
}
119118

120119
return result;
121120
}
122121

123122
geom::osmium_builder_t::wkbs_t
124123
geom_transform_area_t::run(geom::osmium_builder_t *builder,
125-
table_column_type /*target_geom_type*/,
124+
table_column_type target_geom_type,
126125
osmium::Relation const &relation,
127126
osmium::memory::Buffer const &buffer) const
128127
{
129128
assert(builder);
130129

131-
return builder->get_wkb_multipolygon(relation, buffer, m_multi);
130+
bool const wrap_multi = target_geom_type == table_column_type::multipolygon;
131+
132+
return builder->get_wkb_multipolygon(relation, buffer, m_multi, wrap_multi);
132133
}
133134

134135
std::unique_ptr<geom_transform_t> create_geom_transform(char const *type)

src/osmium-builder.cpp

Lines changed: 43 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,26 @@ void add_nodes_to_builder(osmium::builder::WayNodeListBuilder &builder,
4747

4848
namespace geom {
4949

50+
void osmium_builder_t::wrap_in_multipolygon(
51+
osmium_builder_t::wkbs_t *geometries)
52+
{
53+
assert(!geometries->empty());
54+
55+
m_writer.multipolygon_start();
56+
for (auto const &p : *geometries) {
57+
m_writer.add_sub_geometry(p);
58+
}
59+
(*geometries)[0] = m_writer.multipolygon_finish(geometries->size());
60+
geometries->resize(1);
61+
}
62+
63+
void osmium_builder_t::wrap_in_multipolygon(osmium_builder_t::wkb_t *geometry)
64+
{
65+
m_writer.multipolygon_start();
66+
m_writer.add_sub_geometry(*geometry);
67+
*geometry = m_writer.multipolygon_finish(1);
68+
}
69+
5070
osmium_builder_t::wkb_t
5171
osmium_builder_t::get_wkb_node(osmium::Location const &loc) const
5272
{
@@ -145,28 +165,45 @@ osmium_builder_t::get_wkb_polygon(osmium::Way const &way)
145165

146166
auto const wkbs = create_polygons(m_buffer.get<osmium::Area>(0));
147167

148-
return wkbs.empty() ? wkb_t() : wkbs[0];
168+
return wkbs.empty() ? wkb_t{} : wkbs[0];
149169
}
150170

151171
osmium_builder_t::wkbs_t
152172
osmium_builder_t::get_wkb_multipolygon(osmium::Relation const &rel,
153173
osmium::memory::Buffer const &ways,
154-
bool build_multigeoms)
174+
bool build_multigeoms, bool wrap_multi)
155175
{
156-
wkbs_t ret;
157176
osmium::area::AssemblerConfig area_config;
158177
area_config.ignore_invalid_locations = true;
159178
osmium::area::GeomAssembler assembler{area_config};
160179

161180
m_buffer.clear();
181+
182+
wkbs_t ret;
162183
if (assembler(rel, ways, m_buffer)) {
184+
auto const &area = m_buffer.get<osmium::Area>(0);
185+
186+
// This returns a vector of one or more polygons
187+
ret = create_polygons(area);
188+
assert(!ret.empty());
189+
163190
if (build_multigeoms) {
164-
ret.push_back(create_multipolygon(m_buffer.get<osmium::Area>(0)));
191+
if (ret.size() > 1) {
192+
// wrap all polygons into a single multipolygon
193+
wrap_in_multipolygon(&ret);
194+
} else if (wrap_multi) {
195+
// wrap single polygon into a multipolygon
196+
wrap_in_multipolygon(&ret[0]);
197+
}
165198
} else {
166-
ret = create_polygons(m_buffer.get<osmium::Area>(0));
199+
if (wrap_multi) {
200+
for (auto &wkb : ret) {
201+
// wrap each polygon into its own multipolygon
202+
wrap_in_multipolygon(&wkb);
203+
}
204+
}
167205
}
168206
}
169-
170207
return ret;
171208
}
172209

@@ -345,31 +382,6 @@ size_t osmium_builder_t::add_mp_points(osmium::NodeRefList const &nodes)
345382
return num_points;
346383
}
347384

348-
osmium_builder_t::wkb_t
349-
osmium_builder_t::create_multipolygon(osmium::Area const &area)
350-
{
351-
wkb_t ret;
352-
353-
auto const polys = create_polygons(area);
354-
355-
switch (polys.size()) {
356-
case 0:
357-
break; //nothing
358-
case 1:
359-
ret = polys[0];
360-
break;
361-
default:
362-
m_writer.multipolygon_start();
363-
for (auto const &p : polys) {
364-
m_writer.add_sub_geometry(p);
365-
}
366-
ret = m_writer.multipolygon_finish(polys.size());
367-
break;
368-
}
369-
370-
return ret;
371-
}
372-
373385
osmium_builder_t::wkbs_t
374386
osmium_builder_t::create_polygons(osmium::Area const &area)
375387
{

src/osmium-builder.hpp

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,13 +30,24 @@ class osmium_builder_t
3030

3131
wkbs_t get_wkb_multipolygon(osmium::Relation const &rel,
3232
osmium::memory::Buffer const &ways,
33-
bool build_multigeoms);
33+
bool build_multigeoms, bool wrap_multi = false);
3434

3535
wkbs_t get_wkb_multiline(osmium::memory::Buffer const &ways,
3636
double split_at);
3737

38+
/**
39+
* Wrap the geometries (must be one or more polygons) in the parameter
40+
* into a single multipolygon which is returned in-place in geometries.
41+
*/
42+
void wrap_in_multipolygon(wkbs_t *geometries);
43+
44+
/**
45+
* Wrap the polygon geometry in the parameter into a multipolygon which
46+
* is returned in-place in geometry.
47+
*/
48+
void wrap_in_multipolygon(wkb_t *geometry);
49+
3850
private:
39-
wkb_t create_multipolygon(osmium::Area const &area);
4051
wkbs_t create_polygons(osmium::Area const &area);
4152
size_t add_mp_points(osmium::NodeRefList const &nodes);
4253

tests/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ if (HAVE_LUA)
8080
set_test(test-output-flex-invalid-geom)
8181
set_test(test-output-flex-line)
8282
set_test(test-output-flex-lua-fail)
83+
set_test(test-output-flex-multigeom)
8384
set_test(test-output-flex-nodes)
8485
set_test(test-output-flex-nogeom)
8586
set_test(test-output-flex-uni)
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
2+
local polygons = osm2pgsql.define_table{
3+
name = 'osm2pgsql_test_polygon',
4+
ids = { type = 'area', id_column = 'osm_id' },
5+
columns = {
6+
{ column = 'name', type = 'text' },
7+
{ column = 'geom', type = test.type }
8+
}
9+
}
10+
11+
function is_empty(some_table)
12+
return next(some_table) == nil
13+
end
14+
15+
function osm2pgsql.process_way(object)
16+
if is_empty(object.tags) then
17+
return
18+
end
19+
20+
polygons:add_row({
21+
name = object.tags.name,
22+
geom = { create = 'area' }
23+
})
24+
end
25+
26+
function osm2pgsql.process_relation(object)
27+
polygons:add_row({
28+
name = object.tags.name,
29+
geom = { create = 'area', multi = test.multi }
30+
})
31+
end
32+
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
<?xml version='1.0' encoding='UTF-8'?>
2+
<osm version="0.6">
3+
<node id="10" lat="0" lon="0"/>
4+
<node id="11" lat="0" lon="1"/>
5+
<node id="12" lat="1" lon="1"/>
6+
<node id="13" lat="1" lon="0"/>
7+
8+
<node id="14" lat="2" lon="2"/>
9+
<node id="15" lat="2" lon="3"/>
10+
<node id="16" lat="3" lon="3"/>
11+
<node id="17" lat="3" lon="2"/>
12+
13+
<way id="20">
14+
<nd ref="10"/>
15+
<nd ref="11"/>
16+
<nd ref="12"/>
17+
<nd ref="13"/>
18+
<nd ref="10"/>
19+
<tag k="natural" v="water"/>
20+
<tag k="name" v="poly"/>
21+
</way>
22+
23+
<way id="21">
24+
<nd ref="10"/>
25+
<nd ref="11"/>
26+
<nd ref="12"/>
27+
<nd ref="13"/>
28+
<nd ref="10"/>
29+
</way>
30+
<way id="22">
31+
<nd ref="14"/>
32+
<nd ref="15"/>
33+
<nd ref="16"/>
34+
<nd ref="17"/>
35+
<nd ref="14"/>
36+
</way>
37+
38+
<relation id="30">
39+
<member type="way" ref="21" role="outer"/>
40+
<tag k="type" v="multipolygon"/>
41+
<tag k="natural" v="water"/>
42+
<tag k="name" v="poly"/>
43+
</relation>
44+
<relation id="31">
45+
<member type="way" ref="21" role="outer"/>
46+
<member type="way" ref="22" role="outer"/>
47+
<tag k="type" v="multipolygon"/>
48+
<tag k="natural" v="water"/>
49+
<tag k="name" v="multi"/>
50+
</relation>
51+
</osm>
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
2+
test = { type = 'geometry', multi = false }
3+
4+
dofile(os.getenv('SRCPATH') .. '/data/test_output_flex_multigeom.lua')
5+
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
2+
test = { type = 'geometry', multi = true }
3+
4+
dofile(os.getenv('SRCPATH') .. '/data/test_output_flex_multigeom.lua')
5+
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
2+
test = { type = 'multipolygon', multi = false }
3+
4+
dofile(os.getenv('SRCPATH') .. '/data/test_output_flex_multigeom.lua')
5+
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
2+
test = { type = 'multipolygon', multi = true }
3+
4+
dofile(os.getenv('SRCPATH') .. '/data/test_output_flex_multigeom.lua')
5+

0 commit comments

Comments
 (0)