Skip to content

Commit

Permalink
Merge d20e21c into d7db0ef
Browse files Browse the repository at this point in the history
  • Loading branch information
szmoore committed May 1, 2019
2 parents d7db0ef + d20e21c commit cfcd826
Show file tree
Hide file tree
Showing 2 changed files with 64 additions and 1 deletion.
2 changes: 1 addition & 1 deletion contextily/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@
from . import tile_providers as sources
from .place import Place, plot_map
from .tile import *
from .plotting import add_basemap, add_attribution
from .plotting import add_basemap, add_attribution, ViewManager

__version__ = '0.99.0.dev'
63 changes: 63 additions & 0 deletions contextily/plotting.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
from . import tile_providers as sources
from .tile import _calculate_zoom, bounds2img, _sm2ll
from matplotlib import patheffects
import matplotlib.pyplot as plt
import time
import requests

INTERPOLATION = 'bilinear'
ZOOM = 'auto'
Expand Down Expand Up @@ -124,3 +127,63 @@ def add_attribution(ax, att=ATTRIBUTION):
path_effects=[patheffects.withStroke(linewidth=2,
foreground="w")])
return ax

class ViewManager():
'''
Class that calls add_basemap on an axis whenever its xlim and ylim
are both changed. This imitates the experience of an online map viewer.
This is a very rough implementation, as it
blocks the main thread until every tile has been fetched.
'''

_BOTH_LIMITS_CHANGED_TIMEOUT = 0.1 # Timeout for both limits changing

def __init__(self, ax, **kwargs):
'''
ax : Axis to apply the basemap to
kwargs : Arguments passed through to add_basemap
'''
self.ax = add_basemap(ax, **kwargs) # Add initial basemap

# matplotlib uses two separate events for xlim and ylim
# our strategy is to call add_basemap when both have changed within quick succession
# more complicated but better strategy would be to make a new matplotlib figure manager class.
self.ax.callbacks.connect("xlim_changed", lambda _ax : self._xlim_changed(_ax))
self.ax.callbacks.connect("ylim_changed", lambda _ax : self._ylim_changed(_ax))
self.kwargs = kwargs

self._updating = False # Flag to prevent requesting multiple updates at once
self._last_xchange = time.time() # Time xlim last changed
self._last_ychange = time.time() # Time ylim last changed

def _xlim_changed(self, ax):
self._last_xchange = time.time()
if self._last_xchange - self._last_ychange < self._BOTH_LIMITS_CHANGED_TIMEOUT:
self.update(ax)

def _ylim_changed(self, ax):
self._last_ychange = time.time()
if self._last_ychange - self._last_xchange < self._BOTH_LIMITS_CHANGED_TIMEOUT:
self.update(ax)

def update(self, ax=None):
'''
Update the plot based on the current limits of the axis.
This is called automatically when the axis limits are changed.
ax : Optional; defaults to self.ax
'''
ax = ax or self.ax
# Prevent infinite loop of view updates
if self._updating:
return
self._updating = True
# Redraw figure first (gives a low resolution update)
self.ax.get_figure().canvas.draw()
# Update figure (blocks main thread...); ignore HTTP errors
try:
add_basemap(ax, **self.kwargs)
except requests.exceptions.HTTPError:
pass
self._updating = False

0 comments on commit cfcd826

Please sign in to comment.