Skip to content

Commit

Permalink
Merge a86fcd1 into a5c5b9c
Browse files Browse the repository at this point in the history
  • Loading branch information
philippjfr committed Jan 9, 2020
2 parents a5c5b9c + a86fcd1 commit e8371f5
Show file tree
Hide file tree
Showing 4 changed files with 396 additions and 81 deletions.
83 changes: 77 additions & 6 deletions examples/user_guide/Annotators.ipynb
Expand Up @@ -62,7 +62,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"The annotator will return coordinates in the coordinate system they were originally defined in:"
"The annotator will return coordinates in the coordinate system they were originally defined in on the `.annotated` property. The data of this element can be accessed either using the `.dframe` method (or `.array` or `.columns`):"
]
},
{
Expand All @@ -74,6 +74,22 @@
"point_annotate.annotated.dframe()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Alternatively the element can be converted to a shapely geometry:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"point_annotate.annotated.geom()"
]
},
{
"cell_type": "markdown",
"metadata": {},
Expand All @@ -93,11 +109,18 @@
"\n",
"box_annotate = annotate.instance()\n",
"\n",
"annotated = box_annotate(rectangles, name=\"Rectangles\")\n",
"annotated = box_annotate(rectangles, annotations=['Label'], name='Rectangles')\n",
"\n",
"annotate.compose(tiles, annotated)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Just like the Points the data can be accessed using the `.annotated` property:"
]
},
{
"cell_type": "code",
"execution_count": null,
Expand All @@ -107,6 +130,22 @@
"box_annotate.annotated.dframe()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"or as a shapely geometry:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"box_annotate.annotated.geom()"
]
},
{
"cell_type": "markdown",
"metadata": {},
Expand Down Expand Up @@ -141,7 +180,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"Just like the HoloViews version, each path can be accessed using `iloc` or by using split, which will return a list of Path elements representing each geometry:"
"Each path on the `annotated` element can be selected using `iloc` or by using `.split` which will return a list of Path elements representing each geometry:"
]
},
{
Expand All @@ -153,14 +192,30 @@
"path_annotate.annotated.iloc[0].dframe()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"`Path` elements also support the `geom` method:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"path_annotate.annotated.geom()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Annotating Polygons\n",
"\n",
"\n",
"The GeoViews Polygons annotator behaves much like the PolyAnnotator in HoloViews but also projects the coordinates in the table to be more readable:"
"The GeoViews Polygons annotator behaves much like the annotator in HoloViews but also projects the coordinates in the table to be more readable:"
]
},
{
Expand All @@ -182,7 +237,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"Accessing Polygons works the same as with Paths:"
"Accessing `Polygons` works the same as with `Path` elements:"
]
},
{
Expand All @@ -193,6 +248,22 @@
"source": [
"poly_annotate.annotated.iloc[0].dframe()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"and can also be converted to shapely geometries:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"poly_annotate.annotated.iloc[0].geom()"
]
}
],
"metadata": {
Expand All @@ -211,7 +282,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.4"
"version": "3.7.5"
}
},
"nbformat": 4,
Expand Down
157 changes: 143 additions & 14 deletions geoviews/element/geo.py
@@ -1,5 +1,6 @@
import param
import numpy as np

from cartopy import crs as ccrs
from cartopy.feature import Feature as cFeature
from cartopy.io.img_tiles import GoogleTiles
Expand All @@ -17,6 +18,11 @@
)

from shapely.geometry.base import BaseGeometry
from shapely.geometry import (
box, GeometryCollection, MultiPolygon, LineString, MultiLineString,
Point, MultiPoint
)
from shapely.ops import unary_union

try:
from iris.cube import Cube
Expand All @@ -33,8 +39,10 @@
except:
WebMapTileService = None

from ..util import (path_to_geom, polygon_to_geom, load_tiff,
from_xarray, poly_types)
from ..util import (
path_to_geom_dicts, polygons_to_geom_dicts, load_tiff, from_xarray,
poly_types, expand_geoms
)

geographic_types = (GoogleTiles, cFeature, BaseGeometry)

Expand Down Expand Up @@ -253,6 +261,29 @@ class Points(_Element, HvPoints):

group = param.String(default='Points')

def geom(self, union=False):
"""
Converts the Points to a shapely geometry.
Parameters
----------
union: boolean (default=False)
Whether to compute a union between the geometries
Returns
-------
A shapely geometry
"""
points = [Point(x, y) for (x, y) in self.array([0, 1])]
npoints = len(points)
if not npoints:
geom = GeometryCollection()
elif len(points) == 1:
geom = points[0]
else:
geom = MultiPoint(points)
return unary_union(geom) if union else geom


class HexTiles(_Element, HvHexTiles):
"""
Expand Down Expand Up @@ -482,11 +513,28 @@ class Path(_Element, HvPath):

group = param.String(default='Path', constant=True)

def geom(self):
def geom(self, union=False):
"""
Returns Path as a shapely geometry.
Converts the Path to a shapely geometry.
Parameters
----------
union: boolean (default=False)
Whether to compute a union between the geometries
Returns
-------
A shapely geometry
"""
return path_to_geom(self)
geoms = expand_geoms([g['geometry'] for g in path_to_geom_dicts(self)])
ngeoms = len(geoms)
if not ngeoms:
geom = GeometryCollection()
elif ngeoms == 1:
geom = geoms[0]
else:
geom = MultiLineString(geoms)
return unary_union(geom) if union else geom


class EdgePaths(Path):
Expand Down Expand Up @@ -608,11 +656,28 @@ class Contours(_Element, HvContours):

group = param.String(default='Contours', constant=True)

def geom(self):
def geom(self, union=False):
"""
Returns Path as a shapely geometry.
Converts the Contours to a shapely geometry.
Parameters
----------
union: boolean (default=False)
Whether to compute a union between the geometries
Returns
-------
A shapely geometry
"""
return path_to_geom(self)
geoms = expand_geoms([g['geometry'] for g in path_to_geom_dicts(self)])
ngeoms = len(geoms)
if not ngeoms:
geom = GeometryCollection()
elif ngeoms == 1:
geom = geoms[0]
else:
geom = MultiLineString(geoms)
return unary_union(geom) if union else geom


class Polygons(_Element, HvPolygons):
Expand All @@ -624,11 +689,28 @@ class Polygons(_Element, HvPolygons):

group = param.String(default='Polygons', constant=True)

def geom(self):
def geom(self, union=False):
"""
Returns Path as a shapely geometry.
Converts the Path to a shapely geometry.
Parameters
----------
union: boolean (default=False)
Whether to compute a union between the geometries
Returns
-------
A shapely geometry
"""
return polygon_to_geom(self)
geoms = expand_geoms([g['geometry'] for g in polygons_to_geom_dicts(self)])
ngeoms = len(geoms)
if not ngeoms:
geom = GeometryCollection()
elif ngeoms == 1:
geom = geoms[0]
else:
geom = MultiPolygon(geoms)
return unary_union(geom) if union else geom


class Rectangles(_Element, HvRectangles):
Expand All @@ -645,6 +727,28 @@ class Rectangles(_Element, HvRectangles):
bottom-left (lon0, lat0) and top right (lon1, lat1) coordinates
of each box.""")

def geom(self, union=False):
"""
Converts the Rectangles to a shapely geometry.
Parameters
----------
union: boolean (default=False)
Whether to compute a union between the geometries
Returns
-------
A shapely geometry
"""
boxes = [box(*g) for g in self.array([0, 1, 2, 3])]
nboxes = len(boxes)
if not nboxes:
geom = GeometryCollection()
elif nboxes == 1:
geom = boxes[0]
else:
geom = MultiPolygon(boxes)
return unary_union(geom) if union else geom


class Segments(_Element, HvSegments):
Expand All @@ -661,6 +765,21 @@ class Segments(_Element, HvSegments):
bottom-left (lon0, lat0) and top-right (lon1, lat1) coordinates
of each segment.""")

def geom(self, union=False):
"""
Converts the Segments to a shapely geometry.
"""
lines = [LineString([(x0, y0), (x1, y1)]) for (x0, y0, x1, y1)
in self.array([0, 1, 2, 3])]
nlines = len(lines)
if not nlines:
geom = GeometryCollection()
elif nlines == 1:
geom = lines[0]
else:
geom = MultiLineString(lines)
return unary_union(geom) if union else geom


class Shape(Dataset):
"""
Expand Down Expand Up @@ -839,8 +958,18 @@ def from_records(cls, records, dataset=None, on=None, value=None,
return element(data, vdims=kdims+vdims, **kwargs).opts(color=value)


def geom(self):
def geom(self, union=False):
"""
Returns Shape as a shapely geometry
Returns the Shape as a shapely geometry
Parameters
----------
union: boolean (default=False)
Whether to compute a union between the geometries
Returns
-------
A shapely geometry
"""
return self.data['geometry']
geom = self.data['geometry']
return unary_union(geom) if union else geom

0 comments on commit e8371f5

Please sign in to comment.