#include "Kml_reader.h" #include #include #include #include #include #include double Kml::Node::distance_to(const Node& r) const { const auto dx = lon - r.lon; const auto dy = lat - r.lat; return sqrt(dx * dx + dy * dy); } bool Kml::Node::operator == (const Node& r) const { return (lon == r.lon) && (lat == r.lat); } Kml::Vec3d Kml::Node::get_coords_3d(const double r) const { const long double phi = qDegreesToRadians(lat); const long double theta = qDegreesToRadians(lon); const auto z = r * std::sin(phi); const auto rxy = r * std::cos(phi); const auto x = rxy * std::cos(theta); const auto y = rxy * std::sin(theta); return Vec3d{ (double)x, (double)y, (double)z }; } QVector3D Kml::Node::get_coords_3f(const double r) const { const auto v = get_coords_3d(r); return QVector3D(v.x, v.y, v.z); } std::ostream& operator << (std::ostream& os, const Kml::Node& n) { os << n.lon << ", " << n.lat; return os; } Kml::Placemarks Kml::read(const std::string& file_name) { LinearRing lring; Polygon polygon; Placemark placemark; Placemarks placemarks; QFile file(file_name.c_str()); if (file.open(QIODevice::ReadOnly)) { QXmlStreamReader xmlReader; xmlReader.setDevice(&file); xmlReader.readNext(); // Reading from the file while (!xmlReader.isEndDocument()) { QString name = xmlReader.name().toString(); if (xmlReader.isStartElement()) { if (name == "Placemark") { placemark = Placemark{}; } else if (name == "Polygon") { polygon = Polygon{}; } else if (name == "LinearRing") { lring = LinearRing{}; } else if (name == "coordinates") { xmlReader.readNext(); auto str = xmlReader.text().toString(); auto node_strs = str.split(" "); for (const auto& node_str : node_strs) { if (node_str.isEmpty()) continue; auto coord_strs = node_str.split(","); const auto lon = coord_strs[0].toDouble(); const auto lat = coord_strs[1].toDouble(); lring.nodes.push_back(Node{ lon, lat }); } } else if (name == "SimpleData") { auto attributes = xmlReader.attributes(); auto attr_name = attributes[0].name().toString(); auto attr_value = attributes[0].value().toString(); if ((attr_name == "name") && (attr_value == "ADMIN")) { xmlReader.readNext(); placemark.name = xmlReader.text().toString().toStdString();; } } } else if (xmlReader.isEndElement()) { if (name == "Placemark") { placemarks.push_back(std::move(placemark)); } else if (name == "Polygon") { placemark.polygons.push_back(std::move(polygon)); } else if (name == "outerBoundaryIs") { polygon.outer_boundary = std::move(lring); } else if (name == "innerBoundaryIs") { polygon.inner_boundaries.push_back(std::move(lring)); } else if (name == "LinearRing") { // LinearRing is moved to the correct locations via other tags above assert(*lring.nodes.begin() == *(--lring.nodes.end())); } else if (name == "coordinates") { // no need to do anything here: the coordinates are read above! } } xmlReader.readNext(); } if (xmlReader.hasError()) { std::cout << "XML error: " << xmlReader.errorString().data() << std::endl; } } return placemarks; } Kml::Nodes Kml::get_duplicates(const Placemarks& placemarks) { // collect all nodes into a single vector int polygon_count = 0; std::vector nodes; for (const auto& pm : placemarks) { for (const auto& polygon : pm.polygons) { polygon_count++; Kml::LinearRings linear_rings; linear_rings.push_back(polygon.outer_boundary); for (const auto& inner_boundary : polygon.inner_boundaries) linear_rings.push_back(inner_boundary); for(const auto& lring : linear_rings) { for (const auto& node : lring.nodes) nodes.push_back(node); } } } qDebug() << "polygon count = " << polygon_count; int count = nodes.size(); std::vector num_duplicates(count, 0); qDebug() << "node count (with duplicates) = " << count; int dup_count = 0; // this keeps track of how many nodes there are with certain dup-count std::unordered_map dup_count_map; Nodes duplicate_nodes; for (int i = 0; i < count; ++i) { // if the current node has been detected as duplicate skip it if (num_duplicates[i] > 0) continue; const auto& curr_node = nodes[i]; std::vector curr_dup; // current set of duplicates for (int j = i + 1; j < count; ++j) { if (curr_node == nodes[j]) { curr_dup.push_back(j); } } // if duplicates found if (!curr_dup.empty()) { dup_count++; int num_dup = curr_dup.size() + 1; // +1 for the i'th node num_duplicates[i] = num_dup; for (const auto di : curr_dup) num_duplicates[di] = num_dup; duplicate_nodes.push_back(curr_node); dup_count_map[num_dup]++; } } qDebug() << "dup count = " << dup_count; for (const auto& p : dup_count_map) { const auto dup_count = p.first; const auto num_nodes_with_this_dup_count = p.second; qDebug() << dup_count << ": " << num_nodes_with_this_dup_count; } return duplicate_nodes; } Kml::Nodes Kml::generate_ids(Placemarks& placemarks) { // collect all nodes into a single vector int polygon_count = 0; std::vector nodes; for (auto& pm : placemarks) { for (auto& polygon : pm.polygons) { polygon_count++; std::vector linear_rings; linear_rings.push_back(&polygon.outer_boundary); for (auto& inner_boundary : polygon.inner_boundaries) linear_rings.push_back(&inner_boundary); for (auto* lring : linear_rings) { for (const auto& node : lring->nodes) { // check if the node is in the nodes auto it = std::find(nodes.begin(), nodes.end(), node); if (nodes.end() == it) { // insert new node nodes.push_back(node); const int node_id = nodes.size() - 1; lring->ids.push_back(node_id); } else { // get the existing node const int node_id = std::distance(nodes.begin(), it); lring->ids.push_back(node_id); assert(nodes[node_id] == node); } } assert(lring->nodes.size() == lring->ids.size()); for (int i = 0; i < lring->nodes.size(); ++i) { assert(lring->nodes[i] == nodes[lring->ids[i]]); } } } } return nodes; } Kml::Nodes Kml::generate_ids_approx(Placemarks& placemarks, const double eps) { // collect all nodes into a single vector int polygon_count = 0; std::vector nodes; for (auto& pm : placemarks) { for (auto& polygon : pm.polygons) { polygon_count++; std::vector linear_rings; linear_rings.push_back(&polygon.outer_boundary); for (auto& inner_boundary : polygon.inner_boundaries) linear_rings.push_back(&inner_boundary); for (auto* lring : linear_rings) { lring->ids.clear(); for (const auto& node : lring->nodes) { // check if there is a node sufficiently close to the current one int node_index = -1; for (int i = 0; i < nodes.size(); ++i) { const auto dist = node.distance_to(nodes[i]); if (dist < eps) { node_index = i; break; } } if (node_index < 0) { // insert new node nodes.push_back(node); const int node_id = nodes.size() - 1; lring->ids.push_back(node_id); } else { // get the existing node const int node_id = node_index; lring->ids.push_back(node_id); } auto it = std::unique(lring->ids.begin(), lring->ids.end()); std::vector new_ids(lring->ids.begin(), it); if (new_ids.size() < lring->ids.size()) std::cout << "** REDUCED!\n"; lring->ids = std::move(new_ids); } } } } // find the pair of closest nodes double min_dist = std::numeric_limits::max(); int ni1, ni2; int num_nodes = nodes.size(); for (int i = 0; i < num_nodes - 1; ++i) { for (int j = i + 1; j < num_nodes; ++j) { const auto dist = nodes[i].distance_to(nodes[j]); if (min_dist > dist) { min_dist = dist; ni1 = i; ni2 = j; } } } std::cout << "min dist = " << min_dist << std::endl; std::cout << "node 1 = " << nodes[ni1] << std::endl; std::cout << "node 2 = " << nodes[ni2] << std::endl; std::cout << "node 1 = " << nodes[ni1].get_coords_3d() << std::endl; std::cout << "node 2 = " << nodes[ni2].get_coords_3d() << std::endl; return nodes; } Kml::Nodes Kml::Polygon::get_all_nodes() const { Nodes all_nodes; auto source_first = outer_boundary.nodes.begin(); auto source_last = outer_boundary.nodes.end(); all_nodes.insert(all_nodes.begin(), source_first, source_last); for (const auto& inner_boundary : inner_boundaries) { auto source_first = inner_boundary.nodes.begin(); auto source_last = inner_boundary.nodes.end(); all_nodes.insert(all_nodes.begin(), source_first, source_last); } return all_nodes; } std::vector Kml::Polygon::get_all_boundaries() { std::vector linear_rings; linear_rings.push_back(&outer_boundary); for (auto& inner_boundary : inner_boundaries) linear_rings.push_back(&inner_boundary); return linear_rings; } Kml::Nodes Kml::Placemark::get_all_nodes() const { Nodes all_nodes; for (const auto& polygon : polygons) { auto polygon_nodes = polygon.get_all_nodes(); auto first = std::make_move_iterator(polygon_nodes.begin()); auto last = std::make_move_iterator(polygon_nodes.end()); all_nodes.insert(all_nodes.end(), first, last); } return all_nodes; } Kml::Arcs Kml::LinearRing::get_arcs() const { Arcs arcs; const int num_nodes = nodes.size(); for (int i = 0; i < num_nodes - 1; ++i) { const auto from = nodes[i]; const auto to = nodes[i + 1]; arcs.push_back(Arc{ from, to }); } return arcs; } void Kml::LinearRing::get_arcs(Arcs& arcs) const { auto a = get_arcs(); arcs.insert(arcs.end(), a.begin(), a.end()); } Kml::Arcs Kml::Placemark::get_all_arcs() const { Arcs all_arcs; for (const auto& polygon : polygons) { polygon.outer_boundary.get_arcs(all_arcs); for (const auto& inner_boundary : polygon.inner_boundaries) inner_boundary.get_arcs(all_arcs); } return all_arcs; }