Skip to content

Commit 7d72dfe

Browse files
Optimise encodePolyline function (#6940)
1 parent 3d01d96 commit 7d72dfe

File tree

4 files changed

+122
-47
lines changed

4 files changed

+122
-47
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
- NodeJS:
2525
- CHANGED: Use node-api instead of NAN. [#6452](https://github.com/Project-OSRM/osrm-backend/pull/6452)
2626
- Misc:
27+
- CHANGED: Optimise encodePolyline function. [#6940](https://github.com/Project-OSRM/osrm-backend/pull/6940)
2728
- CHANGED: Avoid reallocations in base64 encoding. [#6951](https://github.com/Project-OSRM/osrm-backend/pull/6951)
2829
- CHANGED: Get rid of unused Boost dependencies. [#6960](https://github.com/Project-OSRM/osrm-backend/pull/6960)
2930
- CHANGED: Apply micro-optimisation for Table & Trip APIs. [#6949](https://github.com/Project-OSRM/osrm-backend/pull/6949)

include/engine/polyline_compressor.hpp

Lines changed: 17 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ namespace osrm::engine
1212
{
1313
namespace detail
1414
{
15-
std::string encode(std::vector<int> &numbers);
15+
void encode(int number_to_encode, std::string &output);
1616
std::int32_t decode_polyline_integer(std::string::const_iterator &first,
1717
std::string::const_iterator last);
1818
} // namespace detail
@@ -30,27 +30,24 @@ std::string encodePolyline(CoordVectorForwardIter begin, CoordVectorForwardIter
3030
return {};
3131
}
3232

33-
std::vector<int> delta_numbers;
34-
BOOST_ASSERT(size > 0);
35-
delta_numbers.reserve((size - 1) * 2);
33+
std::string output;
34+
// just a guess that we will need ~4 bytes per coordinate to avoid reallocations
35+
output.reserve(size * 4);
36+
3637
int current_lat = 0;
3738
int current_lon = 0;
38-
std::for_each(
39-
begin,
40-
end,
41-
[&delta_numbers, &current_lat, &current_lon, coordinate_to_polyline](
42-
const util::Coordinate loc)
43-
{
44-
const int lat_diff =
45-
std::round(static_cast<int>(loc.lat) * coordinate_to_polyline) - current_lat;
46-
const int lon_diff =
47-
std::round(static_cast<int>(loc.lon) * coordinate_to_polyline) - current_lon;
48-
delta_numbers.emplace_back(lat_diff);
49-
delta_numbers.emplace_back(lon_diff);
50-
current_lat += lat_diff;
51-
current_lon += lon_diff;
52-
});
53-
return detail::encode(delta_numbers);
39+
for (auto it = begin; it != end; ++it)
40+
{
41+
const int lat_diff =
42+
std::round(static_cast<int>(it->lat) * coordinate_to_polyline) - current_lat;
43+
const int lon_diff =
44+
std::round(static_cast<int>(it->lon) * coordinate_to_polyline) - current_lon;
45+
detail::encode(lat_diff, output);
46+
detail::encode(lon_diff, output);
47+
current_lat += lat_diff;
48+
current_lon += lon_diff;
49+
}
50+
return output;
5451
}
5552

5653
// Decodes geometry from polyline format

src/engine/polyline_compressor.cpp

Lines changed: 12 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,19 @@
1010
namespace osrm::engine::detail // anonymous to keep TU local
1111
{
1212

13-
std::string encode(int number_to_encode)
13+
void encode(int number_to_encode, std::string &output)
1414
{
15-
std::string output;
15+
if (number_to_encode < 0)
16+
{
17+
const unsigned binary = std::llabs(number_to_encode);
18+
const unsigned twos = (~binary) + 1u;
19+
const unsigned shl = twos << 1u;
20+
number_to_encode = static_cast<int>(~shl);
21+
}
22+
else
23+
{
24+
number_to_encode <<= 1u;
25+
}
1626
while (number_to_encode >= 0x20)
1727
{
1828
const int next_value = (0x20 | (number_to_encode & 0x1f)) + 63;
@@ -22,31 +32,6 @@ std::string encode(int number_to_encode)
2232

2333
number_to_encode += 63;
2434
output += static_cast<char>(number_to_encode);
25-
return output;
26-
}
27-
28-
std::string encode(std::vector<int> &numbers)
29-
{
30-
std::string output;
31-
for (auto &number : numbers)
32-
{
33-
if (number < 0)
34-
{
35-
const unsigned binary = std::llabs(number);
36-
const unsigned twos = (~binary) + 1u;
37-
const unsigned shl = twos << 1u;
38-
number = static_cast<int>(~shl);
39-
}
40-
else
41-
{
42-
number <<= 1u;
43-
}
44-
}
45-
for (const int number : numbers)
46-
{
47-
output += encode(number);
48-
}
49-
return output;
5035
}
5136

5237
// https://developers.google.com/maps/documentation/utilities/polylinealgorithm

unit_tests/engine/polyline_compressor.cpp

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,4 +45,96 @@ BOOST_AUTO_TEST_CASE(polyline6_test_case)
4545
decodePolyline<1000000>(encodePolyline<1000000>(coords.begin(), coords.end())).begin()));
4646
}
4747

48+
BOOST_AUTO_TEST_CASE(empty_polyline_test)
49+
{
50+
using namespace osrm::engine;
51+
using namespace osrm::util;
52+
53+
std::vector<Coordinate> empty_coords;
54+
BOOST_CHECK_EQUAL(encodePolyline(empty_coords.begin(), empty_coords.end()), "");
55+
BOOST_CHECK(decodePolyline("").empty());
56+
}
57+
BOOST_AUTO_TEST_CASE(polyline_single_point_test)
58+
{
59+
using namespace osrm::engine;
60+
using namespace osrm::util;
61+
62+
const std::vector<Coordinate> coords({{FixedLongitude{-122414000}, FixedLatitude{37776000}}});
63+
64+
const std::string encoded = encodePolyline(coords.begin(), coords.end());
65+
BOOST_CHECK_EQUAL(encoded, "_cqeFn~cjV");
66+
67+
const auto decoded = decodePolyline(encoded);
68+
BOOST_CHECK_EQUAL(decoded.size(), 1);
69+
BOOST_CHECK_EQUAL(decoded[0].lon, FixedLongitude{-122414000});
70+
BOOST_CHECK_EQUAL(decoded[0].lat, FixedLatitude{37776000});
71+
}
72+
73+
BOOST_AUTO_TEST_CASE(polyline_multiple_points_test)
74+
{
75+
using namespace osrm::engine;
76+
using namespace osrm::util;
77+
78+
const std::vector<Coordinate> coords({{FixedLongitude{-122414000}, FixedLatitude{37776000}},
79+
{FixedLongitude{-122420000}, FixedLatitude{37779000}},
80+
{FixedLongitude{-122421000}, FixedLatitude{37780000}}});
81+
82+
const std::string encoded = encodePolyline(coords.begin(), coords.end());
83+
BOOST_CHECK_EQUAL(encoded, "_cqeFn~cjVwQnd@gEfE");
84+
85+
const auto decoded = decodePolyline(encoded);
86+
BOOST_CHECK_EQUAL(decoded.size(), 3);
87+
for (size_t i = 0; i < coords.size(); ++i)
88+
{
89+
BOOST_CHECK_EQUAL(decoded[i].lon, coords[i].lon);
90+
BOOST_CHECK_EQUAL(decoded[i].lat, coords[i].lat);
91+
}
92+
}
93+
94+
BOOST_AUTO_TEST_CASE(polyline_large_coordinate_difference_test)
95+
{
96+
using namespace osrm::engine;
97+
using namespace osrm::util;
98+
99+
const std::vector<Coordinate> coords({{FixedLongitude{-179000000}, FixedLatitude{-89000000}},
100+
{FixedLongitude{179000000}, FixedLatitude{89000000}}});
101+
102+
const std::string encoded = encodePolyline(coords.begin(), coords.end());
103+
BOOST_CHECK_EQUAL(encoded, "~xe~O~|oca@_sl}`@_{`hcA");
104+
105+
const auto decoded = decodePolyline(encoded);
106+
BOOST_CHECK_EQUAL(decoded.size(), 2);
107+
for (size_t i = 0; i < coords.size(); ++i)
108+
{
109+
BOOST_CHECK_EQUAL(decoded[i].lon, coords[i].lon);
110+
BOOST_CHECK_EQUAL(decoded[i].lat, coords[i].lat);
111+
}
112+
}
113+
114+
BOOST_AUTO_TEST_CASE(roundtrip)
115+
{
116+
using namespace osrm::engine;
117+
using namespace osrm::util;
118+
119+
{
120+
const auto encoded = "_chxEn`zvN\\\\]]";
121+
const auto decoded = decodePolyline(encoded);
122+
const auto reencoded = encodePolyline(decoded.begin(), decoded.end());
123+
BOOST_CHECK_EQUAL(encoded, reencoded);
124+
}
125+
{
126+
const auto encoded =
127+
"gcneIpgxzRcDnBoBlEHzKjBbHlG`@`IkDxIiKhKoMaLwTwHeIqHuAyGXeB~Ew@fFjAtIzExF";
128+
const auto decoded = decodePolyline(encoded);
129+
const auto reencoded = encodePolyline(decoded.begin(), decoded.end());
130+
BOOST_CHECK_EQUAL(encoded, reencoded);
131+
}
132+
{
133+
const auto encoded = "_p~iF~ps|U_ulLnnqC_mqNvxq`@";
134+
const auto decoded = decodePolyline(encoded);
135+
const auto reencoded = encodePolyline(decoded.begin(), decoded.end());
136+
BOOST_CHECK_EQUAL(encoded, reencoded);
137+
}
138+
}
139+
48140
BOOST_AUTO_TEST_SUITE_END()

0 commit comments

Comments
 (0)