Skip to content
This repository has been archived by the owner on Oct 5, 2024. It is now read-only.

Add support for Relation MultiLineString + tests #481

Merged
merged 3 commits into from
Mar 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 19 additions & 1 deletion src/osm/osmobjects.hh
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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
Expand Down
106 changes: 66 additions & 40 deletions src/raw/queryraw.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -573,13 +576,12 @@ void QueryRaw::buildGeometries(std::shared_ptr<OsmChangeFile> 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)) {
Expand All @@ -606,54 +608,76 @@ void QueryRaw::buildGeometries(std::shared_ptr<OsmChangeFile> 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 ((!isMultiPolygon && boost::geometry::num_points(way->linestring) == 0) ||
(isMultiPolygon && boost::geometry::num_points(way->polygon) == 0)
) {
noWay = true;
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);
}

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);
}
}
}
}
Expand Down Expand Up @@ -895,6 +919,8 @@ QueryRaw::getRelationsFromDB(long lastid, int pageSize) {
std::string geometry = (*rel_it)[2].as<std::string>();
if (geometry.substr(0, 12) == "MULTIPOLYGON") {
boost::geometry::read_wkt(geometry, relation.multipolygon);
} else if (geometry.substr(0, 15) == "MULTILINESTRING") {
boost::geometry::read_wkt(geometry, relation.multilinestring);
}
relation.version = (*rel_it)[3].as<long>();
}
Expand Down
13 changes: 12 additions & 1 deletion src/testsuite/libunderpass.all/raw-test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,8 @@ const std::map<long, std::string> 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
Expand Down Expand Up @@ -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;
}

}


Expand Down
23 changes: 23 additions & 0 deletions src/testsuite/testdata/raw/raw-case-6.osc
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?xml version='1.0' encoding='UTF-8'?>
<osmChange version="0.6" generator="Osmosis 0.47.4">
<create>
<node id='111786' visible='true' lat='4.62042952837' lon='21.72600147299' version='1' />
<node id='111787' visible='true' lat='4.62042742837' lon='21.72608657299' version='1'/>
<node id='111788' visible='true' lat='4.62036492836' lon='21.72608497299' version='1'/>
<node id='111789' visible='true' lat='4.62036702836' lon='21.72599987299' version='1'/>
<way id='101894' visible='true' version='1'>
<nd ref='111786' />
<nd ref='111787' />
</way>
<way id='101895' visible='true' version='1'>
<nd ref='111788' />
<nd ref='111789' />
<nd ref='111786' />
</way>
<relation id='211776' visible='true' version='1'>
<tag k="type" v="multilinestring"/>
<member type="way" ref="101894" />
<member type="way" ref="101895" />
</relation>
</create>
</osmChange>
Loading