/** * Find intersection between segments. * * @param problem_reporter Any intersections found are reported to this object. * @returns true if there are intersections. */ bool find_intersections(osmium::area::ProblemReporter* problem_reporter) const { if (m_segments.empty()) { return false; } bool found_intersections = false; for (auto it1 = m_segments.begin(); it1 != m_segments.end()-1; ++it1) { const NodeRefSegment& s1 = *it1; for (auto it2 = it1+1; it2 != m_segments.end(); ++it2) { const NodeRefSegment& s2 = *it2; assert(s1 != s2); // erase_duplicate_segments() should have made sure of that if (outside_x_range(s2, s1)) { break; } if (y_range_overlap(s1, s2)) { osmium::Location intersection = calculate_intersection(s1, s2); if (intersection) { found_intersections = true; if (m_debug) { std::cerr << " segments " << s1 << " and " << s2 << " intersecting at " << intersection << "\n"; } if (problem_reporter) { problem_reporter->report_intersection(s1.way()->id(), s1.first().location(), s1.second().location(), s2.way()->id(), s2.first().location(), s2.second().location(), intersection); } } } } } return found_intersections; }
/** * Find duplicate segments (ie same start and end point) in the * list and remove them. This will always remove pairs of the * same segment. So if there are three, for instance, two will * be removed and one will be left. */ uint32_t erase_duplicate_segments(osmium::area::ProblemReporter* problem_reporter) { uint32_t duplicate_segments = 0; while (true) { auto it = std::adjacent_find(m_segments.begin(), m_segments.end()); if (it == m_segments.end()) { break; } if (m_debug) { std::cerr << " erase duplicate segment: " << *it << "\n"; } // Only count and report duplicate segments if they // belong to the same way or if they don't both have // the role "inner". Those cases are definitely wrong. // If the duplicate segments belong to different // "inner" ways, they could be touching inner rings // which are perfectly okay. Note that for this check // the role has to be correct in the member data. if (it->way() == std::next(it)->way() || !it->role_inner() || !std::next(it)->role_inner()) { ++duplicate_segments; if (problem_reporter) { problem_reporter->report_duplicate_segment(it->first(), it->second()); } } m_segments.erase(it, it+2); } return duplicate_segments; }
/** * Find duplicate segments (ie same start and end point) in the * list and remove them. This will always remove pairs of the same * segment. So if there are three, for instance, two will be * removed and one will be left. */ void erase_duplicate_segments() { while (true) { auto it = std::adjacent_find(m_segments.begin(), m_segments.end()); if (it == m_segments.end()) { return; } if (m_debug) { std::cerr << " erase duplicate segment: " << *it << "\n"; } m_segments.erase(it, it+2); } }
uint32_t extract_segments_from_way_impl(osmium::area::ProblemReporter* problem_reporter, uint64_t& duplicate_nodes, const osmium::Way& way, role_type role) { uint32_t invalid_locations = 0; osmium::NodeRef previous_nr; for (const osmium::NodeRef& nr : way.nodes()) { if (!nr.location().valid()) { ++invalid_locations; if (problem_reporter) { problem_reporter->report_invalid_location(way.id(), nr.ref()); } continue; } if (previous_nr.location()) { if (previous_nr.location() != nr.location()) { m_segments.emplace_back(previous_nr, nr, role, &way); } else { ++duplicate_nodes; if (problem_reporter) { problem_reporter->report_duplicate_node(previous_nr.ref(), nr.ref(), nr.location()); } } } previous_nr = nr; } return invalid_locations; }
/** * Extract segments from given way and add them to the list. * * Segments connecting two nodes with the same location (ie * same node or different nodes with same location) are * removed after reporting the duplicate node. */ uint32_t extract_segments_from_way(osmium::area::ProblemReporter* problem_reporter, const osmium::Way& way) { if (way.nodes().empty()) { return 0; } m_segments.reserve(way.nodes().size() - 1); return extract_segments_from_way_impl(problem_reporter, way, role_type::outer); }
/** * Extract segments from given way and add them to the list. * * Segments connecting two nodes with the same location (ie same * node or different node with same location) are removed. * * XXX should two nodes with same location be reported? */ void extract_segments_from_way(const osmium::Way& way, const char* role) { osmium::NodeRef last_nr; for (const osmium::NodeRef& nr : way.nodes()) { if (last_nr.location() && last_nr.location() != nr.location()) { m_segments.emplace_back(last_nr, nr, role, &way); } last_nr = nr; } }
/** * Extract all segments from all ways that make up this * multipolygon relation and add them to the list. */ uint32_t extract_segments_from_ways(osmium::area::ProblemReporter* problem_reporter, const osmium::Relation& relation, const std::vector<const osmium::Way*>& members) { assert(relation.members().size() >= members.size()); const size_t num_segments = get_num_segments(members); if (problem_reporter) { problem_reporter->set_nodes(num_segments); } m_segments.reserve(num_segments); uint32_t duplicate_nodes = 0; for_each_member(relation, members, [this, &problem_reporter, &duplicate_nodes](const osmium::RelationMember& member, const osmium::Way& way) { duplicate_nodes += extract_segments_from_way_impl(problem_reporter, way, parse_role(member.role())); }); return duplicate_nodes; }
/** * Extract all segments from all ways that make up this * multipolygon relation and add them to the list. */ uint32_t extract_segments_from_ways(osmium::area::ProblemReporter* problem_reporter, uint64_t& duplicate_nodes, const osmium::Relation& relation, const std::vector<const osmium::Way*>& members) { assert(relation.members().size() >= members.size()); const size_t num_segments = get_num_segments(members); if (problem_reporter) { problem_reporter->set_nodes(num_segments); } m_segments.reserve(num_segments); uint32_t invalid_locations = 0; for_each_member(relation, members, [&](const osmium::RelationMember& member, const osmium::Way& way) { const auto role = parse_role(member.role()); invalid_locations += extract_segments_from_way_impl(problem_reporter, duplicate_nodes, way, role); }); return invalid_locations; }
/// Sort the list of segments. void sort() { std::sort(m_segments.begin(), m_segments.end()); }
const_iterator end() const noexcept { return m_segments.end(); }
const_iterator begin() const noexcept { return m_segments.begin(); }
iterator end() noexcept { return m_segments.end(); }
iterator begin() noexcept { return m_segments.begin(); }
NodeRefSegment& back() { return m_segments.back(); }
/// Is the segment list empty? bool empty() const noexcept { return m_segments.empty(); }
/// The number of segments in the list. size_t size() const noexcept { return m_segments.size(); }
/// The number of segments in the list. size_t size() const { return m_segments.size(); }
bool empty() const { return m_segments.empty(); }
NodeRefSegment& operator[](size_t n) noexcept { assert(n < m_segments.size()); return m_segments[n]; }
NodeRefSegment& front() { return m_segments.front(); }
/// Clear the list of segments. All segments are removed. void clear() { m_segments.clear(); }