/** * 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; }
/** * Checks if there are intersections between any coastline segments. * Returns the number of intersections and overlaps. */ unsigned int CoastlineRingCollection::check_for_intersections(OutputDatabase& output, int segments_fd) { unsigned int overlaps = 0; std::vector<osmium::UndirectedSegment> segments; if (debug) std::cerr << "Setting up segments...\n"; for (const auto& ring : m_list) { ring->add_segments_to_vector(segments); } if (debug) std::cerr << "Sorting...\n"; std::sort(segments.begin(), segments.end()); if (segments_fd >= 0) { if (debug) std::cerr << "Writing segments to file...\n"; ssize_t length = segments.size() * sizeof(osmium::UndirectedSegment); #ifndef _MSC_VER if (::write(segments_fd, segments.data(), length) != length) { #else if (_write(segments_fd, segments.data(), length) != length) { #endif throw std::runtime_error{"Write error"}; } } if (debug) std::cerr << "Finding intersections...\n"; std::vector<osmium::Location> intersections; for (auto it1 = segments.cbegin(); it1 != segments.cend()-1; ++it1) { const osmium::UndirectedSegment& s1 = *it1; for (auto it2 = it1+1; it2 != segments.cend(); ++it2) { const osmium::UndirectedSegment& s2 = *it2; if (s1 == s2) { std::unique_ptr<OGRLineString> line = create_ogr_linestring(s1); output.add_error_line(std::move(line), "overlap"); overlaps++; } else { if (outside_x_range(s2, s1)) { break; } if (y_range_overlap(s1, s2)) { osmium::Location i = intersection(s1, s2); if (i) { intersections.push_back(i); } } } } } for (const auto& intersection : intersections) { std::unique_ptr<OGRPoint> point{new OGRPoint(intersection.lon(), intersection.lat())}; output.add_error_point(std::move(point), "intersection"); } return intersections.size() + overlaps; } bool CoastlineRingCollection::close_antarctica_ring(int epsg) { for (const auto& ring : m_list) { const osmium::Location fpos = ring->first_position(); const osmium::Location lpos = ring->last_position(); if (fpos.lon() > 179.99 && lpos.lon() < -179.99 && fpos.lat() < -77.0 && fpos.lat() > -78.0 && lpos.lat() < -77.0 && lpos.lat() > -78.0) { m_end_nodes.erase(ring->last_node_id()); m_start_nodes.erase(ring->first_node_id()); ring->close_antarctica_ring(epsg); return true; } } return false; } void CoastlineRingCollection::close_rings(OutputDatabase& output, bool debug, double max_distance) { std::vector<Connection> connections; // Create vector with all possible combinations of connections between rings. for (idmap_type::iterator eit = m_end_nodes.begin(); eit != m_end_nodes.end(); ++eit) { for (idmap_type::iterator sit = m_start_nodes.begin(); sit != m_start_nodes.end(); ++sit) { const double distance = (*sit->second)->distance_to_start_position((*eit->second)->last_position()); if (distance < max_distance) { connections.emplace_back(distance, eit->first, sit->first); } } } // Sort vector by distance, shortest at end. std::sort(connections.begin(), connections.end(), Connection::sort_by_distance); // Go through vector starting with the shortest connections and close rings // using the connections in turn. while (!connections.empty()) { Connection conn = connections.back(); connections.pop_back(); // Invalidate all other connections using one of the same end points. connections.erase(remove_if(connections.begin(), connections.end(), conn), connections.end()); idmap_type::iterator eit = m_end_nodes.find(conn.start_id); idmap_type::iterator sit = m_start_nodes.find(conn.end_id); if (eit != m_end_nodes.end() && sit != m_start_nodes.end()) { if (debug) { std::cerr << "Closing ring between node " << conn.end_id << " and node " << conn.start_id << "\n"; } m_fixed_rings++; CoastlineRing* e = eit->second->get(); CoastlineRing* s = sit->second->get(); output.add_error_point(e->ogr_last_point(), "fixed_end_point", e->last_node_id()); output.add_error_point(s->ogr_first_point(), "fixed_end_point", s->first_node_id()); if (e->last_position() != s->first_position()) { std::unique_ptr<OGRLineString> linestring{new OGRLineString}; linestring->addPoint(e->last_position().lon(), e->last_position().lat()); linestring->addPoint(s->first_position().lon(), s->first_position().lat()); output.add_error_line(std::move(linestring), "added_line"); } if (e == s) { // connect to itself by closing ring e->close_ring(); m_end_nodes.erase(eit); m_start_nodes.erase(sit); } else { // connect to other ring e->join_over_gap(*s); m_list.erase(sit->second); if (e->first_position() == e->last_position()) { output.add_error_point(e->ogr_first_point(), "double_node", e->first_node_id()); m_start_nodes.erase(e->first_node_id()); m_end_nodes.erase(eit); m_start_nodes.erase(sit); m_end_nodes.erase(e->last_node_id()); e->fake_close(); } else { m_end_nodes[e->last_node_id()] = eit->second; m_end_nodes.erase(eit); m_start_nodes.erase(sit); } } } } }