Merge pull request #4134 from gojuno/polyline_input
Polyline6 support for the http REST API
This commit is contained in:
commit
b8bb12b2e2
@ -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.
|
||||
|
@ -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).
|
||||
|
@ -14,10 +14,9 @@ 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<int> &numbers);
|
||||
std::int32_t decode_polyline_integer(std::string::const_iterator &first,
|
||||
std::string::const_iterator last);
|
||||
}
|
||||
using CoordVectorForwardIter = std::vector<util::Coordinate>::const_iterator;
|
||||
// Encodes geometry into polyline format.
|
||||
@ -57,7 +56,30 @@ std::string encodePolyline(CoordVectorForwardIter begin, CoordVectorForwardIter
|
||||
|
||||
// Decodes geometry from polyline format
|
||||
// See: https://developers.google.com/maps/documentation/utilities/polylinealgorithm
|
||||
std::vector<util::Coordinate> decodePolyline(const std::string &polyline);
|
||||
|
||||
template <unsigned POLYLINE_PRECISION = 100000>
|
||||
std::vector<util::Coordinate> decodePolyline(const std::string &polyline)
|
||||
{
|
||||
double polyline_to_coordinate = COORDINATE_PRECISION / POLYLINE_PRECISION;
|
||||
std::vector<util::Coordinate> 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<std::int32_t>(longitude * polyline_to_coordinate)},
|
||||
util::FixedLatitude{static_cast<std::int32_t>(latitude * polyline_to_coordinate)}});
|
||||
}
|
||||
return coordinates;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -128,9 +128,16 @@ struct BaseParametersGrammar : boost::spirit::qi::grammar<Iterator, Signature>
|
||||
},
|
||||
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];
|
||||
((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) %
|
||||
@ -176,6 +183,7 @@ struct BaseParametersGrammar : boost::spirit::qi::grammar<Iterator, Signature>
|
||||
qi::rule<Iterator, osrm::engine::Bearing()> bearing_rule;
|
||||
qi::rule<Iterator, osrm::util::Coordinate()> location_rule;
|
||||
qi::rule<Iterator, std::vector<osrm::util::Coordinate>()> polyline_rule;
|
||||
qi::rule<Iterator, std::vector<osrm::util::Coordinate>()> polyline6_rule;
|
||||
|
||||
qi::rule<Iterator, unsigned char()> base64_char;
|
||||
qi::rule<Iterator, std::string()> polyline_chars;
|
||||
|
@ -52,54 +52,29 @@ std::string encode(std::vector<int> &numbers)
|
||||
}
|
||||
return output;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<util::Coordinate> decodePolyline(const std::string &geometry_string)
|
||||
// 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
|
||||
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;
|
||||
// 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<std::int32_t>(result);
|
||||
};
|
||||
|
||||
auto polyline_to_coordinate = [](auto value) {
|
||||
return static_cast<std::int32_t>(value * detail::POLYLINE_TO_COORDINATE);
|
||||
};
|
||||
|
||||
std::vector<util::Coordinate> 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<std::int32_t>(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
49
unit_tests/engine/polyline_compressor.cpp
Normal file
49
unit_tests/engine/polyline_compressor.cpp
Normal file
@ -0,0 +1,49 @@
|
||||
#include "engine/polyline_compressor.hpp"
|
||||
#include "util/coordinate.hpp"
|
||||
|
||||
#include <boost/test/test_case_template.hpp>
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(polyline_compression)
|
||||
|
||||
BOOST_AUTO_TEST_CASE(polyline5_test_case)
|
||||
{
|
||||
using namespace osrm::engine;
|
||||
using namespace osrm::util;
|
||||
|
||||
const std::vector<Coordinate> coords({{FixedLongitude{-73990171}, FixedLatitude{40714701}},
|
||||
{FixedLongitude{-73991801}, FixedLatitude{40717571}},
|
||||
{FixedLongitude{-73985751}, FixedLatitude{40715651}}});
|
||||
|
||||
const std::vector<Coordinate> 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<Coordinate> 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()
|
@ -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()
|
||||
|
Loading…
Reference in New Issue
Block a user