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 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/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/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; +} + +// ---- 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