From e65edef142ed71cf452f934c5f299f955ac67ce0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrik=20Drhl=C3=ADk?= Date: Thu, 7 Nov 2024 12:37:28 +0100 Subject: [PATCH 1/3] Fix _minzoom handling in attribute_function Setting _minzoom was being overwritten while processing each feature. It needs to be persisted per feature and per layer. This way it's possible to easily add filtering logic based on zoom in the attribute_function. --- include/shp_processor.h | 2 +- src/shp_processor.cpp | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/include/shp_processor.h b/include/shp_processor.h index aa4fc2a7..61c9247d 100644 --- a/include/shp_processor.h +++ b/include/shp_processor.h @@ -45,7 +45,7 @@ class ShpProcessor { // Process an individual shapefile record void processShapeGeometry(SHPObject* shape, AttributeIndex attrIdx, - const LayerDef &layer, uint layerNum, bool hasName, const std::string &name); + const LayerDef &layer, uint layerNum, bool hasName, const std::string &name, const uint minzoom); }; #endif //_SHP_PROCESSOR_H diff --git a/src/shp_processor.cpp b/src/shp_processor.cpp index 9e56b2ba..7aca4bbf 100644 --- a/src/shp_processor.cpp +++ b/src/shp_processor.cpp @@ -166,9 +166,10 @@ void ShpProcessor::read(class LayerDef &layer, uint layerNum) std::lock_guard lock(attributeMutex); name=DBFReadStringAttribute(dbf, i, indexField); hasName = true; } - AttributeIndex attrIdx = readShapefileAttributes(dbf, i, columnMap, columnTypeMap, layer, layer.minzoom); + uint minzoom = layer.minzoom; + AttributeIndex attrIdx = readShapefileAttributes(dbf, i, columnMap, columnTypeMap, layer, minzoom); // process geometry - processShapeGeometry(shape, attrIdx, layer, layerNum, hasName, name); + processShapeGeometry(shape, attrIdx, layer, layerNum, hasName, name, minzoom); SHPDestroyObject(shape); }); } @@ -178,9 +179,8 @@ void ShpProcessor::read(class LayerDef &layer, uint layerNum) } void ShpProcessor::processShapeGeometry(SHPObject* shape, AttributeIndex attrIdx, - const LayerDef &layer, uint layerNum, bool hasName, const string &name) { + const LayerDef &layer, uint layerNum, bool hasName, const string &name, const uint minzoom) { int shapeType = shape->nSHPType; // 1=point, 3=polyline, 5=(multi)polygon [8=multipoint, 11+=3D] - int minzoom = layer.minzoom; if (shapeType==1 || shapeType==11 || shapeType==21) { // Points From 68929edb22d688aee183c0121b36374785d91604 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrik=20Drhl=C3=ADk?= Date: Fri, 14 Mar 2025 15:45:17 +0100 Subject: [PATCH 2/3] Expand gitignore --- .gitignore | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.gitignore b/.gitignore index 18805e51..adadffe4 100644 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,9 @@ include/vector_tile.pb.h coastline landcover /test.* + +# MacOS +.DS_Store + +# VS Code +.vscode From d3e2196d1b0a4f6611eb47554ea91284885761d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrik=20Drhl=C3=ADk?= Date: Wed, 19 Mar 2025 13:06:46 +0100 Subject: [PATCH 3/3] Add new Lua functions FirstLastNode() - returns the first and the last node coordinates of a way/relation. WayBearing() - calculates the way's bearing in degrees between the first and the last point of the way/relation. --- docs/CONFIGURATION.md | 5 ++ include/osm_lua_processing.h | 6 +++ src/osm_lua_processing.cpp | 92 +++++++++++++++++++++++++++++++++++- 3 files changed, 102 insertions(+), 1 deletion(-) diff --git a/docs/CONFIGURATION.md b/docs/CONFIGURATION.md index 5919f317..9d2d7845 100644 --- a/docs/CONFIGURATION.md +++ b/docs/CONFIGURATION.md @@ -156,6 +156,11 @@ To do that, you use these methods: * `Length()` and `Area()`: return the length (metres)/area (square metres) of the current object. Requires Boost 1.67+. * `Centroid()`: return the lat/lon of the centre of the current object as a two-element Lua table (element 1 is lat, 2 is lon). +These functions extend the base Tilemaker: + +* `FirstLastNode()`: returns a table with coordinates of the first and the last way node (lat1, lon1, lat2, lon2). +* `WayBearing()`: returns a bearing between the first and the last node of a way. The range is <0-360] where zero means north. + The simplest possible function, to include roads/paths and nothing else, might look like this: ```lua diff --git a/include/osm_lua_processing.h b/include/osm_lua_processing.h index bfcb5d4e..364f46de 100644 --- a/include/osm_lua_processing.h +++ b/include/osm_lua_processing.h @@ -119,6 +119,12 @@ class OsmLuaProcessing { // Gets a table of all the OSM tags kaguya::LuaTable AllTags(kaguya::State& luaState); + // Returns a table with the first two nodes' coordinates (lon1, lat1, lon2, lat2) + kaguya::LuaTable FirstLastNode(kaguya::State& luaState); + + // Returns a bearing between the first and the last point of a way + double WayBearing(); + // Check if there's a value for a given key bool Holds(const std::string& key) const; diff --git a/src/osm_lua_processing.cpp b/src/osm_lua_processing.cpp index 62cdf10d..0c5b9f95 100644 --- a/src/osm_lua_processing.cpp +++ b/src/osm_lua_processing.cpp @@ -147,7 +147,8 @@ kaguya::LuaTable rawAllKeys() { auto tags = osmLuaProcessing->currentTags->exportToBoostMap(); return getAllKeys(*g_luaState, &tags); -}kaguya::LuaTable rawAllTags() { +} +kaguya::LuaTable rawAllTags() { if (osmLuaProcessing->isPostScanRelation) { return osmLuaProcessing->AllTags(*g_luaState); } @@ -194,6 +195,17 @@ std::string rawFindInRelation(const std::string& key) { return osmLuaProcessing- void rawAccept() { return osmLuaProcessing->Accept(); } double rawAreaIntersecting(const std::string& layerName) { return osmLuaProcessing->AreaIntersecting(layerName); } +// ---- These functions extend the base Tilemaker + +kaguya::LuaTable rawFirstLastNode() { + return osmLuaProcessing->FirstLastNode(*g_luaState); +} + +double rawWayBearing() { + return osmLuaProcessing->WayBearing(); +} +// ---- + bool supportsRemappingShapefiles = false; @@ -278,6 +290,11 @@ OsmLuaProcessing::OsmLuaProcessing( supportsWritingWays = !!luaState["way_function"]; supportsWritingRelations = !!luaState["relation_function"]; + // ---- These functions extend the base Tilemaker + luaState["FirstLastNode"] = &rawFirstLastNode; + luaState["WayBearing"] = &rawWayBearing; + // ---- + // ---- Call init_function of Lua logic if (!!luaState["init_function"]) { @@ -1176,3 +1193,76 @@ std::vector OsmLuaProcessing::finalizeOutputs() { return list; } +// ---- These functions extend the base Tilemaker + +// Returns a table with the first two nodes' coordinates (lon1, lat1, lon2, lat2) +kaguya::LuaTable OsmLuaProcessing::FirstLastNode(kaguya::State& luaState) { + kaguya::LuaTable coordsTable = luaState.newTable(); + coordsTable[1] = 0.0; + coordsTable[2] = 0.0; + coordsTable[3] = 0.0; + coordsTable[4] = 0.0; + geom::model::linestring ls; + + if (isWay && !isRelation) { + geom::assign(ls, linestringCached()); + if (ls.size() >= 1) { + DegPoint p1 = ls[0]; + DegPoint p2 = ls[ls.size() - 1]; + coordsTable[1] = geom::get<0>(p1); // lon1 + coordsTable[2] = latp2lat(geom::get<1>(p1)); // lat1 + coordsTable[3] = geom::get<0>(p2); // lon2 + coordsTable[4] = latp2lat(geom::get<1>(p2)); // lat2 + } + } else if (isRelation) { + MultiLinestring mls = multiLinestringCached(); + if (!mls.empty() && !mls[0].empty() && mls[0].size() >= 1) { + geom::assign(ls, mls[0]); + DegPoint p1 = ls[0]; + DegPoint p2 = ls[ls.size() - 1]; + coordsTable[1] = geom::get<0>(p1); // lon1 + coordsTable[2] = latp2lat(geom::get<1>(p1)); // lat1 + coordsTable[3] = geom::get<0>(p2); // lon2 + coordsTable[4] = latp2lat(geom::get<1>(p2)); // lat2 + } + } + + return coordsTable; +} + +// Returns a bearing between the first and the last point of a way. +double OsmLuaProcessing::WayBearing() { + kaguya::LuaTable coords = rawFirstLastNode(); + double lon1 = coords[1]; + double lat1 = coords[2]; + double lon2 = coords[3]; + double lat2 = coords[4]; + + // Convert degrees to radians + double lat1_rad = deg2rad(lat1); + double lon1_rad = deg2rad(lon1); + double lat2_rad = deg2rad(lat2); + double lon2_rad = deg2rad(lon2); + + // Calculate longitude difference + double d_lon = lon2_rad - lon1_rad; + + // Calculate eastward (y) and northward (x) components + double y = std::sin(d_lon) * std::cos(lat2_rad); + double x = std::cos(lat1_rad) * std::sin(lat2_rad) - std::sin(lat1_rad) * std::cos(lat2_rad) * std::cos(d_lon); + + // Calculate bearing in radians + double bearing_rad = std::atan2(y, x); + + // Convert to degrees + double bearing_deg = rad2deg(bearing_rad); + + // Adjust to 0-360 degree range + if (bearing_deg < 0) { + bearing_deg += 360.0; + } + + return bearing_deg; +} + +// ----