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

Commit

Permalink
Merge pull request #481 from hotosm/feature/relations
Browse files Browse the repository at this point in the history
Add support for Relation MultiLineString + tests
  • Loading branch information
emi420 authored Mar 6, 2024
2 parents 9fa6d61 + 4a535e4 commit fb5e939
Show file tree
Hide file tree
Showing 4 changed files with 120 additions and 42 deletions.
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>

0 comments on commit fb5e939

Please sign in to comment.