Skip to content

Commit

Permalink
Made it possible to use projection other than GOOGLE_MERCATOR in bokeh
Browse files Browse the repository at this point in the history
  • Loading branch information
philippjfr committed May 14, 2018
1 parent 8021085 commit 2480fa4
Show file tree
Hide file tree
Showing 3 changed files with 29 additions and 19 deletions.
16 changes: 9 additions & 7 deletions geoviews/plotting/bokeh/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import param
import numpy as np
import shapely.geometry
from cartopy.crs import GOOGLE_MERCATOR
from bokeh.models import WMTSTileSource, BBoxTileSource, QUADKEYTileSource

from holoviews import Store, Overlay, NdOverlay
Expand All @@ -24,7 +25,7 @@
project_path, project_graph, project_quadmesh)
from ...tile_sources import _ATTRIBUTIONS
from ...util import geom_to_array
from .plot import GeoPlot, OverlayPlot, DEFAULT_PROJ
from .plot import GeoPlot, OverlayPlot
from . import callbacks # noqa

line_types = (shapely.geometry.MultiLineString, shapely.geometry.LineString)
Expand All @@ -37,7 +38,8 @@ class TilePlot(GeoPlot):
def get_extents(self, element, ranges):
extents = super(TilePlot, self).get_extents(element, ranges)
if not self.overlaid and all(e is None or not np.isfinite(e) for e in extents):
global_extent = (-20026376.39, -20048966.10, 20026376.39, 20048966.10)
(x0, x1), (y0, y1) = GOOGLE_MERCATOR.x_limits, GOOGLE_MERCATOR.y_limits
global_extent = (x0, y0, x1, y1)
return global_extent
return extents

Expand Down Expand Up @@ -150,8 +152,8 @@ def get_data(self, element, ranges, style):
if self.static_source:
data = {}
else:
if self.geographic and element.crs != DEFAULT_PROJ:
element = project_shape(element)
if self.geographic and element.crs != self.projection:
element = project_shape(element, projection=self.projection)
xs, ys = geom_to_array(element.geom()).T
if self.invert_axes: xs, ys = ys, xs
data = dict(xs=[xs], ys=[ys])
Expand Down Expand Up @@ -213,7 +215,7 @@ def get_data(self, element, ranges, style):
self._plot_methods = dict(single='multi_line')
else:
self._plot_methods = dict(single='patches', batched='patches')
geoms = [DEFAULT_PROJ.project_geometry(geom, element.crs)
geoms = [self.projection.project_geometry(geom, element.crs)
for geom in geoms]
xs, ys = zip(*(geom_to_array(geom).T for geom in geoms))
data = dict(xs=list(xs), ys=list(ys))
Expand All @@ -227,8 +229,8 @@ def get_data(self, element, ranges, style):
if not self.geographic:
return super(GeoTextPlot, self).get_data(element, ranges, style)
if element.crs:
x, y = DEFAULT_PROJ.transform_point(element.x, element.y,
element.crs)
x, y = self.projection.transform_point(element.x, element.y,
element.crs)
return (dict(x=[x], y=[y], text=[element.text]), mapping, style)

def get_extents(self, element, ranges=None):
Expand Down
12 changes: 6 additions & 6 deletions geoviews/plotting/bokeh/callbacks.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
MouseLeave, Bounds, BoundsXY)

from ...util import project_extents
from .plot import DEFAULT_PROJ, OverlayPlot
from .plot import OverlayPlot


def get_cb_plot(cb, plot=None):
Expand Down Expand Up @@ -50,7 +50,7 @@ def project_ranges(cb, msg, attributes):
x0, x1 = msg.get('x_range', (0, 1000))
y0, y1 = msg.get('y_range', (0, 1000))
extents = x0, y0, x1, y1
x0, y0, x1, y1 = project_extents(extents, DEFAULT_PROJ,
x0, y0, x1, y1 = project_extents(extents, plot.projection,
plot.current_frame.crs)
coords = {'x_range': (x0, x1), 'y_range': (y0, y1)}
return {k: v for k, v in coords.items() if k in attributes}
Expand All @@ -64,7 +64,7 @@ def project_point(cb, msg, attributes=('x', 'y')):
plot = get_cb_plot(cb)
x, y = msg.get('x', 0), msg.get('y', 0)
crs = plot.current_frame.crs
coordinates = crs.transform_points(DEFAULT_PROJ, np.array([x]), np.array([y]))
coordinates = crs.transform_points(plot.projection, np.array([x]), np.array([y]))
msg['x'], msg['y'] = coordinates[0, :2]
return {k: v for k, v in msg.items() if k in attributes}

Expand Down Expand Up @@ -95,7 +95,7 @@ def _process_msg(self, msg):
msg = super(GeoBoundsXYCallback, self)._process_msg(msg)
if skip(self, msg, ('bounds',)): return msg
plot = get_cb_plot(self)
msg['bounds'] = project_extents(msg['bounds'], DEFAULT_PROJ,
msg['bounds'] = project_extents(msg['bounds'], plot.projection,
plot.current_frame.crs)
return msg

Expand All @@ -107,7 +107,7 @@ def _process_msg(self, msg):
if skip(self, msg, ('boundsx',)): return msg
x0, x1 = msg['boundsx']
plot = get_cb_plot(self)
x0, _, x1, _ = project_extents((x0, 0, x1, 0), DEFAULT_PROJ,
x0, _, x1, _ = project_extents((x0, 0, x1, 0), plot.projection,
plot.current_frame.crs)
return {'boundsx': (x0, x1)}

Expand All @@ -119,7 +119,7 @@ def _process_msg(self, msg):
if skip(self, msg, ('boundsy',)): return msg
y0, y1 = msg['boundsy']
plot = get_cb_plot(self)
_, y0, _, y1 = project_extents((0, y0, 0, y1), DEFAULT_PROJ,
_, y0, _, y1 = project_extents((0, y0, 0, y1), plot.projection,
plot.current_frame.crs)
return {'boundsy': (y0, y1)}

Expand Down
20 changes: 14 additions & 6 deletions geoviews/plotting/bokeh/plot.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
from ...element import is_geographic, _Element
from ...util import project_extents

DEFAULT_PROJ = GOOGLE_MERCATOR

class GeoPlot(ElementPlot):
"""
Expand All @@ -34,6 +33,10 @@ class GeoPlot(ElementPlot):
show_grid = param.Boolean(default=False, doc="""
Whether to show gridlines on the plot.""")

projection = param.Parameter(default=GOOGLE_MERCATOR, doc="""
Allows supplying a custom projection to transform the axis
coordinates during display. Defaults to GOOGLE_MERCATOR.""")

# Project operation to apply to the element
_project_operation = None

Expand All @@ -54,7 +57,8 @@ def _axis_properties(self, axis, key, plot, dimension=None,
ax_mapping={'x': 0, 'y': 1}):
axis_props = super(GeoPlot, self)._axis_properties(axis, key, plot,
dimension, ax_mapping)
if self.geographic:
proj = self.projection
if self.geographic and proj is GOOGLE_MERCATOR:
dimension = 'lon' if axis == 'x' else 'lat'
axis_props['ticker'] = MercatorTicker(dimension=dimension)
axis_props['formatter'] = MercatorTickFormatter(dimension=dimension)
Expand Down Expand Up @@ -99,24 +103,28 @@ def get_extents(self, element, ranges):
set_extent method to project the extents to the
Elements coordinate reference system.
"""

proj = self.projection
if self.is_global:
return (-20026376.39, -20048966.10, 20026376.39, 20048966.10)
(x0, x1), (y0, y1) = proj.x_limits, proj.y_limits
return (x0, y0, x1, y1)
extents = super(GeoPlot, self).get_extents(element, ranges)
if not getattr(element, 'crs', None) or not self.geographic:
return extents
elif any(e is None or not np.isfinite(e) for e in extents):
extents = None
else:
try:
extents = project_extents(extents, element.crs, DEFAULT_PROJ)
extents = project_extents(extents, element.crs, proj)
except:
extents = None
return (np.NaN,)*4 if not extents else extents


def get_data(self, element, ranges, style):
if self._project_operation and self.geographic and element.crs != DEFAULT_PROJ:
element = self._project_operation(element)
proj = self.projection
if self._project_operation and self.geographic and element.crs != proj:
element = self._project_operation(element, projection=proj)
return super(GeoPlot, self).get_data(element, ranges, style)


Expand Down

0 comments on commit 2480fa4

Please sign in to comment.