380 lines
16 KiB
C++
380 lines
16 KiB
C++
#ifndef OSMIUM_AREA_ASSEMBLER_LEGACY_HPP
|
|
#define OSMIUM_AREA_ASSEMBLER_LEGACY_HPP
|
|
|
|
/*
|
|
|
|
This file is part of Osmium (https://osmcode.org/libosmium).
|
|
|
|
Copyright 2013-2023 Jochen Topf <jochen@topf.org> and others (see README).
|
|
|
|
Boost Software License - Version 1.0 - August 17th, 2003
|
|
|
|
Permission is hereby granted, free of charge, to any person or organization
|
|
obtaining a copy of the software and accompanying documentation covered by
|
|
this license (the "Software") to use, reproduce, display, distribute,
|
|
execute, and transmit the Software, and to prepare derivative works of the
|
|
Software, and to permit third-parties to whom the Software is furnished to
|
|
do so, all subject to the following:
|
|
|
|
The copyright notices in the Software and this entire statement, including
|
|
the above license grant, this restriction and the following disclaimer,
|
|
must be included in all copies of the Software, in whole or in part, and
|
|
all derivative works of the Software, unless such copies or derivative
|
|
works are solely in the form of machine-executable object code generated by
|
|
a source language processor.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
|
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
|
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
|
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
DEALINGS IN THE SOFTWARE.
|
|
|
|
*/
|
|
|
|
#include <osmium/area/assembler_config.hpp>
|
|
#include <osmium/area/detail/basic_assembler_with_tags.hpp>
|
|
#include <osmium/area/detail/proto_ring.hpp>
|
|
#include <osmium/area/detail/segment_list.hpp>
|
|
#include <osmium/area/problem_reporter.hpp>
|
|
#include <osmium/area/stats.hpp>
|
|
#include <osmium/builder/osm_object_builder.hpp>
|
|
#include <osmium/memory/buffer.hpp>
|
|
#include <osmium/memory/collection.hpp>
|
|
#include <osmium/osm/area.hpp>
|
|
#include <osmium/osm/item_type.hpp>
|
|
#include <osmium/osm/node_ref.hpp>
|
|
#include <osmium/osm/relation.hpp>
|
|
#include <osmium/osm/tag.hpp>
|
|
#include <osmium/osm/way.hpp>
|
|
#include <osmium/tags/filter.hpp>
|
|
|
|
#include <algorithm>
|
|
#include <cassert>
|
|
#include <cstring>
|
|
#include <functional>
|
|
#include <iostream>
|
|
#include <iterator>
|
|
#include <map>
|
|
#include <set>
|
|
#include <string>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
namespace osmium {
|
|
|
|
namespace area {
|
|
|
|
/**
|
|
* Assembles area objects from closed ways or multipolygon relations
|
|
* and their members.
|
|
*/
|
|
class AssemblerLegacy : public detail::BasicAssemblerWithTags {
|
|
|
|
void add_common_tags(osmium::builder::TagListBuilder& tl_builder, std::set<const osmium::Way*>& ways) const {
|
|
std::map<std::string, std::size_t> counter;
|
|
for (const osmium::Way* way : ways) {
|
|
for (const auto& tag : way->tags()) {
|
|
std::string kv{tag.key()};
|
|
kv.append(1, '\0');
|
|
kv.append(tag.value());
|
|
++counter[kv];
|
|
}
|
|
}
|
|
|
|
const std::size_t num_ways = ways.size();
|
|
for (const auto& t_c : counter) {
|
|
if (debug()) {
|
|
std::cerr << " tag " << t_c.first << " is used " << t_c.second << " times in " << num_ways << " ways\n";
|
|
}
|
|
if (t_c.second == num_ways) {
|
|
const std::size_t len = std::strlen(t_c.first.c_str());
|
|
tl_builder.add_tag(t_c.first.c_str(), t_c.first.c_str() + len + 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
struct MPFilter : public osmium::tags::KeyFilter {
|
|
|
|
MPFilter() : osmium::tags::KeyFilter(true) {
|
|
add(false, "type");
|
|
add(false, "created_by");
|
|
add(false, "source");
|
|
add(false, "note");
|
|
add(false, "test:id");
|
|
add(false, "test:section");
|
|
}
|
|
|
|
}; // struct MPFilter
|
|
|
|
static const MPFilter& filter() noexcept {
|
|
static const MPFilter filter;
|
|
return filter;
|
|
}
|
|
|
|
void add_tags_to_area(osmium::builder::AreaBuilder& builder, const osmium::Relation& relation) {
|
|
const auto count = std::count_if(relation.tags().cbegin(), relation.tags().cend(), std::cref(filter()));
|
|
|
|
if (debug()) {
|
|
std::cerr << " found " << count << " tags on relation (without ignored ones)\n";
|
|
}
|
|
|
|
if (count > 0) {
|
|
if (debug()) {
|
|
std::cerr << " use tags from relation\n";
|
|
}
|
|
|
|
if (config().keep_type_tag) {
|
|
builder.add_item(relation.tags());
|
|
} else {
|
|
copy_tags_without_type(builder, relation.tags());
|
|
}
|
|
} else {
|
|
++stats().no_tags_on_relation;
|
|
if (debug()) {
|
|
std::cerr << " use tags from outer ways\n";
|
|
}
|
|
std::set<const osmium::Way*> ways;
|
|
for (const auto& ring : rings()) {
|
|
if (ring.is_outer()) {
|
|
ring.get_ways(ways);
|
|
}
|
|
}
|
|
if (ways.size() == 1) {
|
|
if (debug()) {
|
|
std::cerr << " only one outer way\n";
|
|
}
|
|
builder.add_item((*ways.cbegin())->tags());
|
|
} else {
|
|
if (debug()) {
|
|
std::cerr << " multiple outer ways, get common tags\n";
|
|
}
|
|
osmium::builder::TagListBuilder tl_builder{builder};
|
|
add_common_tags(tl_builder, ways);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool create_area(osmium::memory::Buffer& out_buffer, const osmium::Way& way) {
|
|
osmium::builder::AreaBuilder builder{out_buffer};
|
|
builder.initialize_from_object(way);
|
|
|
|
const bool area_okay = create_rings();
|
|
if (area_okay || config().create_empty_areas) {
|
|
builder.add_item(way.tags());
|
|
}
|
|
if (area_okay) {
|
|
add_rings_to_area(builder);
|
|
}
|
|
|
|
if (report_ways()) {
|
|
config().problem_reporter->report_way(way);
|
|
}
|
|
|
|
return area_okay || config().create_empty_areas;
|
|
}
|
|
|
|
bool create_area(osmium::memory::Buffer& out_buffer, const osmium::Relation& relation, const std::vector<const osmium::Way*>& members) {
|
|
set_num_members(members.size());
|
|
osmium::builder::AreaBuilder builder{out_buffer};
|
|
builder.initialize_from_object(relation);
|
|
|
|
const bool area_okay = create_rings();
|
|
if (area_okay || config().create_empty_areas) {
|
|
add_tags_to_area(builder, relation);
|
|
}
|
|
if (area_okay) {
|
|
add_rings_to_area(builder);
|
|
}
|
|
|
|
if (report_ways()) {
|
|
for (const osmium::Way* way : members) {
|
|
config().problem_reporter->report_way(*way);
|
|
}
|
|
}
|
|
|
|
return area_okay || config().create_empty_areas;
|
|
}
|
|
|
|
public:
|
|
|
|
explicit AssemblerLegacy(const config_type& config) :
|
|
detail::BasicAssemblerWithTags(config) {
|
|
}
|
|
|
|
/**
|
|
* Assemble an area from the given way.
|
|
* The resulting area is put into the out_buffer.
|
|
*
|
|
* @returns false if there was some kind of error building the
|
|
* area, true otherwise.
|
|
*/
|
|
bool operator()(const osmium::Way& way, osmium::memory::Buffer& out_buffer) {
|
|
if (!config().create_way_polygons) {
|
|
return true;
|
|
}
|
|
|
|
if (way.tags().has_tag("area", "no")) {
|
|
return true;
|
|
}
|
|
|
|
if (config().problem_reporter) {
|
|
config().problem_reporter->set_object(osmium::item_type::way, way.id());
|
|
config().problem_reporter->set_nodes(way.nodes().size());
|
|
}
|
|
|
|
// Ignore (but count) ways without segments.
|
|
if (way.nodes().size() < 2) {
|
|
++stats().short_ways;
|
|
return false;
|
|
}
|
|
|
|
if (!way.ends_have_same_id()) {
|
|
++stats().duplicate_nodes;
|
|
if (config().problem_reporter) {
|
|
config().problem_reporter->report_duplicate_node(way.nodes().front().ref(), way.nodes().back().ref(), way.nodes().front().location());
|
|
}
|
|
}
|
|
|
|
++stats().from_ways;
|
|
stats().invalid_locations = segment_list().extract_segments_from_way(config().problem_reporter,
|
|
stats().duplicate_nodes,
|
|
way);
|
|
if (!config().ignore_invalid_locations && stats().invalid_locations > 0) {
|
|
return false;
|
|
}
|
|
|
|
if (config().debug_level > 0) {
|
|
std::cerr << "\nAssembling way " << way.id() << " containing " << segment_list().size() << " nodes\n";
|
|
}
|
|
|
|
// Now create the Area object and add the attributes and tags
|
|
// from the way.
|
|
const bool okay = create_area(out_buffer, way);
|
|
if (okay) {
|
|
out_buffer.commit();
|
|
} else {
|
|
out_buffer.rollback();
|
|
}
|
|
|
|
if (debug()) {
|
|
std::cerr << "Done: " << stats() << "\n";
|
|
}
|
|
|
|
return okay;
|
|
}
|
|
|
|
/**
|
|
* Assemble an area from the given relation and its members.
|
|
* The resulting area is put into the out_buffer.
|
|
*
|
|
* @returns false if there was some kind of error building the
|
|
* area(s), true otherwise.
|
|
*/
|
|
bool operator()(const osmium::Relation& relation, const std::vector<const osmium::Way*>& members, osmium::memory::Buffer& out_buffer) {
|
|
assert(relation.members().size() >= members.size());
|
|
|
|
if (config().problem_reporter) {
|
|
config().problem_reporter->set_object(osmium::item_type::relation, relation.id());
|
|
}
|
|
|
|
if (relation.members().empty()) {
|
|
++stats().no_way_in_mp_relation;
|
|
return false;
|
|
}
|
|
|
|
++stats().from_relations;
|
|
stats().invalid_locations = segment_list().extract_segments_from_ways(config().problem_reporter,
|
|
stats().duplicate_nodes,
|
|
stats().duplicate_ways,
|
|
relation,
|
|
members);
|
|
if (!config().ignore_invalid_locations && stats().invalid_locations > 0) {
|
|
return false;
|
|
}
|
|
stats().member_ways = members.size();
|
|
|
|
if (stats().member_ways == 1) {
|
|
++stats().single_way_in_mp_relation;
|
|
}
|
|
|
|
if (config().debug_level > 0) {
|
|
std::cerr << "\nAssembling relation " << relation.id() << " containing " << members.size() << " way members with " << segment_list().size() << " nodes\n";
|
|
}
|
|
|
|
const std::size_t area_offset = out_buffer.committed();
|
|
|
|
// Now create the Area object and add the attributes and tags
|
|
// from the relation.
|
|
bool okay = create_area(out_buffer, relation, members);
|
|
if (okay) {
|
|
if ((config().create_new_style_polygons && stats().no_tags_on_relation == 0) ||
|
|
(config().create_old_style_polygons && stats().no_tags_on_relation != 0)) {
|
|
out_buffer.commit();
|
|
} else {
|
|
out_buffer.rollback();
|
|
}
|
|
} else {
|
|
out_buffer.rollback();
|
|
}
|
|
|
|
const osmium::TagList& area_tags = out_buffer.get<osmium::Area>(area_offset).tags(); // tags of the area we just built
|
|
|
|
// Find all closed ways that are inner rings and check their
|
|
// tags. If they are not the same as the tags of the area we
|
|
// just built, add them to a list and later build areas for
|
|
// them, too.
|
|
std::vector<const osmium::Way*> ways_that_should_be_areas;
|
|
if (stats().wrong_role == 0) {
|
|
detail::for_each_member(relation, members, [this, &ways_that_should_be_areas, &area_tags](const osmium::RelationMember& member, const osmium::Way& way) {
|
|
if (!std::strcmp(member.role(), "inner")) {
|
|
if (!way.nodes().empty() && way.is_closed() && !way.tags().empty()) {
|
|
const auto d = std::count_if(way.tags().cbegin(), way.tags().cend(), std::cref(filter()));
|
|
if (d > 0) {
|
|
const osmium::tags::KeyFilter::iterator way_fi_begin(std::cref(filter()), way.tags().cbegin(), way.tags().cend());
|
|
const osmium::tags::KeyFilter::iterator way_fi_end(std::cref(filter()), way.tags().cend(), way.tags().cend());
|
|
const osmium::tags::KeyFilter::iterator area_fi_begin(std::cref(filter()), area_tags.cbegin(), area_tags.cend());
|
|
const osmium::tags::KeyFilter::iterator area_fi_end(std::cref(filter()), area_tags.cend(), area_tags.cend());
|
|
#ifdef __cpp_lib_robust_nonmodifying_seq_ops
|
|
if (!std::equal(way_fi_begin, way_fi_end, area_fi_begin, area_fi_end)) {
|
|
#else
|
|
if (d != std::distance(area_fi_begin, area_fi_end) || !std::equal(way_fi_begin, way_fi_end, area_fi_begin)) {
|
|
#endif
|
|
ways_that_should_be_areas.push_back(&way);
|
|
} else {
|
|
++stats().inner_with_same_tags;
|
|
if (config().problem_reporter) {
|
|
config().problem_reporter->report_inner_with_same_tags(way);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
if (debug()) {
|
|
std::cerr << "Done: " << stats() << "\n";
|
|
}
|
|
|
|
// Now build areas for all ways found in the last step.
|
|
for (const osmium::Way* way : ways_that_should_be_areas) {
|
|
AssemblerLegacy assembler{config()};
|
|
if (!assembler(*way, out_buffer)) {
|
|
okay = false;
|
|
}
|
|
stats() += assembler.stats();
|
|
}
|
|
|
|
return okay;
|
|
}
|
|
|
|
}; // class AssemblerLegacy
|
|
|
|
} // namespace area
|
|
|
|
} // namespace osmium
|
|
|
|
#endif // OSMIUM_AREA_ASSEMBLER_LEGACY_HPP
|