Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added custom PolyVertexEditTool #276

Merged
merged 18 commits into from
Jul 5, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ language: generic
env:
global:
- PYENV_VERSION=3.6
- PYTHON_VERSION=3.6
- PKG_TEST_PYTHON="--test-python=py36"
- CHANS_DEV="-c pyviz/label/earthsim -c pyviz/label/dev -c bokeh -c defaults -c erdc -c conda-forge -c aquaveo"
- CHANS_REL="-c pyviz -c bokeh -c defaults -c erdc -c conda-forge -c aquaveo"
Expand All @@ -32,7 +33,7 @@ stages:
_osx_config: &_osx_config
os: osx
osx_image: xcode10.1
env: PYENV_VERSION=3.6.4 CHANS_DEV="-c pyviz/label/earthsim -c pyviz/label/dev -c erdc -c conda-forge -c aquaveo"
env: PYENV_VERSION=3.7.1 CHANS_DEV="-c pyviz/label/earthsim -c pyviz/label/dev -c erdc -c conda-forge -c aquaveo"
before_install:
# set up python
- eval "$(pyenv init -)"
Expand All @@ -45,6 +46,7 @@ _osx_config: &_osx_config
- conda config --set always_yes True
- conda install -c pyviz "pyctdev>=0.5" && doit ecosystem_setup
- conda config --set path_conflict warn
- conda config --set restore_free_channel true

jobs:
include:
Expand All @@ -58,7 +60,7 @@ jobs:
- conda install -c pyviz "pyctdev>=0.5" && doit ecosystem_setup
- conda config --set path_conflict warn
install:
- travis_wait 20 doit env_create $CHANS_DEV --name=earthsim --python=$PYENV_VERSION
- travis_wait 20 doit env_create $CHANS_DEV --name=earthsim --python=$PYTHON_VERSION
- source activate earthsim
- doit develop_install $CHANS_DEV -o tests
- doit env_capture
Expand Down
39 changes: 35 additions & 4 deletions earthsim/annotators.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,15 @@
from holoviews import DynamicMap, Path, Table, NdOverlay, Store, Options
from holoviews.core.util import disable_constant
from holoviews.plotting.links import DataLink
from holoviews.streams import Selection1D, Stream, PolyDraw, PolyEdit, PointDraw, CDSStream
from holoviews.streams import Selection1D, Stream, PointDraw, CDSStream, PolyEdit, PolyDraw
from geoviews.data.geopandas import GeoPandasInterface
from geoviews import Polygons, Points, WMTS, TriMesh, Path as GeoPath
from geoviews.util import path_to_geom_dicts
from shapely.geometry import Polygon, LinearRing, MultiPolygon

from .models.custom_tools import CheckpointTool, RestoreTool, ClearTool
from .links import VertexTableLink, PointTableLink
from .streams import PolyVertexDraw, PolyVertexEdit


def paths_to_polys(path):
Expand Down Expand Up @@ -158,6 +159,12 @@ class GeoAnnotator(param.Parameterized):
num_polys = param.Integer(default=None, doc="""
Maximum number of polygons to allow drawing (unlimited by default).""")

node_style = param.Dict(default={'fill_color': 'indianred', 'size': 6}, doc="""
Styling to apply to the node vertices.""")

feature_style = param.Dict(default={'fill_color': 'blue', 'size': 10}, doc="""
Styling to apply to the feature vertices.""")

height = param.Integer(default=500, doc="Height of the plot",
precedence=-1)

Expand Down Expand Up @@ -186,9 +193,20 @@ def _init_polys(self, polys=None):
opts = dict(tools=self._tools, finalize_hooks=[initialize_tools], color_index=None)
polys = self.polys if polys is None else polys
self.polys = polys.options(**opts)
self.poly_stream = PolyDraw(source=self.polys, data={}, show_vertices=True, num_objects=self.num_polys)
self.vertex_stream = PolyEdit(source=self.polys, vertex_style={'nonselection_alpha': 0.5})

if isinstance(self.polys, Polygons):
poly_draw, poly_edit = PolyDraw, PolyEdit
style_kwargs = {}
else:
poly_draw, poly_edit = PolyVertexDraw, PolyVertexEdit
style_kwargs = dict(node_style=self.node_style, feature_style=self.feature_style)
self.poly_stream = poly_draw(
source=self.polys, data={}, show_vertices=True,
num_objects=self.num_polys, **style_kwargs)
self.vertex_stream = poly_edit(
source=self.polys, vertex_style={'nonselection_alpha': 0.5},
**style_kwargs)
self._poly_selection = Selection1D(source=self.polys)

@param.depends('points', watch=True)
@preprocess
def _init_points(self, points=None):
Expand Down Expand Up @@ -316,6 +334,7 @@ def _link_polys(self):
self.polys = self.polys.add_dimension(col, 0, '', True)
self.poly_stream.source = self.polys
self.vertex_stream.source = self.polys
self._poly_selection.source = self.polys

if len(self.polys):
poly_data = gv.project(self.polys).split()
Expand Down Expand Up @@ -344,6 +363,13 @@ def table_view(self):
def panel(self):
return pn.Row(self.map_view, self.table_view)

@property
def selected_polygons(self):
index = self._poly_selection.index
if not index:
return []
return [p for i, p in enumerate(self.poly_stream.element.split()) if i in index]

@param.output(path=hv.Path)
def path_output(self):
return self.poly_stream.element
Expand Down Expand Up @@ -380,11 +406,16 @@ def _link_points(self):
projected = gv.project(self.points, projection=ccrs.PlateCarree())
self.point_table = Table(projected).opts(plot=plot, style=style)
self.point_link = PointTableLink(source=self.points, target=self.point_table)
self._point_selection = Selection1D(source=self.points)

@param.depends('points')
def table_view(self):
return self.point_table

@property
def selected_points(self):
return self.point_stream.element.iloc[self._point_selection.index]

def panel(self):
return pn.Row(self.map_view, self.table_view)

Expand Down
Binary file added earthsim/icons/PolyBreak.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
28 changes: 26 additions & 2 deletions earthsim/links.py
Original file line number Diff line number Diff line change
Expand Up @@ -165,16 +165,18 @@ class VertexTableLinkCallback(LinkCallback):
ys_column = target_cds.data[y]
var projected_xs = []
var projected_ys = []
var points = []
for (i = 0; i < xs_column.length; i++) {
var xv = xs_column[i]
var yv = ys_column[i]
p = projections.wgs84_mercator.forward([xv, yv])
projected_xs.push(p[0])
projected_ys.push(p[1])
points.push(i)
}
index = source_cds.selected.indices[0]
source_cds.data['xs'][index] = projected_xs;
source_cds.data['ys'][index] = projected_ys;
const xpaths = source_cds.data['xs']
const ypaths = source_cds.data['ys']
var length = source_cds.data['xs'].length
for (var col in target_cds.data) {
if ((col == x) || (col == y)) { continue; }
Expand All @@ -185,7 +187,29 @@ class VertexTableLinkCallback(LinkCallback):
source_cds.data[col] = empty
}
source_cds.data[col][index] = target_cds.data[col]
for (const p of points) {
for (let pindex = 0; pindex < xpaths.length; pindex++) {
if (pindex == index) { continue }
const xs = xpaths[pindex]
const ys = ypaths[pindex]
const column = source_cds.data[col][pindex]
if (column.length != xs.length) {
for (let ind = 0; ind < xs.length; ind++) {
column.push(null)
}
}
for (let ind = 0; ind < xs.length; ind++) {
if ((xs[ind] == xpaths[index][p]) && (ys[ind] == ypaths[index][p])) {
column[ind] = target_cds.data[col][p]
xs[ind] = projected_xs[p];
ys[ind] = projected_ys[p];
}
}
}
}
}
xpaths[index] = projected_xs;
ypaths[index] = projected_ys;
source_cds.change.emit()
source_cds.properties.data.change.emit();
source_cds.data = source_cds.data
Expand Down
40 changes: 32 additions & 8 deletions earthsim/models/custom_tools.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import os

from bokeh.core.properties import Instance, List
from bokeh.core.properties import Instance, List, Dict, String, Any
from bokeh.core.enums import Dimensions
from bokeh.models import Tool, ColumnDataSource
from bokeh.models import Tool, ColumnDataSource, PolyEditTool, PolyDrawTool

from . import _CUSTOM_MODELS

fpath = os.path.dirname(__file__)
fpath = os.path.abspath(os.path.dirname(__file__))


class CheckpointTool(Tool):
Expand All @@ -15,32 +15,56 @@ class CheckpointTool(Tool):
the RestoreTool to restore the data to a previous state.
"""

__implementation__ = os.path.join(fpath, 'checkpoint_tool.ts')

sources = List(Instance(ColumnDataSource))

__implementation__ = os.path.join(fpath, 'checkpoint_tool.ts')


class RestoreTool(Tool):
"""
Restores the data on the supplied ColumnDataSources to a previous
checkpoint created by the CheckpointTool
"""

__implementation__ = os.path.join(fpath, 'restore_tool.ts')

sources = List(Instance(ColumnDataSource))

__implementation__ = os.path.join(fpath, 'restore_tool.ts')


class ClearTool(Tool):
"""
Clears the data on the supplied ColumnDataSources.
"""

sources = List(Instance(ColumnDataSource))

__implementation__ = os.path.join(fpath, 'clear_tool.ts')

sources = List(Instance(ColumnDataSource))

class PolyVertexEditTool(PolyEditTool):

node_style = Dict(String, Any, help="""
Custom styling to apply to the intermediate nodes of a patch or line glyph.""")

end_style = Dict(String, Any, help="""
Custom styling to apply to the start and nodes of a patch or line glyph.""")

__implementation__ = os.path.join(fpath, 'poly_edit.ts')


class PolyVertexDrawTool(PolyDrawTool):

node_style = Dict(String, Any, help="""
Custom styling to apply to the intermediate nodes of a patch or line glyph.""")

end_style = Dict(String, Any, help="""
Custom styling to apply to the start and nodes of a patch or line glyph.""")

__implementation__ = os.path.join(fpath, 'poly_draw.ts')


_CUSTOM_MODELS['earthsim.models.custom_tools.CheckPointTool'] = CheckpointTool
_CUSTOM_MODELS['earthsim.models.custom_tools.RestoreTool'] = RestoreTool
_CUSTOM_MODELS['earthsim.models.custom_tools.ClearTool'] = ClearTool
_CUSTOM_MODELS['earthsim.models.custom_tools.PolyVertexEditTool'] = PolyVertexEditTool
_CUSTOM_MODELS['earthsim.models.custom_tools.PolyVertexDrawTool'] = PolyVertexDrawTool
Loading