diff --git a/geoviews/__init__.py b/geoviews/__init__.py index b8fad104..d9e5f0fb 100644 --- a/geoviews/__init__.py +++ b/geoviews/__init__.py @@ -9,6 +9,7 @@ from .element import (_Element, Feature, Tiles, # noqa (API import) WMTS, LineContours, FilledContours, Text, Image, Points, Path, Polygons, Shape, Dataset) +from . import operation # noqa (API import) from . import plotting # noqa (API import) from . import feature # noqa (API import) diff --git a/geoviews/operation.py b/geoviews/operation.py index 330567dd..056c6a07 100644 --- a/geoviews/operation.py +++ b/geoviews/operation.py @@ -1,11 +1,11 @@ import param import numpy as np from cartopy import crs as ccrs -from cartopy.img_transform import regrid +from cartopy.img_transform import warp_array from holoviews.operation import ElementOperation -from .element import Image, Shape, Polygons, Path +from .element import Image, Shape, Polygons, Path, Points from .util import project_extents class project_shape(ElementOperation): @@ -19,13 +19,38 @@ class project_shape(ElementOperation): instantiate=False, doc=""" Projection the shape type is projected to.""") + supported_types = [Shape, Polygons, Path] + def _process_element(self, element): geom = self.p.projection.project_geometry(element.geom(), element.crs) return element.clone(geom, crs=self.p.projection) def _process(self, element, key=None): - return element.map(self._process_element, [Shape, Polygons, Path]) + return element.map(self._process_element, self.supported_types) + + +class project_points(ElementOperation): + + projection = param.ClassSelector(default=ccrs.GOOGLE_MERCATOR, + class_=ccrs.Projection, + instantiate=False, doc=""" + Projection the shape type is projected to.""") + + supported_types = [Points] + + def _process_element(self, element): + xdim, ydim = element.dimensions()[:2] + xs, ys = (element.dimension_values(i) for i in range(2)) + coordinates = self.p.projection.transform_points(element.crs, xs, ys) + new_data = element.columns() + new_data[xdim.name] = coordinates[:, 0] + new_data[ydim.name] = coordinates[:, 1] + return element.clone(new_data, crs=self.p.projection, + datatype=[element.interface.datatype]+element.datatype) + + def _process(self, element, key=None): + return element.map(self._process_element, self.supported_types) class project_image(ElementOperation): @@ -41,23 +66,38 @@ class project_image(ElementOperation): instantiate=False, doc=""" Projection the image type is projected to.""") + supported_types = [Image] + def _process(self, img, key=None): proj = self.p.projection if proj == img.crs: return img - arr = img.dimension_values(2, flat=False).T - xs = img.dimension_values(0) - ys = img.dimension_values(1) + arr = img.dimension_values(2, flat=False) x0, x1 = img.range(0) y0, y1 = img.range(1) xn, yn = arr.shape px0, py0, px1, py1 = project_extents((x0, y0, x1, y1), img.crs, proj) - px = np.linspace(px0, px1, xn) - py = np.linspace(py0, py1, yn) - pxs, pys = np.meshgrid(px, py) - pxs = pxs.reshape((yn, xn)) - pys = pys.reshape((yn, xn)) - parray = regrid(arr, xs, ys, img.crs, proj, pxs, pys) - return Image((px, py, parray), kdims=img.kdims, + src_ext, trgt_ext = (x0, x1, y0, y1), (px0, px1, py0, py1) + projected, extents = warp_array(arr, proj, img.crs, (xn, yn), + src_ext, trgt_ext) + bounds = (extents[0], extents[2], extents[1], extents[3]) + data = np.flipud(projected) + return Image(data, bounds=bounds, kdims=img.kdims, vdims=img.vdims, crs=proj) + + +class project(ElementOperation): + """ + Projects GeoViews Element types to the specified projection. + """ + + projection = param.ClassSelector(default=ccrs.GOOGLE_MERCATOR, + class_=ccrs.Projection, + instantiate=False, doc=""" + Projection the image type is projected to.""") + + def _process(self, element, key=None): + element = element.map(project_image, project_image.supported_types) + element = element.map(project_shape, project_shape.supported_types) + return element.map(project_points, project_points.supported_types) diff --git a/geoviews/util.py b/geoviews/util.py index 5e533df6..209e7461 100644 --- a/geoviews/util.py +++ b/geoviews/util.py @@ -38,11 +38,11 @@ def project_extents(extents, src_proj, dest_proj, tol=1e-6): domain_in_src_proj = Polygon([[x1, y1], [x2, y1], [x2, y2], [x1, y2], [x1, y1]]) - boundary_poly = Polygon(dest_proj.boundary) + boundary_poly = Polygon(src_proj.boundary) if src_proj != dest_proj: # Erode boundary by threshold to avoid transform issues. # This is a workaround for numerical issues at the boundary. - eroded_boundary = boundary_poly.buffer(-dest_proj.threshold) + eroded_boundary = boundary_poly.buffer(-src_proj.threshold) geom_in_src_proj = eroded_boundary.intersection( domain_in_src_proj) geom_in_crs = dest_proj.project_geometry(geom_in_src_proj, src_proj)