Skip to content

Commit dd47ab6

Browse files
authored
Merge pull request #1327 from joto/geom-transform-multi
Optionally wrap polygon geometries in multipolygons
2 parents e672af4 + 819d611 commit dd47ab6

21 files changed

Lines changed: 337 additions & 70 deletions

flex-config/compatible.lua

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ local keep_coastlines = false
1414
-- Set this to the table name prefix (what used to be option -p|--prefix).
1515
local prefix = 'planet_osm'
1616

17-
-- Set this to true if multipolygons should be written as polygons into db
18-
-- (what used to be option -G|--multi-geometry).
17+
-- Set this to true if multipolygons should be written as multipolygons into
18+
-- db (what used to be option -G|--multi-geometry).
1919
local multi_geometry = false
2020

2121
-- Set this to true if you want an hstore column (what used to be option
@@ -726,7 +726,10 @@ function osm2pgsql.process_relation(object)
726726
end
727727

728728
if make_boundary or make_polygon then
729-
output.way = { create = 'area', multi = multi_geometry }
729+
output.way = { create = 'area' }
730+
if not multi_geometry then
731+
output.way.split_at = 'multi'
732+
end
730733
tables.polygon:add_row(output)
731734
end
732735
end

src/geom-transform.cpp

Lines changed: 33 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ bool geom_transform_point_t::is_compatible_with(
1616

1717
geom::osmium_builder_t::wkbs_t
1818
geom_transform_point_t::run(geom::osmium_builder_t *builder,
19+
table_column_type /*target_geom_type*/,
1920
osmium::Node const &node) const
2021
{
2122
assert(builder);
@@ -49,6 +50,7 @@ bool geom_transform_line_t::is_compatible_with(
4950

5051
geom::osmium_builder_t::wkbs_t
5152
geom_transform_line_t::run(geom::osmium_builder_t *builder,
53+
table_column_type /*target_geom_type*/,
5254
osmium::Way *way) const
5355
{
5456
assert(builder);
@@ -59,6 +61,7 @@ geom_transform_line_t::run(geom::osmium_builder_t *builder,
5961

6062
geom::osmium_builder_t::wkbs_t
6163
geom_transform_line_t::run(geom::osmium_builder_t *builder,
64+
table_column_type /*target_geom_type*/,
6265
osmium::Relation const & /*relation*/,
6366
osmium::memory::Buffer const &buffer) const
6467
{
@@ -69,61 +72,77 @@ geom_transform_line_t::run(geom::osmium_builder_t *builder,
6972

7073
bool geom_transform_area_t::set_param(char const *name, lua_State *lua_state)
7174
{
72-
if (std::strcmp(name, "multi") != 0) {
75+
if (std::strcmp(name, "multi") == 0) {
76+
throw std::runtime_error{
77+
"The 'multi' field in the geometry transformation has been"
78+
" removed. See docs on how to use 'split_at' instead."};
79+
}
80+
81+
if (std::strcmp(name, "split_at") != 0) {
7382
return false;
7483
}
7584

76-
if (lua_type(lua_state, -1) != LUA_TBOOLEAN) {
85+
auto const val = lua_tostring(lua_state, -1);
86+
87+
if (!val) {
7788
throw std::runtime_error{
78-
"The 'multi' field in a geometry transformation "
79-
"description must be a boolean."};
89+
"The 'split_at' field in a geometry transformation "
90+
"description must be a string."};
8091
}
81-
m_multi = lua_toboolean(lua_state, -1);
8292

83-
return true;
93+
if (std::strcmp(val, "multi") == 0) {
94+
m_multi = false;
95+
return true;
96+
}
97+
98+
throw std::runtime_error{"Unknown value for 'split_at' field in a geometry"
99+
" transformation: '{}'"_format(val)};
84100
}
85101

86102
bool geom_transform_area_t::is_compatible_with(
87103
table_column_type geom_type) const noexcept
88104
{
89-
if (m_multi) {
90-
return geom_type == table_column_type::multipolygon ||
91-
geom_type == table_column_type::geometry;
92-
}
93-
94105
return geom_type == table_column_type::polygon ||
106+
geom_type == table_column_type::multipolygon ||
95107
geom_type == table_column_type::geometry;
96108
}
97109

98110
geom::osmium_builder_t::wkbs_t
99111
geom_transform_area_t::run(geom::osmium_builder_t *builder,
112+
table_column_type target_geom_type,
100113
osmium::Way *way) const
101114
{
102115
assert(builder);
103116
assert(way);
104117

118+
geom::osmium_builder_t::wkbs_t result;
119+
105120
if (!way->is_closed()) {
106-
return {};
121+
return result;
107122
}
108123

109-
geom::osmium_builder_t::wkbs_t result;
110124
result.push_back(builder->get_wkb_polygon(*way));
111125

112126
if (result.front().empty()) {
113127
result.clear();
128+
} else if (target_geom_type == table_column_type::multipolygon) {
129+
builder->wrap_in_multipolygon(&result);
114130
}
115131

116132
return result;
117133
}
118134

119135
geom::osmium_builder_t::wkbs_t
120136
geom_transform_area_t::run(geom::osmium_builder_t *builder,
137+
table_column_type target_geom_type,
121138
osmium::Relation const &relation,
122139
osmium::memory::Buffer const &buffer) const
123140
{
124141
assert(builder);
125142

126-
return builder->get_wkb_multipolygon(relation, buffer, m_multi);
143+
bool const wrap_multi = target_geom_type == table_column_type::multipolygon;
144+
145+
return builder->get_wkb_multipolygon(relation, buffer, m_multi, wrap_multi);
127146
}
128147

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

src/geom-transform.hpp

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,19 +32,22 @@ class geom_transform_t
3232

3333
virtual geom::osmium_builder_t::wkbs_t
3434
run(geom::osmium_builder_t * /*builder*/,
35+
table_column_type /*target_geom_type*/,
3536
osmium::Node const & /*node*/) const
3637
{
3738
return {};
3839
}
3940

4041
virtual geom::osmium_builder_t::wkbs_t
41-
run(geom::osmium_builder_t * /*builder*/, osmium::Way * /*way*/) const
42+
run(geom::osmium_builder_t * /*builder*/,
43+
table_column_type /*target_geom_type*/, osmium::Way * /*way*/) const
4244
{
4345
return {};
4446
}
4547

4648
virtual geom::osmium_builder_t::wkbs_t
4749
run(geom::osmium_builder_t * /*builder*/,
50+
table_column_type /*target_geom_type*/,
4851
osmium::Relation const & /*relation*/,
4952
osmium::memory::Buffer const & /*buffer*/) const
5053
{
@@ -60,6 +63,7 @@ class geom_transform_point_t : public geom_transform_t
6063
noexcept override;
6164

6265
geom::osmium_builder_t::wkbs_t run(geom::osmium_builder_t *builder,
66+
table_column_type target_geom_type,
6367
osmium::Node const &node) const override;
6468

6569
}; // class geom_transform_point_t
@@ -73,10 +77,12 @@ class geom_transform_line_t : public geom_transform_t
7377
noexcept override;
7478

7579
geom::osmium_builder_t::wkbs_t run(geom::osmium_builder_t *builder,
80+
table_column_type target_geom_type,
7681
osmium::Way *way) const override;
7782

7883
geom::osmium_builder_t::wkbs_t
79-
run(geom::osmium_builder_t *builder, osmium::Relation const &relation,
84+
run(geom::osmium_builder_t *builder, table_column_type target_geom_type,
85+
osmium::Relation const &relation,
8086
osmium::memory::Buffer const &buffer) const override;
8187

8288
private:
@@ -93,14 +99,16 @@ class geom_transform_area_t : public geom_transform_t
9399
noexcept override;
94100

95101
geom::osmium_builder_t::wkbs_t run(geom::osmium_builder_t *builder,
102+
table_column_type target_geom_type,
96103
osmium::Way *way) const override;
97104

98105
geom::osmium_builder_t::wkbs_t
99-
run(geom::osmium_builder_t *builder, osmium::Relation const &relation,
106+
run(geom::osmium_builder_t *builder, table_column_type target_geom_type,
107+
osmium::Relation const &relation,
100108
osmium::memory::Buffer const &buffer) const override;
101109

102110
private:
103-
bool m_multi = false;
111+
bool m_multi = true;
104112

105113
}; // class geom_transform_area_t
106114

src/osmium-builder.cpp

Lines changed: 39 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,41 @@ 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 || wrap_multi) {
192+
wrap_in_multipolygon(&ret);
193+
}
165194
} else {
166-
ret = create_polygons(m_buffer.get<osmium::Area>(0));
195+
if (wrap_multi) {
196+
for (auto &wkb : ret) {
197+
// wrap each polygon into its own multipolygon
198+
wrap_in_multipolygon(&wkb);
199+
}
200+
}
167201
}
168202
}
169-
170203
return ret;
171204
}
172205

@@ -345,31 +378,6 @@ size_t osmium_builder_t::add_mp_points(osmium::NodeRefList const &nodes)
345378
return num_points;
346379
}
347380

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-
373381
osmium_builder_t::wkbs_t
374382
osmium_builder_t::create_polygons(osmium::Area const &area)
375383
{

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

src/output-flex.cpp

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -967,25 +967,28 @@ get_default_transform(flex_table_column_t const &column,
967967
geom::osmium_builder_t::wkbs_t
968968
output_flex_t::run_transform(geom::osmium_builder_t *builder,
969969
geom_transform_t const *transform,
970+
table_column_type target_geom_type,
970971
osmium::Node const &node)
971972
{
972-
return transform->run(builder, node);
973+
return transform->run(builder, target_geom_type, node);
973974
}
974975

975976
geom::osmium_builder_t::wkbs_t
976977
output_flex_t::run_transform(geom::osmium_builder_t *builder,
977978
geom_transform_t const *transform,
979+
table_column_type target_geom_type,
978980
osmium::Way const & /*way*/)
979981
{
980982
if (get_way_nodes() <= 1U) {
981983
return {};
982984
}
983-
return transform->run(builder, m_context_way);
985+
return transform->run(builder, target_geom_type, m_context_way);
984986
}
985987

986988
geom::osmium_builder_t::wkbs_t
987989
output_flex_t::run_transform(geom::osmium_builder_t *builder,
988990
geom_transform_t const *transform,
991+
table_column_type target_geom_type,
989992
osmium::Relation const &relation)
990993
{
991994
m_buffer.clear();
@@ -1000,7 +1003,7 @@ output_flex_t::run_transform(geom::osmium_builder_t *builder,
10001003
m_mid->nodes_get_list(&(way.nodes()));
10011004
}
10021005

1003-
return transform->run(builder, relation, m_buffer);
1006+
return transform->run(builder, target_geom_type, relation, m_buffer);
10041007
}
10051008

10061009
template <typename OBJECT>
@@ -1035,7 +1038,8 @@ void output_flex_t::add_row(table_connection_t *table_connection,
10351038
}
10361039

10371040
auto *builder = table_connection->get_builder();
1038-
auto const wkbs = run_transform(builder, transform, object);
1041+
auto const wkbs =
1042+
run_transform(builder, transform, table.geom_column().type(), object);
10391043
for (auto const &wkb : wkbs) {
10401044
m_expire.from_wkb(wkb.c_str(), id);
10411045
write_row(table_connection, object.type(), id, wkb,

0 commit comments

Comments
 (0)