Using GeoTiler Library

Rendering Map

Rendering a map with GeoTiler library consists of two steps

  • create map object

  • render map as an image

Map object is created using geotiler.Map class. Its constructor requires any of the following combinations of map parameters

  • center, zoom and size

  • extent and zoom

  • extent and size

For example, to create map using first combination of the parameters above:

>>> import geotiler
>>> map = geotiler.Map(center=(-6.069, 53.390), zoom=16, size=(512, 512))
>>> map.extent
(-6.074495315551752, 53.38671986409586, -6.063508987426756, 53.39327172612266)

After creating a map object, map can be controlled with extent, center, zoom and size attributes. Each attribute can influence other, i.e. changing extent will change map size. Refer to the geotiler.Map class documentation for details.

Having map object, the map tiles can be downloaded and rendered as an image with geotiler.render_map() function:

>>> image = geotiler.render_map(map) 
_images/map-osm.png

The rendered image is an instance of PIL.Image class. Map image can be used with other library like matplotlib or Cairo to render additional map information, i.e. points of interests, GPS positions, text, etc. See 3rd Party Libraries section for examples.

Alternatively, the map image can be simply saved as a file:

>>> image.save('map.png') 

Asynchronous Map Rendering

The asyncio Python framework enables programmers to write asynchronous, concurrent programs using coroutines.

GeoTiler allows to asynchronously download map tiles and render map image with geotiler.render_map_async() asyncio coroutine.

Very simple example to download a map using asyncio framework:

>>> coro = geotiler.render_map_async(map)  
>>> loop = asyncio.get_event_loop()        
>>> image = loop.run_until_complete(coro)  

We include more complex example below. It reads location data from gpsd daemon, renders the map at the centre of current position and saves map to a file. There are two concurrent tasks running concurrently

  • location reading

  • map tiles downloading and rendering

The tasks communication is done via a queue holding current position.

async def read_gps(queue):
    """
    Read location data from `gpsd` daemon.
    """
    reader, writer = await asyncio.open_connection(port=2947)
    writer.write(b'?WATCH={"enable":true,"json":true}\n')
    while True:
        line = await reader.readline()
        data = json.loads(line.decode())
        if 'lon' in data:
            await queue.put((data['lon'], data['lat']))
        
async def show_map(queue, map):
    """
    Save map centered at location to a file.
    """
    while True:
        pos = yield from queue.get()

        map.center = pos
        img = await render_map_async(map)
        img.save('ex-async-gps.png', 'png')


size = 800, 800
start = 0, 0
mm = geotiler.Map(size=size, center=start, zoom=16)

queue = asyncio.Queue(1)        # queue holding current position from gpsd

# run location and map rendering tasks concurrently
t1 = show_map(queue, mm)
t2 = read_gps(queue)
task = asyncio.gather(t1, t2)
loop = asyncio.get_event_loop()
loop.run_until_complete(task)

Map Providers

GeoTiler supports multiple map providers.

The list of supported map providers is presented in the table below. The default map provider is OpenStreetMap.

Provider

Provider Id

API Key Reference

License

OpenStreetMap

osm

Open Data Commons Open Database License

Stamen Toner

stamen-toner

stadiamaps

Creative Commons Attribution (CC BY 3.0) license

Stamen Toner Lite

stamen-toner-lite

stadiamaps

Stadia Maps Required Attribution

Stamen Terrain

stamen-terrain

stadiamaps

Stadia Maps Required Attribution

Stamen Terrain Background

stamen-terrain-background

stadiamaps

Stadia Maps Required Attribution

Stamen Terrain Lines

stamen-terrain-lines

stadiamaps

Stadia Maps Required Attribution

Stamen Water Color

stamen-watercolor

stadiamaps

Stadia Maps Required Attribution

Modest Maps Blue Marble

bluemarble

NASA guideline

OpenCycleMap

thunderforest-cycle

thunderforest

Thunderforest Terms and Conditions

A map provider might require API key. To add an API key for a map provider, the $HOME/.config/geotiler/geotiler.ini file has to be created with API key reference pointing to an API key, for example:

[api-key]
thunderforest = <api-key>

where <api-key> is usually a fixed size, alphanumeric hash value provided by map provider service.

Identificators of GeoTiler map providers can be listed with geotiler.providers() function. Map provider identificator can be used with geotiler.find_provider() function to create instance of map provider or it can be passed to geotiler.Map class constructor:

>>> map = geotiler.Map(center=(-6.069, 53.390), zoom=16, size=(512, 512), provider='stamen-toner')
>>> image = geotiler.render_map(map) 

# or

>>> map = geotiler.Map(center=(-6.069, 53.390), zoom=16, size=(512, 512))
>>> map.provider = geotiler.find_provider('stamen-toner')
>>> image = geotiler.render_map(map) 
_images/map-stamen-toner.png

3rd Party Libraries

Map image rendered by GeoTiler can be used with other library like matplotlib or Cairo to draw additional map information or use the map in data analysis graphs.

GeoTiler implements various examples of such integration. The examples are presented in the subsections below.

Cairo Example

import cairo
import logging
import math

logging.basicConfig(level=logging.DEBUG)

import geotiler

bbox = 11.78560, 46.48083, 11.79067, 46.48283

#
# download background map using default map tiles provider - OpenStreetMap
#
mm = geotiler.Map(extent=bbox, zoom=18)
width, height = mm.size

img = geotiler.render_map(mm)

#
# create cairo surface
#
buff = bytearray(img.convert('RGBA').tobytes('raw', 'BGRA'))
surface = cairo.ImageSurface.create_for_data(
    buff, cairo.FORMAT_ARGB32, width, height
)
cr = cairo.Context(surface)

#
# plot circles around custom points
#
x0, y0 = 11.78816, 46.48114 # http://www.openstreetmap.org/search?query=46.48114%2C11.78816
x1, y1 = 11.78771, 46.48165 # http://www.openstreetmap.org/search?query=46.48165%2C11.78771
points = ((x0, y0), (x1, y1))
points = (mm.rev_geocode(p) for p in points)
for x, y in points:
    cr.set_source_rgba(0.0, 0.0, 1.0, 0.1)
    cr.arc(x, y, 30, 0, 2 * math.pi)
    cr.fill()

surface.write_to_png('ex-cairo.png')

matplotlib Example

import matplotlib.pyplot as plt

import logging
logging.basicConfig(level=logging.DEBUG)

import geotiler

bbox = 11.78560, 46.48083, 11.79067, 46.48283

fig = plt.figure(figsize=(10, 10))
ax = plt.subplot(111)

#
# download background map using OpenStreetMap
#
mm = geotiler.Map(extent=bbox, zoom=18)

img = geotiler.render_map(mm)
ax.imshow(img)

#
# plot custom points
#
x0, y0 = 11.78816, 46.48114 # http://www.openstreetmap.org/search?query=46.48114%2C11.78816
x1, y1 = 11.78771, 46.48165 # http://www.openstreetmap.org/search?query=46.48165%2C11.78771
points = ((x0, y0), (x1, y1))
x, y = zip(*(mm.rev_geocode(p) for p in points))
ax.scatter(x, y, c='red', edgecolor='none', s=10, alpha=0.9)

plt.savefig('ex-matplotlib.pdf', bbox_inches='tight')
plt.close()

Matplotlib Basemap Toolkit Example

import matplotlib.pyplot as plt
from mpl_toolkits.basemap import Basemap

import logging
logging.basicConfig(level=logging.DEBUG)

import geotiler

bbox = 11.78560, 46.48083, 11.79067, 46.48283

fig = plt.figure(figsize=(10, 10))
ax = plt.subplot(111)

#
# download background map using OpenStreetMap
#
mm = geotiler.Map(extent=bbox, zoom=18)

img = geotiler.render_map(mm)

#
# create basemap
#
map = Basemap(
    llcrnrlon=bbox[0], llcrnrlat=bbox[1],
    urcrnrlon=bbox[2], urcrnrlat=bbox[3],
    projection='merc', ax=ax
)

map.imshow(img, interpolation='lanczos', origin='upper')

#
# plot custom points
#
x0, y0 = 11.78816, 46.48114 # http://www.openstreetmap.org/search?query=46.48114%2C11.78816
x1, y1 = 11.78771, 46.48165 # http://www.openstreetmap.org/search?query=46.48165%2C11.78771
x, y = map((x0, x1), (y0, y1))
ax.scatter(x, y, c='red', edgecolor='none', s=10, alpha=0.9)

plt.savefig('ex-basemap.pdf', bbox_inches='tight')
plt.close()

Caching

GeoTiler allows to cache map tiles. The geotiler.cache.caching_downloader() function enables us to adapt any caching strategy.

Beside generic caching downloader adapter, GeoTiler provides Redis store adapter. While it requires Redis server and Python Redis module installed, such solution gives map tiles persistence and advanced cache management.

The Redis cache example illustrates how Redis can be user for map tiles caching.

import functools
import redis

import geotiler
from geotiler.cache import redis_downloader

import logging
logging.basicConfig(level=logging.DEBUG)

# create tile downloader with Redis client as cache
client = redis.Redis('localhost')
downloader = redis_downloader(client)

# use map renderer with new downloader
render_map = functools.partial(geotiler.render_map, downloader=downloader)

bbox = 11.78560, 46.48083, 11.79067, 46.48283
mm = geotiler.Map(extent=bbox, zoom=18)

# render the map for the first time...
img = render_map(mm)

# ... and second time to demonstrate use of the cache
img = render_map(mm)

# show some recent keys
print('recent cache keys {}'.format(client.keys()[:10]))