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

Reimplement Path, Contours and Polygons plots #1991

Merged
merged 39 commits into from
Oct 22, 2017
Merged
Show file tree
Hide file tree
Changes from 38 commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
1321c4b
Added scalar validation to interfaces and Contours
philippjfr Oct 10, 2017
8e7e682
Fixed MultiInterface dimension_values expand
philippjfr Oct 10, 2017
68d3605
Improved functionality of Path.split
philippjfr Oct 10, 2017
25c1e22
Reimplemented bokeh PathPlots based on new interface
philippjfr Oct 10, 2017
d1c9c01
Rewrote contours operation using new interface
philippjfr Oct 10, 2017
10b6dc8
Small fixes to bokeh path plots
philippjfr Oct 10, 2017
8000134
Fixed Contours levels deprecation warning
philippjfr Oct 10, 2017
a5e7411
Rewrote matplotlib path plots
philippjfr Oct 10, 2017
9adf459
Small fixes for mpl path plots
philippjfr Oct 10, 2017
8db534a
Fixes for levels in contours operation
philippjfr Oct 10, 2017
17ea673
Optimized datashader path splitting
philippjfr Oct 10, 2017
115ac00
Added support for passing stacked arrays to DictInterface
philippjfr Oct 11, 2017
ea5d64e
Overhauled docs with recent path improvements
philippjfr Oct 11, 2017
d0e2f2d
Small fixes for Path constructor
philippjfr Oct 11, 2017
a69a622
Fixes for contours operation
philippjfr Oct 11, 2017
4d9f5ee
Fixed Path colorbars
philippjfr Oct 11, 2017
2cc0e42
Improved contours operation
philippjfr Oct 17, 2017
8cdb15b
Fixes for dictionary interface
philippjfr Oct 18, 2017
c7493af
Fixed Path comparisons
philippjfr Oct 18, 2017
712d04b
Small fixes for contours operation
philippjfr Oct 18, 2017
7debba1
Updated path comparison tests
philippjfr Oct 18, 2017
343b49a
Updated multi-interface test
philippjfr Oct 18, 2017
39f7a82
Updated contours operation tests
philippjfr Oct 18, 2017
a237e85
Minor fix for Path hover opts
philippjfr Oct 18, 2017
9a4c335
Small fix for Path matplotlib plot
philippjfr Oct 18, 2017
5d88475
Updated example notebooks for contours operation changes
philippjfr Oct 18, 2017
c2cd257
Simplified texas choropleth examples
philippjfr Oct 18, 2017
93088bb
Minor fix for dictionary interface
philippjfr Oct 18, 2017
13fb334
Prefer dictionary interface as MultiInterface subtype
philippjfr Oct 18, 2017
4027f06
Removed dimension range from filled contours
philippjfr Oct 18, 2017
b1b95f2
Small fixes to path tests
philippjfr Oct 18, 2017
96de1b0
Fixed MultiInterface validation
philippjfr Oct 18, 2017
1e75ffa
Optimized graph edgepaths plotting
philippjfr Oct 18, 2017
14a91b1
Small fix for MultiInterface validation
philippjfr Oct 19, 2017
1711099
Add class attribute to denote multi interfaces
philippjfr Oct 19, 2017
1b2602d
Ensure splitting MultiInterface does not expand dictionary data
philippjfr Oct 20, 2017
0a73811
Improved Path.split when not using multi-interface
philippjfr Oct 20, 2017
bf217c5
Added static_datasource optimization for bokeh paths
philippjfr Oct 20, 2017
6e9ec29
Addressed various review comments
philippjfr Oct 22, 2017
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
11 changes: 5 additions & 6 deletions doc/Tutorials/Introduction.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -640,10 +640,9 @@
"metadata": {},
"outputs": [],
"source": [
"%%opts Contours.Red (color=Palette('Reds')) Contours.Green (color=Palette('Greens')) Contours.Blue (color=Palette('Blues'))\n",
"data = {lvl:(contours(chans.RedChannel.Macaw, levels=[lvl], group='Red') +\\\n",
" contours(chans.Channel.Green, levels=[lvl], group='Green') +\\\n",
" contours(chans.Channel.Blue, levels=[lvl], group='Blue'))\n",
"data = {lvl:(contours(chans.RedChannel.Macaw, levels=[lvl]).opts(style=dict(cmap='Reds')) +\\\n",
" contours(chans.Channel.Green, levels=[lvl]).opts(style=dict(cmap='Greens')) +\\\n",
" contours(chans.Channel.Blue, levels=[lvl]).opts(style=dict(cmap='Blues')))\n",
" for lvl in np.linspace(0.1,0.9,9)}\n",
"levels = hv.HoloMap(data, kdims=['Levels']).collate()\n",
"levels"
Expand All @@ -662,8 +661,8 @@
"metadata": {},
"outputs": [],
"source": [
"green05 = levels.Overlay.Green[0.5]\n",
"green05 + green05.Channel + green05.Channel.Green.sample(y=0.0)"
"green05 = levels.Contours.Green\n",
"green05 + chans.Channel.Green + chans.Channel.Green.sample(y=0.0)"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't this have implications for backwards compatibility?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Guess it has some, it's no longer overlaid by default. I could have made it be overlaid again but I generally don't want to.

]
},
{
Expand Down
22 changes: 7 additions & 15 deletions examples/gallery/demos/bokeh/texas_choropleth_example.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -38,20 +38,11 @@
"from bokeh.sampledata.us_counties import data as counties\n",
"from bokeh.sampledata.unemployment import data as unemployment\n",
"\n",
"counties = {\n",
" code: county for code, county in counties.items() if county[\"state\"] == \"tx\"\n",
"}\n",
"counties = [dict(county, Unemployment=unemployment[cid])\n",
" for cid, county in counties.items()\n",
" if county[\"state\"] == \"tx\"]\n",
"\n",
"county_xs = [county[\"lons\"] for county in counties.values()]\n",
"county_ys = [county[\"lats\"] for county in counties.values()]\n",
"\n",
"county_names = [county['name'] for county in counties.values()]\n",
"county_rates = [unemployment[county_id] for county_id in counties]\n",
"\n",
"county_polys = {name: hv.Polygons((xs, ys), level=rate, vdims=['Unemployment'])\n",
" for name, xs, ys, rate in zip(county_names, county_xs, county_ys, county_rates)}\n",
"\n",
"choropleth = hv.NdOverlay(county_polys, kdims=['County'])"
"choropleth = hv.Polygons(counties, ['lons', 'lats'], [('detailed name', 'County'), 'Unemployment'])"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume the old code still works and the new approach is just more succinct/flexible/efficient.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right.

]
},
{
Expand All @@ -68,10 +59,11 @@
"outputs": [],
"source": [
"plot_opts = dict(logz=True, tools=['hover'], xaxis=None, yaxis=None,\n",
" show_grid=False, show_frame=False, width=500, height=500)\n",
" show_grid=False, show_frame=False, width=500, height=500,\n",
" color_index='Unemployment', colorbar=True, toolbar='above')\n",
"style = dict(line_color='white')\n",
"\n",
"choropleth({'Polygons': {'style': style, 'plot': plot_opts}})"
"choropleth.opts(style=style, plot=plot_opts)"
]
}
],
Expand Down
22 changes: 7 additions & 15 deletions examples/gallery/demos/matplotlib/texas_choropleth_example.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -39,20 +39,11 @@
"from bokeh.sampledata.us_counties import data as counties\n",
"from bokeh.sampledata.unemployment import data as unemployment\n",
"\n",
"counties = {\n",
" code: county for code, county in counties.items() if county[\"state\"] == \"tx\"\n",
"}\n",
"counties = [dict(county, Unemployment=unemployment[cid])\n",
" for cid, county in counties.items()\n",
" if county[\"state\"] == \"tx\"]\n",
"\n",
"county_xs = [county[\"lons\"] for county in counties.values()]\n",
"county_ys = [county[\"lats\"] for county in counties.values()]\n",
"\n",
"county_names = [county['name'] for county in counties.values()]\n",
"county_rates = [unemployment[county_id] for county_id in counties]\n",
"\n",
"county_polys = {name: hv.Polygons((xs, ys), level=rate, vdims=['Unemployment'])\n",
" for name, xs, ys, rate in zip(county_names, county_xs, county_ys, county_rates)}\n",
"\n",
"choropleth = hv.NdOverlay(county_polys, kdims=['County'])"
"choropleth = hv.Polygons(counties, ['lons', 'lats'], [('detailed name', 'County'), 'Unemployment'])"
]
},
{
Expand All @@ -69,10 +60,11 @@
"outputs": [],
"source": [
"plot_opts = dict(logz=True, xaxis=None, yaxis=None,\n",
" show_grid=False, show_frame=False, fig_size=200, bgcolor='white')\n",
" show_grid=False, show_frame=False, colorbar=True,\n",
" fig_size=200, color_index='Unemployment')\n",
"style = dict(edgecolor='white')\n",
"\n",
"choropleth({'Polygons': {'style': style, 'plot': plot_opts}})"
"choropleth.opts(style=style, plot=plot_opts)"
]
}
],
Expand Down
4 changes: 2 additions & 2 deletions examples/getting_started/4-Gridded_Datasets.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@
"outputs": [],
"source": [
"ROIs = data['ROIs']\n",
"roi_bounds = hv.NdOverlay({i: hv.Bounds(tuple(roi)) for i, roi in enumerate(ROIs)})\n",
"roi_bounds = hv.Path([hv.Bounds(tuple(roi)) for roi in ROIs])\n",
"print(ROIs.shape)"
]
},
Expand All @@ -176,7 +176,7 @@
"outputs": [],
"source": [
"%%opts Image [width=400 height=400 xaxis=None yaxis=None] \n",
"%%opts Bounds (color='white') Text (text_color='white' text_font_size='8pt')\n",
"%%opts Path (color='white') Text (text_color='white' text_font_size='8pt')\n",
"\n",
"opts = dict(halign='left', valign='bottom')\n",
"roi_text = hv.NdOverlay({i: hv.Text(roi[0], roi[1], str(i), **opts) for i, roi in enumerate(ROIs)})\n",
Expand Down
20 changes: 9 additions & 11 deletions examples/reference/elements/bokeh/Contours.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"A ``Contours`` object is similar to ``Path`` object except it may be associated with a numeric value (the ``level``), which can be used to apply colormapping the ``Contours``. To see the effect of this we can create a number of ``Contours`` with varying shapes and ``level`` values. In this case we will create a number of concentric rings with increasing radii and level values and colormap the ``Contours`` with the viridis colormap:"
"A ``Contours`` object is similar to a ``Path`` element but allows each individual path to be associated with a single scalar value, which can be used to apply colormapping the ``Contours``. Just like the ``Path`` element ``Contours`` will accept a list of arrays, dataframes, a dictionaries of columns (or any of the other literal formats including tuples of columns and lists of tuples). In order to efficiently represent the scalar values associated with each path the dictionary format is preferable since it can store the scalar values without expanding them into a whole column.\n",
"\n",
"To see the effect we will create a number of concentric rings with increasing radii and define a colormap to apply color the circles: "
]
},
{
Expand All @@ -37,20 +39,18 @@
"metadata": {},
"outputs": [],
"source": [
"%%opts Contours (cmap='viridis')\n",
"\n",
"def circle(radius, x=0, y=0):\n",
"def circle(radius):\n",
" angles = np.linspace(0, 2*np.pi, 100)\n",
" return np.array(list(zip(x+radius*np.sin(angles), y+radius*np.cos(angles))))\n",
" return {'x': radius*np.sin(angles), 'y': radius*np.cos(angles), 'radius': radius}\n",
"\n",
"hv.Overlay([hv.Contours([circle(i+0.05)], level=i) for i in np.linspace(0, 1, 10)])"
"hv.Contours([circle(i) for i in np.linspace(0, 1, 10)], vdims=['radius'])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Often ``Contours`` will be directly computed from an underlying ``Image``, which is made easy using the ``contours`` operation. The operation accepts an ``Image`` type as input and will compute an ``NdOverlay`` containing a ``Contours`` Element for each of the specified ``levels``. We will declare an ``Image`` of sine rings\n",
"Often ``Contours`` will be directly computed from an underlying ``Image``, which is made easy using the ``contours`` operation. The operation accepts an ``Image`` type as input and will return ``Contours`` containing iso-contours for each of the specified ``levels``. We will declare an ``Image`` of sine rings\n",
"and then compute ``Contours`` at 5 levels spaced linearly over the range of values in the Image:"
]
},
Expand All @@ -60,12 +60,10 @@
"metadata": {},
"outputs": [],
"source": [
"%%opts Contours [show_legend=False colorbar=True width=325] (cmap='fire')\n",
"%%opts Contours [colorbar=True width=325 tools=['hover']] (cmap='fire')\n",
"x,y = np.mgrid[-50:51, -50:51] * 0.05\n",
"img = hv.Image(np.sin(x**2+y**3))\n",
"\n",
"z0, z1 = img.range('z')\n",
"img + hv.operation.contours(img, levels=np.linspace(z0, z1, 5), overlaid=False)"
"img + hv.operation.contours(img, levels=5)"
]
}
],
Expand Down
34 changes: 27 additions & 7 deletions examples/reference/elements/bokeh/Path.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,9 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"A ``Path`` object is actually a collection of lines, which are all plotted with the same style. Unlike ``Curve`` where the y-axis is the dependent variable, a ``Path`` consists of lines connecting arbitrary points in two-dimensional space as is not expected to be a function.\n",
"\n"
"A ``Path`` object is actually a collection of lines, unlike ``Curve`` where the y-axis is the dependent variable, a ``Path`` consists of lines connecting arbitrary points in two-dimensional space. The individual subpaths should be supplied as a list and will be stored as NumPy arrays, DataFrames or dictionaries for each column, i.e. any of the formats accepted by columnar data formats.\n",
"\n",
"In this example we will create a Lissajous curve, which describe complex harmonic motion:"
]
},
{
Expand All @@ -39,19 +40,38 @@
"outputs": [],
"source": [
"%%opts Path (color='black' line_width=4)\n",
"lin = np.linspace(-np.pi,np.pi,300)\n",
"\n",
"def lissajous(t, a,b, delta):\n",
" return (np.sin(a * t + delta), np.sin(b * t))\n",
"lin = np.linspace(0, np.pi*2, 200)\n",
"\n",
"def lissajous(t, a, b, delta):\n",
" return (np.sin(a * t + delta), np.sin(b * t), t)\n",
"\n",
"hv.Path([lissajous(lin, 3, 5, np.pi/2)])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n",
"hv.Path(lissajous(np.linspace(-np.pi, np.pi, 1000),3,5,np.pi/2))"
"If you looked carefully the ``lissajous`` function actually returns three columns, respectively for the x, y columns and a third column describing the point in time. By declaring a value dimension for that third column we can also color the Path by time. Since the value is cyclical we will also use a cyclic colormap (``'hsv'``) to represent this variable:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"%%opts Path [color_index='time'] (line_width=4 cmap='hsv')\n",
"hv.Path([lissajous(lin, 3, 5, np.pi/2)], vdims=['time'])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Unlike ``Curve`` as single ``Path`` element can contain multiple lines that are disconnected from each other which will all be plotted in the same style. Only by overlaying multiple ``Path`` objects do you iterate through the defined color cycle (or any other style options that have been defined). A ``Path`` is often useful to draw arbitrary annotations on top of an existing plot.\n",
"If we do not provide a ``color_index`` overlaid ``Path`` elements will cycle colors just like other elements do unlike ``Curve`` a single ``Path`` element can contain multiple lines that are disconnected from each other. A ``Path`` can therefore often useful to draw arbitrary annotations on top of an existing plot.\n",
"\n",
"A ``Path`` Element accepts multiple formats for specifying the paths, the simplest of which is passing a list of ``Nx2`` arrays of the x- and y-coordinates, alternative we can pass lists of coordinates. In this example we will create some coordinates representing rectangles and ellipses annotating an ``RGB`` image:"
]
Expand Down
46 changes: 31 additions & 15 deletions examples/reference/elements/bokeh/Polygons.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,11 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"A ``Polygons`` object is similar to a ``Contours`` object except that each supplied path is closed and filled. Just like ``Contours``, an optional ``level`` value may be supplied; the Polygons will then be colored according to the supplied ``cmap``. Non-finite values such as ``np.NaN`` or ``np.inf`` will default to the supplied ``facecolor``.\n",
"A ``Polygons`` represents a contiguous filled area in a 2D space as a list of paths. Just like the ``Contours`` element additional scalar value dimensions maybe may be supplied, which can be used to color the ``Polygons`` with the defined ``cmap``. Like other ``Path`` types it accepts a list of arrays, dataframes, a dictionary of columns (or any of the other literal formats including tuples of columns and lists of tuples).\n",
"\n",
"Polygons with values can be used to build heatmaps with arbitrary shapes."
"In order to efficiently represent the scalar values associated with each path the dictionary format is preferable since it can store the scalar values without expanding them into a whole column. Additionally it allows passing multiple columns as a single array by specifying the dimension names as a tuple.\n",
"\n",
"In this example we will createa list of random polygons each with an associated ``level`` value. Polygons will default to using the first value dimension as the ``color_index`` but for clarity we will define the ``color_index`` explicitly:"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

createa -> create a

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks.

]
},
{
Expand All @@ -39,19 +41,21 @@
"metadata": {},
"outputs": [],
"source": [
"%%opts Polygons (cmap='hot' line_color='black' line_width=2)\n",
"np.random.seed(35)\n",
"hv.Polygons([np.random.rand(4,2)], level=0.5) *\\\n",
"hv.Polygons([np.random.rand(4,2)], level=1.0) *\\\n",
"hv.Polygons([np.random.rand(4,2)], level=1.5) *\\\n",
"hv.Polygons([np.random.rand(4,2)], level=2.0)"
"%%opts Polygons [color_index='level'] (line_color='black' line_width=1)\n",
"np.random.seed(1)\n",
"\n",
"def rectangle(x=0, y=0, width=.05, height=.05):\n",
" return np.array([(x,y), (x+width, y), (x+width, y+height), (x, y+height)])\n",
"\n",
"hv.Polygons([{('x', 'y'): rectangle(x, y), 'level': z}\n",
" for x, y, z in np.random.rand(100, 3)], vdims=['level']).redim.range(x=(-.1,1.1), y=(-0.1, 1.1))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"``Polygons`` without a value are useful as annotation, but also allow us to draw arbitrary shapes."
"``Polygons`` is a very versatile element which may be used to draw custom annotations, choropleth maps (as can be seen in the [texas_unemploment example](../../../gallery/demos/bokeh/texas_choropleth_example.ipynb)) among many other examples. We can also use some of the other path based annotations to quickly generate polygons, including ``Box``, ``Bounds`` and ``Ellipse`` elements. In the simple case we can simply pass a list of these elements:"
]
},
{
Expand All @@ -60,12 +64,24 @@
"metadata": {},
"outputs": [],
"source": [
"def rectangle(x=0, y=0, width=1, height=1):\n",
" return np.array([(x,y), (x+width, y), (x+width, y+height), (x, y+height)])\n",
"\n",
"(hv.Polygons([rectangle(width=2), rectangle(x=6, width=2)]).opts(style={'fill_color': '#a50d0d'})\n",
"* hv.Polygons([rectangle(x=2, height=2), rectangle(x=5, height=2)]).opts(style={'fill_color': '#ffcc00'})\n",
"* hv.Polygons([rectangle(x=3, height=2, width=2)]).opts(style={'fill_color': 'cyan'}))"
"hv.Polygons([hv.Box(i, i, i) for i in range(1, 10)])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Alternatively we can use the ``array`` method to return the x/y-coordinates of the annotations and define additional z-values by declaring a dictionary:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"hv.Polygons([{('x', 'y'): hv.Box(0, 0, i).array(), 'z': i} for i in range(1, 10)[::-1]], vdims=['z']) +\\\n",
"hv.Polygons([{('x', 'y'): hv.Ellipse(0, 0, (i, i)).array(), 'z': i} for i in range(1, 10)[::-1]], vdims=['z'])"
]
}
],
Expand Down
20 changes: 10 additions & 10 deletions examples/reference/elements/matplotlib/Contours.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"A ``Contours`` object is similar to ``Path`` object except it may be associated with a numeric value (the ``level``), which can be used to apply colormapping the ``Contours``. To see the effect of this we can create a number of ``Contours`` with varying shapes and ``level`` values. In this case we will create a number of concentric rings with increasing radii and level values and colormap the ``Contours`` with the viridis colormap:"
"A ``Contours`` object is similar to a ``Path`` element but allows each individual path to be associated with a single scalar value, which can be used to apply colormapping the ``Contours``. Just like ``Path`` contours will accept a list of arrays, dataframes, a dictionaries of columns. In order to efficiently represent the scalar values associated with each path the dictionary format is preferable since it can store the scalar values without expanding them into a whole column.\n",
"\n",
"To see the effect we will create a number of concentric rings with increasing radii and define a colormap to apply color the circles: "
]
},
{
Expand All @@ -37,20 +39,18 @@
"metadata": {},
"outputs": [],
"source": [
"%%opts Contours (cmap='viridis')\n",
"\n",
"def circle(radius, x=0, y=0):\n",
" angles = np.linspace(0, 2*np.pi, 100)\n",
" return np.array(list(zip(x+radius*np.sin(angles), y+radius*np.cos(angles))))\n",
"def circle(radius):\n",
" angles = np.linspace(0, 2*np.pi, 50)\n",
" return {'x': radius*np.sin(angles), 'y': radius*np.cos(angles), 'radius': radius}\n",
"\n",
"hv.Overlay([hv.Contours([circle(i+0.05)], level=i) for i in np.linspace(0, 1, 10)])"
"hv.Contours([circle(i) for i in np.linspace(0, 1, 10)], vdims=['radius'])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Often ``Contours`` will be directly computed from an underlying ``Image``, which is made easy using the ``contours`` operation. The operation accepts an ``Image`` type as input and will compute an ``NdOverlay`` containing a ``Contours`` Element for each of the specified ``levels``. We will declare an ``Image`` of sine rings\n",
"Often ``Contours`` will be directly computed from an underlying ``Image``, which is made easy using the ``contours`` operation. The operation accepts an ``Image`` type as input and will return ``Contours`` containing iso-contours for each of the specified ``levels``. We will declare an ``Image`` of sine rings\n",
"and then compute ``Contours`` at 5 levels spaced linearly over the range of values in the Image:"
]
},
Expand All @@ -60,12 +60,12 @@
"metadata": {},
"outputs": [],
"source": [
"%%opts Contours [show_legend=False colorbar=True width=325] (cmap='fire')\n",
"%%opts Contours [colorbar=True] (cmap='fire')\n",
"x,y = np.mgrid[-50:51, -50:51] * 0.05\n",
"img = hv.Image(np.sin(x**2+y**3))\n",
"\n",
"z0, z1 = img.range('z')\n",
"img + hv.operation.contours(img, levels=np.linspace(z0, z1, 5), overlaid=False)"
"img + hv.operation.contours(img, levels=5)"
]
}
],
Expand Down
Loading