3

I'm using the Mapbox Vector Tile API and I want to get the data into one or multiple GeoPandas GeoDataFrame. (Python)

After I download the .mvt file, how do I decode it?

What I've tried already:

  • mapbox-vector-tile library, this decodes the file into a dictionary (JSON) but does not transform the given coordinates.
  • python-vt2geojson library, this transforms the coordinates but the information about the layers is lost.
  • ogr2ogr with ogr2ogr -f GeoJSON $file_path_json $file_path_mvt -oo x=$x -oo y=$y -oo z=$z but this yields the error
ERROR 1: Layer 'aeroway' does not already exist in the output dataset, and cannot be created by the output driver.
ERROR 1: Terminating translation prematurely after failed
translation of layer aeroway (use -skipfailures to skip errors)
  • ogr2ogr with ogr2ogr -f "ESRI Shapefile" $file_path_shp $file_path_mvt -oo x=$x -oo y=$y -oo z=$z but this yields the error
Warning 6: Normalized/laundered field name: 'admin_level' to 'admin_leve'
Warning 6: Normalized/laundered field name: 'name_script' to 'name_scrip'
Warning 6: Normalized/laundered field name: 'text_anchor' to 'text_ancho'
Warning 6: Normalized/laundered field name: 'name_zh-Hans' to 'name_zh-Ha'
Warning 6: Normalized/laundered field name: 'name_script' to 'name_scrip'
Warning 6: Normalized/laundered field name: 'name_zh-Hant' to 'name_zh-_1'
Warning 1: One or several characters couldn't be converted correctly from UTF-8 to ISO-8859-1.  This warning will not be emitted anymore.
Warning 6: Normalized/laundered field name: 'name_script' to 'name_scrip'
Warning 6: Normalized/laundered field name: 'shield_text_color' to 'shield_tex'
ERROR 1: Attempt to write non-linestring (GEOMETRYCOLLECTION) geometry to ARC type shapefile.
ERROR 1: Unable to write feature 91 from layer road.
ERROR 1: Terminating translation prematurely after failed
translation of layer road (use -skipfailures to skip errors)

Has anyone a clue of how to decode Mapbox Vector Tile files into files that can be read into GeoPandas with the coordinates transformed to longitude and latitude?

6
  • MVT is a display format - it would be much easier to fetch the data in a better format for importing
    – Ian Turton
    Commented Jun 15, 2021 at 13:42
  • I looked for different solutions, but Mapbox Vector Tiles API seems to be the best solution. My goal is explained in this StackOverflow post
    – Nico G.
    Commented Jun 15, 2021 at 13:49
  • so just download the OSM data as suggested in that answer
    – Ian Turton
    Commented Jun 15, 2021 at 14:02
  • What do you mean by "does not transform the given coordinates."? Commented Jun 15, 2021 at 14:57
  • 1
    @Stefan it returns the coordinates in pixel coordinates between 0 and 4096 rather than Mercator or Geodetic coordinates.
    – Nico G.
    Commented Jun 15, 2021 at 15:41

3 Answers 3

2

Using the mapbox-vector-tile library you can convert back to WGS84 decimal degrees if you know the zoom, column (tile_x), and row (tile_y) of the tile. E.g.:

import mapbox_vector_tile
import math

def pixel2deg(xtile, ytile, zoom, xpixel, ypixel, extent = 4096):
  n = 2.0 ** zoom
  xtile = xtile + (xpixel / extent)
  ytile = ytile + ((extent - ypixel) / extent)
  lon_deg = (xtile / n) * 360.0 - 180.0
  lat_rad = math.atan(math.sinh(math.pi * (1 - 2 * ytile / n)))
  lat_deg = math.degrees(lat_rad)
  return (lon_deg, lat_deg)

mapbox_vector_tile.decode(your_mvt, 
  transformer = lambda x, y: pixel2deg(tile_x, tile_y, tile_zoom, x, y)
)
2
  • What exactly is your_mvt? A Path to an MVT file, or a binary object?
    – zabop
    Commented Nov 8, 2023 at 12:47
  • Dear Mark, how can I convert from WGS84 decimal degrees to zoom/tile_x/tile_y ?
    – doubts
    Commented May 10 at 15:22
0

According to https://github.com/OSGeo/gdal/issues/801#issuecomment-408111458, GDAL only supports one layer per file. Thus, you need to append layer name after your GDAL command.

For example, ogr2ogr -f GeoJSON $file_path_json $file_path_mvt -oo x=$x -oo y=$y -oo z=$z aeroway will generate a geojson file for the aeroway layer.

0

I had a similar problem, and wanted to accomplish this in pure Python and avoid using ogr2ogr as an intermediate step. I adapted @Mark Egge's answer while clarifying the data type question raised by @zabop. From the documentation for mapbox_vector_tile, it expects a byte string so when opening the file, it should be read in as binary, e.g. "rb" GeoPandas's read_file function can parse the resulting dictionary once its converted to a string.

import mapbox_vector_tile
import math
import pandas as pd
import geopandas as gpd
# from Mark Egge's answer
def pixel2deg(xtile, ytile, zoom, xpixel, ypixel, extent = 4096): 
    n = 2.0 ** zoom
    xtile = xtile + (xpixel / extent)
    ytile = ytile + ((extent - ypixel) / extent)
    lon_deg = (xtile / n) * 360.0 - 180.0
    lat_rad = math.atan(math.sinh(math.pi * (1 - 2 * ytile / n)))
    lat_deg = math.degrees(lat_rad)
    return (lon_deg, lat_deg)

filepath = "myfile.vector.pbf" # change to your file
xtile = 168 # replace with the correct values for your file
ytile = 404
zoom = 10
with open(filepath, 'rb') as file:
    data = file.read()
    converted = mapbox_vector_tile.decode(
        data, transformer = lambda x, y: pixel2deg(xtile, ytile, zoom, x, y))
    gdfs = []
    for k in converted.keys():
        gdf = gpd.read_file(str(converted), driver='GeoJSON', layer = k)
        gdfs.append(gdf)

gdfs = pd.concat(gdfs)
1
  • This does not work, because converted is a dictionary. However, you can json.dump(converted, sink) directly to GeoJSON, without the need for (geo)pandas. Commented Jul 8 at 12:21

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.