Сервис построения маршрутов использует кодированный формат полилинии для хранения серии координат широты и долготы в виде одной строки. Полилинейное кодирование значительно уменьшает размер ответа.
Поле shape в ответе сервиса содержит закодированную строку. Для декодирования полилинии в серию координат можно использовать приведённые ниже примеры кода.
polyline.decode = function(str, precision) {
var index = 0,
lat = 0,
lng = 0,
coordinates = [],
shift = 0,
result = 0,
byte = null,
latitude_change,
longitude_change,
factor = Math.pow(10, precision || 6);
// Coordinates have variable length when encoded, so just keep
// track of whether we've hit the end of the string. In each
// loop iteration, a single coordinate is decoded.
while (index < str.length) {
// Reset shift, result, and byte
byte = null;
shift = 0;
result = 0;
do {
byte = str.charCodeAt(index++) - 63;
result |= (byte & 0x1f) << shift;
shift += 5;
} while (byte >= 0x20);
latitude_change = ((result & 1) ? ~(result >> 1) : (result >> 1));
shift = result = 0;
do {
byte = str.charCodeAt(index++) - 63;
result |= (byte & 0x1f) << shift;
shift += 5;
} while (byte >= 0x20);
longitude_change = ((result & 1) ? ~(result >> 1) : (result >> 1));
lat += latitude_change;
lng += longitude_change;
coordinates.push([lat / factor, lng / factor]);
}
return coordinates;
};
#include <vector>
constexpr double kPolylinePrecision = 1E6;
constexpr double kInvPolylinePrecision = 1.0 / kPolylinePrecision;
struct PointLL {
float lat;
float lon;
};
std::vector<PointLL> decode(const std::string& encoded) {
size_t i = 0; // what byte are we looking at
// Handy lambda to turn a few bytes of an encoded string into an integer
auto deserialize = [&encoded, &i](const int previous) {
// Grab each 5 bits and mask it in where it belongs using the shift
int byte, shift = 0, result = 0;
do {
byte = static_cast<int>(encoded[i++]) - 63;
result |= (byte & 0x1f) << shift;
shift += 5;
} while (byte >= 0x20);
// Undo the left shift from above or the bit flipping and add to previous
// since its an offset
return previous + (result & 1 ? ~(result >> 1) : (result >> 1));
};
// Iterate over all characters in the encoded string
std::vector<PointLL> shape;
int last_lon = 0, last_lat = 0;
while (i < encoded.length()) {
// Decode the coordinates, lat first for some reason
int lat = deserialize(last_lat);
int lon = deserialize(last_lon);
// Shift the decimal point 5 places to the left
shape.emplace_back(
static_cast<float>(static_cast<double>(lat) * kInvPolylinePrecision),
static_cast<float>(static_cast<double>(lon) * kInvPolylinePrecision));
// Remember the last one we encountered
last_lon = lon;
last_lat = lat;
}
return shape;
}
#!/usr/bin/env python
import sys
# Six degrees of precision
inv = 1.0 / 1e6
def decode(encoded):
"""Decodes route polyline returned by the routing service."""
decoded = []
i = 0
previous_coords = {'lat': 0, 'lon': 0}
while i < len(encoded):
coords = dict()
for coord_name in ('lat', 'lon'):
coord = 0
shift = 0
byte = 0x20
# Keep decoding bytes until you have this coord.
while byte >= 0x20:
byte = ord(encoded[i]) - 63
i += 1
coord |= (byte & 0x1f) << shift
shift += 5
# Get the final value adding the previous offset and
# remember it for the next.
coords[coord_name] = previous_coords[coord_name] + (
~(coord >> 1)
if coord & 1
else (coord >> 1)
)
# Scale by the precision and chop off long coords.
# Also flip the positions so its the far more standard
# (lon, lat) instead of (lat, lon).
decoded.append([
float(f"{coords['lon'] * inv:.6f}"),
float(f"{coords['lat'] * inv:.6f}"),
])
previous_coords = coords
return decoded
print(decode(sys.argv[1]))