Skip to main content

Locally serve geospatial raster tiles in the Slippy Map standard.

Project description

🌐 Local Tile Server for Geospatial Rasters

codecov PyPI conda

Need to visualize a rather large (gigabytes) raster you have locally? This is for you.

A Flask application for serving tiles from large raster files in the Slippy Maps standard (i.e., /zoom/x/y.png)

Launch a demo on MyBinder MyBinder

🌟 Highlights

  • Launch a tile server for large geospatial images
  • View local or remote* raster files with ipyleaflet or folium in Jupyter
  • View rasters with CesiumJS with the built-in Flask web application
  • Extract regions of interest (ROIs) interactively
  • Use the example datasets to generate Digital Elevation Models

*remote raster files should be pre-tiled Cloud Optimized GeoTiffs

tile-diagram

ℹ️ Overview

This is a Flask application (blueprint) for serving tiles of large images. The TileClient class can be used to to launch a tile server in a background thread which will serve raster imagery to a viewer (see ipyleaflet and folium Jupyter notebook examples below).

This tile server can efficiently deliver varying levels of detail of your raster imagery to your viewer; it helps to have pre-tiled, Cloud Optimized GeoTIFFs (COG), but no wories if not as the backing libraries, large_image, will tile and cache for you when opening the raster.

There is an included, standalone web viewer leveraging CesiumJS and GeoJS. You can use the web viewer to select and extract regions of interest from rasters.

Disclaimer: This is a hobby project and I am doing my best to make it more stable/robust. Things might break between minor releases (I use the major.minor.patch versioning scheme).

⬇️ Installation

Get started with localtileserver to view rasters locally in Jupyter or deploy in your own Flask application.

🐍 Installing with conda

Conda makes managing localtileserver's dependencies across platforms quite easy and this is the recommended method to install:

conda install -c conda-forge localtileserver

🎡 Installing with pip

If you prefer pip, and know how to install GDAL on your system, then you can install from PyPI: https://pypi.org/project/localtileserver/

pip install localtileserver

📝 A Brief Note on Installing GDAL

GDAL can be a pain in the 🍑 to install, so you may want to handle GDAL before installing localtileserver when using pip.

If on linux, I highly recommend using the large_image_wheels from Kitware.

pip install --find-links=https://girder.github.io/large_image_wheels --no-cache GDAL

💭 Feedback

Please share your thoughts and questions on the Discussions board. If you would like to report any bugs or make feature requests, please open an issue.

If filing a bug report, please share a scooby Report:

import localtileserver
print(localtileserver.Report())

🚀 Usage

localtileserver can be used in a few different ways:

🍃 ipyleaflet Tile Layers

The TileClient class is a nifty tool to launch a tile server as a background thread to serve image tiles from any raster file on your local file system. Additionally, it can be used in conjunction with the get_leaflet_tile_layer utility to create an ipyleaflet.TileLayer for interactive visualization in a Jupyter notebook. Here is an example:

from localtileserver import get_leaflet_tile_layer, TileClient
from ipyleaflet import Map

# First, create a tile server from local raster file
tile_client = TileClient('~/Desktop/TC_NG_SFBay_US_Geo.tif')

# Create ipyleaflet tile layer from that server
t = get_leaflet_tile_layer(tile_client)

# Create ipyleaflet map, add tile layer, and display
m = Map(center=tile_client.center())
m.add_layer(t)
m

ipyleaflet

🥓 Two Rasters at Once

from localtileserver import get_leaflet_tile_layer
from ipyleaflet import Map, ScaleControl, FullScreenControl, SplitMapControl

# Create 2 tile layers from 2 separate raster files
l = get_leaflet_tile_layer('~/Desktop/TC_NG_SFBay_US_Geo.tif',
                           band=1, palette='viridis', vmin=50, vmax=200)
r = get_leaflet_tile_layer('~/Desktop/small.tif',
                           band=2, palette='plasma', vmin=0, vmax=150)

# Make the ipyleaflet map
m = Map(center=(37.7249511580583, -122.27230466902257), zoom=9)
control = SplitMapControl(left_layer=l, right_layer=r)
m.add_control(control)
m.add_control(ScaleControl(position='bottomleft'))
m.add_control(FullScreenControl())
m

ipyleaflet-double

🧮 Controlling the RGB Bands

The ipyleaflet and folium tile layer utilities support setting which bands to view as the RGB channels. To set the RGB bands, pass a length three list of the band indices to the band argument.

Here is an example where I create two tile layers from the same raster but viewing a different set of bands:

from localtileserver import get_leaflet_tile_layer, TileClient
from ipyleaflet import Map, ScaleControl, FullScreenControl, SplitMapControl

# First, create a tile server from local raster file
tile_client = TileClient('landsat.tif')

# Create 2 tile layers from same raster viewing different bands
l = get_leaflet_tile_layer(tile_client, band=[7, 5, 4])
r = get_leaflet_tile_layer(tile_client, band=[5, 3, 2])

# Make the ipyleaflet map
m = Map(center=tile_client.center(), zoom=12)
control = SplitMapControl(left_layer=l, right_layer=r)
m.add_control(control)
m.add_control(ScaleControl(position='bottomleft'))
m.add_control(FullScreenControl())
m

ipyleaflet-multi-bands

🎯 Using ipyleaflet for ROI Extraction

I have included the get_leaflet_roi_controls utility to create some leaflet UI controls for extracting regions of interest from a tile client. You can use it as follows and then draw a polygon and click the "Extract ROI" button.

The outputs are save in your working directory by default (next to the Jupyter notebook).

from localtileserver import get_leaflet_tile_layer, get_leaflet_roi_controls
from localtileserver import TileClient
from ipyleaflet import Map

# First, create a tile server from local raster file
tile_client = TileClient('~/Desktop/TC_NG_SFBay_US_Geo.tif')

# Create ipyleaflet tile layer from that server
t = get_leaflet_tile_layer(tile_client)

# Create ipyleaflet controls to extract an ROI
draw_control, roi_control = get_leaflet_roi_controls(tile_client)

# Create ipyleaflet map, add layers, add controls, and display
m = Map(center=(37.7249511580583, -122.27230466902257), zoom=9)
m.add_layer(t)
m.add_control(draw_control)
m.add_control(roi_control)
m

ipyleaflet-draw-roi

🌳 folium Tile Layers

Similarly to the support provided for ipyleaflet, I have included a utility to generate a folium.TileLayer with get_folium_tile_layer. Here is an example with almost the exact same code as the ipyleaflet example, just note that Map is imported from folium and we use add_child instead of add_layer:

from localtileserver import get_folium_tile_layer
from localtileserver import TileClient
from folium import Map

# First, create a tile server from local raster file
tile_client = TileClient('~/Desktop/TC_NG_SFBay_US_Geo.tif')

# Create folium tile layer from that server
t = get_folium_tile_layer(tile_client)

m = Map(location=tile_client.center())
m.add_child(t)
m

folium

☁️ Remote Cloud Optimized GeoTiffs (COGs)

While localtileserver is intended to be used only with raster files existing on your local filesystem, there is support for URL files through GDAL's Virtual Storage Interface. Simply pass your http<s>:// or s3:// URL to the TileClient. This will work quite well for pre-tiled Cloud Optimized GeoTiffs, but I do not recommend doing this with non-tiled raster formats.

For example, the raster at the url below is ~3GiB but because it is pre-tiled, we can view tiles of the remote file very efficiently in a Jupyter notebook.

from localtileserver import get_folium_tile_layer
from localtileserver import TileClient
from folium import Map

url = 'https://opendata.digitalglobe.com/events/california-fire-2020/pre-event/2018-02-16/pine-gulch-fire20/1030010076004E00.tif'

# First, create a tile server from local raster file
tile_client = TileClient(url)

# Create folium tile layer from that server
t = get_folium_tile_layer(tile_client)

m = Map(location=tile_client.center())
m.add_child(t)
m

vsi

Note that the Virtual Storage Interface is a complex API, and TileClient currently only handles vsis3 and vsicurl. If you need a different VFS mechanism, simply create your /vsi path and pass that to TileClient.

🗺️ Example Datasets

A few example datasets are included with localtileserver. A particularly useful one has global elevation data which you can use to create high resolution Digital Elevation Models (DEMs) of a local region.

from localtileserver import get_leaflet_tile_layer, get_leaflet_roi_controls, examples
from ipyleaflet import Map

# Load example tile layer from publicly available DEM source
tile_client = examples.get_elevation()

# Create ipyleaflet tile layer from that server
t = get_leaflet_tile_layer(tile_client,
                           band=1, vmin=-500, vmax=5000,
                           palette='plasma',
                           opacity=0.75)

# Create ipyleaflet controls to extract an ROI
draw_control, roi_control = get_leaflet_roi_controls(tile_client)

m = Map(zoom=2)
m.add_layer(t)
m.add_control(draw_control)
m.add_control(roi_control)
m

elevation

Then you can follow the same routine as described above to extract an ROI.

I zoomed in over Golden, Colorado and drew a polygon of the extent of the DEM I would like to create:

golden

And perform the extraction:

roi_path = '...'  # Look in your working directory

r = get_leaflet_tile_layer(roi_path, band=1,
                           palette='plasma', opacity=0.75)

m2 = Map(
        center=(39.763427033262175, -105.20614908076823),
        zoom=12,
       )
m2.add_layer(r)
m2

golden-dem

Here is another example with the Virtual Earth satellite imagery

from localtileserver import get_leaflet_tile_layer, examples
from ipyleaflet import Map

# Load example tile layer from publicly available imagery
tile_client = examples.get_virtual_earth()

# Create ipyleaflet tile layer from that server
t = get_leaflet_tile_layer(tile_client, opacity=1)

m = Map(center=(39.751343612695145, -105.22181306125279), zoom=18)
m.add_layer(t)
m

kafadar

🖥️ Local Web Application

Launch the tileserver from the commandline to use the included web application where you can view the raster and extract regions of interest.

python -m localtileserver path/to/raster.tif

cesium-viewer

You can use the web viewer to extract regions of interest:

webviewer-roi

You can also launch the web viewer with any of the available example datasets:

python -m localtileserver dem

Available choices are:

  • dem or elevation: global elevation dataset
  • blue_marble: Blue Marble satellite imagery
  • virtual_earth: Microsoft's satellite/aerial imagery
  • arcgis: ArcGIS World Street Map
  • bahamas: Sample raster over the Bahamas

🗒️ Usage Notes

  • get_leaflet_tile_layer accepts either an existing TileClient or a path from which to create a TileClient under the hood.
  • The color palette choices come from palettable.
  • If matplotlib is installed, any matplotlib colormap name cane be used a palette choice

🐳 Docker

Included in this repository's packages is a pre-built Docker image that can be used as a local tile serving service. To use, pull the image and run it by mounting your local volume where the imagery is stored and forward port 8000.

This is particularly useful if you do not want to install GDAL on your system or want a dedicated and isolated service for tile serving.

To use the docker image:

docker pull ghcr.io/banesullivan/localtileserver:latest
docker run -p 8000:8000 ghcr.io/banesullivan/localtileserver:latest

Then visit http://localhost:8000 in your browser. You can pass the ?filename= argument in the URL parameters to access any URL/S3 raster image file.

You can mount your local file system to access files on your filesystem. For example, mount your Desktop by:

docker run -p 8000:8000 -v /Users/bane/Desktop/:/data/ ghcr.io/banesullivan/localtileserver:latest

Then add the ?filename= parameter to the URL in your browser to access the local files. Since this is mounted under /data/ in the container, you must build the path as /data/<filename on Desktop>, such that the URL would be: http://localhost:8000/?filename=/data/TC_NG_SFBay_US_Geo.tif

📓 Jupyter in Docker

There is also a pre-built image with localtileserver configured to be used in Jupyer from a Docker container.

docker run -p 8888:8888 ghcr.io/banesullivan/localtileserver-jupyter:latest

🧬 Flask Blueprint

Under the hood, localtileserver is a basic Flask Blueprint that can be easily incorporated into any Flask application. To utilize in your own application:

from flask import Flask
from localtileserver.tileserver.blueprint import cache, tileserver

app = Flask(__name__)
cache.init_app(app)
app.register_blueprint(tileserver, url_prefix='/')

There is an example Flask application and deployment in banesullivan/remotetileserver

⚛️ Standalone Electron App

If you're interested in using localtileserver as a standalone application, check out this experimental Electron app: banesullivan/localtileserver-electron

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

localtileserver-0.4.1.tar.gz (11.2 MB view hashes)

Uploaded Source

Built Distribution

localtileserver-0.4.1-py3-none-any.whl (11.2 MB view hashes)

Uploaded Python 3

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page