The new Mapillary API v4 is now online, replacing API v3. The old API will continue to function until August 18th, 2021, however, from June 21 onwards it will not serve new images. If you are building a new app, or looking to migrate your existing one, then API v4 is the way to go!
API v4 comes with fresh documentation to help you get acquainted, but in addition, we are going to review some important aspects of the API and examples of how to get started.
https://tiles.mapillary.com/maps/vtp/mly1_public/2/{z}/{x}/{y}?access_token=XXX
This differs from using the metadata endpoint, where the access token should be included in the request header. If you already know an image key or a list of keys, it is a simple process to retrieve data about that image. First, think about what fields you want to you include, some options being:
There are several more options in the documentation. Assuming we already have an access token `MLY|XXX`, and want to retrieve the image key `1933525276802129`, we can make the following API request in the command line:
Curl -H “Authorization OAuth: MLY|XXX” https://graph.mapillary.com/1933525276802129?fields=id,captured_at,compass_angle,sequence,geometry
This generates a JSON response:
{ "id": "1933525276802129", "captured_at": 1507560881000, "compass_angle": 200.93476466846, "sequence": "f1ihYvTZNTEhCCnRjyMAuQ", "geometry": { "type": "Point", "coordinates": [ -97.743279722222, 30.270651388889 ] } }
By default, the id property will be included in the response, but all others must be specified in the list of fields.
If we know the ID of a map feature, we can retrieve information about it using the same method as images. For example, with map feature ID `852766358956987` we can find out what type it is (traffic sign or point), the value, the timestamp of the first image to capture it, and the geometry of the point:
Curl -H “Authorization OAuth: MLY|XXX” https://graph.mapillary.com/852766358956987?fields=id,object_value,object_type,geometry,first_seen_at
The response is a JSON object:
{ "id": "852766358956987", "object_value": "object--street-light", "object_type": "mvd_fast", "geometry": { "type": "Point", "coordinates": [ 55.616435388977, 12.99796317955 ] }, "first_seen_at": "2013-11-01T10:41:56+0000" }
With an image key in mind, you can also make a request to get all detections in the image. This is achieved by making the same basic API request as previously demonstrated, but adding `/detections`. For detections, we also should specify fields which we want in the response data, such as `value` which describes what the detection’s class is, and `created_at` which is the timestamp when the detection was derived from the image. If a field is not specified, it will be excluded from the response data. Recycling our last example, we can make this request:
Curl -H “Authorization OAuth: MLY|XXX” https://graph.mapillary.com/1933525276802129/detections?fields=id,value,created_at
This will return a list of detections, each a JSON object: { "id": "1942105415944115", "value": "regulatory--no-parking--g2", "created_at": "2021-05-20T17:49:01+0000" }
Similarly, you can make the same request with a map feature ID rather than an image ID, and get a list of detections that were used to compose the map feature. If you have a known geographic location such as map coordinates where a user clicked, you may want to retrieve images nearby. This requires use of a third party library to find the `z/x/y` tile coordinates, retrieve that tile, then filter for images or map features in the tile that are in a specified radius of the input coordinates.
Similarly, you may want to get all Mapillary data in a bounding box. This can again be achieved by calculating the `z/x/y` coordinates of tiles that overlap this bounding box with a third party library, then selecting from this only the images that fall inside the bounding box.
In the following example, we will utilize Python with the Mercantile library and the Mapbox Vector Tile library to request utility poles and street lights within a bounding box.
import mercantile, mapbox_vector_tile, requests, json
from vt2geojson.tools import vt_bytes_to_geojson
# define an empty geojson as output
output= { "type": "FeatureCollection", "features": [] }
# Mapillary access token -- user should provide their own
access_token = 'MLY|XXX'
# a bounding box in [west_lng,_south_lat,east_lng,north_lat] format
west, south, east, north = [-80.13423442840576,25.77376933762778,-80.1264238357544,25.788608487732198]
filter_values = ['object--support--utility-pole','object--street-light']
# get the tiles with x and y coors intersecting bbox at zoom 14 only
tiles = list(mercantile.tiles(east, south, west, north, 14))
With this, we have a list of tiles that contain all the features we want to download, as well as extra features outside the bounding box. To download, we will use `vt2geojson` library to decode the data, filter it according to what we want, and then save what falls inside the bounding box:
# loop through all tiles to get IDs of Mapillary data
for tile in tiles:
tile_url = 'https://tiles.mapillary.com/maps/vtp/mly_map_feature_point/2/{}/{}/{}?access_token={}'.format(tile.z,tile.x,tile.y,access_token)
response = requests.get(tile_url)
data = vt_bytes_to_geojson(response.content, tile.x, tile.y, tile.z)
filtered_data = [feature for feature in data['features'] if feature['properties']['value'] in filter_values]
for feature in filtered_data:
if (feature['geometry']['coordinates'][0] > east and feature['geometry']['coordinates'][0] < west)\
and (feature['geometry']['coordinates'][1] > south and feature['geometry']['coordinates'][1] < north):
output['features'].append(feature)
Similarly, you can input a point and use Mercantile to find the tile it intersects. After retrieving that tile, you should use a geospatial function such as `haversine()` from the `haversine` library to calculate distance between point pairs. The following example demonstrates finding the tile intersecting a point, then filtering for data within a specified radius and with matching values:
import mercantile, mapbox_vector_tile, requests, json
from vt2geojson.tools import vt_bytes_to_geojson
from haversine import haversine
# define an empty geojson as output
output= { "type": "FeatureCollection", "features": [] }
# Mapillary access token -- in a library the user should provide their own
access_token = 'MLY|XXX'
# values we want in the output
filter_values = ['object--support--utility-pole','object--street-light']
input_coords = [13.01765352487564,55.60817207168841]
zoom = 14
filter_radius = 200
tile = mercantile.tile(input_coords[0],input_coords[1],zoom)
tile_url = 'https://tiles.mapillary.com/maps/vtp/mly_map_feature_point/2/{}/{}/{}?access_token={}'.format(zoom,tile[0],tile[1],access_token)
response = requests.get(tile_url)
data = vt_bytes_to_geojson(response.content, tile.x, tile.y, tile.z)
filtered_data = [feature for feature in data['features'] if feature['properties']['value'] in filter_values]
for feature in filtered_data:
distance = haversine(input_coords,feature['geometry']['coordinates'],unit="m")
if distance < filter_radius:
output['features'].append(feature)
print(len(output['features']))
Finally, you can save the `output` variable to a local file:
with open('mydata.geojson', 'w') as f:
json.dump(output, f)
If you are creating a web map, you can use libraries like Leaflet.js, the Esri JS API, or MapLibre to add the Mapillary vector tiles.For example, in MapLibre JS GL, you might add a layer using the following code:
map.on('load', function() {
map.addSource('mapillary', {
type: 'vector',
tiles: ['https://tiles.mapillary.com/maps/vtp/mly1_public/2/{z}/{x}/{y}'],
minzoom: 6,
maxzoom: 14
});
map.addLayer({
'id': 'mapillary-sequences',
'type': 'line',
'source': 'mapillary',
'source-layer': 'sequence',
'layout': {
'line-join': 'round',
'line-cap': 'round'
},
'paint': {
'line-color': '#05CB63',
'line-width': 1
}
});
map.addLayer({
'id': 'mapillary-images',
'type': 'circle',
'source': 'mapillary',
'source-layer': 'image',
'paint': {
'circle-color': '#05CB63',
'circle-radius': 5,
}
});
});
The lines and points will load on the map, turning out like the image below:
Mapillary vector tiles showing image coverage in central DublinIf your use case is not covered in this post but you still have a question, feel free to share with the community in the Mapillary forum or reach out to Mapillary support. We will continue to share any news and updates about API v4 as we process user and developer feedback, as well as sharing new examples to help cover diverse use cases.
In addition to sending us questions, make sure to share examples of your developer work! We are happy to see integrations across multiple platforms upgrading to the v4 API, including RapiD and JOSM editors for OpenStreetMap, and hope you will find the new API helpful in developing your own projects going forward.