Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions CITATION.cff
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,5 @@ authors:
repository-code: 'https://github.com/Grufoony/DynamicalSystemFramework'
url: 'https://grufoony.github.io/DynamicalSystemFramework/'
license: AGPL-3.0-only
version: 6.3.3
date-released: '2026-07-01'
version: 6.4.0
date-released: '2026-07-06'
4 changes: 2 additions & 2 deletions src/dsf/dsf.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
#include <spdlog/sinks/basic_file_sink.h>

static constexpr uint8_t DSF_VERSION_MAJOR = 6;
static constexpr uint8_t DSF_VERSION_MINOR = 3;
static constexpr uint8_t DSF_VERSION_PATCH = 3;
static constexpr uint8_t DSF_VERSION_MINOR = 4;
static constexpr uint8_t DSF_VERSION_PATCH = 0;

static auto const DSF_VERSION =
std::format("{}.{}.{}", DSF_VERSION_MAJOR, DSF_VERSION_MINOR, DSF_VERSION_PATCH);
Expand Down
42 changes: 10 additions & 32 deletions src/dsf/mobility/Road.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,14 @@

#include "../base/Edge.hpp"

#include <cstdint>
#include <format>
#include <memory>
#include <optional>
#include <set>
#include <string>

namespace dsf::mobility {
enum class RoadType : std::uint8_t {
HIGHWAY = 0,
PRIMARY = 1,
SECONDARY = 2,
TERTIARY = 3,
RESIDENTIAL = 4,
UNKNOWN = 255,
};
enum class RoadStatus : std::uint8_t {
OPEN = 0,
CLOSED = 1,
Expand All @@ -34,7 +27,7 @@ namespace dsf::mobility {
std::string m_name;
bool m_hasPriority = false;
std::set<Id> m_forbiddenTurns; // Stores the forbidden turns (road ids)
RoadType m_roadType = RoadType::UNKNOWN;
std::uint8_t m_mobilityClass = 0u;
RoadStatus m_roadStatus = RoadStatus::OPEN;

public:
Expand Down Expand Up @@ -86,8 +79,10 @@ namespace dsf::mobility {
/// @param forbiddenTurns The set of forbidden turns
void setForbiddenTurns(std::set<Id> const& forbiddenTurns);
/// @brief Set the road type
/// @param roadType The road type
inline void setRoadType(RoadType const roadType) { m_roadType = roadType; }
/// @param mobilityClass The road type
inline void setMobilityClass(std::uint8_t const mobilityClass) {
m_mobilityClass = mobilityClass;
}
/// @brief Set the road status
/// @param status The road status
inline void setStatus(RoadStatus const status) { m_roadStatus = status; }
Expand Down Expand Up @@ -128,27 +123,10 @@ namespace dsf::mobility {
/// @details The forbidden turns are the road ids that are not allowed to be used by the agents
/// when they are on the road.
inline auto const& forbiddenTurns() const noexcept { return m_forbiddenTurns; }
/// @brief Get the road type
/// @return RoadType The road type
inline auto roadType() const noexcept { return m_roadType; }
/// @brief Get the string representation of the road type
/// @return std::string The string representation of the road type
constexpr std::string_view strRoadType() const {
switch (m_roadType) {
case RoadType::HIGHWAY:
return "highway";
case RoadType::PRIMARY:
return "primary";
case RoadType::SECONDARY:
return "secondary";
case RoadType::TERTIARY:
return "tertiary";
case RoadType::RESIDENTIAL:
return "residential";
default:
return "unknown";
}
};
/// @brief Get the road mobility class
/// @return std::uint8_t The mobility class of the road, which is a number between 0 and 255
inline auto mobilityClass() const noexcept { return m_mobilityClass; }

/// @brief Get the road status
/// @return RoadStatus The road status
inline auto roadStatus() const noexcept { return m_roadStatus; }
Expand Down
67 changes: 47 additions & 20 deletions src/dsf/mobility/RoadNetwork.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ static constexpr auto EDGE_DEFAULT_ATTRIBUTES =
"status",
"coilcode",
"priority",
"mobility_class",
"geometry"});

namespace dsf::mobility {
Expand Down Expand Up @@ -71,6 +72,8 @@ namespace dsf::mobility {
bool const bHasForbiddenTurns =
(std::find(colNames.begin(), colNames.end(), "forbidden_turns") !=
colNames.end());
bool const bHasMobilityClass =
(std::find(colNames.begin(), colNames.end(), "mobility_class") != colNames.end());

for (auto& row : reader) {
auto const sourceId = row["source"].get<Id>();
Expand Down Expand Up @@ -118,21 +121,21 @@ namespace dsf::mobility {
name,
polyline));

if (!strType.empty()) {
if (!strType.empty() && !bHasMobilityClass) {
std::transform(
strType.begin(), strType.end(), strType.begin(), [](unsigned char c) {
return std::tolower(c);
});
if (strType.find("motorway") != std::string::npos) {
edge(streetId).setRoadType(RoadType::HIGHWAY);
edge(streetId).setMobilityClass(128u);
} else if (strType.find("primary") != std::string::npos) {
edge(streetId).setRoadType(RoadType::PRIMARY);
edge(streetId).setMobilityClass(64u);
} else if (strType.find("secondary") != std::string::npos) {
edge(streetId).setRoadType(RoadType::SECONDARY);
edge(streetId).setMobilityClass(32u);
} else if (strType.find("tertiary") != std::string::npos) {
edge(streetId).setRoadType(RoadType::TERTIARY);
edge(streetId).setMobilityClass(16u);
} else if (strType.find("residential") != std::string::npos) {
edge(streetId).setRoadType(RoadType::RESIDENTIAL);
edge(streetId).setMobilityClass(0u);
}
}

Expand Down Expand Up @@ -194,6 +197,17 @@ namespace dsf::mobility {
edge(streetId).setStatus(RoadStatus::OPEN);
}
}
// Handle mobility_class field if present
if (bHasMobilityClass) {
try {
auto mobilityClassValue = row["mobility_class"].get<std::uint8_t>();
edge(streetId).setMobilityClass(mobilityClassValue);
} catch (...) {
spdlog::warn("Invalid mobility_class for edge {}. Using default (0).",
streetId);
edge(streetId).setMobilityClass(0u);
}
}
// Parse forbidden_turns field if present
if (bHasForbiddenTurns) {
// Expect a string of the form [edgeId1, edgeId2, ...]
Expand Down Expand Up @@ -408,21 +422,34 @@ namespace dsf::mobility {
edge_lanes,
name,
geometry));
if (!strType.empty()) {
auto const mobilityClassResult = edge_properties.at_key("mobility_class");
if (!mobilityClassResult.error()) {
if (mobilityClassResult.is_uint64()) {
edge(edge_id).setMobilityClass(
static_cast<std::uint8_t>(mobilityClassResult.get_uint64()));
} else if (mobilityClassResult.is_int64()) {
edge(edge_id).setMobilityClass(
static_cast<std::uint8_t>(mobilityClassResult.get_int64()));
} else {
spdlog::warn("Invalid mobility_class for edge {}, adding default (0u)",
edge_id);
edge(edge_id).setMobilityClass(0u);
}
} else if (!strType.empty()) {
std::transform(
strType.begin(), strType.end(), strType.begin(), [](unsigned char c) {
return std::tolower(c);
});
if (strType.find("motorway") != std::string::npos) {
edge(edge_id).setRoadType(RoadType::HIGHWAY);
edge(edge_id).setMobilityClass(128u);
} else if (strType.find("primary") != std::string::npos) {
edge(edge_id).setRoadType(RoadType::PRIMARY);
edge(edge_id).setMobilityClass(64u);
} else if (strType.find("secondary") != std::string::npos) {
edge(edge_id).setRoadType(RoadType::SECONDARY);
edge(edge_id).setMobilityClass(32u);
} else if (strType.find("tertiary") != std::string::npos) {
edge(edge_id).setRoadType(RoadType::TERTIARY);
edge(edge_id).setMobilityClass(16u);
} else if (strType.find("residential") != std::string::npos) {
edge(edge_id).setRoadType(RoadType::RESIDENTIAL);
edge(edge_id).setMobilityClass(0u);
}
}
// Check if there is coilcode property
Expand Down Expand Up @@ -923,17 +950,17 @@ namespace dsf::mobility {
[this, &nAssigned, &nNotAssigned](auto const& pair) {
auto const& pNode{pair.second};
auto const& inNeighbours{pNode->ingoingEdges()};
// NOTE: std::multimap iterates keys in ascending order of RoadType.
// RoadType is defined so that more important roads (e.g., HIGHWAY = 0,
// PRIMARY = 1, SECONDARY = 2, ...) have smaller enum values. The logic
// NOTE: std::multimap iterates keys in descending order of std::uint8_t.
// std::uint8_t is defined so that more important roads (e.g., HIGHWAY = 128u,
// PRIMARY = 64u, SECONDARY = 32u, ...) have bigger values. The logic
// below relies on this ordering to consider higher-priority road types
// first when selecting streets to mark as priority roads.
std::multimap<RoadType, Id> types;
std::multimap<std::uint8_t, Id, std::greater<std::uint8_t>> types;
for (auto const& edgeId : inNeighbours) {
auto* pStreet{&this->edge(edgeId)};
auto const roadType = pStreet->roadType();
if (roadType != RoadType::UNKNOWN) {
types.emplace(roadType, pStreet->id());
auto const mobilityClass = pStreet->mobilityClass();
if (mobilityClass != 0u) {
types.emplace(mobilityClass, pStreet->id());
}
}
if (types.size() < 2) {
Expand Down Expand Up @@ -1368,7 +1395,7 @@ namespace dsf::mobility {
edgeRow.emplace_back(std::format("{}", pStreet->length()));
edgeRow.emplace_back(std::format("{}", pStreet->maxSpeed() * 3.6));
edgeRow.emplace_back(std::format("{}", pStreet->nLanes()));
edgeRow.emplace_back(pStreet->strRoadType());
edgeRow.emplace_back(std::format("{}", pStreet->mobilityClass()));
edgeRow.emplace_back(std::format("{}", pStreet->capacity()));
edgeRow.emplace_back(std::format("{}", pStreet->roadStatus()));
edgeRow.emplace_back(pStreet->name());
Expand Down
Loading
Loading