diff --git a/CHANGELOG.md b/CHANGELOG.md index 27c46d53a..823a340ac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ - ADDED: optionally build Node `lts` and `latest` bindings [#5347](https://github.com/Project-OSRM/osrm-backend/pull/5347) - Features: - ADDED: new waypoints parameter to the `route` plugin, enabling silent waypoints [#5345](https://github.com/Project-OSRM/osrm-backend/pull/5345) + - ADDED: data timestamp information in the response (saved in new file `.osrm.timestamp`). [#5115](https://github.com/Project-OSRM/osrm-backend/issues/5115) # 5.21.0 - Changes from 5.20.0 diff --git a/docs/http.md b/docs/http.md index bd47fcc7e..7caf2509c 100644 --- a/docs/http.md +++ b/docs/http.md @@ -70,6 +70,8 @@ curl 'http://router.project-osrm.org/route/v1/driving/polyline(ofp_Ik_vpAilAyu@t ### Responses +#### Code + Every response object has a `code` property containing one of the strings below or a service dependent code: | Type | Description | @@ -87,12 +89,17 @@ Every response object has a `code` property containing one of the strings below - `message` is a **optional** human-readable error message. All other status types are service dependent. - In case of an error the HTTP status code will be `400`. Otherwise the HTTP status code will be `200` and `code` will be `Ok`. +#### Data version + +Every response object has a `data_version` propetry containing timestamp from the original OpenStreetMap file. This field is optional. It can be ommited if data_version parametr was not set on osrm-extract stage or OSM file has not `osmosis_replication_timestamp` section. + #### Example response ```json { "code": "Ok", -"message": "Everything worked" +"message": "Everything worked", +"data_version": "2017-11-17T21:43:02Z" } ``` diff --git a/features/support/shared_steps.js b/features/support/shared_steps.js index bd16542c7..20fc0cf71 100644 --- a/features/support/shared_steps.js +++ b/features/support/shared_steps.js @@ -75,6 +75,10 @@ module.exports = function () { got.message = json.message || ''; } + if (headers.has('data_version')) { + got.data_version = json.data_version || ''; + } + if (headers.has('#')) { // comment column got['#'] = row['#']; diff --git a/features/testbot/basic.feature b/features/testbot/basic.feature index c7da7b243..0041a133e 100644 --- a/features/testbot/basic.feature +++ b/features/testbot/basic.feature @@ -17,9 +17,26 @@ Feature: Basic Routing | ab | When I route I should get - | from | to | route | - | a | b | ab,ab | - | b | a | ab,ab | + | from | to | route | data_version | + | a | b | ab,ab | | + | b | a | ab,ab | | + + Scenario: Data_version test + Given the node map + """ + a b + """ + + And the extract extra arguments "--data_version cucumber_data_version" + + And the ways + | nodes | + | ab | + + When I route I should get + | from | to | route | data_version | + | a | b | ab,ab | cucumber_data_version | + | b | a | ab,ab | cucumber_data_version | Scenario: Routing in between two nodes of way Given the node map diff --git a/include/engine/api/route_api.hpp b/include/engine/api/route_api.hpp index d5736cbfe..caaa92eb3 100644 --- a/include/engine/api/route_api.hpp +++ b/include/engine/api/route_api.hpp @@ -68,6 +68,11 @@ class RouteAPI : public BaseAPI response.values["waypoints"] = BaseAPI::MakeWaypoints(all_start_end_points); response.values["routes"] = std::move(jsRoutes); response.values["code"] = "Ok"; + auto data_timestamp = facade.GetTimestamp(); + if (!data_timestamp.empty()) + { + response.values["data_version"] = data_timestamp; + } } protected: diff --git a/include/engine/datafacade/contiguous_internalmem_datafacade.hpp b/include/engine/datafacade/contiguous_internalmem_datafacade.hpp index 7aabc704e..899fb86d6 100644 --- a/include/engine/datafacade/contiguous_internalmem_datafacade.hpp +++ b/include/engine/datafacade/contiguous_internalmem_datafacade.hpp @@ -137,6 +137,7 @@ class ContiguousInternalMemoryDataFacadeBase : public BaseDataFacade extractor::Datasources *m_datasources; std::uint32_t m_check_sum; + StringView m_data_timestamp; util::vector_view m_coordinate_list; extractor::PackedOSMIDsView m_osmnodeid_list; util::vector_view m_lane_description_offsets; @@ -183,6 +184,8 @@ class ContiguousInternalMemoryDataFacadeBase : public BaseDataFacade m_check_sum = *index.GetBlockPtr("/common/connectivity_checksum"); + m_data_timestamp = make_timestamp_view(index, "/common/timestamp"); + std::tie(m_coordinate_list, m_osmnodeid_list) = make_nbn_data_view(index, "/common/nbn_data"); @@ -432,6 +435,11 @@ class ContiguousInternalMemoryDataFacadeBase : public BaseDataFacade std::uint32_t GetCheckSum() const override final { return m_check_sum; } + std::string GetTimestamp() const override final + { + return std::string(m_data_timestamp.begin(), m_data_timestamp.end()); + } + GeometryID GetGeometryIndex(const NodeID id) const override final { return edge_based_node_data.GetGeometryID(id); diff --git a/include/engine/datafacade/datafacade_base.hpp b/include/engine/datafacade/datafacade_base.hpp index 23b2bdae7..157dbb1ca 100644 --- a/include/engine/datafacade/datafacade_base.hpp +++ b/include/engine/datafacade/datafacade_base.hpp @@ -74,6 +74,8 @@ class BaseDataFacade virtual std::uint32_t GetCheckSum() const = 0; + virtual std::string GetTimestamp() const = 0; + // node and edge information access virtual util::Coordinate GetCoordinateOfNode(const NodeID id) const = 0; diff --git a/include/extractor/extractor_config.hpp b/include/extractor/extractor_config.hpp index 1bd305821..1ca72a7dd 100644 --- a/include/extractor/extractor_config.hpp +++ b/include/extractor/extractor_config.hpp @@ -55,6 +55,7 @@ struct ExtractorConfig final : storage::IOConfig ".osrm.geometry", ".osrm.nbg_nodes", ".osrm.ebg_nodes", + ".osrm.timestamp", ".osrm.edges", ".osrm.ebg", ".osrm.ramIndex", @@ -82,6 +83,7 @@ struct ExtractorConfig final : storage::IOConfig boost::filesystem::path input_path; boost::filesystem::path profile_path; std::vector location_dependent_data_paths; + std::string data_version; unsigned requested_num_threads; unsigned small_component_size; diff --git a/include/extractor/files.hpp b/include/extractor/files.hpp index 0807e5c56..493c54765 100644 --- a/include/extractor/files.hpp +++ b/include/extractor/files.hpp @@ -308,6 +308,26 @@ inline void writeTurnLaneData(const boost::filesystem::path &path, storage::serialization::write(writer, "/common/turn_lanes/data", turn_lane_data); } +// reads .osrm.timestamp +template +inline void readTimestamp(const boost::filesystem::path &path, TimestampDataT ×tamp) +{ + const auto fingerprint = storage::tar::FileReader::VerifyFingerprint; + storage::tar::FileReader reader{path, fingerprint}; + + storage::serialization::read(reader, "/common/timestamp", timestamp); +} + +// writes .osrm.timestamp +template +inline void writeTimestamp(const boost::filesystem::path &path, const TimestampDataT ×tamp) +{ + const auto fingerprint = storage::tar::FileWriter::GenerateFingerprint; + storage::tar::FileWriter writer{path, fingerprint}; + + storage::serialization::write(writer, "/common/timestamp", timestamp); +} + // reads .osrm.maneuver_overrides template inline void readManeuverOverrides(const boost::filesystem::path &path, diff --git a/include/storage/storage_config.hpp b/include/storage/storage_config.hpp index 4d133f087..83b51a0bd 100644 --- a/include/storage/storage_config.hpp +++ b/include/storage/storage_config.hpp @@ -58,6 +58,7 @@ struct StorageConfig final : IOConfig ".osrm.turn_duration_penalties", ".osrm.datasource_names", ".osrm.names", + ".osrm.timestamp", ".osrm.properties", ".osrm.icd", ".osrm.maneuver_overrides"}, diff --git a/include/storage/view_factory.hpp b/include/storage/view_factory.hpp index b04e3026e..fb45b57d6 100644 --- a/include/storage/view_factory.hpp +++ b/include/storage/view_factory.hpp @@ -272,6 +272,11 @@ inline auto make_partition_view(const SharedDataIndex &index, const std::string level_data_ptr, std::move(partition), std::move(cell_to_children)}; } +inline auto make_timestamp_view(const SharedDataIndex &index, const std::string &name) +{ + return util::StringView(index.GetBlockPtr(name), index.GetBlockEntries(name)); +} + inline auto make_cell_storage_view(const SharedDataIndex &index, const std::string &name) { auto source_boundary = make_vector_view(index, name + "/source_boundary"); diff --git a/include/util/typedefs.hpp b/include/util/typedefs.hpp index d647a7db7..f29406d8b 100644 --- a/include/util/typedefs.hpp +++ b/include/util/typedefs.hpp @@ -79,6 +79,7 @@ using EdgeDistance = float; using SegmentWeight = std::uint32_t; using SegmentDuration = std::uint32_t; using TurnPenalty = std::int16_t; // turn penalty in 100ms units +using DataTimestamp = std::string; static const std::size_t INVALID_INDEX = std::numeric_limits::max(); diff --git a/src/extractor/extractor.cpp b/src/extractor/extractor.cpp index abd0bab93..36f137293 100644 --- a/src/extractor/extractor.cpp +++ b/src/extractor/extractor.cpp @@ -51,6 +51,7 @@ #include #include #include +#include #include #include @@ -425,6 +426,14 @@ Extractor::ParseOSMData(ScriptingEnvironment &scripting_environment, // write .timestamp data file std::string timestamp = header.get("osmosis_replication_timestamp"); + if (config.data_version == "osmosis") + { + files::writeTimestamp(config.GetPath(".osrm.timestamp").string(), timestamp); + } + else + { + files::writeTimestamp(config.GetPath(".osrm.timestamp").string(), config.data_version); + } if (timestamp.empty()) { timestamp = "n/a"; diff --git a/src/storage/storage.cpp b/src/storage/storage.cpp index 503b1c10e..e3d5ede2b 100644 --- a/src/storage/storage.cpp +++ b/src/storage/storage.cpp @@ -302,6 +302,7 @@ std::vector> Storage::GetStaticFiles() {REQUIRED, config.GetPath(".osrm.ebg_nodes")}, {REQUIRED, config.GetPath(".osrm.tls")}, {REQUIRED, config.GetPath(".osrm.tld")}, + {REQUIRED, config.GetPath(".osrm.timestamp")}, {REQUIRED, config.GetPath(".osrm.maneuver_overrides")}, {REQUIRED, config.GetPath(".osrm.edges")}, {REQUIRED, config.GetPath(".osrm.names")}, @@ -401,6 +402,17 @@ void Storage::PopulateStaticData(const SharedDataIndex &index) extractor::files::readNames(config.GetPath(".osrm.names"), name_table); } + // Timestamp mark + { + auto timestamp_ref = make_timestamp_view(index, "/common/timestamp"); + std::string ts; + extractor::files::readTimestamp(config.GetPath(".osrm.timestamp"), ts); + if (!ts.empty()) + { + memcpy(const_cast(timestamp_ref.data()), ts.data(), ts.size()); + } + } + // Turn lane data { auto turn_lane_data = make_lane_data_view(index, "/common/turn_lanes"); diff --git a/src/tools/extract.cpp b/src/tools/extract.cpp index bca3ef8d1..f44744bfe 100644 --- a/src/tools/extract.cpp +++ b/src/tools/extract.cpp @@ -43,6 +43,10 @@ return_code parseArguments(int argc, boost::program_options::value(&extractor_config.profile_path) ->default_value("profiles/car.lua"), "Path to LUA routing profile")( + "data_version,d", + boost::program_options::value(&extractor_config.data_version) + ->default_value(""), + "Data version. Leave blank to avoid. osmosis - to get timestamp from file")( "threads,t", boost::program_options::value(&extractor_config.requested_num_threads) ->default_value(tbb::task_scheduler_init::default_num_threads()), diff --git a/unit_tests/engine/offline_facade.cpp b/unit_tests/engine/offline_facade.cpp index bede4eabe..dab7e2327 100644 --- a/unit_tests/engine/offline_facade.cpp +++ b/unit_tests/engine/offline_facade.cpp @@ -341,6 +341,7 @@ class ContiguousInternalMemoryDataFacade StringView GetDestinationsForID(const NameID /*id*/) const override { return StringView{}; } StringView GetExitsForID(const NameID /*id*/) const override { return StringView{}; } bool GetContinueStraightDefault() const override { return false; } + std::string GetTimestamp() const override { return ""; } double GetMapMatchingMaxSpeed() const override { return 0; } const char *GetWeightName() const override { return ""; } unsigned GetWeightPrecision() const override { return 0; } diff --git a/unit_tests/mocks/mock_datafacade.hpp b/unit_tests/mocks/mock_datafacade.hpp index e68a1a306..efea099d0 100644 --- a/unit_tests/mocks/mock_datafacade.hpp +++ b/unit_tests/mocks/mock_datafacade.hpp @@ -10,7 +10,6 @@ #include "extractor/turn_lane_types.hpp" #include "guidance/turn_bearing.hpp" #include "guidance/turn_instruction.hpp" -#include "guidance/turn_instruction.hpp" #include "engine/algorithm.hpp" #include "engine/datafacade/algorithm_datafacade.hpp" @@ -54,6 +53,7 @@ class MockBaseDataFacade : public engine::datafacade::BaseDataFacade { return 0; } + std::string GetTimestamp() const override { return ""; } NodeForwardRange GetUncompressedForwardGeometry(const EdgeID /* id */) const override { static NodeID data[] = {0, 1, 2, 3};