diff --git a/src/dsf/mobility/RoadNetwork.cpp b/src/dsf/mobility/RoadNetwork.cpp index d7b3bddd..63708817 100644 --- a/src/dsf/mobility/RoadNetwork.cpp +++ b/src/dsf/mobility/RoadNetwork.cpp @@ -40,6 +40,8 @@ namespace dsf::mobility { (std::find(colNames.begin(), colNames.end(), "coilcode") != colNames.end()); bool const bHasCustomWeight = (std::find(colNames.begin(), colNames.end(), "customWeight") != colNames.end()); + bool const bHasPriority = + (std::find(colNames.begin(), colNames.end(), "priority") != colNames.end()); for (auto& row : reader) { auto const sourceId = row["source"].get(); @@ -105,6 +107,16 @@ namespace dsf::mobility { } } + if (bHasPriority) { + try { + if (row["priority"].get()) { + edge(streetId)->setPriority(); + } + } catch (...) { + spdlog::warn("Invalid priority for edge {}.", streetId); + } + } + if (bHasCoilcode) { auto strCoilCode = row["coilcode"].get(); // Make this lowercase @@ -329,6 +341,26 @@ namespace dsf::mobility { addCoil(edge_id); } } + // Check if there is custom weight property + if (!edge_properties.at_key("customWeight").error()) { + auto const& epCustomWeight = edge_properties["customWeight"]; + if (epCustomWeight.is_number()) { + edge(edge_id)->setWeight(epCustomWeight.get_double()); + } else { + spdlog::warn("Invalid custom weight for edge {}, keeping default", edge_id); + } + } + // Check if there is priority property + if (!edge_properties.at_key("priority").error()) { + auto const& epPriority = edge_properties["priority"]; + if (epPriority.is_bool()) { + if (epPriority.get_bool()) { + edge(edge_id)->setPriority(); + } + } else { + spdlog::warn("Invalid priority for edge {}, keeping default", edge_id); + } + } } this->m_nodes.rehash(0); this->m_edges.rehash(0); diff --git a/src/dsf/mobility/RoadNetwork.hpp b/src/dsf/mobility/RoadNetwork.hpp index 94436c70..8b0988fb 100644 --- a/src/dsf/mobility/RoadNetwork.hpp +++ b/src/dsf/mobility/RoadNetwork.hpp @@ -123,6 +123,7 @@ namespace dsf::mobility { /// - forbiddenTurns: The forbidden turns of the street, encoding information about street into which the street cannot output agents. The format is a string "sourceId1-targetid1, sourceId2-targetid2,..." /// - coilcode: An integer code to identify the coil located on the street /// - customWeight: will be stored in the `weight` parameter of the Edge class. You can use it for the shortest path via dsf::weight_functions::customWeight. + /// - priority: boolean, whether the street is a priority road or not. This information can be used in the traffic light cycle generation. /// @param args Additional arguments template void importEdges(const std::string& fileName, TArgs&&... args); diff --git a/test/mobility/Test_graph.cpp b/test/mobility/Test_graph.cpp index b22abdf6..1ec8b1fc 100644 --- a/test/mobility/Test_graph.cpp +++ b/test/mobility/Test_graph.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN @@ -218,6 +219,93 @@ TEST_CASE("RoadNetwork") { CHECK_EQ(graph.nTrafficLights(), 30); } } + + WHEN("We import edges from CSV with priority field") { + auto const tmpCsvPath = + std::filesystem::temp_directory_path() / "dsf_priority_edges_test.csv"; + { + std::ofstream tmpCsv(tmpCsvPath); + REQUIRE(tmpCsv.is_open()); + tmpCsv << "id;source;target;oneway;length;geometry;travel_time;maxspeed;nlanes;" + "type;name;priority\n"; + tmpCsv << "100;1;2;False;100.0;LINESTRING (8.0 45.0, 8.1 45.1);12.0;30.0;1;" + "residential;priority_street_true;1\n"; + tmpCsv << "101;2;3;False;120.0;LINESTRING (8.1 45.1, 8.2 45.2);14.4;30.0;1;" + "residential;priority_street_false;0\n"; + } + + RoadNetwork graphWithPriority; + graphWithPriority.importEdges(tmpCsvPath.string()); + + THEN("Priority is correctly imported from CSV") { + REQUIRE(graphWithPriority.edge(static_cast(100))); + REQUIRE(graphWithPriority.edge(static_cast(101))); + CHECK(graphWithPriority.edge(static_cast(100))->hasPriority()); + CHECK_FALSE(graphWithPriority.edge(static_cast(101))->hasPriority()); + std::filesystem::remove(tmpCsvPath); + } + } + + WHEN("We import edges from GeoJSON with priority field") { + auto const tmpGeoJsonPath = + std::filesystem::temp_directory_path() / "dsf_priority_edges_test.geojson"; + { + std::ofstream tmpGeoJson(tmpGeoJsonPath); + REQUIRE(tmpGeoJson.is_open()); + tmpGeoJson << R"({ + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "properties": { + "id": 200, + "source": 10, + "target": 11, + "length": 50.0, + "maxspeed": 30.0, + "nlanes": 1, + "type": "residential", + "name": "geo_priority_true", + "priority": true + }, + "geometry": { + "type": "LineString", + "coordinates": [[8.0, 45.0], [8.1, 45.1]] + } + }, + { + "type": "Feature", + "properties": { + "id": 201, + "source": 11, + "target": 12, + "length": 60.0, + "maxspeed": 30.0, + "nlanes": 1, + "type": "residential", + "name": "geo_priority_false", + "priority": false + }, + "geometry": { + "type": "LineString", + "coordinates": [[8.1, 45.1], [8.2, 45.2]] + } + } + ] +})"; + } + + RoadNetwork graphWithPriority; + graphWithPriority.importEdges(tmpGeoJsonPath.string()); + + THEN("Priority is correctly imported from GeoJSON") { + REQUIRE(graphWithPriority.edge(static_cast(200))); + REQUIRE(graphWithPriority.edge(static_cast(201))); + CHECK(graphWithPriority.edge(static_cast(200))->hasPriority()); + CHECK_FALSE(graphWithPriority.edge(static_cast(201))->hasPriority()); + std::filesystem::remove(tmpGeoJsonPath); + } + } } SUBCASE("street") { /// GIVEN: a graph