Skip to content

Commit

Permalink
Merge pull request #2840 from plotly/hline-vline-multifacets-rebase
Browse files Browse the repository at this point in the history
`add_{hline,vline,hrect,vrect}` and `add_{shape,image,annotation}` to multiple facets, squashed
  • Loading branch information
nicolaskruchten authored Oct 22, 2020
2 parents 1ca15e9 + e2f18b1 commit abd8609
Show file tree
Hide file tree
Showing 20 changed files with 3,052 additions and 589 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ node_modules/

# virtual envs
vv
venv
venv*

# dist files
build
Expand All @@ -46,4 +46,5 @@ temp-plot.html
doc/python/.ipynb_checkpoints
doc/python/.mapbox_token
doc/.ipynb_checkpoints
tags
doc/check-or-enforce-order.py
17 changes: 15 additions & 2 deletions doc/python/figure-structure.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,18 +95,31 @@ The third of the three top-level attributes of a figure is `frames`, whose value

### The `config` Object

At [render-time](/python/renderers/), it is also possible to control certain figure behaviors which are not considered part of the figure proper i.e. the behaviour of the "modebar" and how the figure relates to mouse actions like scrolling etc. The object that contains these options is called the [`config`, and has its own documentation page](/python/configuration-options/). It is exposed in Python as the `config` keyword argument of the `.show()` method on `plotly.graph_objects.Figure` objects.
At [render-time](/python/renderers/), it is also possible to control certain figure behaviors which are not considered part of the figure proper i.e. the behavior of the "modebar" and how the figure relates to mouse actions like scrolling etc. The object that contains these options is called the [`config`, and has its own documentation page](/python/configuration-options/). It is exposed in Python as the `config` keyword argument of the `.show()` method on `plotly.graph_objects.Figure` objects.

### Positioning With Paper or Container Coordinates
### Positioning With Paper, Container Coordinates, or Axis Domain Coordinates

Various figure components configured within the layout of the figure support positioning attributes named `x` or `y`, whose values may be specified in "paper coordinates" (sometimes referred to as "plot fractions" or "normalized coordinates"). Examples include `layout.xaxis.domain` or `layout.legend.x` or `layout.annotation[].x`.

Positioning in paper coordinates is *not* done in absolute pixel terms, but rather in terms relative to a coordinate system defined with an origin `(0,0)` at `(layout.margin.l, layout.margin.b)` and a point `(1,1)` at `(layout.width-layout.margin.r, layout.height-layout.margin.t)` (note: `layout.margin` values are pixel values, as are `layout.width` and `layout.height`). Paper coordinate values less than 0 or greater than 1 are permitted, and refer to areas within the plot margins.

To position an object in "paper" coordinates, the corresponding axis reference
is set to `"paper"`. For instance a shape's `xref` attribute would be set to
`"paper"` so that the `x` value of the shape refers to its position in paper
coordinates.

Note that the contents of the `layout.margin` attribute are by default computed based on the position and dimensions of certain items like the title or legend, and may be made dependent on the position and dimensions of tick labels as well when setting the `layout.xaxis.automargin` attribute to `True`. This has the effect of automatically increasing the margin values and therefore shrinking the physical area defined between the `(0,0)` and `(1,1)` points. Positioning certain items at paper coordinates less than 0 or greater than 1 will also trigger this behavior. The `layout.width` and `layout.height`, however, are taken as givens, so a figure will never grow or shrink based on its contents.

The figure title may be positioned using "container coordinates" which have `(0,0)` and `(1,1)` anchored at the bottom-left and top-right of the figure, respectively, and therefore are independent of the values of layout.margin.

Furthermore, shapes, annotations, and images can be placed relative to an axis's
domain so that, for instance, an `x` value of `0.5` would place the object
halfway along the x-axis, regardless of the domain as specified in the
`layout.xaxis.domain` attribute. This behavior can be specified by adding
`' domain'` to the axis reference in the axis referencing attribute of the object.
For example, setting `yref = 'y2 domain'` for a shape will refer to the length
and position of the axis named `y2`.

### 2D Cartesian Trace Types and Subplots

The most commonly-used kind of subplot is a [two-dimensional Cartesian subplot](/python/axes/). Traces compatible with these subplots support `xaxis` and `yaxis` attributes whose values must refer to corresponding objects in the layout portion of the figure. For example, if `xaxis="x"`, and `yaxis="y"` (which is the default) then this trace is drawn on the subplot at the intersection of the axes configured under `layout.xaxis` and `layout.yaxis`, but if `xaxis="x2"` and `yaxis="y3"` then the trace is drawn at the intersection of the axes configured under `layout.xaxis2` and `layout.yaxis3`. Note that attributes such as `layout.xaxis` and `layout.xaxis2` etc do not have to be explicitly defined, in which case default values will be inferred. Multiple traces of different types can be drawn on the same subplot.
Expand Down
121 changes: 121 additions & 0 deletions doc/python/horizontal-vertical-shapes.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
---
jupyter:
jupytext:
notebook_metadata_filter: all
text_representation:
extension: .md
format_name: markdown
format_version: '1.2'
jupytext_version: 1.3.0
kernelspec:
display_name: Python 3
language: python
name: python3
language_info:
codemirror_mode:
name: ipython
version: 3
file_extension: .py
mimetype: text/x-python
name: python
nbconvert_exporter: python
pygments_lexer: ipython3
version: 3.7.3
plotly:
description: How to add annotated horizontal and vertical lines in Python.
display_as: file_settings
language: python
layout: base
name: Shapes
order: 37
permalink: python/horizontal-vertical-shapes/
thumbnail: thumbnail/horizontal-vertical-shapes.jpg
---

### Horizontal and Vertical Lines and Boxes (Autoshapes) in Plotly.py

*introduced in plotly 4.12*

Horizontal and vertical lines and rectangles (autoshapes) that span an entire
plot can be added via the `add_hline`, `add_vline`, `add_hrect`, and `add_vrect`
methods of `plotly.graph_objects.Figure`. Shapes added with these methods are
added as [layout shapes](/python/shapes) (as shown when doing `print(fig)`, for
example). These shapes are fixed to the endpoints of one axis, regardless of the
range of the plot, and fixed to data coordinates on the other axis. The
following shows some possibilities, try panning and zooming the resulting figure
to see how the shapes stick to some axes:


```python
import plotly.express as px

df = px.data.iris()
fig = px.scatter(df, x="sepal_length", y="sepal_width")

# Add a vertical line that spans the entire y axis
# intersecting the x axis at x=5
fig.add_vline(x=5, line_color="red")
# Add a horizontal line that spans the entire x axis
# intersecting the y axis at y=3
fig.add_hline(y=3, line_color="blue")
# Add a vertical rectangle that spans the entire y axis
# intersecting the x axis at 5.5 and 6.5
fig.add_vrect(x0=5.5, x1=6.5, line_color="purple")
# Add a horizontal rectangle that spans the entire x axis
# intersecting the y axis at 2.5 and 4
fig.add_hrect(y0=2.5, y1=4, line_color="orange")
# (try panning and zooming the plot)
fig.show()
```

#### Adding Autoshapes to Multiple Facets / Subplots

The same line or box can be added to multiple facets by using the `'all'`
keyword in the `row` and `col` arguments like with `Figure.add_shape`. For
example
```python
import plotly.express as px

df = px.data.tips()
fig = px.scatter(df, x="total_bill", y="tip", facet_row="smoker", facet_col="sex")
# Adds a vertical line to all facets
fig.add_vline(x=30, row="all", col="all", line_color="purple")
# Adds a horizontal line to all the rows of the second column
fig.add_hline(y=6, row="all", col=2, line_color="yellow")
# Adds a vertical rectangle to all the columns of the first row
fig.add_vrect(x0=20, x1=40, row=1, col="all", line_color="green")
fig.show()
```
The default `row` and `col` values are `"all"` so
`fig.add_vline(x=30, line_color="purple")` is equivalent
to `fig.add_vline(x=30, row="all", col="all", line_color="purple")` in the above
example.

#### Adding Text Annotations to Autoshapes

Text [annotations](/python/text-and-annotations) can be added to an autoshape
using the `annotation` keyword. Using the above example:
```python
import plotly.express as px
import plotly.graph_objects as go

df = px.data.tips()
fig = px.scatter(df, x="total_bill", y="tip", facet_row="smoker", facet_col="sex")
# Add annotations anchored to the top right corner of the resulting lines
fig.add_vline(x=30, line_color="purple", annotation=go.layout.Annotation(text="A"))
# Another way to add annotations when we are only interested in specifying text
fig.add_hline(y=6, row="all", col=2, line_color="yellow", annotation_text="B")
# Specify the position of the resulting annotations
fig.add_vrect(
x0=20,
x1=40,
row=1,
col="all",
line_color="green",
annotation_text="C",
annotation_position="bottom inside left",
)
fig.show()
```
Call `help` on any of the autoshape functions in the Python interpreter to learn
more (e.g., `help(fig.add_vline)`).
45 changes: 41 additions & 4 deletions doc/python/images.md
Original file line number Diff line number Diff line change
Expand Up @@ -309,15 +309,15 @@ fig.show(config={'doubleClick': 'reset'})

_introduced in plotly 4.7_

It can be useful to add shapes to a layout image, for highlighting an object, drawing bounding boxes as part of a machine learning training set, or identifying seeds for a segmentation algorithm.
It can be useful to add shapes to a layout image, for highlighting an object, drawing bounding boxes as part of a machine learning training set, or identifying seeds for a segmentation algorithm.

In order to enable shape drawing, you need to
- define a dragmode corresponding to a drawing tool (`'drawline'`,`'drawopenpath'`, `'drawclosedpath'`, `'drawcircle'`, or `'drawrect'`)
- add [modebar buttons](/python/configuration-options#add-optional-shapedrawing-buttons-to-modebar) corresponding to the drawing tools you wish to use.

The style of new shapes is specified by the `newshape` layout attribute. Shapes can be selected and modified after they have been drawn. More details and examples are given in the [tutorial on shapes](/python/shapes#drawing-shapes-on-cartesian-plots).

Drawing or modifying a shape triggers a `relayout` event, which [can be captured by a callback inside a Dash application](https://dash.plotly.com/interactive-graphing).
Drawing or modifying a shape triggers a `relayout` event, which [can be captured by a callback inside a Dash application](https://dash.plotly.com/interactive-graphing).

```python
import plotly.graph_objects as go
Expand All @@ -339,7 +339,7 @@ fig.add_layout_image(
)
fig.update_xaxes(showgrid=False, range=(0, img_width))
fig.update_yaxes(showgrid=False, scaleanchor='x', range=(img_height, 0))
# Line shape added programatically
# Line shape added programatically
fig.add_shape(
type='line', xref='x', yref='y',
x0=650, x1=1080, y0=380, y1=180, line_color='cyan'
Expand All @@ -359,5 +359,42 @@ fig.show(config={'modeBarButtonsToAdd':['drawline',
]})
```


### Images Placed Relative to Axes

Using `xref='x domain'` or `yref='y domain'`, images can be placed relative to
axes. As an example, the following shows how to put an image in the top corner
of a subplot (try panning and zooming the resulting figure):

```python
import plotly.express as px

df = px.data.iris()
fig = px.scatter(df, x="sepal_length", y="sepal_width", facet_col="species")
# sources of images
sources = [
"https://upload.wikimedia.org/wikipedia/commons/thumb/f/fe/Iris_setosa_var._setosa_%282595031014%29.jpg/360px-Iris_setosa_var._setosa_%282595031014%29.jpg",
"https://upload.wikimedia.org/wikipedia/commons/thumb/3/38/Iris_versicolor_quebec_1.jpg/320px-Iris_versicolor_quebec_1.jpg",
"https://upload.wikimedia.org/wikipedia/commons/thumb/f/f8/Iris_virginica_2.jpg/480px-Iris_virginica_2.jpg",
]
# add images
for col, src in enumerate(sources):
fig.add_layout_image(
row=1,
col=col + 1,
source=src,
xref="x domain",
yref="y domain",
x=1,
y=1,
xanchor="right",
yanchor="top",
sizex=0.2,
sizey=0.2,
)

fig.show()
```

#### Reference
See https://plotly.com/python/reference/layout/images/ for more information and chart attribute options!
See https://plotly.com/python/reference/layout/images/ for more information and chart attribute options!
Loading

0 comments on commit abd8609

Please sign in to comment.