plotjs 0.0.2__tar.gz → 0.0.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {plotjs-0.0.2 → plotjs-0.0.4}/.coverage +0 -0
- {plotjs-0.0.2 → plotjs-0.0.4}/.github/workflows/tests.yaml +5 -2
- {plotjs-0.0.2 → plotjs-0.0.4}/.gitignore +6 -0
- plotjs-0.0.4/Makefile +21 -0
- {plotjs-0.0.2/plotjs.egg-info → plotjs-0.0.4}/PKG-INFO +13 -6
- plotjs-0.0.4/README.md +32 -0
- {plotjs-0.0.2 → plotjs-0.0.4}/coverage-badge.svg +1 -1
- plotjs-0.0.4/docs/developers/contributing.md +68 -0
- plotjs-0.0.4/docs/developers/how-it-works.md +158 -0
- plotjs-0.0.4/docs/gallery/index.md +97 -0
- plotjs-0.0.4/docs/gallery/index.qmd +31 -0
- plotjs-0.0.4/docs/guides/advanced/advanced.py +213 -0
- plotjs-0.0.4/docs/guides/advanced/index.md +201 -0
- plotjs-0.0.4/docs/guides/css/CSS.py +56 -0
- plotjs-0.0.4/docs/guides/css/index.md +121 -0
- {plotjs-0.0.2 → plotjs-0.0.4}/docs/guides/javascript/index.md +42 -14
- {plotjs-0.0.2 → plotjs-0.0.4}/docs/guides/javascript/javascript.py +9 -6
- plotjs-0.0.4/docs/guides/troubleshooting/index.md +28 -0
- plotjs-0.0.4/docs/iframes/CSS-2.html +1574 -0
- plotjs-0.0.4/docs/iframes/CSS.html +1570 -0
- plotjs-0.0.4/docs/iframes/area-natural-disasters.html +3878 -0
- plotjs-0.0.4/docs/iframes/javascript.html +1257 -0
- plotjs-0.0.4/docs/iframes/javascript2.html +1346 -0
- plotjs-0.0.4/docs/iframes/quickstart.html +1253 -0
- plotjs-0.0.4/docs/iframes/quickstart10.html +989 -0
- plotjs-0.0.4/docs/iframes/quickstart11.html +2407 -0
- plotjs-0.0.4/docs/iframes/quickstart2.html +1253 -0
- plotjs-0.0.4/docs/iframes/quickstart3.html +1253 -0
- plotjs-0.0.4/docs/iframes/quickstart4.html +1253 -0
- plotjs-0.0.4/docs/iframes/quickstart5.html +1507 -0
- plotjs-0.0.4/docs/iframes/quickstart6.html +1034 -0
- plotjs-0.0.4/docs/iframes/quickstart7.html +1317 -0
- plotjs-0.0.4/docs/iframes/quickstart8.html +1777 -0
- plotjs-0.0.4/docs/iframes/quickstart9.html +1952 -0
- {plotjs-0.0.2 → plotjs-0.0.4}/docs/index.md +199 -15
- {plotjs-0.0.2 → plotjs-0.0.4}/docs/index.qmd +176 -12
- plotjs-0.0.4/docs/stylesheets/style.css +52 -0
- {plotjs-0.0.2 → plotjs-0.0.4}/mkdocs.yaml +6 -2
- plotjs-0.0.4/overrides/partials/footer.html +7 -0
- plotjs-0.0.4/package-lock.json +6317 -0
- plotjs-0.0.4/package.json +11 -0
- {plotjs-0.0.2 → plotjs-0.0.4}/plotjs/__init__.py +1 -1
- {plotjs-0.0.2 → plotjs-0.0.4}/plotjs/main.py +71 -48
- plotjs-0.0.4/plotjs/static/main.js +87 -0
- plotjs-0.0.4/plotjs/static/template.html +236 -0
- {plotjs-0.0.2 → plotjs-0.0.4/plotjs.egg-info}/PKG-INFO +13 -6
- {plotjs-0.0.2 → plotjs-0.0.4}/plotjs.egg-info/SOURCES.txt +24 -16
- {plotjs-0.0.2 → plotjs-0.0.4}/pyproject.toml +6 -6
- plotjs-0.0.4/tests/PlotSVGParser.test.js +83 -0
- {plotjs-0.0.2/tests → plotjs-0.0.4/tests/test-python}/test_css_and_js_utils.py +3 -3
- {plotjs-0.0.2/tests → plotjs-0.0.4/tests/test-python}/test_magic_plot.py +0 -6
- {plotjs-0.0.2/tests → plotjs-0.0.4/tests/test-python}/test_main.py +18 -18
- plotjs-0.0.4/vitest.config.js +9 -0
- plotjs-0.0.2/README.md +0 -25
- plotjs-0.0.2/docs/guides/advanced/advanced.py +0 -2
- plotjs-0.0.2/docs/guides/advanced/index.md +0 -1
- plotjs-0.0.2/docs/guides/css/CSS-2.html +0 -1376
- plotjs-0.0.2/docs/guides/css/CSS.html +0 -1372
- plotjs-0.0.2/docs/guides/css/CSS.py +0 -47
- plotjs-0.0.2/docs/guides/css/index.md +0 -80
- plotjs-0.0.2/docs/guides/javascript/javascript.html +0 -1185
- plotjs-0.0.2/docs/guides/javascript/javascript2.html +0 -1274
- plotjs-0.0.2/docs/guides/troubleshooting/index.md +0 -28
- plotjs-0.0.2/docs/how-it-works.md +0 -158
- plotjs-0.0.2/docs/iframes/quickstart.html +0 -1180
- plotjs-0.0.2/docs/iframes/quickstart2.html +0 -1180
- plotjs-0.0.2/docs/iframes/quickstart3.html +0 -1180
- plotjs-0.0.2/docs/iframes/quickstart4.html +0 -1180
- plotjs-0.0.2/docs/iframes/quickstart5.html +0 -1406
- plotjs-0.0.2/docs/iframes/quickstart6.html +0 -961
- plotjs-0.0.2/docs/iframes/quickstart7.html +0 -1244
- plotjs-0.0.2/docs/iframes/quickstart8.html +0 -1704
- plotjs-0.0.2/docs/index_files/figure-commonmark/cell-2-output-1.png +0 -0
- plotjs-0.0.2/docs/stylesheets/style.css +0 -106
- plotjs-0.0.2/overrides/partials/footer.html +0 -20
- plotjs-0.0.2/plotjs/static/d3.min.js +0 -2
- plotjs-0.0.2/plotjs/static/main.js +0 -143
- plotjs-0.0.2/plotjs/static/template.html +0 -163
- plotjs-0.0.2/scripts/coverage.sh +0 -5
- plotjs-0.0.2/scripts/release.sh +0 -36
- {plotjs-0.0.2 → plotjs-0.0.4}/.gitattributes +0 -0
- {plotjs-0.0.2 → plotjs-0.0.4}/.github/workflows/doc.yaml +0 -0
- {plotjs-0.0.2 → plotjs-0.0.4}/.github/workflows/lint.yaml +0 -0
- {plotjs-0.0.2 → plotjs-0.0.4}/.github/workflows/pypi.yaml +0 -0
- {plotjs-0.0.2 → plotjs-0.0.4}/.github/workflows/type.yaml +0 -0
- {plotjs-0.0.2 → plotjs-0.0.4}/.pre-commit-config.yaml +0 -0
- {plotjs-0.0.2 → plotjs-0.0.4}/LICENSE +0 -0
- {plotjs-0.0.2 → plotjs-0.0.4}/docs/img/how-it-works-1.png +0 -0
- {plotjs-0.0.2 → plotjs-0.0.4}/docs/img/how-it-works-2.png +0 -0
- {plotjs-0.0.2 → plotjs-0.0.4}/docs/index_files/figure-commonmark/cell-3-output-1.png +0 -0
- {plotjs-0.0.2 → plotjs-0.0.4}/docs/reference/css.md +0 -0
- {plotjs-0.0.2 → plotjs-0.0.4}/docs/reference/datasets.md +0 -0
- {plotjs-0.0.2 → plotjs-0.0.4}/docs/reference/javascript.md +0 -0
- {plotjs-0.0.2 → plotjs-0.0.4}/docs/reference/magic-plot.md +0 -0
- {plotjs-0.0.2 → plotjs-0.0.4}/docs/static/style.css +0 -0
- {plotjs-0.0.2 → plotjs-0.0.4}/plotjs/css.py +0 -0
- {plotjs-0.0.2 → plotjs-0.0.4}/plotjs/data/__init__.py +0 -0
- {plotjs-0.0.2 → plotjs-0.0.4}/plotjs/data/datasets.py +0 -0
- {plotjs-0.0.2 → plotjs-0.0.4}/plotjs/data/iris.csv +0 -0
- {plotjs-0.0.2 → plotjs-0.0.4}/plotjs/data/mtcars.csv +0 -0
- {plotjs-0.0.2 → plotjs-0.0.4}/plotjs/data/titanic.csv +0 -0
- {plotjs-0.0.2 → plotjs-0.0.4}/plotjs/javascript.py +0 -0
- {plotjs-0.0.2 → plotjs-0.0.4}/plotjs/static/default.css +0 -0
- {plotjs-0.0.2 → plotjs-0.0.4}/plotjs/utils.py +0 -0
- {plotjs-0.0.2 → plotjs-0.0.4}/plotjs.egg-info/dependency_links.txt +0 -0
- {plotjs-0.0.2 → plotjs-0.0.4}/plotjs.egg-info/requires.txt +0 -0
- {plotjs-0.0.2 → plotjs-0.0.4}/plotjs.egg-info/top_level.txt +0 -0
- {plotjs-0.0.2 → plotjs-0.0.4}/setup.cfg +0 -0
- {plotjs-0.0.2/tests → plotjs-0.0.4/tests/test-python}/__init__.py +0 -0
- {plotjs-0.0.2/tests → plotjs-0.0.4/tests/test-python}/static/script.js +0 -0
- {plotjs-0.0.2/tests → plotjs-0.0.4/tests/test-python}/static/style.css +0 -0
- {plotjs-0.0.2/tests → plotjs-0.0.4/tests/test-python}/test_data.py +0 -0
|
Binary file
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
name:
|
|
1
|
+
name: Tests (Python + JS)
|
|
2
2
|
|
|
3
3
|
on:
|
|
4
4
|
pull_request:
|
|
@@ -24,5 +24,8 @@ jobs:
|
|
|
24
24
|
- name: Install the project
|
|
25
25
|
run: uv sync --all-groups
|
|
26
26
|
|
|
27
|
+
- name: Install npm dependencies
|
|
28
|
+
run: npm install
|
|
29
|
+
|
|
27
30
|
- name: Run tests
|
|
28
|
-
run:
|
|
31
|
+
run: make test
|
plotjs-0.0.4/Makefile
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
.PHONY: all examples gallery
|
|
2
|
+
|
|
3
|
+
examples:
|
|
4
|
+
quarto render docs/index.qmd
|
|
5
|
+
uv run docs/guides/advanced/advanced.py
|
|
6
|
+
uv run docs/guides/css/css.py
|
|
7
|
+
uv run docs/guides/javascript/javascript.py
|
|
8
|
+
|
|
9
|
+
gallery:
|
|
10
|
+
quarto render docs/gallery/index.qmd
|
|
11
|
+
|
|
12
|
+
coverage:
|
|
13
|
+
uv run coverage run --source=plotjs -m pytest
|
|
14
|
+
uv run coverage report -m
|
|
15
|
+
uv run coverage xml
|
|
16
|
+
uv run genbadge coverage -i coverage.xml
|
|
17
|
+
rm coverage.xml
|
|
18
|
+
|
|
19
|
+
test:
|
|
20
|
+
uv run pytest # run python tests
|
|
21
|
+
npm test # run javascript tests
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: plotjs
|
|
3
|
-
Version: 0.0.
|
|
4
|
-
Summary:
|
|
3
|
+
Version: 0.0.4
|
|
4
|
+
Summary: Turn static matplotlib charts into interactive web visualizations
|
|
5
5
|
Author-email: Joseph Barbier <joseph.barbierdarnal@mail.com>
|
|
6
6
|
License-Expression: MIT
|
|
7
7
|
Project-URL: Homepage, https://y-sunflower.github.io/plotjs/
|
|
@@ -11,7 +11,7 @@ Project-URL: Repository, https://github.com/y-sunflower/plotjs
|
|
|
11
11
|
Keywords: matplotlib,interactive,javascript,web,css,d3,mpld3,plotnine
|
|
12
12
|
Classifier: Programming Language :: Python :: 3
|
|
13
13
|
Classifier: Operating System :: OS Independent
|
|
14
|
-
Classifier: Development Status ::
|
|
14
|
+
Classifier: Development Status :: 4 - Beta
|
|
15
15
|
Requires-Python: >=3.10
|
|
16
16
|
Description-Content-Type: text/markdown
|
|
17
17
|
License-File: LICENSE
|
|
@@ -20,11 +20,18 @@ Requires-Dist: matplotlib>=3.10.0
|
|
|
20
20
|
Requires-Dist: narwhals>=2.0.0
|
|
21
21
|
Dynamic: license-file
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+

|
|
24
24
|
|
|
25
|
-
`plotjs
|
|
25
|
+
# `plotjs`: Turn static matplotlib charts into interactive web visualizations
|
|
26
26
|
|
|
27
|
-
|
|
27
|
+
<img src="https://github.com/JosephBARBIERDARNAL/static/blob/main/python-libs/plotjs/image.png?raw=true" alt="plotjs logo" align="right" width="150px"/>
|
|
28
|
+
|
|
29
|
+
`plotjs` is a Python package that transform matplotlib plots into interactive charts with minimum user inputs. You can:
|
|
30
|
+
|
|
31
|
+
- control tooltip labels and grouping
|
|
32
|
+
- add CSS
|
|
33
|
+
- add JavaScript
|
|
34
|
+
- and many more
|
|
28
35
|
|
|
29
36
|
> Consider that the project is still **very unstable**.
|
|
30
37
|
|
plotjs-0.0.4/README.md
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+

|
|
2
|
+
|
|
3
|
+
# `plotjs`: Turn static matplotlib charts into interactive web visualizations
|
|
4
|
+
|
|
5
|
+
<img src="https://github.com/JosephBARBIERDARNAL/static/blob/main/python-libs/plotjs/image.png?raw=true" alt="plotjs logo" align="right" width="150px"/>
|
|
6
|
+
|
|
7
|
+
`plotjs` is a Python package that transform matplotlib plots into interactive charts with minimum user inputs. You can:
|
|
8
|
+
|
|
9
|
+
- control tooltip labels and grouping
|
|
10
|
+
- add CSS
|
|
11
|
+
- add JavaScript
|
|
12
|
+
- and many more
|
|
13
|
+
|
|
14
|
+
> Consider that the project is still **very unstable**.
|
|
15
|
+
|
|
16
|
+
[Online demo](https://y-sunflower.github.io/plotjs/)
|
|
17
|
+
|
|
18
|
+
<br><br>
|
|
19
|
+
|
|
20
|
+
## Installation
|
|
21
|
+
|
|
22
|
+
From PyPI:
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
pip install plotjs
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Latest dev version:
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
pip install git+https://github.com/y-sunflower/plotjs.git
|
|
32
|
+
```
|
|
@@ -1 +1 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" width="114" height="20" role="img" aria-label="coverage:
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="114" height="20" role="img" aria-label="coverage: 93.02%"><title>coverage: 93.02%</title><linearGradient id="s" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="r"><rect width="114" height="20" rx="3" fill="#fff"/></clipPath><g clip-path="url(#r)"><rect width="61" height="20" fill="#555"/><rect x="61" width="53" height="20" fill="#4c1"/><rect width="114" height="20" fill="url(#s)"/></g><g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="110"><text aria-hidden="true" x="315" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="510">coverage</text><text x="315" y="140" transform="scale(.1)" fill="#fff" textLength="510">coverage</text><text aria-hidden="true" x="865" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="430">93.02%</text><text x="865" y="140" transform="scale(.1)" fill="#fff" textLength="430">93.02%</text></g></svg>
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
`plotjs` is still in a very early stage, but contributions is still welcomed!
|
|
2
|
+
|
|
3
|
+
The best way to help the development is to:
|
|
4
|
+
|
|
5
|
+
- list the bugs you found by opening [GitHub issues](https://github.com/y-sunflower/plotjs/issues){target="_blank"}
|
|
6
|
+
- list the features you'd like to see by opening [GitHub issues](https://github.com/y-sunflower/plotjs/issues){target="_blank"}
|
|
7
|
+
|
|
8
|
+
## Setting up your environment
|
|
9
|
+
|
|
10
|
+
### Install for development
|
|
11
|
+
|
|
12
|
+
- Fork the repository to your own GitHub account.
|
|
13
|
+
|
|
14
|
+
- Clone your forked repository to your local machine (ensure you have [Git installed](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git)):
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
git clone https://github.com/github_user_name/plotjs.git
|
|
18
|
+
cd plotjs
|
|
19
|
+
git remote add upstream https://github.com/y-sunflower/plotjs.git
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
- Create a new branch:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
git checkout -b my-feature
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
- Set up your Python environment (ensure you have [uv installed](https://docs.astral.sh/uv/getting-started/installation/)):
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
uv sync --all-groups --dev
|
|
32
|
+
uv run pre-commit install
|
|
33
|
+
uv pip install -e .
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Code
|
|
37
|
+
|
|
38
|
+
You can now make changes to the package and start coding!
|
|
39
|
+
|
|
40
|
+
### Run the test
|
|
41
|
+
|
|
42
|
+
- Test that everything works correctly by running:
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
uv run pytest
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Preview documentation locally
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
uv run mkdocs serve
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Push changes
|
|
55
|
+
|
|
56
|
+
- Commit and push your changes:
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
git add -A
|
|
60
|
+
git commit -m "description of what you did"
|
|
61
|
+
git push
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
- Navigate to your fork on GitHub and click the "Compare & pull request" button to open a new pull request.
|
|
65
|
+
|
|
66
|
+
Congrats! Once your PR is merged, it will be part of `plotjs`.
|
|
67
|
+
|
|
68
|
+
<br>
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
Transforming a static plot into something interactive can't be done (unfortunately) just by saying "make this interactive."
|
|
2
|
+
|
|
3
|
+
But that doesn't mean we have to do mystic things to make it work, because **yes**, that's perfectly possible without weird hacking stuff.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
There are two ways to tackle this problem:
|
|
8
|
+
|
|
9
|
+
- Take a matplotlib Figure (an instance containing all plot elements) and convert it to a more common format such as JSON. We call this **serialization**. Then, with this JSON file, we recreate the figure with an interactive tool such as D3.js (that's what [mpld3](https://github.com/mpld3/mpld3) does, by the way!).
|
|
10
|
+
- Use the native matplotlib figure output format (especially SVG) and parse this instead (that's what `plotjs` does).
|
|
11
|
+
|
|
12
|
+
The second option is **much simpler** (well, it depends), because we don't have to:
|
|
13
|
+
|
|
14
|
+
- translate the figure to JSON (which can be painfully complex if you want to handle all edge cases and make it robust),
|
|
15
|
+
- recreate the chart (browsers can display SVG perfectly).
|
|
16
|
+
|
|
17
|
+
But it means we don't have full control over how the plot is structured (from the browser's point of view). We need to find a way to parse this SVG.
|
|
18
|
+
|
|
19
|
+
## Parsing SVG
|
|
20
|
+
|
|
21
|
+
For the moment, we just take the user's matplotlib figure and save it as SVG. This is just:
|
|
22
|
+
|
|
23
|
+
```python
|
|
24
|
+
fig.savefig("plot.svg")
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Now, let's say the figure contains a scatter plot and we want to add a tooltip: when someone hovers their mouse over a point, it displays a label.
|
|
28
|
+
|
|
29
|
+
The **core problem to solve** is: "how do I know what elements from the SVG are points?"
|
|
30
|
+
|
|
31
|
+
If we're able to find a solution to this, then we're able to do pretty much **anything we want**.
|
|
32
|
+
|
|
33
|
+
The thing is, there's nothing in the SVG output file that tells us "this element is a point from the scatter plot." **Even worse**, we don't even know if it's a scatter plot or something completely unrelated, like a choropleth map.
|
|
34
|
+
|
|
35
|
+
For example, here is a polygon of a choropleth map:
|
|
36
|
+
|
|
37
|
+
```svg
|
|
38
|
+
<path d="M -5.94098 449.279178
|
|
39
|
+
L -4.961244 447.127034
|
|
40
|
+
L -4.623284 444.786333
|
|
41
|
+
L -3.951831 442.584471
|
|
42
|
+
L -5.459995 439.551953
|
|
43
|
+
L -5.763812 436.050818
|
|
44
|
+
L -3.764043 431.650797
|
|
45
|
+
L -2.459548 432.213824
|
|
46
|
+
L 0.368893 433.425033
|
|
47
|
+
L 4.436122 437.74466
|
|
48
|
+
L 5.072043 439.840475
|
|
49
|
+
L 2.799722 444.523728
|
|
50
|
+
L 1.620088 448.292228
|
|
51
|
+
L 0.147953 450.236113
|
|
52
|
+
L -1.691322 450.601856
|
|
53
|
+
L -2.215184 449.161231
|
|
54
|
+
L -3.074426 448.947286
|
|
55
|
+
L -4.263489 450.334887
|
|
56
|
+
z
|
|
57
|
+
" clip-path="url(#pf43ab1627f)" style="fill: #424186"/>
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Here is a point from a scatter plot:
|
|
61
|
+
|
|
62
|
+
```svg
|
|
63
|
+
<use
|
|
64
|
+
xlink:href="#m81e2893e84"
|
|
65
|
+
x="145.978182"
|
|
66
|
+
y="144.288"
|
|
67
|
+
style="fill: #1f77b4;
|
|
68
|
+
stroke: #1f77b4"
|
|
69
|
+
/>
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Here is a line from a line chart:
|
|
73
|
+
|
|
74
|
+
```svg
|
|
75
|
+
<g id="line2d_19">
|
|
76
|
+
<path d="M 73.832727 295.488
|
|
77
|
+
L 154.996364 235.008
|
|
78
|
+
L 236.16 174.528
|
|
79
|
+
L 317.323636 114.048
|
|
80
|
+
L 398.487273 53.568
|
|
81
|
+
"
|
|
82
|
+
clip-path="url(#pd511a61f39)"
|
|
83
|
+
style="fill: none;
|
|
84
|
+
stroke: #1f77b4;
|
|
85
|
+
stroke-width: 1.5;
|
|
86
|
+
stroke-linecap: square" />
|
|
87
|
+
</g>
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
If you pay close attention, you'll see potential patterns in the structure of certain elements.
|
|
91
|
+
|
|
92
|
+
That's exactly what we'll use to determine what kind of plot elements we have.
|
|
93
|
+
|
|
94
|
+
> Note: determining the kind of plot elements could have been done (partially) from the Python side, but this felt easier to me to do from the JavaScript side.
|
|
95
|
+
|
|
96
|
+
The next step is to understand matplotlib's underlying objects (called [artists](https://matplotlib.org/stable/users/explain/artists/artist_intro.html){target="\_blank"}) and how that translates to SVG.
|
|
97
|
+
|
|
98
|
+
## TL;DR: Artists in matplotlib
|
|
99
|
+
|
|
100
|
+
In matplotlib, artists are all the visual elements you see on a plot. There is the `Artist` base class, and all other artists inherit from this class.
|
|
101
|
+
|
|
102
|
+
For example:
|
|
103
|
+
|
|
104
|
+
- the `scatter()` function returns a `PathCollection` object, a subclass of `Artist`,
|
|
105
|
+
- the `plot()` function returns a `Line2D` object, a subclass of `Artist`,
|
|
106
|
+
- and so on.
|
|
107
|
+
|
|
108
|
+
## Selecting artists from SVG
|
|
109
|
+
|
|
110
|
+
In the SVG output of `savefig("plot.svg")`, we can find some info about what object was used.
|
|
111
|
+
|
|
112
|
+
For example, all `PathCollection` objects look like `<g id="PathCollection_1">`, `<g id="PathCollection_2">`. And since `PathCollection` is just one or multiple points, we can easily know how many scatter plots there are.
|
|
113
|
+
|
|
114
|
+
For lines, they are represented by `Line2D`. In the SVG, they look like `<g id="line2d_1">`, `<g id="line2d_2">`, etc. With this, we can easily detect that there are lines in the chart.
|
|
115
|
+
|
|
116
|
+
But there's a major issue here: not all `PathCollection` elements are relevant, same for `Line2D`, and so on.
|
|
117
|
+
|
|
118
|
+
By "relevant," I mean those we want to add interactivity to. For example, what elements here are considered to be a `Line2D`?
|
|
119
|
+
|
|
120
|
+

|
|
121
|
+
|
|
122
|
+
At first, I thought there were three: one for each main line. But in practice, it's much more:
|
|
123
|
+
|
|
124
|
+

|
|
125
|
+
|
|
126
|
+
What that means is that we can't just select all `Line2D` elements and give them a hover effect, for instance. We need to find a way to discriminate relevant lines (the three big ones) from the other ones.
|
|
127
|
+
|
|
128
|
+
## Filtering artists from SVG
|
|
129
|
+
|
|
130
|
+
This section might not be up to date with the latest version, but it'll give you an idea of how `plotjs` detects what is a "core" plot element and what is not.
|
|
131
|
+
|
|
132
|
+
It mostly consists of handling edge cases here, and is very different depending on the plot element (`Line2D`, `PathCollection`, etc.).
|
|
133
|
+
|
|
134
|
+
For example, in order to select only "core" `Line2D` elements (the three colored ones in the previous image), we do:
|
|
135
|
+
|
|
136
|
+
```javascript
|
|
137
|
+
const lines = svg.selectAll('g[id^="line2d"] path').filter(function () {
|
|
138
|
+
const clip = d3.select(this).attr("clip-path");
|
|
139
|
+
|
|
140
|
+
return (
|
|
141
|
+
// keep only <path> with clip-path attribute
|
|
142
|
+
clip &&
|
|
143
|
+
// that starts with "url("
|
|
144
|
+
clip.startsWith("url(") &&
|
|
145
|
+
// and are not child of a #matplotlib.axis
|
|
146
|
+
!this.closest('g[id^="matplotlib.axis"]')
|
|
147
|
+
);
|
|
148
|
+
});
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
The idea is basically:
|
|
152
|
+
|
|
153
|
+
- select all `Line2D` elements with the first line,
|
|
154
|
+
- filter to remove non-wanted `Line2D` elements.
|
|
155
|
+
|
|
156
|
+
This gives us a `lines` variable that only contains the lines of interest!
|
|
157
|
+
|
|
158
|
+
The logic is the same for other plot elements: bars, points, polygons, etc.
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
This page contains all the `plotjs` examples from this website.
|
|
4
|
+
|
|
5
|
+
**TODO: make this page less ugly and add more diverse examples.**
|
|
6
|
+
|
|
7
|
+
<div style="display: flex;">
|
|
8
|
+
|
|
9
|
+
<iframe width="100%" height="300" src="../iframes/quickstart8.html" style="flex: 1; border: none;">
|
|
10
|
+
|
|
11
|
+
</iframe>
|
|
12
|
+
|
|
13
|
+
<iframe width="100%" height="300" src="../iframes/quickstart.html" style="flex: 1; border: none;">
|
|
14
|
+
|
|
15
|
+
</iframe>
|
|
16
|
+
|
|
17
|
+
</div>
|
|
18
|
+
|
|
19
|
+
<div style="display: flex;">
|
|
20
|
+
|
|
21
|
+
<iframe width="100%" height="300" src="../iframes/area-natural-disasters.html" style="flex: 1; border: none;">
|
|
22
|
+
|
|
23
|
+
</iframe>
|
|
24
|
+
|
|
25
|
+
<iframe width="100%" height="300" src="../iframes/quickstart4.html" style="flex: 1; border: none;">
|
|
26
|
+
|
|
27
|
+
</iframe>
|
|
28
|
+
|
|
29
|
+
</div>
|
|
30
|
+
|
|
31
|
+
<div style="display: flex;">
|
|
32
|
+
|
|
33
|
+
<iframe width="100%" height="300" src="../iframes/quickstart5.html" style="flex: 1; border: none;">
|
|
34
|
+
|
|
35
|
+
</iframe>
|
|
36
|
+
|
|
37
|
+
<iframe width="100%" height="300" src="../iframes/quickstart9.html" style="flex: 1; border: none;">
|
|
38
|
+
|
|
39
|
+
</iframe>
|
|
40
|
+
|
|
41
|
+
</div>
|
|
42
|
+
|
|
43
|
+
<div style="display: flex;">
|
|
44
|
+
|
|
45
|
+
<iframe width="100%" height="300" src="../iframes/CSS.html" style="flex: 1; border: none;">
|
|
46
|
+
|
|
47
|
+
</iframe>
|
|
48
|
+
|
|
49
|
+
<iframe width="100%" height="300" src="../iframes/quickstart2.html" style="flex: 1; border: none;">
|
|
50
|
+
|
|
51
|
+
</iframe>
|
|
52
|
+
|
|
53
|
+
</div>
|
|
54
|
+
|
|
55
|
+
<div style="display: flex;">
|
|
56
|
+
|
|
57
|
+
<iframe width="100%" height="300" src="../iframes/javascript.html" style="flex: 1; border: none;">
|
|
58
|
+
|
|
59
|
+
</iframe>
|
|
60
|
+
|
|
61
|
+
<iframe width="100%" height="300" src="../iframes/quickstart3.html" style="flex: 1; border: none;">
|
|
62
|
+
|
|
63
|
+
</iframe>
|
|
64
|
+
|
|
65
|
+
</div>
|
|
66
|
+
|
|
67
|
+
<div style="display: flex;">
|
|
68
|
+
|
|
69
|
+
<iframe width="100%" height="300" src="../iframes/quickstart10.html" style="flex: 1; border: none;">
|
|
70
|
+
|
|
71
|
+
</iframe>
|
|
72
|
+
|
|
73
|
+
<iframe width="100%" height="300" src="../iframes/javascript2.html" style="flex: 1; border: none;">
|
|
74
|
+
|
|
75
|
+
</iframe>
|
|
76
|
+
|
|
77
|
+
</div>
|
|
78
|
+
|
|
79
|
+
<div style="display: flex;">
|
|
80
|
+
|
|
81
|
+
<iframe width="100%" height="300" src="../iframes/CSS-2.html" style="flex: 1; border: none;">
|
|
82
|
+
|
|
83
|
+
</iframe>
|
|
84
|
+
|
|
85
|
+
<iframe width="100%" height="300" src="../iframes/quickstart6.html" style="flex: 1; border: none;">
|
|
86
|
+
|
|
87
|
+
</iframe>
|
|
88
|
+
|
|
89
|
+
</div>
|
|
90
|
+
|
|
91
|
+
<div style="display: flex;">
|
|
92
|
+
|
|
93
|
+
<iframe width="100%" height="300" src="../iframes/quickstart7.html" style="flex: 1; border: none;">
|
|
94
|
+
|
|
95
|
+
</iframe>
|
|
96
|
+
|
|
97
|
+
</div>
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
---
|
|
2
|
+
format: gfm
|
|
3
|
+
from: markdown-smart
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
This page contains all the `plotjs` examples from this website.
|
|
7
|
+
|
|
8
|
+
**TODO: make this page less ugly and add more diverse examples.**
|
|
9
|
+
|
|
10
|
+
```{python}
|
|
11
|
+
# | echo: false
|
|
12
|
+
# | output: asis
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
|
|
15
|
+
html_files = list(Path("../iframes").rglob("*.html"))
|
|
16
|
+
html_paths = [str(path) for path in html_files]
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
# Split the list into chunks of 2
|
|
20
|
+
def chunks(lst, n):
|
|
21
|
+
for i in range(0, len(lst), n):
|
|
22
|
+
yield lst[i : i + n]
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
for pair in chunks(html_paths, 2):
|
|
26
|
+
print('<div style="display: flex;">')
|
|
27
|
+
for html_path in pair:
|
|
28
|
+
iframe = f'<iframe width="100%" height="300" src="{html_path}" style="flex: 1; border: none;"></iframe>'
|
|
29
|
+
print(iframe)
|
|
30
|
+
print("</div>")
|
|
31
|
+
```
|