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
Global topography (referenced to sea-level) in a 10 arcminute resolution grid

Earth - Topography grid at 10 arc-minute resolution Global 10 arc-minute resolution grids of topography (ETOPO1 ice-surface) referenced to mean sea-le

Fatiando a Terra Datasets 1 Jan 20, 2022
Using Global fishing watch's data to build a machine learning model that can identify illegal fishing and poaching activities through satellite and geo-location data.

Using Global fishing watch's data to build a machine learning model that can identify illegal fishing and poaching activities through satellite and geo-location data.

Ayush Mishra 3 May 06, 2022
Creates 3D geometries from 2D vector graphics, for use in geodynamic models

geomIO - creating 3D geometries from 2D input This is the Julia and Python version of geomIO, a free open source software to generate 3D volumes and s

3 Feb 01, 2022
A library to access OpenStreetMap related services

OSMPythonTools The python package OSMPythonTools provides easy access to OpenStreetMap (OSM) related services, among them an Overpass endpoint, Nomina

Franz-Benjamin Mocnik 342 Dec 31, 2022
Open GeoJSON data on geojson.io

geojsonio.py Open GeoJSON data on geojson.io from Python. geojsonio.py also contains a command line utility that is a Python port of geojsonio-cli. Us

Jacob Wasserman 114 Dec 21, 2022
GeoNode is an open source platform that facilitates the creation, sharing, and collaborative use of geospatial data.

Table of Contents What is GeoNode? Try out GeoNode Install Learn GeoNode Development Contributing Roadmap Showcase Most useful links Licensing What is

GeoNode Development Team 1.2k Dec 26, 2022
Read images to numpy arrays

mahotas-imread: Read Image Files IO with images and numpy arrays. Mahotas-imread is a simple module with a small number of functions: imread Reads an

Luis Pedro Coelho 67 Jan 07, 2023
Construct and use map tile grids in different projection.

Morecantile +-------------+-------------+ ymax | | | | x: 0 | x: 1 | | y: 0 | y: 0

Development Seed 67 Dec 23, 2022
ColoringMapAlgorithm-CSP- - Graphical Coloring of Countries with Condition Satisfaction Algorithm

ColoringMapAlgorithm-CSP- Condition Satisfaction Algorithm Output Condition

Kerem TAN 2 Jan 10, 2022
Platform for building statistical models of cities and regions

UrbanSim UrbanSim is a platform for building statistical models of cities and regions. These models help forecast long-range patterns in real estate d

Urban Data Science Toolkit 419 Dec 30, 2022
Water Detect Algorithm

WaterDetect Synopsis WaterDetect is an end-to-end algorithm to generate open water cover mask, specially conceived for L2A Sentinel 2 imagery from MAJ

142 Dec 30, 2022
A Python framework for building geospatial web-applications

Hey there, this is Greppo... A Python framework for building geospatial web-applications. Greppo is an open-source Python framework that makes it easy

Greppo 304 Dec 27, 2022
Solving the Traveling Salesman Problem using Self-Organizing Maps

Solving the Traveling Salesman Problem using Self-Organizing Maps This repository contains an implementation of a Self Organizing Map that can be used

Diego Vicente 3.1k Dec 31, 2022
leafmap - A Python package for geospatial analysis and interactive mapping in a Jupyter environment.

A Python package for geospatial analysis and interactive mapping with minimal coding in a Jupyter environment

Qiusheng Wu 1.4k Jan 02, 2023
A Jupyter - Leaflet.js bridge

ipyleaflet A Jupyter / Leaflet bridge enabling interactive maps in the Jupyter notebook. Usage Selecting a basemap for a leaflet map: Loading a geojso

Jupyter Widgets 1.3k Dec 27, 2022
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
A light-weight, versatile XYZ tile server, built with Flask and Rasterio :earth_africa:

Terracotta is a pure Python tile server that runs as a WSGI app on a dedicated webserver or as a serverless app on AWS Lambda. It is built on a modern

DHI GRAS 531 Dec 28, 2022
Open Data Cube analyses continental scale Earth Observation data through time

Open Data Cube Core Overview The Open Data Cube Core provides an integrated gridded data analysis environment for decades of analysis ready earth obse

Open Data Cube 410 Dec 13, 2022
Google maps for Jupyter notebooks

gmaps gmaps is a plugin for including interactive Google maps in the IPython Notebook. Let's plot a heatmap of taxi pickups in San Francisco: import g

Pascal Bugnion 747 Dec 19, 2022
Code and coordinates for Matt's 2021 xmas tree

xmastree2021 Code and coordinates for Matt's 2021 xmas tree This repository contains the code and coordinates used for Matt's 2021 Christmas tree, as

Stand-up Maths 117 Jan 01, 2023