From 6b8f3c7fefaf83e782f4cc5da1ce6d5ceaecc9b4 Mon Sep 17 00:00:00 2001 From: Lev Dragunov Date: Tue, 6 Jun 2017 19:51:00 +0300 Subject: [PATCH 1/5] Polyline6 support in the REST input --- include/engine/polyline_compressor.hpp | 29 ++++++++-- .../server/api/base_parameters_grammar.hpp | 10 +++- src/engine/polyline_compressor.cpp | 57 +++++-------------- 3 files changed, 49 insertions(+), 47 deletions(-) diff --git a/include/engine/polyline_compressor.hpp b/include/engine/polyline_compressor.hpp index 96d735930..81eedec6c 100644 --- a/include/engine/polyline_compressor.hpp +++ b/include/engine/polyline_compressor.hpp @@ -14,10 +14,8 @@ namespace engine { namespace detail { -constexpr double POLYLINE_DECODING_PRECISION = 1e5; -constexpr double POLYLINE_TO_COORDINATE = COORDINATE_PRECISION / POLYLINE_DECODING_PRECISION; - std::string encode(std::vector &numbers); +std::int32_t decode_polyline_integer(std::string::const_iterator &first, std::string::const_iterator last); } using CoordVectorForwardIter = std::vector::const_iterator; // Encodes geometry into polyline format. @@ -57,7 +55,30 @@ std::string encodePolyline(CoordVectorForwardIter begin, CoordVectorForwardIter // Decodes geometry from polyline format // See: https://developers.google.com/maps/documentation/utilities/polylinealgorithm -std::vector decodePolyline(const std::string &polyline); + +template +std::vector decodePolyline(const std::string &polyline) +{ + double polyline_to_coordinate = COORDINATE_PRECISION / POLYLINE_PRECISION ; + std::vector coordinates; + std::int32_t latitude = 0, longitude = 0; + + std::string::const_iterator first = polyline.begin(); + const std::string::const_iterator last = polyline.end(); + while (first != last) + { + const auto dlat = detail::decode_polyline_integer(first, last); + const auto dlon = detail::decode_polyline_integer(first, last); + + latitude += dlat; + longitude += dlon; + + coordinates.emplace_back( + util::Coordinate{util::FixedLongitude{static_cast(longitude*polyline_to_coordinate)}, + util::FixedLatitude{static_cast(latitude*polyline_to_coordinate)}}); + } + return coordinates; +} } } diff --git a/include/server/api/base_parameters_grammar.hpp b/include/server/api/base_parameters_grammar.hpp index 8d3c5af7c..f176e0acd 100644 --- a/include/server/api/base_parameters_grammar.hpp +++ b/include/server/api/base_parameters_grammar.hpp @@ -128,9 +128,16 @@ struct BaseParametersGrammar : boost::spirit::qi::grammar }, qi::_1)]; + polyline6_rule = qi::as_string[qi::lit("polyline6(") > +polyline_chars > ')'] + [qi::_val = ph::bind( + [](const std::string &polyline) { + return engine::decodePolyline<1000000>(polyline); + }, + qi::_1)]; + query_rule = ((location_rule % ';') | - polyline_rule)[ph::bind(&engine::api::BaseParameters::coordinates, qi::_r1) = qi::_1]; + polyline_rule | polyline6_rule)[ph::bind(&engine::api::BaseParameters::coordinates, qi::_r1) = qi::_1]; radiuses_rule = qi::lit("radiuses=") > (-(qi::double_ | unlimited_rule) % @@ -176,6 +183,7 @@ struct BaseParametersGrammar : boost::spirit::qi::grammar qi::rule bearing_rule; qi::rule location_rule; qi::rule()> polyline_rule; + qi::rule()> polyline6_rule; qi::rule base64_char; qi::rule polyline_chars; diff --git a/src/engine/polyline_compressor.cpp b/src/engine/polyline_compressor.cpp index 24c64f823..ff1bc9c9f 100644 --- a/src/engine/polyline_compressor.cpp +++ b/src/engine/polyline_compressor.cpp @@ -52,54 +52,27 @@ std::string encode(std::vector &numbers) } return output; } -} -std::vector decodePolyline(const std::string &geometry_string) -{ // https://developers.google.com/maps/documentation/utilities/polylinealgorithm - auto decode_polyline_integer = [](auto &first, auto last) { - // varint coding parameters - const std::uint32_t bits_in_chunk = 5; - const std::uint32_t continuation_bit = 1 << bits_in_chunk; - const std::uint32_t chunk_mask = (1 << bits_in_chunk) - 1; +std::int32_t decode_polyline_integer(std::string::const_iterator &first, std::string::const_iterator last) { + // varint coding parameters + const std::uint32_t bits_in_chunk = 5; + const std::uint32_t continuation_bit = 1 << bits_in_chunk; + const std::uint32_t chunk_mask = (1 << bits_in_chunk) - 1; - std::uint32_t result = 0; - for (std::uint32_t value = continuation_bit, shift = 0; - (value & continuation_bit) && (shift < CHAR_BIT * sizeof(result) - 1) && first != last; - ++first, shift += bits_in_chunk) - { - value = *first - 63; // convert ASCII coding [?..~] to an integer [0..63] - result |= (value & chunk_mask) << shift; - } - - // change "zig-zag" sign coding to two's complement - result = ((result & 1) == 1) ? ~(result >> 1) : (result >> 1); - return static_cast(result); - }; - - auto polyline_to_coordinate = [](auto value) { - return static_cast(value * detail::POLYLINE_TO_COORDINATE); - }; - - std::vector coordinates; - std::int32_t latitude = 0, longitude = 0; - - std::string::const_iterator first = geometry_string.begin(); - const std::string::const_iterator last = geometry_string.end(); - while (first != last) + std::uint32_t result = 0; + for (std::uint32_t value = continuation_bit, shift = 0; + (value & continuation_bit) && (shift < CHAR_BIT * sizeof(result) - 1) && first != last; + ++first, shift += bits_in_chunk) { - const auto dlat = decode_polyline_integer(first, last); - const auto dlon = decode_polyline_integer(first, last); - - latitude += dlat; - longitude += dlon; - - coordinates.emplace_back( - util::Coordinate{util::FixedLongitude{polyline_to_coordinate(longitude)}, - util::FixedLatitude{polyline_to_coordinate(latitude)}}); + value = *first - 63; // convert ASCII coding [?..~] to an integer [0..63] + result |= (value & chunk_mask) << shift; } - return coordinates; + // change "zig-zag" sign coding to two's complement + result = ((result & 1) == 1) ? ~(result >> 1) : (result >> 1); + return static_cast(result); +} } } } From 6b0bcb51717e4174c23107b6a8e789b1116c3a8f Mon Sep 17 00:00:00 2001 From: Lev Dragunov Date: Wed, 7 Jun 2017 15:24:00 +0300 Subject: [PATCH 2/5] Docs and tests --- docs/http.md | 2 +- unit_tests/engine/polyline_compressor.cpp | 60 +++++++++++++++++++++++ 2 files changed, 61 insertions(+), 1 deletion(-) create mode 100644 unit_tests/engine/polyline_compressor.cpp diff --git a/docs/http.md b/docs/http.md index e6c5da427..28601ba1b 100644 --- a/docs/http.md +++ b/docs/http.md @@ -15,7 +15,7 @@ GET /{service}/{version}/{profile}/{coordinates}[.{format}]?option=value&option= | `service` | One of the following values: [`route`](#route-service), [`nearest`](#nearest-service), [`table`](#table-service), [`match`](#match-service), [`trip`](#trip-service), [`tile`](#tile-service) | | `version` | Version of the protocol implemented by the service. `v1` for all OSRM 5.x installations | | `profile` | Mode of transportation, is determined statically by the Lua profile that is used to prepare the data using `osrm-extract`. Typically `car`, `bike` or `foot` if using one of the supplied profiles. | -| `coordinates`| String of format `{longitude},{latitude};{longitude},{latitude}[;{longitude},{latitude} ...]` or `polyline({polyline})`. | +| `coordinates`| String of format `{longitude},{latitude};{longitude},{latitude}[;{longitude},{latitude} ...]` or `polyline({polyline}) or polyline6({polyline6})`. | | `format`| Only `json` is supported at the moment. This parameter is optional and defaults to `json`. | Passing any `option=value` is optional. `polyline` follows Google's polyline format with precision 5 by default and can be generated using [this package](https://www.npmjs.com/package/polyline). diff --git a/unit_tests/engine/polyline_compressor.cpp b/unit_tests/engine/polyline_compressor.cpp new file mode 100644 index 000000000..b8c0fb49a --- /dev/null +++ b/unit_tests/engine/polyline_compressor.cpp @@ -0,0 +1,60 @@ +#include "engine/polyline_compressor.hpp" +#include "util/coordinate.hpp" + +#include +#include + +#include +#include + +BOOST_AUTO_TEST_SUITE(polyline_compression) + +BOOST_AUTO_TEST_CASE(polyline5_test_case) +{ + using namespace osrm::engine; + using namespace osrm::util; + + const std::vector coords({ + {FixedLongitude{-73990171}, FixedLatitude{40714701}}, + {FixedLongitude{-73991801}, FixedLatitude{40717571}}, + {FixedLongitude{-73985751}, FixedLatitude{40715651}} + }); + + const std::vector coords_truncated({ + {FixedLongitude{-73990170}, FixedLatitude{40714700}}, + {FixedLongitude{-73991800}, FixedLatitude{40717570}}, + {FixedLongitude{-73985750}, FixedLatitude{40715650}} + }); + + BOOST_CHECK_EQUAL(encodePolyline(coords.begin(), coords.end()), "{aowFperbM}PdI~Jyd@"); + BOOST_CHECK( + std::equal( + coords_truncated.begin(), + coords_truncated.end(), + decodePolyline(encodePolyline(coords.begin(), coords.end())).begin() + ) + ); +} + +BOOST_AUTO_TEST_CASE(polyline6_test_case) +{ + using namespace osrm::engine; + using namespace osrm::util; + + const std::vector coords({ + {FixedLongitude{-73990171}, FixedLatitude{40714701}}, + {FixedLongitude{-73991801}, FixedLatitude{40717571}}, + {FixedLongitude{-73985751}, FixedLatitude{40715651}} + }); + + BOOST_CHECK_EQUAL(encodePolyline<1000000>(coords.begin(), coords.end()), "y{_tlAt`_clCkrDzdB~vBcyJ"); + BOOST_CHECK( + std::equal( + coords.begin(), + coords.end(), + decodePolyline<1000000>(encodePolyline<1000000>(coords.begin(), coords.end())).begin() + ) + ); +} + +BOOST_AUTO_TEST_SUITE_END() From d7035291ea72c54feba63f9cce852073aef6f261 Mon Sep 17 00:00:00 2001 From: Lev Dragunov Date: Wed, 7 Jun 2017 15:39:08 +0300 Subject: [PATCH 3/5] clang-format --- include/engine/polyline_compressor.hpp | 11 +++-- .../server/api/base_parameters_grammar.hpp | 14 +++--- src/engine/polyline_compressor.cpp | 6 ++- unit_tests/engine/polyline_compressor.cpp | 49 +++++++------------ 4 files changed, 36 insertions(+), 44 deletions(-) diff --git a/include/engine/polyline_compressor.hpp b/include/engine/polyline_compressor.hpp index 81eedec6c..fb64b9de9 100644 --- a/include/engine/polyline_compressor.hpp +++ b/include/engine/polyline_compressor.hpp @@ -15,7 +15,8 @@ namespace engine namespace detail { std::string encode(std::vector &numbers); -std::int32_t decode_polyline_integer(std::string::const_iterator &first, std::string::const_iterator last); +std::int32_t decode_polyline_integer(std::string::const_iterator &first, + std::string::const_iterator last); } using CoordVectorForwardIter = std::vector::const_iterator; // Encodes geometry into polyline format. @@ -59,7 +60,7 @@ std::string encodePolyline(CoordVectorForwardIter begin, CoordVectorForwardIter template std::vector decodePolyline(const std::string &polyline) { - double polyline_to_coordinate = COORDINATE_PRECISION / POLYLINE_PRECISION ; + double polyline_to_coordinate = COORDINATE_PRECISION / POLYLINE_PRECISION; std::vector coordinates; std::int32_t latitude = 0, longitude = 0; @@ -73,9 +74,9 @@ std::vector decodePolyline(const std::string &polyline) latitude += dlat; longitude += dlon; - coordinates.emplace_back( - util::Coordinate{util::FixedLongitude{static_cast(longitude*polyline_to_coordinate)}, - util::FixedLatitude{static_cast(latitude*polyline_to_coordinate)}}); + coordinates.emplace_back(util::Coordinate{ + util::FixedLongitude{static_cast(longitude * polyline_to_coordinate)}, + util::FixedLatitude{static_cast(latitude * polyline_to_coordinate)}}); } return coordinates; } diff --git a/include/server/api/base_parameters_grammar.hpp b/include/server/api/base_parameters_grammar.hpp index f176e0acd..0db81cd4d 100644 --- a/include/server/api/base_parameters_grammar.hpp +++ b/include/server/api/base_parameters_grammar.hpp @@ -129,15 +129,15 @@ struct BaseParametersGrammar : boost::spirit::qi::grammar qi::_1)]; polyline6_rule = qi::as_string[qi::lit("polyline6(") > +polyline_chars > ')'] - [qi::_val = ph::bind( - [](const std::string &polyline) { - return engine::decodePolyline<1000000>(polyline); - }, - qi::_1)]; + [qi::_val = ph::bind( + [](const std::string &polyline) { + return engine::decodePolyline<1000000>(polyline); + }, + qi::_1)]; query_rule = - ((location_rule % ';') | - polyline_rule | polyline6_rule)[ph::bind(&engine::api::BaseParameters::coordinates, qi::_r1) = qi::_1]; + ((location_rule % ';') | polyline_rule | + polyline6_rule)[ph::bind(&engine::api::BaseParameters::coordinates, qi::_r1) = qi::_1]; radiuses_rule = qi::lit("radiuses=") > (-(qi::double_ | unlimited_rule) % diff --git a/src/engine/polyline_compressor.cpp b/src/engine/polyline_compressor.cpp index ff1bc9c9f..eca177980 100644 --- a/src/engine/polyline_compressor.cpp +++ b/src/engine/polyline_compressor.cpp @@ -53,8 +53,10 @@ std::string encode(std::vector &numbers) return output; } - // https://developers.google.com/maps/documentation/utilities/polylinealgorithm -std::int32_t decode_polyline_integer(std::string::const_iterator &first, std::string::const_iterator last) { +// https://developers.google.com/maps/documentation/utilities/polylinealgorithm +std::int32_t decode_polyline_integer(std::string::const_iterator &first, + std::string::const_iterator last) +{ // varint coding parameters const std::uint32_t bits_in_chunk = 5; const std::uint32_t continuation_bit = 1 << bits_in_chunk; diff --git a/unit_tests/engine/polyline_compressor.cpp b/unit_tests/engine/polyline_compressor.cpp index b8c0fb49a..966900bb7 100644 --- a/unit_tests/engine/polyline_compressor.cpp +++ b/unit_tests/engine/polyline_compressor.cpp @@ -14,26 +14,19 @@ BOOST_AUTO_TEST_CASE(polyline5_test_case) using namespace osrm::engine; using namespace osrm::util; - const std::vector coords({ - {FixedLongitude{-73990171}, FixedLatitude{40714701}}, - {FixedLongitude{-73991801}, FixedLatitude{40717571}}, - {FixedLongitude{-73985751}, FixedLatitude{40715651}} - }); + const std::vector coords({{FixedLongitude{-73990171}, FixedLatitude{40714701}}, + {FixedLongitude{-73991801}, FixedLatitude{40717571}}, + {FixedLongitude{-73985751}, FixedLatitude{40715651}}}); - const std::vector coords_truncated({ - {FixedLongitude{-73990170}, FixedLatitude{40714700}}, - {FixedLongitude{-73991800}, FixedLatitude{40717570}}, - {FixedLongitude{-73985750}, FixedLatitude{40715650}} - }); + const std::vector coords_truncated( + {{FixedLongitude{-73990170}, FixedLatitude{40714700}}, + {FixedLongitude{-73991800}, FixedLatitude{40717570}}, + {FixedLongitude{-73985750}, FixedLatitude{40715650}}}); BOOST_CHECK_EQUAL(encodePolyline(coords.begin(), coords.end()), "{aowFperbM}PdI~Jyd@"); - BOOST_CHECK( - std::equal( - coords_truncated.begin(), - coords_truncated.end(), - decodePolyline(encodePolyline(coords.begin(), coords.end())).begin() - ) - ); + BOOST_CHECK(std::equal(coords_truncated.begin(), + coords_truncated.end(), + decodePolyline(encodePolyline(coords.begin(), coords.end())).begin())); } BOOST_AUTO_TEST_CASE(polyline6_test_case) @@ -41,20 +34,16 @@ BOOST_AUTO_TEST_CASE(polyline6_test_case) using namespace osrm::engine; using namespace osrm::util; - const std::vector coords({ - {FixedLongitude{-73990171}, FixedLatitude{40714701}}, - {FixedLongitude{-73991801}, FixedLatitude{40717571}}, - {FixedLongitude{-73985751}, FixedLatitude{40715651}} - }); + const std::vector coords({{FixedLongitude{-73990171}, FixedLatitude{40714701}}, + {FixedLongitude{-73991801}, FixedLatitude{40717571}}, + {FixedLongitude{-73985751}, FixedLatitude{40715651}}}); - BOOST_CHECK_EQUAL(encodePolyline<1000000>(coords.begin(), coords.end()), "y{_tlAt`_clCkrDzdB~vBcyJ"); - BOOST_CHECK( - std::equal( - coords.begin(), - coords.end(), - decodePolyline<1000000>(encodePolyline<1000000>(coords.begin(), coords.end())).begin() - ) - ); + BOOST_CHECK_EQUAL(encodePolyline<1000000>(coords.begin(), coords.end()), + "y{_tlAt`_clCkrDzdB~vBcyJ"); + BOOST_CHECK(std::equal( + coords.begin(), + coords.end(), + decodePolyline<1000000>(encodePolyline<1000000>(coords.begin(), coords.end())).begin())); } BOOST_AUTO_TEST_SUITE_END() From 3602b585170f8678027616c55538141ee102720f Mon Sep 17 00:00:00 2001 From: Lev Dragunov Date: Wed, 7 Jun 2017 21:43:49 +0300 Subject: [PATCH 4/5] Review fixes --- unit_tests/server/url_parser.cpp | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/unit_tests/server/url_parser.cpp b/unit_tests/server/url_parser.cpp index b06c8ce5b..2c3fa0f8d 100644 --- a/unit_tests/server/url_parser.cpp +++ b/unit_tests/server/url_parser.cpp @@ -93,9 +93,9 @@ BOOST_AUTO_TEST_CASE(valid_urls) CHECK_EQUAL_RANGE(reference_5.query, result_5->query); BOOST_CHECK_EQUAL(reference_5.prefix_length, result_5->prefix_length); - // tile - api::ParsedURL reference_6{"route", 1, "profile", "tile(1,2,3).mvt", 18UL}; - auto result_6 = api::parseURL("/route/v1/profile/tile(1,2,3).mvt"); + // polyline6 + api::ParsedURL reference_6{"route", 1, "profile", "polyline6(_ibE?_seK_seK_seK_seK)?", 18UL}; + auto result_6 = api::parseURL("/route/v1/profile/polyline6(_ibE?_seK_seK_seK_seK)?"); BOOST_CHECK(result_6); BOOST_CHECK_EQUAL(reference_6.service, result_6->service); BOOST_CHECK_EQUAL(reference_6.version, result_6->version); @@ -103,17 +103,27 @@ BOOST_AUTO_TEST_CASE(valid_urls) CHECK_EQUAL_RANGE(reference_6.query, result_6->query); BOOST_CHECK_EQUAL(reference_6.prefix_length, result_6->prefix_length); - // polyline with %HEX - api::ParsedURL reference_7{ - "match", 1, "car", "polyline(}jmwFz~ubMyCa@`@yDJqE)?你好&\n \"%~", 14UL}; - auto result_7 = api::parseURL( - "/match/v1/car/polyline(%7DjmwFz~ubMyCa@%60@yDJqE)?%e4%bd%a0%e5%a5%bd&%0A%20%22%25%7e"); + // tile + api::ParsedURL reference_7{"route", 1, "profile", "tile(1,2,3).mvt", 18UL}; + auto result_7 = api::parseURL("/route/v1/profile/tile(1,2,3).mvt"); BOOST_CHECK(result_7); BOOST_CHECK_EQUAL(reference_7.service, result_7->service); BOOST_CHECK_EQUAL(reference_7.version, result_7->version); BOOST_CHECK_EQUAL(reference_7.profile, result_7->profile); CHECK_EQUAL_RANGE(reference_7.query, result_7->query); BOOST_CHECK_EQUAL(reference_7.prefix_length, result_7->prefix_length); + + // polyline with %HEX + api::ParsedURL reference_8{ + "match", 1, "car", "polyline(}jmwFz~ubMyCa@`@yDJqE)?你好&\n \"%~", 14UL}; + auto result_8 = api::parseURL( + "/match/v1/car/polyline(%7DjmwFz~ubMyCa@%60@yDJqE)?%e4%bd%a0%e5%a5%bd&%0A%20%22%25%7e"); + BOOST_CHECK(result_8); + BOOST_CHECK_EQUAL(reference_8.service, result_8->service); + BOOST_CHECK_EQUAL(reference_8.version, result_8->version); + BOOST_CHECK_EQUAL(reference_8.profile, result_8->profile); + CHECK_EQUAL_RANGE(reference_8.query, result_8->query); + BOOST_CHECK_EQUAL(reference_8.prefix_length, result_8->prefix_length); } BOOST_AUTO_TEST_SUITE_END() From bd1532847c404dcd62e7b3b400a574f81f0116a6 Mon Sep 17 00:00:00 2001 From: Lev Dragunov Date: Wed, 7 Jun 2017 21:46:19 +0300 Subject: [PATCH 5/5] Changelog entry --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5dc637665..2d27c7d65 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # 5.8.0 - Changes from 5.7 - API: + - polyline6 support in request string - new parameter `approaches` for `route`, `table`, `trip` and `nearest` requests. This parameter keep waypoints on the curb side. 'approaches' accepts both 'curb' and 'unrestricted' values. Note : the curb side depend on the `ProfileProperties::left_hand_driving`, it's a global property set once by the profile. If you are working with a planet dataset, the api will be wrong in some countries, and right in others.