From 574e50aafcc0f6a1ff11b85f0bd5ea0e7cfbca5b Mon Sep 17 00:00:00 2001 From: Emillio Mariscal Date: Wed, 6 Mar 2024 16:15:59 -0300 Subject: [PATCH 1/3] + MultiLinestring for relations --- src/osm/osmobjects.hh | 20 ++++++++- src/raw/queryraw.cc | 102 +++++++++++++++++++++++++----------------- 2 files changed, 81 insertions(+), 41 deletions(-) diff --git a/src/osm/osmobjects.hh b/src/osm/osmobjects.hh index 2c3c64930..9a758f19a 100644 --- a/src/osm/osmobjects.hh +++ b/src/osm/osmobjects.hh @@ -218,7 +218,7 @@ class OsmRelation : public OsmObject { public: OsmRelation(void) { type = relation; }; - multilinestring_t multilinestring; ///< Store the members as a linestring + multilinestring_t multilinestring; ///< Store the members as a multilinestring multipolygon_t multipolygon; ///< Store the members as a multipolygon point_t center; ///< Store the centroid of the relation @@ -230,6 +230,24 @@ class OsmRelation : public OsmObject { /// Dump internal data to the terminal, only for debugging void dump(void) const; + + /// Relation can be composed of closed ways, resulting in a multipolygon + bool isMultiPolygon(void) const + { + return (tags.count("type") && + (tags.at("type") == "multipolygon" || + tags.at("type") == "boundary") + ); + }; + + /// Relation can be componed of open ways, resulting in a multilinestring + bool isMultiLineString(void) const + { + return (tags.count("type") && + tags.at("type") == "multilinestring" + ); + }; + }; } // namespace osmobjects diff --git a/src/raw/queryraw.cc b/src/raw/queryraw.cc index 5cb53ba25..cd7696714 100644 --- a/src/raw/queryraw.cc +++ b/src/raw/queryraw.cc @@ -302,9 +302,12 @@ QueryRaw::applyChange(const OsmRelation &relation) const { std::string query = ""; std::stringstream ss; - ss << std::setprecision(12) << boost::geometry::wkt(relation.multipolygon); - // TODO: support for both multipolygon & multilinestring - // ss << std::setprecision(12) << boost::geometry::wkt(relation.multilinestring); + if (relation.isMultiPolygon()) { + ss << std::setprecision(12) << boost::geometry::wkt(relation.multipolygon); + } else { + ss << std::setprecision(12) << boost::geometry::wkt(relation.multilinestring); + } + std::string geostring = ss.str(); if (relation.action == osmobjects::create || relation.action == osmobjects::modify) { @@ -573,13 +576,12 @@ void QueryRaw::buildGeometries(std::shared_ptr osmchanges, const } // Filter out all relations that doesn't have at least 1 way in cache - // and are not type=multipolygon std::string relsForWayCacheIds; for (auto it = std::begin(osmchanges->changes); it != std::end(osmchanges->changes); it++) { OsmChange *change = it->get(); for (auto rel_it = std::begin(change->relations); rel_it != std::end(change->relations); ++rel_it) { OsmRelation *relation = rel_it->get(); - if (relation->tags.count("type") && relation->tags.at("type") == "multipolygon") { + if (relation->isMultiPolygon() || relation->isMultiLineString()) { bool getWaysForRelation = false; for (auto mit = relation->members.begin(); mit != relation->members.end(); ++mit) { if (osmchanges->waycache.count(mit->ref)) { @@ -606,54 +608,72 @@ void QueryRaw::buildGeometries(std::shared_ptr osmchanges, const getWaysByIds(relsForWayCacheIds, osmchanges->waycache); } - // Build relation MultiPolyon geometries + // Build relation geometries for (auto it = std::begin(osmchanges->changes); it != std::end(osmchanges->changes); it++) { OsmChange *change = it->get(); for (auto rel_it = std::begin(change->relations); rel_it != std::end(change->relations); ++rel_it) { OsmRelation *relation = rel_it->get(); if (relation->priority) { bool first = true; - std::string multipolygon_str = "MULTIPOLYGON(("; - std::string innerGeoStr; - bool noWay = false; - for (auto mit = relation->members.begin(); mit != relation->members.end(); ++mit) { - if (!osmchanges->waycache.count(mit->ref)) { - noWay = true; - break; + bool isMultiPolygon = relation->isMultiPolygon(); + bool isMultiLineString = relation->isMultiLineString(); + if (isMultiPolygon || isMultiLineString) { + std::string innerGeoStr; + std::string geometry_str; + if (isMultiPolygon) { + geometry_str = "MULTIPOLYGON(("; + } else { + geometry_str = "MULTILINESTRING(("; } - auto way = osmchanges->waycache.at(mit->ref); + bool noWay = false; + for (auto mit = relation->members.begin(); mit != relation->members.end(); ++mit) { + if (!osmchanges->waycache.count(mit->ref)) { + noWay = true; + break; + } + auto way = osmchanges->waycache.at(mit->ref); - if (boost::geometry::num_points(way->linestring) == 0 && - boost::geometry::num_points(way->polygon) == 0 - ) { - noWay = true; - break; + if (boost::geometry::num_points(way->linestring) == 0 && + boost::geometry::num_points(way->polygon) == 0 + ) { + noWay = true; + break; + } + std::stringstream ss; + if (isMultiPolygon) { + ss << std::setprecision(12) << boost::geometry::wkt(way->polygon); + } else { + ss << std::setprecision(12) << boost::geometry::wkt(way->linestring); + } + + std::string geometry = ss.str(); + geometry.erase(0,8); + geometry.erase(geometry.size() - 1); + if (first && mit->role == "outer") { + geometry_str += geometry + ","; + } else { + if (mit->role == "inner") { + innerGeoStr += geometry + ","; + } else { + geometry_str += geometry + ","; + geometry_str += innerGeoStr; + innerGeoStr = ""; + } + } } - std::stringstream ss; - ss << std::setprecision(12) << boost::geometry::wkt(way->polygon); - std::string geometry = ss.str(); - geometry.erase(0,8); - geometry.erase(geometry.size() - 1); - if (first && mit->role == "outer") { - multipolygon_str += geometry + ","; - } else { - if (mit->role == "inner") { - innerGeoStr += geometry + ","; + if (innerGeoStr.size() > 0) { + geometry_str += innerGeoStr; + } + geometry_str.erase(geometry_str.size() - 1); + geometry_str += "))"; + if (!noWay) { + if (isMultiPolygon) { + boost::geometry::read_wkt(geometry_str, relation->multipolygon); } else { - multipolygon_str += geometry + ","; - multipolygon_str += innerGeoStr; - innerGeoStr = ""; + boost::geometry::read_wkt(geometry_str, relation->multilinestring); } } } - if (innerGeoStr.size() > 0) { - multipolygon_str += innerGeoStr; - } - multipolygon_str.erase(multipolygon_str.size() - 1); - multipolygon_str += "))"; - if (!noWay) { - boost::geometry::read_wkt(multipolygon_str, relation->multipolygon); - } } } } @@ -895,6 +915,8 @@ QueryRaw::getRelationsFromDB(long lastid, int pageSize) { std::string geometry = (*rel_it)[2].as(); if (geometry.substr(0, 12) == "MULTIPOLYGON") { boost::geometry::read_wkt(geometry, relation.multipolygon); + } else if (geometry.substr(0, 12) == "MULTILINESTRING") { + boost::geometry::read_wkt(geometry, relation.multilinestring); } relation.version = (*rel_it)[3].as(); } From 56601d70d8bffc4055856eb51c108203886de125 Mon Sep 17 00:00:00 2001 From: Emillio Mariscal Date: Wed, 6 Mar 2024 17:17:33 -0300 Subject: [PATCH 2/3] + MultiLineString for relations, tests --- src/raw/queryraw.cc | 12 +++++++---- src/testsuite/libunderpass.all/raw-test.cc | 13 +++++++++++- src/testsuite/testdata/raw/raw-case-6.osc | 23 ++++++++++++++++++++++ 3 files changed, 43 insertions(+), 5 deletions(-) create mode 100644 src/testsuite/testdata/raw/raw-case-6.osc diff --git a/src/raw/queryraw.cc b/src/raw/queryraw.cc index cd7696714..51b571bdc 100644 --- a/src/raw/queryraw.cc +++ b/src/raw/queryraw.cc @@ -640,16 +640,20 @@ void QueryRaw::buildGeometries(std::shared_ptr osmchanges, const break; } std::stringstream ss; + std::string geometry; + if (isMultiPolygon) { ss << std::setprecision(12) << boost::geometry::wkt(way->polygon); + geometry = ss.str(); + geometry.erase(0,8); } else { ss << std::setprecision(12) << boost::geometry::wkt(way->linestring); + geometry = ss.str(); + geometry.erase(0,11); } - std::string geometry = ss.str(); - geometry.erase(0,8); geometry.erase(geometry.size() - 1); - if (first && mit->role == "outer") { + if (first && (mit->role == "outer" || mit->role == "")) { geometry_str += geometry + ","; } else { if (mit->role == "inner") { @@ -915,7 +919,7 @@ QueryRaw::getRelationsFromDB(long lastid, int pageSize) { std::string geometry = (*rel_it)[2].as(); if (geometry.substr(0, 12) == "MULTIPOLYGON") { boost::geometry::read_wkt(geometry, relation.multipolygon); - } else if (geometry.substr(0, 12) == "MULTILINESTRING") { + } else if (geometry.substr(0, 15) == "MULTILINESTRING") { boost::geometry::read_wkt(geometry, relation.multilinestring); } relation.version = (*rel_it)[3].as(); diff --git a/src/testsuite/libunderpass.all/raw-test.cc b/src/testsuite/libunderpass.all/raw-test.cc index 4260dca2b..691b0e4e2 100644 --- a/src/testsuite/libunderpass.all/raw-test.cc +++ b/src/testsuite/libunderpass.all/raw-test.cc @@ -110,7 +110,8 @@ const std::map expectedGeometries = { {101875, "POLYGON((21.726001473 4.62042952837,21.726086573 4.62042742837,21.726084973 4.62036492836,21.725999873 4.62036702836,21.726001473 4.62042952837))"}, {101875-2, "POLYGON((21.72600148 4.62042953,21.726086573 4.62042742837,21.726084973 4.62036492836,21.725999873 4.62036702836,21.72600148 4.62042953))"}, {211766, "MULTIPOLYGON(((21.72600148 4.62042953,21.726086573 4.62042742837,21.726084973 4.62036492836,21.725999873 4.62036702836,21.72600148 4.62042953),(21.7260170728 4.62041343508,21.7260713875 4.62041326798,21.7260708846 4.62037684165,21.7260165699 4.62038035061,21.7260170728 4.62041343508)))"}, - {211766-2, "MULTIPOLYGON(((21.72600148 4.62042953,21.726086573 4.62042742837,21.7260807753 4.62037032501,21.725999873 4.62036702836,21.72600148 4.62042953),(21.7260170728 4.62041343508,21.7260713875 4.62041326798,21.7260708846 4.62037684165,21.7260165699 4.62038035061,21.7260170728 4.62041343508)))"} + {211766-2, "MULTIPOLYGON(((21.72600148 4.62042953,21.726086573 4.62042742837,21.7260807753 4.62037032501,21.725999873 4.62036702836,21.72600148 4.62042953),(21.7260170728 4.62041343508,21.7260713875 4.62041326798,21.7260708846 4.62037684165,21.7260165699 4.62038035061,21.7260170728 4.62041343508)))"}, + {211776, "MULTILINESTRING((21.726001473 4.62042952837,21.726086573 4.62042742837,21.726084973 4.62036492836,21.725999873 4.62036702836,21.726001473 4.62042952837))"} }; std::string @@ -205,6 +206,16 @@ main(int argc, char *argv[]) runtest.fail("1 modified Node, indirectly modify other existing Ways and 1 Relation (different changesets)"); return 1; } + + // 4 created Nodes, 2 created Ways, 1 created Relation with type=multilinestring + processFile("raw-case-6.osc", db); + if ( getWKTFromDB("relations", 211776, db).compare(expectedGeometries.at(211776)) == 0) { + runtest.pass("4 created Nodes, 2 created Ways, 1 created Relation with type=multilinestring (same changeset)"); + } else { + runtest.fail("4 created Nodes, 2 created Ways, 1 created Relation with type=multilinestring (same changeset)"); + return 1; + } + } diff --git a/src/testsuite/testdata/raw/raw-case-6.osc b/src/testsuite/testdata/raw/raw-case-6.osc new file mode 100644 index 000000000..948188d17 --- /dev/null +++ b/src/testsuite/testdata/raw/raw-case-6.osc @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + From 4a535e45a824a9c099d055118d5f07f668772766 Mon Sep 17 00:00:00 2001 From: Emillio Mariscal Date: Wed, 6 Mar 2024 18:49:59 -0300 Subject: [PATCH 3/3] Fix for building Relation geometry --- src/raw/queryraw.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/raw/queryraw.cc b/src/raw/queryraw.cc index 51b571bdc..0a8467423 100644 --- a/src/raw/queryraw.cc +++ b/src/raw/queryraw.cc @@ -633,8 +633,8 @@ void QueryRaw::buildGeometries(std::shared_ptr osmchanges, const } auto way = osmchanges->waycache.at(mit->ref); - if (boost::geometry::num_points(way->linestring) == 0 && - boost::geometry::num_points(way->polygon) == 0 + if ((!isMultiPolygon && boost::geometry::num_points(way->linestring) == 0) || + (isMultiPolygon && boost::geometry::num_points(way->polygon) == 0) ) { noWay = true; break; @@ -653,7 +653,7 @@ void QueryRaw::buildGeometries(std::shared_ptr osmchanges, const } geometry.erase(geometry.size() - 1); - if (first && (mit->role == "outer" || mit->role == "")) { + if (first && (mit->role == "outer")) { geometry_str += geometry + ","; } else { if (mit->role == "inner") {