mirror of https://github.com/CGAL/cgal
434 lines
11 KiB
C++
434 lines
11 KiB
C++
|
|
#include "Kml_reader.h"
|
|
|
|
#include <algorithm>
|
|
#include <iostream>
|
|
#include <unordered_map>
|
|
|
|
#include <qdebug.h>
|
|
#include <qfile.h>
|
|
#include <qxmlstream.h>
|
|
|
|
|
|
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<Kml::Node> 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<int> 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<int, int> 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<int> 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<Node> nodes;
|
|
for (auto& pm : placemarks)
|
|
{
|
|
for (auto& polygon : pm.polygons)
|
|
{
|
|
polygon_count++;
|
|
|
|
std::vector<LinearRing*> 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<Node> nodes;
|
|
for (auto& pm : placemarks)
|
|
{
|
|
for (auto& polygon : pm.polygons)
|
|
{
|
|
polygon_count++;
|
|
|
|
std::vector<LinearRing*> 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<int> 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<double>::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::LinearRing*> Kml::Polygon::get_all_boundaries()
|
|
{
|
|
std::vector<LinearRing*> 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;
|
|
}
|
|
|