Libosmium  2.12.2
Fast and flexible C++ library for working with OpenStreetMap data
assembler.hpp
Go to the documentation of this file.
1 #ifndef OSMIUM_AREA_ASSEMBLER_HPP
2 #define OSMIUM_AREA_ASSEMBLER_HPP
3 
4 /*
5 
6 This file is part of Osmium (http://osmcode.org/libosmium).
7 
8 Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
9 
10 Boost Software License - Version 1.0 - August 17th, 2003
11 
12 Permission is hereby granted, free of charge, to any person or organization
13 obtaining a copy of the software and accompanying documentation covered by
14 this license (the "Software") to use, reproduce, display, distribute,
15 execute, and transmit the Software, and to prepare derivative works of the
16 Software, and to permit third-parties to whom the Software is furnished to
17 do so, all subject to the following:
18 
19 The copyright notices in the Software and this entire statement, including
20 the above license grant, this restriction and the following disclaimer,
21 must be included in all copies of the Software, in whole or in part, and
22 all derivative works of the Software, unless such copies or derivative
23 works are solely in the form of machine-executable object code generated by
24 a source language processor.
25 
26 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
27 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
28 FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
29 SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
30 FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
31 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
32 DEALINGS IN THE SOFTWARE.
33 
34 */
35 
36 #include <algorithm>
37 #include <cassert>
38 #include <cstdint>
39 #include <cstdlib>
40 #include <cstring>
41 #include <functional>
42 #include <iostream>
43 #include <iterator>
44 #include <set>
45 #include <string>
46 #include <map>
47 #include <utility>
48 #include <vector>
49 
50 #include <osmium/area/detail/basic_assembler.hpp>
52 #include <osmium/memory/buffer.hpp>
53 #include <osmium/osm/area.hpp>
54 #include <osmium/osm/item_type.hpp>
55 #include <osmium/osm/location.hpp>
56 #include <osmium/osm/relation.hpp>
57 #include <osmium/osm/tag.hpp>
58 #include <osmium/osm/types.hpp>
59 #include <osmium/osm/way.hpp>
60 #include <osmium/tags/filter.hpp>
62 
63 namespace osmium {
64 
65  namespace area {
66 
71  class Assembler : public detail::BasicAssembler {
72 
73  bool report_ways() const noexcept {
74  if (!config().problem_reporter) {
75  return false;
76  }
77  return stats().duplicate_nodes ||
78  stats().duplicate_segments ||
79  stats().intersections ||
80  stats().open_rings ||
81  stats().short_ways ||
82  stats().touching_rings ||
83  stats().ways_in_multiple_rings ||
84  stats().wrong_role;
85  }
86 
87  void add_tags_to_area(osmium::builder::AreaBuilder& builder, const osmium::Way& way) const {
88  builder.add_item(way.tags());
89  }
90 
91  void add_common_tags(osmium::builder::TagListBuilder& tl_builder, std::set<const osmium::Way*>& ways) const {
92  std::map<std::string, size_t> counter;
93  for (const osmium::Way* way : ways) {
94  for (const auto& tag : way->tags()) {
95  std::string kv{tag.key()};
96  kv.append(1, '\0');
97  kv.append(tag.value());
98  ++counter[kv];
99  }
100  }
101 
102  const size_t num_ways = ways.size();
103  for (const auto& t_c : counter) {
104  if (debug()) {
105  std::cerr << " tag " << t_c.first << " is used " << t_c.second << " times in " << num_ways << " ways\n";
106  }
107  if (t_c.second == num_ways) {
108  const size_t len = std::strlen(t_c.first.c_str());
109  tl_builder.add_tag(t_c.first.c_str(), t_c.first.c_str() + len + 1);
110  }
111  }
112  }
113 
115 
116  MPFilter() : osmium::tags::KeyFilter(true) {
117  add(false, "type");
118  add(false, "created_by");
119  add(false, "source");
120  add(false, "note");
121  add(false, "test:id");
122  add(false, "test:section");
123  }
124 
125  }; // struct MPFilter
126 
127  static const MPFilter& filter() noexcept {
128  static const MPFilter filter;
129  return filter;
130  }
131 
133  osmium::builder::TagListBuilder tl_builder{builder};
134  for (const osmium::Tag& tag : tags) {
135  if (std::strcmp(tag.key(), "type")) {
136  tl_builder.add_tag(tag.key(), tag.value());
137  }
138  }
139  }
140 
142  const auto count = std::count_if(relation.tags().cbegin(), relation.tags().cend(), std::cref(filter()));
143 
144  if (debug()) {
145  std::cerr << " found " << count << " tags on relation (without ignored ones)\n";
146  }
147 
148  if (count > 0) {
149  if (debug()) {
150  std::cerr << " use tags from relation\n";
151  }
152 
153  if (config().keep_type_tag) {
154  builder.add_item(relation.tags());
155  } else {
156  copy_tags_without_type(builder, relation.tags());
157  }
158  } else {
159  ++stats().no_tags_on_relation;
160  if (debug()) {
161  std::cerr << " use tags from outer ways\n";
162  }
163  std::set<const osmium::Way*> ways;
164  for (const auto& ring : rings()) {
165  if (ring.is_outer()) {
166  ring.get_ways(ways);
167  }
168  }
169  if (ways.size() == 1) {
170  if (debug()) {
171  std::cerr << " only one outer way\n";
172  }
173  builder.add_item((*ways.cbegin())->tags());
174  } else {
175  if (debug()) {
176  std::cerr << " multiple outer ways, get common tags\n";
177  }
178  osmium::builder::TagListBuilder tl_builder{builder};
179  add_common_tags(tl_builder, ways);
180  }
181  }
182  }
183 
184  bool create_area(osmium::memory::Buffer& out_buffer, const osmium::Way& way) {
185  osmium::builder::AreaBuilder builder{out_buffer};
186  builder.initialize_from_object(way);
187 
188  const bool area_okay = create_rings();
189  if (area_okay || config().create_empty_areas) {
190  add_tags_to_area(builder, way);
191  }
192  if (area_okay) {
193  add_rings_to_area(builder);
194  }
195 
196  if (report_ways()) {
197  config().problem_reporter->report_way(way);
198  }
199 
200  return area_okay || config().create_empty_areas;
201  }
202 
203  bool create_area(osmium::memory::Buffer& out_buffer, const osmium::Relation& relation, const std::vector<const osmium::Way*>& members) {
204  set_num_members(members.size());
205  osmium::builder::AreaBuilder builder{out_buffer};
206  builder.initialize_from_object(relation);
207 
208  const bool area_okay = create_rings();
209  if (area_okay || config().create_empty_areas) {
210  add_tags_to_area(builder, relation);
211  }
212  if (area_okay) {
213  add_rings_to_area(builder);
214  }
215 
216  if (report_ways()) {
217  for (const osmium::Way* way : members) {
218  config().problem_reporter->report_way(*way);
219  }
220  }
221 
222  return area_okay || config().create_empty_areas;
223  }
224 
225  public:
226 
228 
229  explicit Assembler(const config_type& config) :
230  detail::BasicAssembler(config) {
231  }
232 
233  ~Assembler() noexcept = default;
234 
242  bool operator()(const osmium::Way& way, osmium::memory::Buffer& out_buffer) {
243  if (!config().create_way_polygons) {
244  return true;
245  }
246 
247  if (way.tags().has_tag("area", "no")) {
248  return true;
249  }
250 
251  if (config().problem_reporter) {
252  config().problem_reporter->set_object(osmium::item_type::way, way.id());
253  config().problem_reporter->set_nodes(way.nodes().size());
254  }
255 
256  // Ignore (but count) ways without segments.
257  if (way.nodes().size() < 2) {
258  ++stats().short_ways;
259  return false;
260  }
261 
262  if (!way.ends_have_same_id()) {
263  ++stats().duplicate_nodes;
264  if (config().problem_reporter) {
265  config().problem_reporter->report_duplicate_node(way.nodes().front().ref(), way.nodes().back().ref(), way.nodes().front().location());
266  }
267  }
268 
269  ++stats().from_ways;
270  stats().invalid_locations = segment_list().extract_segments_from_way(config().problem_reporter,
271  stats().duplicate_nodes,
272  way);
273  if (!config().ignore_invalid_locations && stats().invalid_locations > 0) {
274  return false;
275  }
276 
277  if (config().debug_level > 0) {
278  std::cerr << "\nAssembling way " << way.id() << " containing " << segment_list().size() << " nodes\n";
279  }
280 
281  // Now create the Area object and add the attributes and tags
282  // from the way.
283  const bool okay = create_area(out_buffer, way);
284  if (okay) {
285  out_buffer.commit();
286  } else {
287  out_buffer.rollback();
288  }
289 
290  if (debug()) {
291  std::cerr << "Done: " << stats() << "\n";
292  }
293 
294  return okay;
295  }
296 
307  OSMIUM_DEPRECATED void operator()(const osmium::Relation& relation, const std::vector<size_t>& members, const osmium::memory::Buffer& in_buffer, osmium::memory::Buffer& out_buffer) {
308  std::vector<const osmium::Way*> ways;
309  for (size_t offset : members) {
310  const osmium::Way& way = in_buffer.get<const osmium::Way>(offset);
311  ways.push_back(&way);
312  }
313  operator()(relation, ways, out_buffer);
314  }
315 
323  bool operator()(const osmium::Relation& relation, const std::vector<const osmium::Way*>& members, osmium::memory::Buffer& out_buffer) {
324  assert(relation.members().size() >= members.size());
325 
326  if (config().problem_reporter) {
327  config().problem_reporter->set_object(osmium::item_type::relation, relation.id());
328  }
329 
330  if (relation.members().empty()) {
331  ++stats().no_way_in_mp_relation;
332  return false;
333  }
334 
335  ++stats().from_relations;
336  stats().invalid_locations = segment_list().extract_segments_from_ways(config().problem_reporter,
337  stats().duplicate_nodes,
338  stats().duplicate_ways,
339  relation,
340  members);
341  if (!config().ignore_invalid_locations && stats().invalid_locations > 0) {
342  return false;
343  }
344  stats().member_ways = members.size();
345 
346  if (stats().member_ways == 1) {
347  ++stats().single_way_in_mp_relation;
348  }
349 
350  if (config().debug_level > 0) {
351  std::cerr << "\nAssembling relation " << relation.id() << " containing " << members.size() << " way members with " << segment_list().size() << " nodes\n";
352  }
353 
354  const size_t area_offset = out_buffer.committed();
355 
356  // Now create the Area object and add the attributes and tags
357  // from the relation.
358  bool okay = create_area(out_buffer, relation, members);
359  if (okay) {
360  if ((config().create_new_style_polygons && stats().no_tags_on_relation == 0) ||
361  (config().create_old_style_polygons && stats().no_tags_on_relation != 0)) {
362  out_buffer.commit();
363  } else {
364  out_buffer.rollback();
365  }
366  } else {
367  out_buffer.rollback();
368  }
369 
370  const osmium::TagList& area_tags = out_buffer.get<osmium::Area>(area_offset).tags(); // tags of the area we just built
371 
372  // Find all closed ways that are inner rings and check their
373  // tags. If they are not the same as the tags of the area we
374  // just built, add them to a list and later build areas for
375  // them, too.
376  std::vector<const osmium::Way*> ways_that_should_be_areas;
377  if (stats().wrong_role == 0) {
378  detail::for_each_member(relation, members, [this, &ways_that_should_be_areas, &area_tags](const osmium::RelationMember& member, const osmium::Way& way) {
379  if (!std::strcmp(member.role(), "inner")) {
380  if (!way.nodes().empty() && way.is_closed() && way.tags().size() > 0) {
381  const auto d = std::count_if(way.tags().cbegin(), way.tags().cend(), std::cref(filter()));
382  if (d > 0) {
383  osmium::tags::KeyFilter::iterator way_fi_begin(std::cref(filter()), way.tags().cbegin(), way.tags().cend());
384  osmium::tags::KeyFilter::iterator way_fi_end(std::cref(filter()), way.tags().cend(), way.tags().cend());
385  osmium::tags::KeyFilter::iterator area_fi_begin(std::cref(filter()), area_tags.cbegin(), area_tags.cend());
386  osmium::tags::KeyFilter::iterator area_fi_end(std::cref(filter()), area_tags.cend(), area_tags.cend());
387 
388  if (!std::equal(way_fi_begin, way_fi_end, area_fi_begin) || d != std::distance(area_fi_begin, area_fi_end)) {
389  ways_that_should_be_areas.push_back(&way);
390  } else {
391  ++stats().inner_with_same_tags;
392  if (config().problem_reporter) {
393  config().problem_reporter->report_inner_with_same_tags(way);
394  }
395  }
396  }
397  }
398  }
399  });
400  }
401 
402  if (debug()) {
403  std::cerr << "Done: " << stats() << "\n";
404  }
405 
406  // Now build areas for all ways found in the last step.
407  for (const osmium::Way* way : ways_that_should_be_areas) {
408  Assembler assembler{config()};
409  if (!assembler(*way, out_buffer)) {
410  okay = false;
411  }
412  stats() += assembler.stats();
413  }
414 
415  return okay;
416  }
417 
418  }; // class Assembler
419 
420  } // namespace area
421 
422 } // namespace osmium
423 
424 #endif // OSMIUM_AREA_ASSEMBLER_HPP
WayNodeList & nodes()
Definition: way.hpp:89
Definition: tag.hpp:48
#define OSMIUM_DEPRECATED
Definition: compatibility.hpp:50
Definition: filter.hpp:94
bool operator()(const osmium::Way &way, osmium::memory::Buffer &out_buffer)
Definition: assembler.hpp:242
void add_item(const osmium::memory::Item &item)
Definition: builder.hpp:217
const TagList & tags() const
Get the list of tags for this object.
Definition: object.hpp:325
Definition: tag.hpp:107
RelationMemberList & members()
Definition: relation.hpp:185
static void copy_tags_without_type(osmium::builder::AreaBuilder &builder, const osmium::TagList &tags)
Definition: assembler.hpp:132
Definition: relation.hpp:168
bool report_ways() const noexcept
Definition: assembler.hpp:73
static const MPFilter & filter() noexcept
Definition: assembler.hpp:127
Definition: area.hpp:126
Definition: assembler_config.hpp:49
const_iterator cbegin() const noexcept
Definition: collection.hpp:164
Filter & add(bool result, const key_type &key, const value_type &value)
Definition: filter.hpp:136
Definition: entity_bits.hpp:72
void add_tags_to_area(osmium::builder::AreaBuilder &builder, const osmium::Relation &relation)
Definition: assembler.hpp:141
size_type size() const noexcept
Definition: collection.hpp:152
bool has_tag(const char *key, const char *value) const noexcept
Definition: tag.hpp:159
double distance(const osmium::geom::Coordinates &c1, const osmium::geom::Coordinates &c2)
Definition: haversine.hpp:66
bool is_closed() const
Definition: way.hpp:112
Definition: way.hpp:72
bool create_area(osmium::memory::Buffer &out_buffer, const osmium::Way &way)
Definition: assembler.hpp:184
Filter< std::string > KeyFilter
Definition: filter.hpp:179
constexpr osmium::object_id_type ref() const noexcept
Definition: node_ref.hpp:65
bool empty() const noexcept
Definition: node_ref_list.hpp:74
~Assembler() noexcept=default
Definition: relation.hpp:57
bool operator()(const osmium::Relation &relation, const std::vector< const osmium::Way *> &members, osmium::memory::Buffer &out_buffer)
Definition: assembler.hpp:323
Namespace for everything in the Osmium library.
Definition: assembler.hpp:63
Definition: attr.hpp:333
const_iterator cend() const noexcept
Definition: collection.hpp:168
bool empty() const noexcept
Definition: collection.hpp:143
Definition: assembler.hpp:114
Assembler(const config_type &config)
Definition: assembler.hpp:229
osmium::Location & location() noexcept
Definition: node_ref.hpp:79
bool operator()(const osmium::Tag &tag) const
Definition: filter.hpp:146
void add_common_tags(osmium::builder::TagListBuilder &tl_builder, std::set< const osmium::Way *> &ways) const
Definition: assembler.hpp:91
object_id_type id() const noexcept
Get ID of this object.
Definition: object.hpp:126
size_t committed() const noexcept
Definition: buffer.hpp:263
bool ends_have_same_id() const
Definition: way.hpp:116
Definition: buffer.hpp:97
const char * role() const noexcept
Definition: relation.hpp:138
bool create_area(osmium::memory::Buffer &out_buffer, const osmium::Relation &relation, const std::vector< const osmium::Way *> &members)
Definition: assembler.hpp:203
const NodeRef & front() const noexcept
Definition: node_ref_list.hpp:126
const NodeRef & back() const noexcept
Definition: node_ref_list.hpp:138
MPFilter()
Definition: assembler.hpp:116
size_t count() const noexcept
Definition: filter.hpp:160
Definition: assembler.hpp:71
void rollback()
Definition: buffer.hpp:374
T & get(const size_t offset) const
Definition: buffer.hpp:408
size_type size() const noexcept
Definition: node_ref_list.hpp:83
void add_tag(const char *key, const char *value)
Definition: osm_object_builder.hpp:95
boost::filter_iterator< filter_type, osmium::TagList::const_iterator > iterator
Definition: filter.hpp:129
OSMIUM_DEPRECATED void operator()(const osmium::Relation &relation, const std::vector< size_t > &members, const osmium::memory::Buffer &in_buffer, osmium::memory::Buffer &out_buffer)
Definition: assembler.hpp:307
Definition: osm_object_builder.hpp:71
size_t commit()
Definition: buffer.hpp:358
void add_tags_to_area(osmium::builder::AreaBuilder &builder, const osmium::Way &way) const
Definition: assembler.hpp:87
Definition: osm_object_builder.hpp:528