Stitch image tiles into larger composite TIFs

Overview

untiler

Build Status Coverage Status

Utility to take a directory of {z}/{x}/{y}.(jpg|png) tiles, and stitch into a scenetiff (tif w/ exact merc tile bounds). Future versions will support fast indexed reading directly from tar archives.

Install

make a virtual env + activate, then:

pip install untiler

Dev installation

git clone [email protected]:mapbox/untiler.git

cd untiler

pip install -e .

Usage

Usage: untiler [OPTIONS] COMMAND [ARGS]...

Options:
  --help  Show this message and exit.

Commands:
  inspectdir
  streamdir
  streammbtiles

streamdir

Given a directory of tiles + a read template, mosaic into tifs at a lower parent "composite" zoom extent

untiler streamdir [OPTIONS] INPUT_DIR OUTPUT_DIR

-c, --compositezoom INTEGER  Tile size to mosaic into [default=13]
-z, --maxzoom INTEGER        Force a maxzom [default=max in each
                           compositezoom area]
-l, --logdir TEXT            Location for log files [default=None]
-t, --readtemplate TEXT      File path template
                           [default='jpg/{z}/{x}/{y}.jpg']
-s, --scenetemplate TEXT     Template for output scenetif filenames
                           [default='{z}-{x}-{y}-tile.tif']
-w, --workers INTEGER        Number of workers in the processing pool
                           [default=4]
-x, --no-fill                Don't fill in with lower zooms
-r, --tile-resolution       Size of input tiles for eg 256, 512 etc
--help                       Show this message and exit.

streammbtiles

Mosaic an mbtiles into tifs of "composite" zoom extent

untiler streammbtiles [OPTIONS] MBTILES OUTPUT_DIR

Options:
  --co NAME=VALUE              Driver specific creation options.See the
                               documentation for the selected output driver
                               for more information.
  -c, --compositezoom INTEGER  Tile size to mosaic into [default=13]
  -z, --maxzoom INTEGER        Force a maxzom [default=max in each
                               compositezoom area]
  -s, --scenetemplate TEXT     Template for output scenetif filenames
                               [default='{z}-{x}-{y}-tile.tif']
  -w, --workers INTEGER        Number of workers in the processing pool
                               [default=4]
  -x, --no-fill                Don't fill in with lower zooms
  --help                       Show this message and exit.

inspectdir

Stream [x, y, z]s of a directory

untiler inspectdir [OPTIONS] INPUT_DIR

Options:
-z, --zoom INTEGER  Zoom to inspect [default = all]
--help              Show this message and exit.

Outputs a line-delimited stream of tile [x, y, z]s; useful to pipe into mercantile shapes to visualize geometry:

untiler inspectdir <dir> -z 19 | mercantile shapes | fio collect | geojsonio
Comments
  • KeyError: '\\d'

    KeyError: '\\d'

    Hi, I recently installed untiler. When I run any of the untiler commands, I get the error attached below. I am using Python 3.9.1.

    I'm not sure if it's an issue with the python version, I am currently trying to downgrade my python version, and will update this issue accordingly.

    in __exit__
    Traceback (most recent call last):
      File "/usr/lib/python3.9/sre_parse.py", line 1039, in parse_template
        this = chr(ESCAPES[this][1])
    KeyError: '\\d'
    
    During handling of the above exception, another exception occurred:
    
    Traceback (most recent call last):
      File "/home/c_abraham/.local/bin/untiler", line 8, in <module>
        sys.exit(cli())
      File "/usr/lib/python3.9/site-packages/click/core.py", line 829, in __call__
        return self.main(*args, **kwargs)
      File "/usr/lib/python3.9/site-packages/click/core.py", line 782, in main
        rv = self.invoke(ctx)
      File "/usr/lib/python3.9/site-packages/click/core.py", line 1259, in invoke
        return _process_result(sub_ctx.command.invoke(sub_ctx))
      File "/usr/lib/python3.9/site-packages/click/core.py", line 1066, in invoke
        return ctx.invoke(self.callback, **ctx.params)
      File "/usr/lib/python3.9/site-packages/click/core.py", line 610, in invoke
        return callback(*args, **kwargs)
      File "/home/c_abraham/.local/lib/python3.9/site-packages/untiler/scripts/cli.py", line 53, in streammbtiles
        untiler.stream_dir(input_tile_dir, output_dir, compositezoom, maxzoom, None, readtemplate, scenetemplate, workers, creation_options, no_fill)
      File "/home/c_abraham/.local/lib/python3.9/site-packages/untiler/__init__.py", line 225, in stream_dir
        template, readTemplate, separator = tile_utils.parse_template("%s/%s" % (inputDir, read_template))
      File "/home/c_abraham/.local/lib/python3.9/site-packages/untiler/scripts/tile_utils.py", line 151, in parse_template
        return valPattern.sub('\d+', template), valPattern.sub('%s', template), separator[0]
      File "/usr/lib/python3.9/re.py", line 327, in _subx
        template = _compile_repl(template, pattern)
      File "/usr/lib/python3.9/re.py", line 318, in _compile_repl
        return sre_parse.parse_template(repl, pattern)
      File "/usr/lib/python3.9/sre_parse.py", line 1042, in parse_template
        raise s.error('bad escape %s' % this, len(this))
    re.error: bad escape \d at position 0
    
    opened by abrac 5
  • Fix tests, update Rasterio

    Fix tests, update Rasterio

    Changes:

    • Replaces libgdal1h with libgdal1i in the Travis build
    • Updates rasterio from 1.0a8 to 1.1.2
    • Instead of checking exit codes, tests now explicitly check for the type of exception raised
    • Fixes #24
    • Drops Python 2.7 Travis builds
    opened by bhavika 4
  • adding compression selection handling

    adding compression selection handling

    Adds:

    • Input of compress and other --cos
    • An option to not fill w/ lower zooms -x
    • When an input tiles is RGBA, use the alpha band
    • Adds streaming directly from mbtiles

    cc @camillacaros

    opened by dnomadb 4
  • sudo: false with rasterio deps

    sudo: false with rasterio deps

    As per chat w/ @yhahn (https://mapbox.slack.com/archives/satellite/p1441040393007215 + many other convos), we want to able to have modules such as this testable on travis w/ sudo: false. However, I am not sure how to accomplish this on a repo that uses rasterio, which has dependencies that are installed via sudo apt-get install ....

    @sgillies can you help guide me in this?

    opened by dnomadb 2
  • Support for 512x512 tiles: ValueError: Source shape is inconsistent with given indexes

    Support for 512x512 tiles: ValueError: Source shape is inconsistent with given indexes

    I am having trouble getting untiler to work with a directory of 512x512 tiles. Support for this does not seem to be documented, but it sounds like a typical use case given Mapbox's 512x512 default in Studio. Is this supported? Is it a rasterio issue, like this?

    This is what happens when trying to untile with 512x512 images. (An equivalent run with 256x256 tiles succeeds.)

    $ untiler streamdir -t {z}/{x}/{y}.png ./tiles ./untiles 
    /usr/local/lib/python3.6/site-packages/rasterio/__init__.py:240: NotGeoreferencedWarning: Dataset has no geotransform set. Default transform will be applied (Affine.identity())
      s = DatasetReader(fp, driver=driver, **kwargs)
    /usr/local/lib/python3.6/site-packages/rasterio/__init__.py:240: NotGeoreferencedWarning: Dataset has no geotransform set. Default transform will be applied (Affine.identity())
      s = DatasetReader(fp, driver=driver, **kwargs)
    tiles/18/66179/97359.png errored
    tiles/18/66179/97403.png errored
    multiprocessing.pool.RemoteTraceback: 
    """
    Traceback (most recent call last):
      File "/usr/local/Cellar/python/3.6.5/Frameworks/Python.framework/Versions/3.6/lib/python3.6/multiprocessing/pool.py", line 119, in worker
        result = (True, func(*args, **kwds))
      File "/usr/local/lib/python3.6/site-packages/untiler/__init__.py", line 201, in streaming_tile_worker
        raise e
      File "/usr/local/lib/python3.6/site-packages/untiler/__init__.py", line 192, in streaming_tile_worker
        dst.write(imdata, window=window)
      File "rasterio/_io.pyx", line 1234, in rasterio._io.DatasetWriterBase.write
    ValueError: Source shape is inconsistent with given indexes
    """
    
    The above exception was the direct cause of the following exception:
    
    Traceback (most recent call last):
      File "/usr/local/bin/untiler", line 11, in <module>
        sys.exit(cli())
      File "/usr/local/lib/python3.6/site-packages/click/core.py", line 722, in __call__
        return self.main(*args, **kwargs)
      File "/usr/local/lib/python3.6/site-packages/click/core.py", line 697, in main
        rv = self.invoke(ctx)
      File "/usr/local/lib/python3.6/site-packages/click/core.py", line 1066, in invoke
        return _process_result(sub_ctx.command.invoke(sub_ctx))
      File "/usr/local/lib/python3.6/site-packages/click/core.py", line 895, in invoke
        return ctx.invoke(self.callback, **ctx.params)
      File "/usr/local/lib/python3.6/site-packages/click/core.py", line 535, in invoke
        return callback(*args, **kwargs)
      File "/usr/local/lib/python3.6/site-packages/untiler/scripts/cli.py", line 32, in streamdir
        untiler.stream_dir(input_dir, output_dir, compositezoom, maxzoom, logdir, readtemplate, scenetemplate, workers, creation_options, no_fill)
      File "/usr/local/lib/python3.6/site-packages/untiler/__init__.py", line 255, in stream_dir
        for p in pool.imap_unordered(streaming_tile_worker, tiler.get_sub_tiles(allTiles, superTiles)):
      File "/usr/local/Cellar/python/3.6.5/Frameworks/Python.framework/Versions/3.6/lib/python3.6/multiprocessing/pool.py", line 735, in next
        raise value
    ValueError: Source shape is inconsistent with given indexes
    
    opened by waissbluth 1
  • Tests broken on master

    Tests broken on master

    Tests fail on Python 3.6 - see this Travis run.

        def test_extract_mbtiles():
            with TestTiler() as tt:
                testpath = tt.path
                testmbtiles = os.path.join(os.path.dirname(__file__), 'fixtures/testtiles.mbtiles')
                runner = CliRunner()
                result = runner.invoke(cli, [
                    'streammbtiles', testmbtiles, testpath, '-z', '16', '-x', '-s',
                    '{z}-{x}-{y}-mbtiles.tif', '--co', 'compress=lzw'])
                assert result.exit_code == 0
                expected_checksums = [[13858, 8288, 51489, 31223], [17927, 52775, 411, 9217]]
                for o, c in zip(result.output.rstrip().split('\n'), expected_checksums):
                    with rio.open(o) as src:
                        checksums = [src.checksum(i) for i in src.indexes]
    >                   assert checksums == c
    E                   assert [17927, 52775, 411, 9217] == [13858, 8288, 51489, 31223]
    E                     At index 0 diff: 17927 != 13858
    E                     Use -v to get the full diff
    

    Noticed this while working on the autodeploy branch. Initially thought this was failing due to changes I'd made, but I think this is some sort of config/dependency rot that's happening otherwise.

    bug 
    opened by jqtrde 1
  • Inspectar

    Inspectar

    Proof of concept to show viability of: (a) indexing tar (b) accessing data from the indexed offsets

    This would unlock untiling without having to untar the tar.

    cc @jacquestardie @perrygeo

    opened by dnomadb 1
  • initial python3 changes [WIP]

    initial python3 changes [WIP]

    resolves #18

    TODO

    • [x] get a new release of mbutil to pypi, semi-blocked by https://github.com/mapbox/mbutil/issues/83 ( it doesn't technically affect untiler's behavior but we can't say mbutils works with py36 until we fix it)
    opened by perrygeo 1
  • Python 3.6 compatibility

    Python 3.6 compatibility

    A few things to change (that I see now):

    • A few integer division changes: https://github.com/mapbox/untiler/blob/master/untiler/scripts/tile_utils.py#L49
    • Bigger prob: mbutil is (a lot farther) from 3.5 https://github.com/mapbox/untiler/blob/master/untiler/scripts/mbtiles_extract.py#L5. I think removing it entirely and reading tiles using sqlite3 is preferable.

    cc @perrygeo

    opened by dnomadb 0
  • Revisit command line options

    Revisit command line options

    At the risk of :bike: :house_with_garden: ... @dnomadb we have some conflicts with traditional options (-c for one) and I'd like to suggest --foo-bar instead of --foobar.

    opened by sgillies 0
  • Use rasterio for tile reading

    Use rasterio for tile reading

    Using rasterio for input tile reading should be faster for reading input images. To implement:

    • [x] Use syntax:

       with rasterio.drivers():
           with rasterio.open(...) as src:
               imdata = src.read()
      
    • [x] Update tiler to handle (depth, height, width) arrays vs (height, width, depth) and (height, depth) arrays

    cc @sgillies

    opened by dnomadb 0
  • How to get this to work with QGIS

    How to get this to work with QGIS

    So it doesn't seem to work with QGIS Tiles, and the things it works with output nonsense. image these were originally grey, and you can see they didn't even stitch properly, any insight on how to make this work?

    opened by GodPhase777 0
Releases(0.0.4)
Owner
Mapbox
Mapbox is the location data platform for mobile and web applications. We're changing the way people move around cities and explore our world.
Mapbox
Logging the position of the car on an sdcard

audi-mmi-3g-gps-logging Logging the position of the car on an sdcard, startup script origin not clear to me, logging setup and time change is what I d

2 May 31, 2022
Replace MSFS2020's bing map to google map

English verison here 中文 免责声明 本教程提到的方法仅用于研究和学习用途。我不对使用、拓展该教程及方法所造成的任何法律责任和损失负责。 背景 微软模拟飞行2020的地景使用了Bing的卫星地图,然而卫星地图比较老旧,很多地区都是几年前的图设置直接是没有的。这种现象在全球不同地区

hesicong 272 Dec 24, 2022
Raster-based Spatial Analysis for Python

🌍 xarray-spatial: Raster-Based Spatial Analysis in Python 📍 Fast, Accurate Python library for Raster Operations ⚡ Extensible with Numba ⏩ Scalable w

makepath 649 Jan 01, 2023
Django model field that can hold a geoposition, and corresponding widget

django-geoposition A model field that can hold a geoposition (latitude/longitude), and corresponding admin/form widget. Prerequisites Starting with ve

Philipp Bosch 324 Oct 17, 2022
WIP: extracting Geometry utilities from datacube-core

odc.geo This is still work in progress. This repository contains geometry related code extracted from Open Datacube. For details and motivation see OD

Open Data Cube 34 Jan 09, 2023
Python interface to PROJ (cartographic projections and coordinate transformations library)

pyproj Python interface to PROJ (cartographic projections and coordinate transformations library). Documentation Stable: http://pyproj4.github.io/pypr

832 Dec 31, 2022
List of Land Cover datasets in the GEE Catalog

List of Land Cover datasets in the GEE Catalog A list of all the Land Cover (or discrete) datasets in Google Earth Engine. Values, Colors and Descript

David Montero Loaiza 5 Aug 24, 2022
pure-Python (Numpy optional) 3D coordinate conversions for geospace ecef enu eci

Python 3-D coordinate conversions Pure Python (no prerequistes beyond Python itself) 3-D geographic coordinate conversions and geodesy. API similar to

Geospace code 292 Dec 29, 2022
A ninja python package that unifies the Google Earth Engine ecosystem.

A Python package that unifies the Google Earth Engine ecosystem. EarthEngine.jl | rgee | rgee+ | eemont GitHub: https://github.com/r-earthengine/ee_ex

47 Dec 27, 2022
Ingest and query genomic intervals from multiple BED files

Ingest and query genomic intervals from multiple BED files.

4 May 29, 2021
Simple CLI for Google Earth Engine Uploads

geeup: Simple CLI for Earth Engine Uploads with Selenium Support This tool came of the simple need to handle batch uploads of both image assets to col

Samapriya Roy 79 Nov 26, 2022
prettymaps - A minimal Python library to draw customized maps from OpenStreetMap data.

A small set of Python functions to draw pretty maps from OpenStreetMap data. Based on osmnx, matplotlib and shapely libraries.

Marcelo de Oliveira Rosa Prates 9k Jan 08, 2023
gpdvega is a bridge between GeoPandas and Altair that allows to seamlessly chart geospatial data

gpdvega gpdvega is a bridge between GeoPandas a geospatial extension of Pandas and the declarative statistical visualization library Altair, which all

Ilia Timofeev 49 Jul 25, 2022
Centroids as a Service

Centroids! This application reads a valid geojson FeatureCollection and returns a valid geojson FeatureColleciton of centroids. In the output: All pro

Lyzi Diamond 20 Aug 29, 2021
A multi-page streamlit app for the geospatial community.

A multi-page streamlit app for the geospatial community.

Qiusheng Wu 522 Dec 30, 2022
QLUSTER is a relative orbit design tool for formation flying satellite missions and space rendezvous scenarios

QLUSTER is a relative orbit design tool for formation flying satellite missions and space rendezvous scenarios, that I wrote in Python 3 for my own research and visualisation. It is currently unfinis

Samuel Low 9 Aug 23, 2022
Pure python WMS

Ogcserver Python WMS implementation using Mapnik. Depends Mapnik = 0.7.0 (and python bindings) Pillow PasteScript WebOb You will need to install Map

Mapnik 130 Dec 28, 2022
Geographic add-ons for Django REST Framework. Maintained by the OpenWISP Project.

Geographic add-ons for Django REST Framework. Maintained by the OpenWISP Project.

OpenWISP 982 Jan 06, 2023
Summary statistics of geospatial raster datasets based on vector geometries.

rasterstats rasterstats is a Python module for summarizing geospatial raster datasets based on vector geometries. It includes functions for zonal stat

Matthew Perry 437 Dec 23, 2022
🌐 Local tile server for viewing geospatial raster files with ipyleaflet

🌐 Local Tile Server for Geospatial Rasters Need to visualize a rather large raster (gigabytes) you have locally? This is for you. A Flask application

Bane Sullivan 192 Jan 04, 2023