7#include "marblegeometryassembler_p.h"
8#include "reassembly-logging.h"
14enum { NodeMatchDistance = 2 };
19 return std::abs((int32_t)lhs.latitude - (int32_t)rhs.latitude) <= NodeMatchDistance && std::abs((int32_t)lhs.longitude - (int32_t)rhs.longitude) <= NodeMatchDistance;
22OSM::Id MarbleGeometryAssembler::s_nextInternalId = -(1ll << 32);
24MarbleGeometryAssembler::MarbleGeometryAssembler() =
default;
25MarbleGeometryAssembler::~MarbleGeometryAssembler() =
default;
27void MarbleGeometryAssembler::setDataSet(
OSM::DataSet* dataSet)
32 m_typeKey = m_dataSet->makeTagKey(
"type");
42 std::vector<OSM::Way> prevPendingWays;
43 std::swap(m_pendingWays, prevPendingWays);
45 mergeNodes(mergeBuffer);
46 deduplicateWays(mergeBuffer->ways);
47 remapWayNodes(mergeBuffer->ways);
48 mergeWays(mergeBuffer->ways);
49 mergeWays(prevPendingWays);
50 mergeRelations(mergeBuffer);
55void MarbleGeometryAssembler::finalize()
57 m_dataSet->ways.reserve(m_dataSet->ways.size() + m_pendingWays.size());
58 for (
auto &way : m_pendingWays) {
59 if (!std::binary_search(m_dataSet->ways.begin(), m_dataSet->ways.end(), way)) {
60 m_dataSet->ways.push_back(std::move(way));
63 std::sort(m_dataSet->ways.begin(), m_dataSet->ways.end());
69 if (m_dataSet->nodes.empty()) {
70 m_dataSet->nodes = std::move(mergeBuffer->nodes);
71 std::sort(m_dataSet->nodes.begin(), m_dataSet->nodes.end());
80 for (
auto &node : mergeBuffer->nodes) {
81 const auto it = std::lower_bound(m_dataSet->nodes.begin(), m_dataSet->nodes.end(), node);
82 if (it != m_dataSet->nodes.end() && (*it).id == node.id) {
84 node.id = s_nextInternalId++;
85 m_nodeIdMap[(*it).id] = node.id;
93 m_dataSet->nodes.reserve(m_dataSet->nodes.size() + mergeBuffer->nodes.size());
94 for (
auto &node : mergeBuffer->nodes) {
96 m_dataSet->nodes.push_back(std::move(node));
99 std::sort(m_dataSet->nodes.begin(), m_dataSet->nodes.end());
102void MarbleGeometryAssembler::mergeWays(std::vector<OSM::Way> &ways)
108 for (
auto &way : ways) {
109 if (way.id > 0 || way.nodes.empty()) {
110 m_dataSet->addWay(std::move(way));
114 const OSM::Id mxoid = takeMxOid(way);
116 m_dataSet->addWay(std::move(way));
120 const auto syntheticId = way.id;
123 const auto it = std::lower_bound(m_dataSet->ways.begin(), m_dataSet->ways.end(), way);
124 if (it != m_dataSet->ways.end() && (*it).id == way.id) {
127 if (way.nodes.empty()) {
129 m_wayIdMap[syntheticId] = mxoid;
132 way.id = syntheticId;
134 m_pendingWays.push_back(std::move(way));
138 m_wayIdMap[syntheticId] = mxoid;
139 m_dataSet->ways.insert(it, std::move(way));
146 if (lhs.nodes.size() != rhs.nodes.size()) {
149 for (std::size_t i = 0; i < lhs.nodes.size(); ++i) {
150 if (lhs.nodes[i] != rhs.nodes[i] && (lhs.nodes[i] > 0 || rhs.nodes[i] > 0)) {
157void MarbleGeometryAssembler::deduplicateWays(std::vector<OSM::Way>& ways)
159 for (
auto it = ways.begin(); it != ways.end();) {
170 const auto duplIt = m_duplicateWays.find(mxoid);
171 if (duplIt != m_duplicateWays.end()) {
173 for (
auto it2 = (*duplIt).second.begin(); it2 != (*duplIt).second.end(); ++it2) {
174 if (isDuplicateWay(*it, ways[*it2])) {
175 m_wayIdMap[(*it).id] = mxoid;
176 qCDebug(ReassemblyLog) <<
"removing duplicate way:" << (*it).id << (*it).url();
183 m_duplicateWays[mxoid].push_back(std::distance(ways.begin(), it));
187 m_duplicateWays[mxoid] = {(std::size_t)std::distance(ways.begin(), it)};
191 m_duplicateWays.clear();
194void MarbleGeometryAssembler::remapWayNodes(std::vector<OSM::Way> &ways)
const
196 if (m_nodeIdMap.empty()) {
200 for (
auto &way : ways) {
201 for (
auto &
id : way.nodes) {
205 const auto it = m_nodeIdMap.find(
id);
206 if (it != m_nodeIdMap.end()) {
213void MarbleGeometryAssembler::mergeWay(
OSM::Way &way,
OSM::Way &otherWay)
const
227 if (!way.isClosed() && !otherWay.isClosed()) {
228 mergeLine(way, otherWay);
229 }
else if (way.isClosed() && !otherWay.isClosed()) {
231 }
else if (!way.isClosed() && otherWay.isClosed()) {
232 std::swap(way, otherWay);
235 way.nodes = mergeArea(way, otherWay);
239void MarbleGeometryAssembler::mergeLine(
OSM::Way &way,
OSM::Way &otherWay)
const
241 const auto begin1 = m_dataSet->node(way.nodes.front());
242 const auto end1 = m_dataSet->node(way.nodes.back());
243 const auto begin2 = m_dataSet->node(otherWay.nodes.front());
244 const auto end2 = m_dataSet->node(otherWay.nodes.back());
245 if (!begin1 || !end1 || !begin2 || !end2) {
246 qDebug() <<
"failed to find way nodes!?" << begin1 << end1 << begin2 << end2;;
255 way.nodes.reserve(way.nodes.size() + otherWay.nodes.size() - 2);
256 if (end1->id < 0 && begin2->id < 0 && fuzzyEquals(end1->coordinate, begin2->coordinate)) {
257 way.nodes.pop_back();
258 std::copy(std::next(otherWay.nodes.begin()), otherWay.nodes.end(), std::back_inserter(way.nodes));
259 }
else if (end1->id < 0 && end2->id < 0 && fuzzyEquals(end1->coordinate, end2->coordinate)) {
260 way.nodes.pop_back();
261 std::copy(std::next(otherWay.nodes.rbegin()), otherWay.nodes.rend(), std::back_inserter(way.nodes));
262 }
else if (begin1->id < 0 && end2->id < 0 && fuzzyEquals(begin1->coordinate, end2->coordinate)) {
263 way.nodes.erase(way.nodes.begin());
264 way.nodes.insert(way.nodes.begin(), otherWay.nodes.begin(), std::prev(otherWay.nodes.end()));
265 }
else if (begin1->id < 0 && begin2->id < 0 && fuzzyEquals(begin1->coordinate, begin2->coordinate)) {
266 way.nodes.erase(way.nodes.begin());
267 way.nodes.insert(way.nodes.begin(), otherWay.nodes.rbegin(), std::prev(otherWay.nodes.rend()));
273 otherWay.nodes.clear();
276std::vector<OSM::Id> MarbleGeometryAssembler::mergeArea(
OSM::Way &way,
OSM::Way &otherWay)
const
279 if (way.nodes.size() < 3 || otherWay.nodes.size() < 3 || !way.isClosed() || !otherWay.isClosed()) {
280 qCWarning(ReassemblyLog()) <<
"got invalid polygons!" << way.url() << way.nodes.size() << way.isClosed() << otherWay.url() << otherWay.nodes.size() << otherWay.isClosed();
281 return way.nodes.empty() ? std::move(way.nodes) : std::
move(otherWay.nodes);
285 way.nodes.pop_back();
286 otherWay.nodes.pop_back();
288 std::vector<OSM::Id> nodes;
289 mergeAreaSection(nodes, way.nodes, way.nodes.begin(), otherWay.nodes);
292 if (!nodes.empty()) {
293 nodes.push_back(nodes.front());
300 if (!otherWay.nodes.empty()) {
301 otherWay.nodes.push_back(otherWay.nodes.front());
306bool MarbleGeometryAssembler::mergeAreaSection(std::vector<OSM::Id> &assembledPath, std::vector<OSM::Id> &path,
const std::vector<OSM::Id>::iterator &pathBegin, std::vector<OSM::Id> &otherPath)
const
308 for (
auto nodeIt = pathBegin; nodeIt !=
path.
end(); ++nodeIt) {
309 if ((*nodeIt) >= 0) {
312 const auto node = m_dataSet->node(*nodeIt);
314 qCDebug(ReassemblyLog) <<
"could not find node" << (*nodeIt);
320 for (
auto otherNodeIt = otherPath.begin(); otherNodeIt != otherPath.end(); ++otherNodeIt) {
321 if ((*otherNodeIt) >= 0) {
325 const auto otherNode = m_dataSet->node(*otherNodeIt);
327 qCDebug(ReassemblyLog) <<
"could not find node" << (*otherNodeIt);
331 if (!fuzzyEquals(node->coordinate, otherNode->coordinate)) {
336 std::copy(pathBegin, nodeIt, std::back_inserter(assembledPath));
337 nodeIt =
path.
erase(pathBegin, ++nodeIt);
341 otherNodeIt = otherPath.erase(otherNodeIt);
342 if (otherNodeIt == otherPath.end()) {
343 otherNodeIt = otherPath.begin();
347 if (mergeAreaSection(assembledPath, otherPath, otherNodeIt, path)) {
349 std::copy(nodeIt,
path.
end(), std::back_inserter(assembledPath));
350 std::copy(
path.
begin(), nodeIt, std::back_inserter(assembledPath));
359 std::copy(pathBegin,
path.
end(), std::back_inserter(assembledPath));
364 return mergeAreaSection(assembledPath, path,
path.
begin(), otherPath);
377 for (
auto &rel : mergeBuffer->relations) {
378 const OSM::Id mxoid = takeMxOid(rel);
380 m_dataSet->addRelation(std::move(rel));
384 m_relIdMap[rel.id] = mxoid;
387 for (
auto &member : rel.members) {
388 if (member.id >= 0) {
392 if (member.type() == OSM::Type::Node) {
393 const auto it = m_nodeIdMap.find(member.id);
394 if (it != m_nodeIdMap.end()) {
395 member.id = (*it).second;
397 }
else if (member.type() == OSM::Type::Way) {
398 const auto it = m_wayIdMap.find(member.id);
399 if (it != m_wayIdMap.end()) {
400 member.id = (*it).second;
402 }
else if (member.type() == OSM::Type::Relation) {
403 const auto it = m_relIdMap.find(member.id);
404 if (it != m_relIdMap.end()) {
405 member.id = (*it).second;
410 const auto it = std::lower_bound(m_dataSet->relations.begin(), m_dataSet->relations.end(), rel);
411 if (it != m_dataSet->relations.end() && (*it).id == rel.id) {
412 mergeRelation(*it, rel);
414 m_dataSet->relations.insert(it, std::move(rel));
421 for (
auto &otherMember : otherRelation.members) {
422 const auto it = std::find(relation.members.begin(), relation.members.end(), otherMember);
423 if (it == relation.members.end()) {
424 relation.members.push_back(otherMember);
431 for (
auto it = relation.members.begin(); it != relation.members.end();) {
432 if ((*it).id > 0 || (*it).type() != OSM::Type::Way) {
437 if (std::strcmp((*it).role().name(),
"outer") != 0 && std::strcmp((*it).role().name(),
"inner") != 0) {
442 auto way = m_dataSet->way((*it).id);
443 if (!way || !way->isClosed()) {
449 for (
auto it2 = std::next(it); it2 != relation.members.end(); ++it2) {
450 if ((*it2).id > 0 || (*it2).type() != OSM::Type::Way || (*it2).role() != (*it).role()) {
454 auto otherWay = m_dataSet->way((*it2).id);
455 if (!otherWay || !otherWay->isClosed()) {
459 way->nodes = mergeArea(*way, *otherWay);
460 if (otherWay->nodes.empty()) {
461 relation.members.erase(it2);
473template<
typename Elem>
474OSM::Id MarbleGeometryAssembler::takeMxOid(Elem &elem)
const
476 const auto it = std::lower_bound(elem.tags.begin(), elem.tags.end(), m_mxoidKey, [](
const auto &lhs,
const auto &rhs) { return lhs.key < rhs; });
477 if (it != elem.tags.end() && (*it).key == m_mxoidKey) {
479 const OSM::Id id = (*it).value.toLongLong(&result);
Coordinate, stored as 1e7 * degree to avoid floating point precision issues, and offset to unsigned v...
Holds OSM elements produced by a parser prior to merging into OSM::DataSet.
A set of nodes, ways and relations.
TagKey makeTagKey(const char *keyName, StringMemory keyMemOpt=StringMemory::Transient)
Create a tag key for the given tag name.
KIOCORE_EXPORT CopyJob * move(const QList< QUrl > &src, const QUrl &dest, JobFlags flags=DefaultFlags)
QString path(const QString &relativePath)
OSM-based multi-floor indoor maps for buildings.
void setTagValue(Elem &elem, TagKey key, QByteArray &&value)
Inserts a new tag, or updates an existing one.
int64_t Id
OSM element identifier.
QByteArray tagValue(const Elem &elem, TagKey key)
Returns the tag value for key of elem.
QByteArray number(double n, char format, int precision)
qlonglong toLongLong(bool *ok, int base) const const
iterator erase(const_iterator first, const_iterator last)