98 lines
3.2 KiB
Python
98 lines
3.2 KiB
Python
import json
|
|
|
|
from .errors import InvalidFeatureError
|
|
import polyline
|
|
|
|
|
|
def _geom_points(geom):
|
|
"""GeoJSON geometry to a sequence of point tuples
|
|
"""
|
|
if geom['type'] == 'Point':
|
|
yield tuple(geom['coordinates'])
|
|
elif geom['type'] in ('MultiPoint', 'LineString'):
|
|
for position in geom['coordinates']:
|
|
yield tuple(position)
|
|
else:
|
|
raise InvalidFeatureError(
|
|
"Unsupported geometry type:{0}".format(geom['type']))
|
|
|
|
|
|
def read_points(features):
|
|
""" Iterable of features to a sequence of point tuples
|
|
Where "features" can be either GeoJSON mappings
|
|
or objects implementing the geo_interface
|
|
"""
|
|
for feature in features:
|
|
|
|
if isinstance(feature, (tuple, list)) and len(feature) == 2:
|
|
yield feature
|
|
|
|
elif hasattr(feature, '__geo_interface__'):
|
|
# An object implementing the geo_interface
|
|
try:
|
|
# Could be a Feature...
|
|
geom = feature.__geo_interface__['geometry']
|
|
for pt in _geom_points(geom):
|
|
yield pt
|
|
except KeyError:
|
|
# ... or a geometry directly
|
|
for pt in _geom_points(feature.__geo_interface__):
|
|
yield pt
|
|
|
|
elif 'type' in feature and feature['type'] == 'Feature':
|
|
# A GeoJSON-like mapping
|
|
geom = feature['geometry']
|
|
for pt in _geom_points(geom):
|
|
yield pt
|
|
|
|
elif 'coordinates' in feature:
|
|
geom = feature
|
|
for pt in _geom_points(geom):
|
|
yield pt
|
|
|
|
else:
|
|
raise InvalidFeatureError(
|
|
"Unknown object: Not a GeoJSON Point feature or "
|
|
"an object with __geo_interface__:\n{0}".format(feature))
|
|
|
|
|
|
def encode_waypoints(features, min_limit=None, max_limit=None, precision=6):
|
|
"""Given an iterable of features
|
|
return a string encoded in waypoint-style used by certain mapbox APIs
|
|
("lon,lat" pairs separated by ";")
|
|
"""
|
|
coords = ['{lon},{lat}'.format(
|
|
lon=float(round(lon, precision)),
|
|
lat=float(round(lat, precision)))
|
|
for lon, lat in read_points(features)]
|
|
|
|
if min_limit is not None and len(coords) < min_limit:
|
|
raise InvalidFeatureError(
|
|
"Not enough features to encode coordinates, "
|
|
"need at least {0}".format(min_limit))
|
|
if max_limit is not None and len(coords) > max_limit:
|
|
raise InvalidFeatureError(
|
|
"Too many features to encode coordinates, "
|
|
"need at most {0}".format(max_limit))
|
|
|
|
return ';'.join(coords)
|
|
|
|
|
|
def encode_polyline(features):
|
|
"""Encode and iterable of features as a polyline
|
|
"""
|
|
points = list(read_points(features))
|
|
latlon_points = [(x[1], x[0]) for x in points]
|
|
return polyline.encode(latlon_points)
|
|
|
|
|
|
def encode_coordinates_json(features):
|
|
"""Given an iterable of features
|
|
return a JSON string to be used as the request body for the distance API:
|
|
a JSON object, with a key coordinates,
|
|
which has an array of [ Longitude, Lattitude ] pairs
|
|
"""
|
|
coords = {
|
|
'coordinates': list(read_points(features))}
|
|
return json.dumps(coords)
|