diff --git a/docs/RELATIONS.md b/docs/RELATIONS.md index 09b5229f..a30cfdcc 100644 --- a/docs/RELATIONS.md +++ b/docs/RELATIONS.md @@ -1,6 +1,6 @@ ## Relations -Tilemaker has (as yet not complete) support for reading relations in the Lua process scripts. This means you can support route and boundary relations when creating your vector tiles. +Tilemaker has (as yet not complete) support for reading relations in the Lua process scripts. This means you can support objects like route relations when creating your vector tiles. Note that relation support is in its early stages and behaviour may change between point versions. @@ -9,6 +9,8 @@ Note that relation support is in its early stages and behaviour may change betwe Multipolygon relations are supported natively by tilemaker; you do not need to write special Lua code for them. When a multipolygon is read, tilemaker constructs the geometry as normal, and passes the tags to `way_function` just as it would a simple area. +Boundary relations also have automatic handling of inner/outer ways, but are otherwise processed as relations. This means that you can choose to treat boundaries as properties on ways (see "Stage 2" below) or as complete geometries (see "Writing relation geometries"). You will typically want the properties-on-ways approach for administrative boundaries, and the complete-geometries approach for filled areas such as forests or nature reserves. + ### Reading relation memberships diff --git a/include/osm_lua_processing.h b/include/osm_lua_processing.h index 3fc7c12d..150fc9a6 100644 --- a/include/osm_lua_processing.h +++ b/include/osm_lua_processing.h @@ -81,7 +81,7 @@ class OsmLuaProcessing { * (note that we store relations as ways with artificial IDs, and that * we use decrementing positive IDs to give a bit more space for way IDs) */ - void setRelation(int64_t relationId, WayVec const &outerWayVec, WayVec const &innerWayVec, const tag_map_t &tags, bool isNativeMP); + void setRelation(int64_t relationId, WayVec const &outerWayVec, WayVec const &innerWayVec, const tag_map_t &tags, bool isNativeMP, bool isInnerOuter); // ---- Metadata queries called from Lua diff --git a/include/osm_store.h b/include/osm_store.h index 311890f6..2b9cdf64 100644 --- a/include/osm_store.h +++ b/include/osm_store.h @@ -336,6 +336,7 @@ class WayStore { }; // relation store +// (this isn't currently used as we don't need to store relations for later processing, but may be needed for nested relations) class RelationStore { @@ -384,7 +385,7 @@ class RelationStore { OSMStore will be mainly used for geometry generation. Geometry generation logic is implemented in this class. These functions are used by osm_output, and can be used by OsmLuaProcessing to provide the geometry information to Lua. - Internal data structures are encapsulated in NodeStore, WayStore and RelationStore classes. + Internal data structures are encapsulated in NodeStore, WayStore and [unused] RelationStore classes. These store can be altered for efficient memory use without global code changes. Such data structures have to return const ForwardInputIterators (only *, ++ and == should be supported). @@ -429,7 +430,7 @@ class OSMStore bool require_integrity = true; WayStore ways; - RelationStore relations; + RelationStore relations; // unused UsedWays used_ways; RelationScanStore scanned_relations; diff --git a/include/read_pbf.h b/include/read_pbf.h index 15867e8d..df42e275 100644 --- a/include/read_pbf.h +++ b/include/read_pbf.h @@ -53,6 +53,14 @@ class PbfReader bool ScanRelations(OsmLuaProcessing &output, PrimitiveGroup &pg, PrimitiveBlock const &pb); bool ReadRelations(OsmLuaProcessing &output, PrimitiveGroup &pg, PrimitiveBlock const &pb); + inline bool RelationIsType(Relation const &rel, int typeKey, int val) { + if (typeKey==-1 || val==-1) return false; + auto typeI = std::find(rel.keys().begin(), rel.keys().end(), typeKey); + if (typeI==rel.keys().end()) return false; + int typePos = typeI - rel.keys().begin(); + return rel.vals().Get(typePos) == val; + } + /// Find a string in the dictionary static int findStringPosition(PrimitiveBlock const &pb, char const *str); diff --git a/resources/process-openmaptiles.lua b/resources/process-openmaptiles.lua index df9fd091..c121eb52 100644 --- a/resources/process-openmaptiles.lua +++ b/resources/process-openmaptiles.lua @@ -247,6 +247,8 @@ function way_function(way) if landuse == "meadow" and way:Find("meadow")=="agricultural" then landuse="farmland" end -- Boundaries within relations + -- note that we process administrative boundaries as properties on ways, rather than as single relation geometries, + -- because otherwise we get multiple renderings where boundaries are coterminous local admin_level = 11 local isBoundary = false while true do @@ -261,10 +263,11 @@ function way_function(way) admin_level = math.min(admin_level, tonumber(way:Find("admin_level")) or 11) isBoundary = true end - + -- Administrative boundaries -- https://openmaptiles.org/schema/#boundary if isBoundary and not (way:Find("maritime")=="yes") then + local admin_level = math.min(11, tonumber(way:Find("admin_level")) or 11) local mz = 0 if admin_level>=3 and admin_level<5 then mz=4 elseif admin_level>=5 and admin_level<7 then mz=8 diff --git a/src/osm_lua_processing.cpp b/src/osm_lua_processing.cpp index 7d7081f5..3efd9be4 100644 --- a/src/osm_lua_processing.cpp +++ b/src/osm_lua_processing.cpp @@ -674,25 +674,24 @@ void OsmLuaProcessing::setWay(WayID wayId, LatpLonVec const &llVec, const tag_ma // We are now processing a relation // (note that we store relations as ways with artificial IDs, and that // we use decrementing positive IDs to give a bit more space for way IDs) -void OsmLuaProcessing::setRelation(int64_t relationId, WayVec const &outerWayVec, WayVec const &innerWayVec, const tag_map_t &tags, bool isNativeMP) { +void OsmLuaProcessing::setRelation(int64_t relationId, WayVec const &outerWayVec, WayVec const &innerWayVec, const tag_map_t &tags, + bool isNativeMP, // only OSM type=multipolygon + bool isInnerOuter) { // any OSM relation with "inner" and "outer" roles (e.g. type=multipolygon|boundary) reset(); osmID = (relationId & OSMID_MASK) | OSMID_RELATION; originalOsmID = relationId; isWay = true; isRelation = true; - isClosed = isNativeMP; + isClosed = isNativeMP || isInnerOuter; llVecPtr = nullptr; outerWayVecPtr = &outerWayVec; innerWayVecPtr = &innerWayVec; currentTags = tags; - bool ok = true; - if (ok) { - //Start Lua processing for relation - luaState[isNativeMP ? "way_function" : "relation_function"](this); - } - + // Start Lua processing for relation + if (!isNativeMP && !supportsWritingRelations) return; + luaState[isNativeMP ? "way_function" : "relation_function"](this); if (this->empty()) return; // Assemble multipolygon diff --git a/src/read_pbf.cpp b/src/read_pbf.cpp index aa61372c..218ba6b9 100644 --- a/src/read_pbf.cpp +++ b/src/read_pbf.cpp @@ -130,15 +130,15 @@ bool PbfReader::ScanRelations(OsmLuaProcessing &output, PrimitiveGroup &pg, Prim for (int j=0; j(pbfRelation.id()); if (!isMultiPolygon) { - if (!output.canReadRelations()) continue; - tag_map_t tags; - readTags(pbfRelation, pb, tags); - isAccepted = output.scanRelation(relid, tags); + if (output.canReadRelations()) { + tag_map_t tags; + readTags(pbfRelation, pb, tags); + isAccepted = output.scanRelation(relid, tags); + } if (!isAccepted) continue; } int64_t lastID = 0; @@ -160,24 +160,25 @@ bool PbfReader::ReadRelations(OsmLuaProcessing &output, PrimitiveGroup &pg, Prim int typeKey = findStringPosition(pb, "type"); int mpKey = findStringPosition(pb, "multipolygon"); + int boundaryKey = findStringPosition(pb, "boundary"); int innerKey= findStringPosition(pb, "inner"); - //int outerKey= findStringPosition(pb, "outer"); + int outerKey= findStringPosition(pb, "outer"); if (typeKey >-1 && mpKey>-1) { for (int j=0; j(lastID); (role == innerKey ? innerWayVec : outerWayVec).push_back(wayId); } @@ -185,14 +186,7 @@ bool PbfReader::ReadRelations(OsmLuaProcessing &output, PrimitiveGroup &pg, Prim try { tag_map_t tags; readTags(pbfRelation, pb, tags); - - // Store the relation members in the global relation store - relations.push_back(std::make_pair(pbfRelation.id(), - std::make_pair( - RelationStore::wayid_vector_t(outerWayVec.begin(), outerWayVec.end()), - RelationStore::wayid_vector_t(innerWayVec.begin(), innerWayVec.end())))); - - output.setRelation(pbfRelation.id(), outerWayVec, innerWayVec, tags, isMultiPolygon); + output.setRelation(pbfRelation.id(), outerWayVec, innerWayVec, tags, isMultiPolygon, isInnerOuter); } catch (std::out_of_range &err) { // Relation is missing a member?