Geometry Decoding
When a response includes a geometry the data might be encoded as single string. This is a so-called encoded polyline. If no additional elevation data is requested, this can be decoded with a standard polyline decoder, available e.g. from Mapbox or others.
When you request additional elevation data, this encoded string can not be decoded with a standard polyline decoder. The reason for that is, that the elevation data is included in the polyline in addition to the latitude and longitude values. To decode X,Y and X,Y,Z polylines the decoder needs to know whether the geometry has elevation information. Examples on how to decode X,Y,Z polylines can be found below:
Java
java
import org.json.JSONArray;
public class GeometryDecoder {
private static JSONArray decodeGeometry(String encodedGeometry, boolean inclElevation) {
JSONArray geometry = new JSONArray();
int len = encodedGeometry.length();
int index = 0;
int lat = 0;
int lng = 0;
int ele = 0;
while (index < len) {
int result = 1;
int shift = 0;
int b;
do {
b = encodedGeometry.charAt(index++) - 63 - 1;
result += b << shift;
shift += 5;
} while (b >= 0x1f);
lat += (result & 1) != 0 ? ~(result >> 1) : (result >> 1);
result = 1;
shift = 0;
do {
b = encodedGeometry.charAt(index++) - 63 - 1;
result += b << shift;
shift += 5;
} while (b >= 0x1f);
lng += (result & 1) != 0 ? ~(result >> 1) : (result >> 1);
if(inclElevation){
result = 1;
shift = 0;
do {
b = encodedGeometry.charAt(index++) - 63 - 1;
result += b << shift;
shift += 5;
} while (b >= 0x1f);
ele += (result & 1) != 0 ? ~(result >> 1) : (result >> 1);
}
JSONArray location = new JSONArray();
try {
location.put(lat / 1E5);
location.put(lng / 1E5);
if(inclElevation){
location.put((float) (ele / 100));
}
geometry.put(location);
} catch (JSONException e) {
e.printStackTrace();
}
}
return geometry;
}
}
JavaScript
js
/**
* Decode an x,y or x,y,z encoded polyline
* @param {*} encodedPolyline
* @param {Boolean} includeElevation - true for x,y,z polyline
* @returns {Array} of coordinates
*/
decodePolyline: (encodedPolyline, includeElevation) => {
// array that holds the points
let points = []
let index = 0
const len = encodedPolyline.length
let lat = 0
let lng = 0
let ele = 0
while (index < len) {
let b
let shift = 0
let result = 0
do {
b = encodedPolyline.charAt(index++).charCodeAt(0) - 63 // finds ascii
// and subtract it by 63
result |= (b & 0x1f) << shift
shift += 5
} while (b >= 0x20)
lat += ((result & 1) !== 0 ? ~(result >> 1) : (result >> 1))
shift = 0
result = 0
do {
b = encodedPolyline.charAt(index++).charCodeAt(0) - 63
result |= (b & 0x1f) << shift
shift += 5
} while (b >= 0x20)
lng += ((result & 1) !== 0 ? ~(result >> 1) : (result >> 1))
if (includeElevation) {
shift = 0
result = 0
do {
b = encodedPolyline.charAt(index++).charCodeAt(0) - 63
result |= (b & 0x1f) << shift
shift += 5
} while (b >= 0x20)
ele += ((result & 1) !== 0 ? ~(result >> 1) : (result >> 1))
}
try {
let location = [(lat / 1E5), (lng / 1E5)]
if (includeElevation) location.push((ele / 100))
points.push(location)
} catch (e) {
console.log(e)
}
}
return points
}
Python
python
def decode_polyline(polyline, is3d=False):
"""Decodes a Polyline string into a GeoJSON geometry.
:param polyline: An encoded polyline, only the geometry.
:type polyline: string
:param is3d: Specifies if geometry contains Z component.
:type is3d: boolean
:returns: GeoJSON Linestring geometry
:rtype: dict
"""
points = []
index = lat = lng = z = 0
while index < len(polyline):
result = 1
shift = 0
while True:
b = ord(polyline[index]) - 63 - 1
index += 1
result += b << shift
shift += 5
if b < 0x1F:
break
lat += (~result >> 1) if (result & 1) != 0 else (result >> 1)
result = 1
shift = 0
while True:
b = ord(polyline[index]) - 63 - 1
index += 1
result += b << shift
shift += 5
if b < 0x1F:
break
lng += ~(result >> 1) if (result & 1) != 0 else (result >> 1)
if is3d:
result = 1
shift = 0
while True:
b = ord(polyline[index]) - 63 - 1
index += 1
result += b << shift
shift += 5
if b < 0x1F:
break
if (result & 1) != 0:
z += ~(result >> 1)
else:
z += result >> 1
points.append(
[
round(lng * 1e-5, 6),
round(lat * 1e-5, 6),
round(z * 1e-2, 1),
]
)
else:
points.append([round(lng * 1e-5, 6), round(lat * 1e-5, 6)])
geojson = {u"type": u"LineString", u"coordinates": points}
return geojson