Compare commits

...

17 commits

Author SHA1 Message Date
Yohan Boniface
4ee9f10fc9
Merge 169be73488 into 23b82975f5 2025-04-17 09:39:57 -04:00
Yohan Boniface
23b82975f5 chore: bump togeojson
Some checks failed
Release Charts / release (push) Has been cancelled
Test & Docs / tests (postgresql, 3.10) (push) Has been cancelled
Test & Docs / tests (postgresql, 3.12) (push) Has been cancelled
Test & Docs / lint (push) Has been cancelled
2025-04-16 17:30:17 +02:00
Yohan Boniface
6945a5e867
feat: pass CSRF_TRUSTED_ORIGINS env to settings (#2656)
Some checks are pending
Release Charts / release (push) Waiting to run
Test & Docs / tests (postgresql, 3.10) (push) Waiting to run
Test & Docs / tests (postgresql, 3.12) (push) Waiting to run
Test & Docs / lint (push) Waiting to run
Allow to manage djangos CSRF_TRUSTED_ORIGINS by environment variables.

Closes #2655
2025-04-16 12:34:49 +02:00
Oliver Lippert
2ed890202e
code formatting 2025-04-16 12:25:17 +02:00
Oliver Lippert
cc7685ca3f
pass CSRF_TRUSTED_ORIGINS env to settings
fixes https://github.com/umap-project/umap/issues/2655
2025-04-16 12:23:09 +02:00
David Larlet
23688b6444
feat: implement a design system for UI consistency (#2654)
Some checks are pending
Release Charts / release (push) Waiting to run
Test & Docs / tests (postgresql, 3.10) (push) Waiting to run
Test & Docs / tests (postgresql, 3.12) (push) Waiting to run
Test & Docs / lint (push) Waiting to run
2025-04-15 08:19:31 -04:00
David Larlet
d3c0947fa5
feat: implement a design system for UI consistency 2025-04-14 23:20:55 -04:00
Yohan Boniface
a135e5c3e1
chore: bump pytest-django from 4.10.0 to 4.11.1 (#2653)
Some checks are pending
Release Charts / release (push) Waiting to run
Test & Docs / tests (postgresql, 3.10) (push) Waiting to run
Test & Docs / tests (postgresql, 3.12) (push) Waiting to run
Test & Docs / lint (push) Waiting to run
2025-04-14 19:50:30 +02:00
Yohan Boniface
2ad46046f8
chore: bump uvicorn from 0.34.0 to 0.34.1 (#2651) 2025-04-14 19:50:14 +02:00
Yohan Boniface
5271d7c19d
chore: bump pydantic from 2.11.2 to 2.11.3 (#2652) 2025-04-14 19:36:42 +02:00
Yohan Boniface
a535c86f6b
chore: bump mkdocs-material from 9.6.10 to 9.6.11 (#2649) 2025-04-14 19:36:16 +02:00
Yohan Boniface
9aa1f58dc8
chore: bump pillow from 11.1.0 to 11.2.1 (#2650)
Bumps [pillow](https://github.com/python-pillow/Pillow) from 11.1.0 to
11.2.1.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/python-pillow/Pillow/releases">pillow's
releases</a>.</em></p>
<blockquote>
<h2>11.2.1</h2>
<p><a
href="https://pillow.readthedocs.io/en/stable/releasenotes/11.2.1.html">https://pillow.readthedocs.io/en/stable/releasenotes/11.2.1.html</a></p>
<h2>Deprecations</h2>
<ul>
<li>Moved get_child_images() to ImageFile <a
href="https://redirect.github.com/python-pillow/Pillow/issues/8689">#8689</a>
[<a
href="https://github.com/radarhere"><code>@​radarhere</code></a>]</li>
</ul>
<h2>Documentation</h2>
<ul>
<li>Add 11.2.1 release notes <a
href="https://redirect.github.com/python-pillow/Pillow/issues/8885">#8885</a>
[<a
href="https://github.com/radarhere"><code>@​radarhere</code></a>]</li>
<li>Added avif to config settings <a
href="https://redirect.github.com/python-pillow/Pillow/issues/8875">#8875</a>
[<a
href="https://github.com/radarhere"><code>@​radarhere</code></a>]</li>
<li>Added release notes for <a
href="https://redirect.github.com/python-pillow/Pillow/issues/8330">#8330</a>
<a
href="https://redirect.github.com/python-pillow/Pillow/issues/8853">#8853</a>
[<a
href="https://github.com/radarhere"><code>@​radarhere</code></a>]</li>
<li>Added release notes for <a
href="https://redirect.github.com/python-pillow/Pillow/issues/8781">#8781</a>
and <a
href="https://redirect.github.com/python-pillow/Pillow/issues/8837">#8837</a>
<a
href="https://redirect.github.com/python-pillow/Pillow/issues/8843">#8843</a>
[<a
href="https://github.com/radarhere"><code>@​radarhere</code></a>]</li>
<li>Added media_white_point to ImageCms documentation <a
href="https://redirect.github.com/python-pillow/Pillow/issues/8829">#8829</a>
[<a
href="https://github.com/radarhere"><code>@​radarhere</code></a>]</li>
<li>Removed FIXME <a
href="https://redirect.github.com/python-pillow/Pillow/issues/8825">#8825</a>
[<a
href="https://github.com/radarhere"><code>@​radarhere</code></a>]</li>
<li>Updated comment <a
href="https://redirect.github.com/python-pillow/Pillow/issues/8822">#8822</a>
[<a
href="https://github.com/radarhere"><code>@​radarhere</code></a>]</li>
<li>Added release notes for <a
href="https://redirect.github.com/python-pillow/Pillow/issues/8807">#8807</a>
<a
href="https://redirect.github.com/python-pillow/Pillow/issues/8824">#8824</a>
[<a
href="https://github.com/radarhere"><code>@​radarhere</code></a>]</li>
<li>DXT3 images are read in RGBA mode <a
href="https://redirect.github.com/python-pillow/Pillow/issues/8817">#8817</a>
[<a
href="https://github.com/radarhere"><code>@​radarhere</code></a>]</li>
<li>JPEG comments are from the COM marker <a
href="https://redirect.github.com/python-pillow/Pillow/issues/8788">#8788</a>
[<a
href="https://github.com/radarhere"><code>@​radarhere</code></a>]</li>
<li>Update Sphinx to 8.2 to remove nitpick ignore <a
href="https://redirect.github.com/python-pillow/Pillow/issues/8763">#8763</a>
[<a
href="https://github.com/radarhere"><code>@​radarhere</code></a>]</li>
<li>Updated AffineTransform docstring to mention it uses the inverse
matrix <a
href="https://redirect.github.com/python-pillow/Pillow/issues/8735">#8735</a>
[<a
href="https://github.com/radarhere"><code>@​radarhere</code></a>]</li>
<li>Added MozJPEG documentation <a
href="https://redirect.github.com/python-pillow/Pillow/issues/8699">#8699</a>
[<a
href="https://github.com/radarhere"><code>@​radarhere</code></a>]</li>
<li>Added Sphinx configuration key <a
href="https://redirect.github.com/python-pillow/Pillow/issues/8691">#8691</a>
[<a
href="https://github.com/radarhere"><code>@​radarhere</code></a>]</li>
<li>Updated macOS tested Pillow versions <a
href="https://redirect.github.com/python-pillow/Pillow/issues/8654">#8654</a>
[<a
href="https://github.com/radarhere"><code>@​radarhere</code></a>]</li>
</ul>
<h2>Dependencies</h2>
<ul>
<li>Updated xz to 5.8.1 <a
href="https://redirect.github.com/python-pillow/Pillow/issues/8868">#8868</a>
[<a
href="https://github.com/radarhere"><code>@​radarhere</code></a>]</li>
<li>Updated harfbuzz to 11.0.1 <a
href="https://redirect.github.com/python-pillow/Pillow/issues/8870">#8870</a>
[<a
href="https://github.com/radarhere"><code>@​radarhere</code></a>]</li>
<li>Update scientific-python/upload-nightly-action action to v0.6.2 <a
href="https://redirect.github.com/python-pillow/Pillow/issues/8865">#8865</a>
[@<a href="https://github.com/apps/renovate">renovate[bot]</a>]</li>
<li>Updated xz to 5.8.0 in macOS and Linux wheels, but not on
manylinux2014 <a
href="https://redirect.github.com/python-pillow/Pillow/issues/8836">#8836</a>
[<a
href="https://github.com/radarhere"><code>@​radarhere</code></a>]</li>
<li>Update dependency cibuildwheel to v2.23.2 <a
href="https://redirect.github.com/python-pillow/Pillow/issues/8832">#8832</a>
[@<a href="https://github.com/apps/renovate">renovate[bot]</a>]</li>
<li>Updated harfbuzz to 11.0.0 <a
href="https://redirect.github.com/python-pillow/Pillow/issues/8830">#8830</a>
[<a
href="https://github.com/radarhere"><code>@​radarhere</code></a>]</li>
<li>Update dependency cibuildwheel to v2.23.1 <a
href="https://redirect.github.com/python-pillow/Pillow/issues/8818">#8818</a>
[@<a href="https://github.com/apps/renovate">renovate[bot]</a>]</li>
<li>Updated Ghostscript to 10.5.0 <a
href="https://redirect.github.com/python-pillow/Pillow/issues/8814">#8814</a>
[<a
href="https://github.com/radarhere"><code>@​radarhere</code></a>]</li>
<li>Updated libtiff to 4.7.0 <a
href="https://redirect.github.com/python-pillow/Pillow/issues/8812">#8812</a>
[<a
href="https://github.com/radarhere"><code>@​radarhere</code></a>]</li>
<li>[pre-commit.ci] pre-commit autoupdate <a
href="https://redirect.github.com/python-pillow/Pillow/issues/8795">#8795</a>
[@<a
href="https://github.com/apps/pre-commit-ci">pre-commit-ci[bot]</a>]</li>
<li>Updated harfbuzz to 10.4.0 <a
href="https://redirect.github.com/python-pillow/Pillow/issues/8770">#8770</a>
[<a
href="https://github.com/radarhere"><code>@​radarhere</code></a>]</li>
<li>Update dependency mypy to v1.15.0 <a
href="https://redirect.github.com/python-pillow/Pillow/issues/8791">#8791</a>
[@<a href="https://github.com/apps/renovate">renovate[bot]</a>]</li>
<li>Updated libpng to 1.6.47 <a
href="https://redirect.github.com/python-pillow/Pillow/issues/8764">#8764</a>
[<a
href="https://github.com/radarhere"><code>@​radarhere</code></a>]</li>
<li>Updated lcms2 to 2.17 <a
href="https://redirect.github.com/python-pillow/Pillow/issues/8742">#8742</a>
[<a
href="https://github.com/radarhere"><code>@​radarhere</code></a>]</li>
<li>Update dependency cibuildwheel to v2.23.0 <a
href="https://redirect.github.com/python-pillow/Pillow/issues/8785">#8785</a>
[@<a href="https://github.com/apps/renovate">renovate[bot]</a>]</li>
<li>Updated zlib-ng to 2.2.4 <a
href="https://redirect.github.com/python-pillow/Pillow/issues/8745">#8745</a>
[<a
href="https://github.com/radarhere"><code>@​radarhere</code></a>]</li>
<li>Updated libimagequant to 4.3.4 on Windows <a
href="https://redirect.github.com/python-pillow/Pillow/issues/8744">#8744</a>
[<a
href="https://github.com/radarhere"><code>@​radarhere</code></a>]</li>
<li>[pre-commit.ci] pre-commit autoupdate <a
href="https://redirect.github.com/python-pillow/Pillow/issues/8729">#8729</a>
[@<a
href="https://github.com/apps/pre-commit-ci">pre-commit-ci[bot]</a>]</li>
<li>Updated harfbuzz to 10.2.0 <a
href="https://redirect.github.com/python-pillow/Pillow/issues/8688">#8688</a>
[<a
href="https://github.com/radarhere"><code>@​radarhere</code></a>]</li>
<li>Updated xz to 5.6.4 <a
href="https://redirect.github.com/python-pillow/Pillow/issues/8711">#8711</a>
[<a
href="https://github.com/radarhere"><code>@​radarhere</code></a>]</li>
<li>Updated libpng to 1.6.46 <a
href="https://redirect.github.com/python-pillow/Pillow/issues/8712">#8712</a>
[<a
href="https://github.com/radarhere"><code>@​radarhere</code></a>]</li>
<li>Updated libimagequant to 4.3.4 <a
href="https://redirect.github.com/python-pillow/Pillow/issues/8710">#8710</a>
[<a
href="https://github.com/radarhere"><code>@​radarhere</code></a>]</li>
<li>Updated libpng to 1.6.45 <a
href="https://redirect.github.com/python-pillow/Pillow/issues/8670">#8670</a>
[<a
href="https://github.com/radarhere"><code>@​radarhere</code></a>]</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/python-pillow/Pillow/blob/main/CHANGES.rst">pillow's
changelog</a>.</em></p>
<blockquote>
<h1>Changelog (Pillow)</h1>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="339bc5db93"><code>339bc5d</code></a>
11.2.1 version bump</li>
<li><a
href="857b8846ea"><code>857b884</code></a>
Merge pull request <a
href="https://redirect.github.com/python-pillow/Pillow/issues/8885">#8885</a>
from radarhere/releasenotes</li>
<li><a
href="7a0092f207"><code>7a0092f</code></a>
Remove incomplete 11.2.0 release, bill as 11.2.1 instead</li>
<li><a
href="d52c2db317"><code>d52c2db</code></a>
Do not include libavif in wheels</li>
<li><a
href="8dafc38371"><code>8dafc38</code></a>
Added 11.2.1 release notes</li>
<li><a
href="07d7800248"><code>07d7800</code></a>
Removed release notes update</li>
<li><a
href="04909483a7"><code>0490948</code></a>
Remove GPL v2 license (<a
href="https://redirect.github.com/python-pillow/Pillow/issues/8884">#8884</a>)</li>
<li><a
href="774d0aedce"><code>774d0ae</code></a>
Merge pull request <a
href="https://redirect.github.com/python-pillow/Pillow/issues/8868">#8868</a>
from radarhere/xz_upgrade</li>
<li><a
href="d1e27fc86c"><code>d1e27fc</code></a>
Merge pull request <a
href="https://redirect.github.com/python-pillow/Pillow/issues/8870">#8870</a>
from radarhere/harfbuzz_upgrade</li>
<li><a
href="c8d98d56a0"><code>c8d98d5</code></a>
Added avif to config settings (<a
href="https://redirect.github.com/python-pillow/Pillow/issues/8875">#8875</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/python-pillow/Pillow/compare/11.1.0...11.2.1">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=pillow&package-manager=pip&previous-version=11.1.0&new-version=11.2.1)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>
2025-04-14 19:36:00 +02:00
dependabot[bot]
102104f818
chore: bump pytest-django from 4.10.0 to 4.11.1
Bumps [pytest-django](https://github.com/pytest-dev/pytest-django) from 4.10.0 to 4.11.1.
- [Release notes](https://github.com/pytest-dev/pytest-django/releases)
- [Changelog](https://github.com/pytest-dev/pytest-django/blob/main/docs/changelog.rst)
- [Commits](https://github.com/pytest-dev/pytest-django/compare/v4.10.0...v4.11.1)

---
updated-dependencies:
- dependency-name: pytest-django
  dependency-version: 4.11.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-14 17:30:01 +00:00
dependabot[bot]
35e5a4bda6
chore: bump pydantic from 2.11.2 to 2.11.3
Bumps [pydantic](https://github.com/pydantic/pydantic) from 2.11.2 to 2.11.3.
- [Release notes](https://github.com/pydantic/pydantic/releases)
- [Changelog](https://github.com/pydantic/pydantic/blob/main/HISTORY.md)
- [Commits](https://github.com/pydantic/pydantic/compare/v2.11.2...v2.11.3)

---
updated-dependencies:
- dependency-name: pydantic
  dependency-version: 2.11.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-14 17:29:38 +00:00
dependabot[bot]
6bae315efe
chore: bump uvicorn from 0.34.0 to 0.34.1
Bumps [uvicorn](https://github.com/encode/uvicorn) from 0.34.0 to 0.34.1.
- [Release notes](https://github.com/encode/uvicorn/releases)
- [Changelog](https://github.com/encode/uvicorn/blob/master/docs/release-notes.md)
- [Commits](https://github.com/encode/uvicorn/compare/0.34.0...0.34.1)

---
updated-dependencies:
- dependency-name: uvicorn
  dependency-version: 0.34.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-14 17:29:19 +00:00
dependabot[bot]
93120d91d6
chore: bump pillow from 11.1.0 to 11.2.1
Bumps [pillow](https://github.com/python-pillow/Pillow) from 11.1.0 to 11.2.1.
- [Release notes](https://github.com/python-pillow/Pillow/releases)
- [Changelog](https://github.com/python-pillow/Pillow/blob/main/CHANGES.rst)
- [Commits](https://github.com/python-pillow/Pillow/compare/11.1.0...11.2.1)

---
updated-dependencies:
- dependency-name: pillow
  dependency-version: 11.2.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-14 17:29:05 +00:00
dependabot[bot]
f037973a03
chore: bump mkdocs-material from 9.6.10 to 9.6.11
Bumps [mkdocs-material](https://github.com/squidfunk/mkdocs-material) from 9.6.10 to 9.6.11.
- [Release notes](https://github.com/squidfunk/mkdocs-material/releases)
- [Changelog](https://github.com/squidfunk/mkdocs-material/blob/master/CHANGELOG)
- [Commits](https://github.com/squidfunk/mkdocs-material/compare/9.6.10...9.6.11)

---
updated-dependencies:
- dependency-name: mkdocs-material
  dependency-version: 9.6.11
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-14 17:28:43 +00:00
10 changed files with 508 additions and 186 deletions

View file

@ -1,5 +1,5 @@
# Force rtfd to use a recent version of mkdocs
mkdocs==1.6.1
pymdown-extensions==10.14.3
mkdocs-material==9.6.10
mkdocs-material==9.6.11
mkdocs-static-i18n==1.3.0

View file

@ -1,5 +1,5 @@
# Force rtfd to use a recent version of mkdocs
mkdocs==1.6.1
pymdown-extensions==10.14.3
mkdocs-material==9.6.10
mkdocs-material==9.6.11
mkdocs-static-i18n==1.3.0

View file

@ -38,7 +38,7 @@
"dependencies": {
"@dwayneparton/geojson-to-gpx": "^0.2.0",
"@placemarkio/tokml": "0.3.4",
"@tmcw/togeojson": "^5.8.0",
"@tmcw/togeojson": "^7.1.0",
"colorbrewer": "1.5.7",
"csv2geojson": "github:umap-project/csv2geojson#patched",
"dompurify": "3.2.4",

View file

@ -32,7 +32,7 @@ dependencies = [
"django-agnocomplete==2.2.0",
"django-environ==0.12.0",
"django-probes==1.7.0",
"Pillow==11.1.0",
"Pillow==11.2.1",
"psycopg==3.2.6",
"requests==2.32.3",
"rcssmin==1.2.1",
@ -47,7 +47,7 @@ dev = [
"ruff==0.11.4",
"djlint==1.36.4",
"mkdocs==1.6.1",
"mkdocs-material==9.6.10",
"mkdocs-material==9.6.11",
"mkdocs-static-i18n==1.3.0",
"vermin==1.6.0",
"pymdown-extensions==10.14.3",
@ -58,20 +58,20 @@ test = [
"factory-boy==3.3.3",
"playwright>=1.39",
"pytest==8.3.5",
"pytest-django==4.10.0",
"pytest-django==4.11.1",
"pytest-playwright==0.7.0",
"pytest-rerunfailures==15.0",
"pytest-xdist>=3.5.0,<4",
"moto[s3]==5.1.3"
]
docker = [
"uvicorn==0.34.0",
"uvicorn==0.34.1",
]
s3 = [
"django-storages[s3]==1.14.6",
]
sync = [
"pydantic==2.11.2",
"pydantic==2.11.3",
"redis==5.2.1",
"websockets==15.0.1",
]

View file

@ -19,6 +19,7 @@ env = environ.Env()
INTERNAL_IPS = env.list("INTERNAL_IPS", default=["127.0.0.1"])
ALLOWED_HOSTS = env.list("ALLOWED_HOSTS", default=["*"])
CSRF_TRUSTED_ORIGINS = env.list("CSRF_TRUSTED_ORIGINS", default=[])
ADMINS = tuple(parseaddr(email) for email in env.list("ADMINS", default=[]))
ASGI_APPLICATION = "umap.asgi.application"

View file

@ -12,7 +12,7 @@ function $ns(element, tagName, ns) {
*/
function nodeVal(node) {
node?.normalize();
return (node && node.textContent) || "";
return node?.textContent || "";
}
/**
* Get one Y child of X, if any, otherwise null
@ -42,16 +42,16 @@ function val1(node, tagName, callback) {
return {};
}
function $num(node, tagName, callback) {
const val = parseFloat(nodeVal(get1(node, tagName)));
if (isNaN(val))
const val = Number.parseFloat(nodeVal(get1(node, tagName)));
if (Number.isNaN(val))
return undefined;
if (val && callback)
return callback(val) || {};
return {};
}
function num1(node, tagName, callback) {
const val = parseFloat(nodeVal(get1(node, tagName)));
if (isNaN(val))
const val = Number.parseFloat(nodeVal(get1(node, tagName)));
if (Number.isNaN(val))
return undefined;
if (callback)
callback(val);
@ -70,20 +70,6 @@ function isElement(node) {
return node?.nodeType === 1;
}
function getLineStyle(node) {
return get(node, "line", (lineStyle) => {
const val = Object.assign({}, val1(lineStyle, "color", (color) => {
return { stroke: `#${color}` };
}), $num(lineStyle, "opacity", (opacity) => {
return { "stroke-opacity": opacity };
}), $num(lineStyle, "width", (width) => {
// GPX width is in mm, convert to px with 96 px per inch
return { "stroke-width": (width * 96) / 25.4 };
}));
return val;
});
}
function getExtensions(node) {
let values = [];
if (node === null)
@ -108,16 +94,16 @@ function abbreviateName(name) {
return ["heart", "gpxtpx:hr", "hr"].includes(name) ? "heart" : name;
}
function parseNumeric(val) {
const num = parseFloat(val);
return isNaN(num) ? val : num;
const num = Number.parseFloat(val);
return Number.isNaN(num) ? val : num;
}
function coordPair$1(node) {
const ll = [
parseFloat(node.getAttribute("lon") || ""),
parseFloat(node.getAttribute("lat") || ""),
Number.parseFloat(node.getAttribute("lon") || ""),
Number.parseFloat(node.getAttribute("lat") || ""),
];
if (isNaN(ll[0]) || isNaN(ll[1])) {
if (Number.isNaN(ll[0]) || Number.isNaN(ll[1])) {
return null;
}
num1(node, "ele", (val) => {
@ -131,7 +117,21 @@ function coordPair$1(node) {
};
}
function extractProperties(node) {
function getLineStyle(node) {
return get(node, "line", (lineStyle) => {
const val = Object.assign({}, val1(lineStyle, "color", (color) => {
return { stroke: `#${color}` };
}), $num(lineStyle, "opacity", (opacity) => {
return { "stroke-opacity": opacity };
}), $num(lineStyle, "width", (width) => {
// GPX width is in mm, convert to px with 96 px per inch
return { "stroke-width": (width * 96) / 25.4 };
}));
return val;
});
}
function extractProperties(ns, node) {
const properties = getMulti(node, [
"name",
"cmt",
@ -140,10 +140,9 @@ function extractProperties(node) {
"time",
"keywords",
]);
const extensions = Array.from(node.getElementsByTagNameNS("http://www.garmin.com/xmlschemas/GpxExtensions/v3", "*"));
for (const child of extensions) {
if (child.parentNode?.parentNode === node) {
properties[child.tagName.replace(":", "_")] = nodeVal(child);
for (const [n, url] of ns) {
for (const child of Array.from(node.getElementsByTagNameNS(url, "*"))) {
properties[child.tagName.replace(":", "_")] = nodeVal(child)?.trim();
}
}
const links = $(node, "link");
@ -170,7 +169,7 @@ function getPoints$1(node, pointname) {
if (c.time)
times.push(c.time);
for (const [name, val] of c.extendedValues) {
const plural = name === "heart" ? name : name.replace("gpxtpx:", "") + "s";
const plural = name === "heart" ? name : `${name.replace("gpxtpx:", "")}s`;
if (!extendedValues[plural]) {
extendedValues[plural] = Array(pts.length).fill(null);
}
@ -189,20 +188,20 @@ function getPoints$1(node, pointname) {
* Extract a LineString geometry from a rte
* element.
*/
function getRoute(node) {
function getRoute(ns, node) {
const line = getPoints$1(node, "rtept");
if (!line)
return;
return {
type: "Feature",
properties: Object.assign({ _gpxType: "rte" }, extractProperties(node), getLineStyle(get1(node, "extensions"))),
properties: Object.assign({ _gpxType: "rte" }, extractProperties(ns, node), getLineStyle(get1(node, "extensions"))),
geometry: {
type: "LineString",
coordinates: line.line,
},
};
}
function getTrack(node) {
function getTrack(ns, node) {
const segments = $(node, "trkseg");
const track = [];
const times = [];
@ -211,14 +210,14 @@ function getTrack(node) {
const line = getPoints$1(segment, "trkpt");
if (line) {
extractedLines.push(line);
if (line.times && line.times.length)
if (line.times?.length)
times.push(line.times);
}
}
if (extractedLines.length === 0)
return null;
const multi = extractedLines.length > 1;
const properties = Object.assign({ _gpxType: "trk" }, extractProperties(node), getLineStyle(get1(node, "extensions")), times.length
const properties = Object.assign({ _gpxType: "trk" }, extractProperties(ns, node), getLineStyle(get1(node, "extensions")), times.length
? {
coordinateProperties: {
times: multi ? times : times[0],
@ -263,8 +262,8 @@ function getTrack(node) {
* Extract a point, if possible, from a given node,
* which is usually a wpt or trkpt
*/
function getPoint(node) {
const properties = Object.assign(extractProperties(node), getMulti(node, ["sym"]));
function getPoint(ns, node) {
const properties = Object.assign(extractProperties(ns, node), getMulti(node, ["sym"]));
const pair = coordPair$1(node);
if (!pair)
return null;
@ -283,18 +282,31 @@ function getPoint(node) {
* that yields output feature by feature.
*/
function* gpxGen(node) {
for (const track of $(node, "trk")) {
const feature = getTrack(track);
const n = node;
const GPXX = "gpxx";
const GPXX_URI = "http://www.garmin.com/xmlschemas/GpxExtensions/v3";
// Namespaces
const ns = [[GPXX, GPXX_URI]];
const attrs = n.getElementsByTagName("gpx")[0]?.attributes;
if (attrs) {
for (const attr of Array.from(attrs)) {
if (attr.name?.startsWith("xmlns:") && attr.value !== GPXX_URI) {
ns.push([attr.name, attr.value]);
}
}
}
for (const track of $(n, "trk")) {
const feature = getTrack(ns, track);
if (feature)
yield feature;
}
for (const route of $(node, "rte")) {
const feature = getRoute(route);
for (const route of $(n, "rte")) {
const feature = getRoute(ns, route);
if (feature)
yield feature;
}
for (const waypoint of $(node, "wpt")) {
const point = getPoint(waypoint);
for (const waypoint of $(n, "wpt")) {
const point = getPoint(ns, waypoint);
if (point)
yield point;
}
@ -346,8 +358,8 @@ function getProperties(node, attributeNames) {
elem = elements[0];
}
}
const val = parseFloat(nodeVal(elem));
if (!isNaN(val)) {
const val = Number.parseFloat(nodeVal(elem));
if (!Number.isNaN(val)) {
properties.push([alias, val]);
}
}
@ -356,23 +368,23 @@ function getProperties(node, attributeNames) {
function coordPair(node) {
const ll = [num1(node, "LongitudeDegrees"), num1(node, "LatitudeDegrees")];
if (ll[0] === undefined ||
isNaN(ll[0]) ||
Number.isNaN(ll[0]) ||
ll[1] === undefined ||
isNaN(ll[1])) {
Number.isNaN(ll[1])) {
return null;
}
const heartRate = get1(node, "HeartRateBpm");
const time = nodeVal(get1(node, "Time"));
get1(node, "AltitudeMeters", (alt) => {
const a = parseFloat(nodeVal(alt));
if (!isNaN(a)) {
const a = Number.parseFloat(nodeVal(alt));
if (!Number.isNaN(a)) {
ll.push(a);
}
});
return {
coordinates: ll,
time: time || null,
heartRate: heartRate ? parseFloat(nodeVal(heartRate)) : null,
heartRate: heartRate ? Number.parseFloat(nodeVal(heartRate)) : null,
extensions: getProperties(node, TRACKPOINT_ATTRIBUTES),
};
}
@ -504,17 +516,18 @@ function tcx(node) {
function fixColor(v, prefix) {
const properties = {};
const colorProp = prefix == "stroke" || prefix === "fill" ? prefix : prefix + "-color";
const colorProp = prefix === "stroke" || prefix === "fill" ? prefix : `${prefix}-color`;
if (v[0] === "#") {
v = v.substring(1);
}
if (v.length === 6 || v.length === 3) {
properties[colorProp] = "#" + v;
properties[colorProp] = `#${v}`;
}
else if (v.length === 8) {
properties[prefix + "-opacity"] = parseInt(v.substring(0, 2), 16) / 255;
properties[`${prefix}-opacity`] =
Number.parseInt(v.substring(0, 2), 16) / 255;
properties[colorProp] =
"#" + v.substring(6, 8) + v.substring(4, 6) + v.substring(2, 4);
`#${v.substring(6, 8)}${v.substring(4, 6)}${v.substring(2, 4)}`;
}
return properties;
}
@ -540,11 +553,11 @@ function extractIconHref(node) {
function extractIcon(node) {
return get(node, "IconStyle", (iconStyle) => {
return Object.assign(getColor(iconStyle, "icon"), numericProperty(iconStyle, "scale", "icon-scale"), numericProperty(iconStyle, "heading", "icon-heading"), get(iconStyle, "hotSpot", (hotspot) => {
const left = parseFloat(hotspot.getAttribute("x") || "");
const top = parseFloat(hotspot.getAttribute("y") || "");
const left = Number.parseFloat(hotspot.getAttribute("x") || "");
const top = Number.parseFloat(hotspot.getAttribute("y") || "");
const xunits = hotspot.getAttribute("xunits") || "";
const yunits = hotspot.getAttribute("yunits") || "";
if (!isNaN(left) && !isNaN(top))
if (!Number.isNaN(left) && !Number.isNaN(top))
return {
"icon-offset": [left, top],
"icon-offset-units": [xunits, yunits],
@ -578,71 +591,6 @@ function extractStyle(node) {
return Object.assign({}, extractPoly(node), extractLine(node), extractLabel(node), extractIcon(node));
}
const toNumber = (x) => Number(x);
const typeConverters = {
string: (x) => x,
int: toNumber,
uint: toNumber,
short: toNumber,
ushort: toNumber,
float: toNumber,
double: toNumber,
bool: (x) => Boolean(x),
};
function extractExtendedData(node, schema) {
return get(node, "ExtendedData", (extendedData, properties) => {
for (const data of $(extendedData, "Data")) {
properties[data.getAttribute("name") || ""] = nodeVal(get1(data, "value"));
}
for (const simpleData of $(extendedData, "SimpleData")) {
const name = simpleData.getAttribute("name") || "";
const typeConverter = schema[name] || typeConverters.string;
properties[name] = typeConverter(nodeVal(simpleData));
}
return properties;
});
}
function getMaybeHTMLDescription(node) {
const descriptionNode = get1(node, "description");
for (const c of Array.from(descriptionNode?.childNodes || [])) {
if (c.nodeType === 4) {
return {
description: {
"@type": "html",
value: nodeVal(c),
},
};
}
}
return {};
}
function extractTimeSpan(node) {
return get(node, "TimeSpan", (timeSpan) => {
return {
timespan: {
begin: nodeVal(get1(timeSpan, "begin")),
end: nodeVal(get1(timeSpan, "end")),
},
};
});
}
function extractTimeStamp(node) {
return get(node, "TimeStamp", (timeStamp) => {
return { timestamp: nodeVal(get1(timeStamp, "when")) };
});
}
function extractCascadedStyle(node, styleMap) {
return val1(node, "styleUrl", (styleUrl) => {
styleUrl = normalizeId(styleUrl);
if (styleMap[styleUrl]) {
return Object.assign({ styleUrl }, styleMap[styleUrl]);
}
// For backward-compatibility. Should we still include
// styleUrl even if it's not resolved?
return { styleUrl };
});
}
const removeSpace = /\s*/g;
const trimSpace = /^\s*|\s*$/g;
const splitSpace = /\s+/;
@ -653,8 +601,8 @@ function coord1(value) {
return value
.replace(removeSpace, "")
.split(",")
.map(parseFloat)
.filter((num) => !isNaN(num))
.map(Number.parseFloat)
.filter((num) => !Number.isNaN(num))
.slice(0, 3);
}
/**
@ -675,7 +623,7 @@ function gxCoords(node) {
elems = $ns(node, "coord", "*");
}
const coordinates = elems.map((elem) => {
return nodeVal(elem).split(" ").map(parseFloat);
return nodeVal(elem).split(" ").map(Number.parseFloat);
});
if (coordinates.length === 0) {
return null;
@ -785,47 +733,92 @@ function getGeometry(node) {
};
}
function geometryListToGeometry(geometries) {
return geometries.length === 0
? null
: geometries.length === 1
? geometries[0]
: {
type: "GeometryCollection",
geometries,
};
const toNumber = (x) => Number(x);
const typeConverters = {
string: (x) => x,
int: toNumber,
uint: toNumber,
short: toNumber,
ushort: toNumber,
float: toNumber,
double: toNumber,
bool: (x) => Boolean(x),
};
function extractExtendedData(node, schema) {
return get(node, "ExtendedData", (extendedData, properties) => {
for (const data of $(extendedData, "Data")) {
properties[data.getAttribute("name") || ""] = nodeVal(get1(data, "value"));
}
for (const simpleData of $(extendedData, "SimpleData")) {
const name = simpleData.getAttribute("name") || "";
const typeConverter = schema[name] || typeConverters.string;
properties[name] = typeConverter(nodeVal(simpleData));
}
return properties;
});
}
function getPlacemark(node, styleMap, schema, options) {
const { coordTimes, geometries } = getGeometry(node);
const geometry = geometryListToGeometry(geometries);
if (!geometry && options.skipNullGeometry) {
return null;
}
const feature = {
type: "Feature",
geometry,
properties: Object.assign(getMulti(node, [
"name",
"address",
"visibility",
"open",
"phoneNumber",
"description",
]), getMaybeHTMLDescription(node), extractCascadedStyle(node, styleMap), extractStyle(node), extractExtendedData(node, schema), extractTimeSpan(node), extractTimeStamp(node), coordTimes.length
? {
coordinateProperties: {
times: coordTimes.length === 1 ? coordTimes[0] : coordTimes,
function getMaybeHTMLDescription(node) {
const descriptionNode = get1(node, "description");
for (const c of Array.from(descriptionNode?.childNodes || [])) {
if (c.nodeType === 4) {
return {
description: {
"@type": "html",
value: nodeVal(c),
},
}
: {}),
};
if (feature.properties?.visibility !== undefined) {
feature.properties.visibility = feature.properties.visibility !== "0";
};
}
}
const id = node.getAttribute("id");
if (id !== null && id !== "")
feature.id = id;
return feature;
return {};
}
function extractTimeSpan(node) {
return get(node, "TimeSpan", (timeSpan) => {
return {
timespan: {
begin: nodeVal(get1(timeSpan, "begin")),
end: nodeVal(get1(timeSpan, "end")),
},
};
});
}
function extractTimeStamp(node) {
return get(node, "TimeStamp", (timeStamp) => {
return { timestamp: nodeVal(get1(timeStamp, "when")) };
});
}
function extractCascadedStyle(node, styleMap) {
return val1(node, "styleUrl", (styleUrl) => {
styleUrl = normalizeId(styleUrl);
if (styleMap[styleUrl]) {
return Object.assign({ styleUrl }, styleMap[styleUrl]);
}
// For backward-compatibility. Should we still include
// styleUrl even if it's not resolved?
return { styleUrl };
});
}
var AltitudeMode;
(function (AltitudeMode) {
AltitudeMode["ABSOLUTE"] = "absolute";
AltitudeMode["RELATIVE_TO_GROUND"] = "relativeToGround";
AltitudeMode["CLAMP_TO_GROUND"] = "clampToGround";
AltitudeMode["CLAMP_TO_SEAFLOOR"] = "clampToSeaFloor";
AltitudeMode["RELATIVE_TO_SEAFLOOR"] = "relativeToSeaFloor";
})(AltitudeMode || (AltitudeMode = {}));
function processAltitudeMode(mode) {
switch (mode?.textContent) {
case AltitudeMode.ABSOLUTE:
return AltitudeMode.ABSOLUTE;
case AltitudeMode.CLAMP_TO_GROUND:
return AltitudeMode.CLAMP_TO_GROUND;
case AltitudeMode.CLAMP_TO_SEAFLOOR:
return AltitudeMode.CLAMP_TO_SEAFLOOR;
case AltitudeMode.RELATIVE_TO_GROUND:
return AltitudeMode.RELATIVE_TO_GROUND;
case AltitudeMode.RELATIVE_TO_SEAFLOOR:
return AltitudeMode.RELATIVE_TO_SEAFLOOR;
}
return null;
}
function getGroundOverlayBox(node) {
@ -848,7 +841,7 @@ function rotateBox(bbox, coordinates, rotation) {
coordinates[0].map((coordinate) => {
const dy = coordinate[1] - center[1];
const dx = coordinate[0] - center[0];
const distance = Math.sqrt(Math.pow(dy, 2) + Math.pow(dx, 2));
const distance = Math.sqrt(dy ** 2 + dx ** 2);
const angle = Math.atan2(dy, dx) + rotation * DEGREES_TO_RADIANS;
return [
center[0] + Math.cos(angle) * distance,
@ -872,10 +865,10 @@ function getLatLonBox(node) {
const bbox = [west, south, east, north];
let coordinates = [
[
[west, north],
[east, north],
[east, south],
[west, south],
[west, north], // top left
[east, north], // top right
[east, south], // top right
[west, south], // bottom left
[west, north], // top left (again)
],
];
@ -928,6 +921,178 @@ function getGroundOverlay(node, styleMap, schema, options) {
return feature;
}
function getNetworkLinkRegion(node) {
const region = get1(node, "Region");
if (region) {
return {
coordinateBox: getLatLonAltBox(region),
lod: getLod(node),
};
}
return null;
}
function getLod(node) {
const lod = get1(node, "Lod");
if (lod) {
return [
num1(lod, "minLodPixels") ?? -1,
num1(lod, "maxLodPixels") ?? -1,
num1(lod, "minFadeExtent") ?? null,
num1(lod, "maxFadeExtent") ?? null,
];
}
return null;
}
function getLatLonAltBox(node) {
const latLonAltBox = get1(node, "LatLonAltBox");
if (latLonAltBox) {
const north = num1(latLonAltBox, "north");
const west = num1(latLonAltBox, "west");
const east = num1(latLonAltBox, "east");
const south = num1(latLonAltBox, "south");
const altitudeMode = processAltitudeMode(get1(latLonAltBox, "altitudeMode") ||
get1(latLonAltBox, "gx:altitudeMode"));
if (altitudeMode) {
console.debug("Encountered an unsupported feature of KML for togeojson: please contact developers for support of altitude mode.");
}
if (typeof north === "number" &&
typeof south === "number" &&
typeof west === "number" &&
typeof east === "number") {
const bbox = [west, south, east, north];
const coordinates = [
[
[west, north], // top left
[east, north], // top right
[east, south], // top right
[west, south], // bottom left
[west, north], // top left (again)
],
];
return {
bbox,
geometry: {
type: "Polygon",
coordinates,
},
};
}
}
return null;
}
function getLinkObject(node) {
/*
<Link id="ID">
<!-- specific to Link -->
<href>...</href> <!-- string -->
<refreshMode>onChange</refreshMode>
<!-- refreshModeEnum: onChange, onInterval, or onExpire -->
<refreshInterval>4</refreshInterval> <!-- float -->
<viewRefreshMode>never</viewRefreshMode>
<!-- viewRefreshModeEnum: never, onStop, onRequest, onRegion -->
<viewRefreshTime>4</viewRefreshTime> <!-- float -->
<viewBoundScale>1</viewBoundScale> <!-- float -->
<viewFormat>BBOX=[bboxWest],[bboxSouth],[bboxEast],[bboxNorth]</viewFormat>
<!-- string -->
<httpQuery>...</httpQuery> <!-- string -->
</Link>
*/
const linkObj = get1(node, "Link");
if (linkObj) {
return getMulti(linkObj, [
"href",
"refreshMode",
"refreshInterval",
"viewRefreshMode",
"viewRefreshTime",
"viewBoundScale",
"viewFormat",
"httpQuery",
]);
}
return {};
}
function getNetworkLink(node, styleMap, schema, options) {
const box = getNetworkLinkRegion(node);
const geometry = box?.coordinateBox?.geometry || null;
if (!geometry && options.skipNullGeometry) {
return null;
}
const feature = {
type: "Feature",
geometry,
properties: Object.assign(
/**
* Related to
* https://gist.github.com/tmcw/037a1cb6660d74a392e9da7446540f46
*/
{ "@geometry-type": "networklink" }, getMulti(node, [
"name",
"address",
"visibility",
"open",
"phoneNumber",
"styleUrl",
"refreshVisibility",
"flyToView",
"description",
]), getMaybeHTMLDescription(node), extractCascadedStyle(node, styleMap), extractStyle(node), extractIconHref(node), extractExtendedData(node, schema), extractTimeSpan(node), extractTimeStamp(node), getLinkObject(node), box?.lod ? { lod: box.lod } : {}),
};
if (box?.coordinateBox?.bbox) {
feature.bbox = box.coordinateBox.bbox;
}
if (feature.properties?.visibility !== undefined) {
feature.properties.visibility = feature.properties.visibility !== "0";
}
const id = node.getAttribute("id");
if (id !== null && id !== "")
feature.id = id;
return feature;
}
function geometryListToGeometry(geometries) {
return geometries.length === 0
? null
: geometries.length === 1
? geometries[0]
: {
type: "GeometryCollection",
geometries,
};
}
function getPlacemark(node, styleMap, schema, options) {
const { coordTimes, geometries } = getGeometry(node);
const geometry = geometryListToGeometry(geometries);
if (!geometry && options.skipNullGeometry) {
return null;
}
const feature = {
type: "Feature",
geometry,
properties: Object.assign(getMulti(node, [
"name",
"address",
"visibility",
"open",
"phoneNumber",
"description",
]), getMaybeHTMLDescription(node), extractCascadedStyle(node, styleMap), extractStyle(node), extractExtendedData(node, schema), extractTimeSpan(node), extractTimeStamp(node), coordTimes.length
? {
coordinateProperties: {
times: coordTimes.length === 1 ? coordTimes[0] : coordTimes,
},
}
: {}),
};
if (feature.properties?.visibility !== undefined) {
feature.properties.visibility = feature.properties.visibility !== "0";
}
const id = node.getAttribute("id");
if (id !== null && id !== "")
feature.id = id;
return feature;
}
function getStyleId(style) {
let id = style.getAttribute("id");
const parentNode = style.parentNode;
@ -958,8 +1123,7 @@ function buildSchema(node) {
const schema = {};
for (const field of $(node, "SimpleField")) {
schema[field.getAttribute("name") || ""] =
typeConverters[field.getAttribute("type") || ""] ||
typeConverters["string"];
typeConverters[field.getAttribute("type") || ""] || typeConverters.string;
}
return schema;
}
@ -1028,8 +1192,9 @@ function getFolder(node) {
function kmlWithFolders(node, options = {
skipNullGeometry: false,
}) {
const styleMap = buildStyleMap(node);
const schema = buildSchema(node);
const n = node;
const styleMap = buildStyleMap(n);
const schema = buildSchema(n);
const tree = { type: "root", children: [] };
function traverse(node, pointer, options) {
if (isElement(node)) {
@ -1054,6 +1219,13 @@ function kmlWithFolders(node, options = {
pointer = folder;
break;
}
case "NetworkLink": {
const networkLink = getNetworkLink(node, styleMap, schema, options);
if (networkLink) {
pointer.children.push(networkLink);
}
break;
}
}
}
if (node.childNodes) {
@ -1062,7 +1234,7 @@ function kmlWithFolders(node, options = {
}
}
}
traverse(node, tree, options);
traverse(n, tree, options);
return tree;
}
/**
@ -1073,14 +1245,15 @@ function kmlWithFolders(node, options = {
function* kmlGen(node, options = {
skipNullGeometry: false,
}) {
const styleMap = buildStyleMap(node);
const schema = buildSchema(node);
for (const placemark of $(node, "Placemark")) {
const n = node;
const styleMap = buildStyleMap(n);
const schema = buildSchema(n);
for (const placemark of $(n, "Placemark")) {
const feature = getPlacemark(placemark, styleMap, schema, options);
if (feature)
yield feature;
}
for (const groundOverlay of $(node, "GroundOverlay")) {
for (const groundOverlay of $(n, "GroundOverlay")) {
const feature = getGroundOverlay(groundOverlay, styleMap, schema, options);
if (feature)
yield feature;

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,140 @@
{% extends "umap/content.html" %}
{% load umap_tags i18n %}
{% block messages %}
{# We don't want to display errors (yet?). #}
{% endblock messages %}
{% block extra_head %}
{{ block.super }}
<style type="text/css">
h3, h4 {
margin-top: 2rem;
}
.colors-container {
display: flex;
flex-wrap: wrap;
}
.colors-container * {
width: 20%;
margin: 1rem;
border-style: solid;
border-width: 2rem 1px 1px 1px;
padding: 4px;
}
.panel {
position: initial;
padding: 0 1rem 1rem;
}
</style>
{% endblock extra_head %}
{% block maincontent %}
<div class="wrapper">
<h2 class="section">uMap Design System</h2>
<div class="row">
<h3>Forms</h3>
<h4>Copiable link</h4>
<div class="copiable-input">
<label>Lien vers la carte<input type="text" readonly value="http://example.org"></label><button class="icon icon-24 icon-copy" title="copier" type="button"></button>
</div>
<h4>Toggle</h4>
<div class="formbox">
<input type="checkbox" class="switch" id="inBbox"><label for="inBbox">Lister seulement les éléments visibles</label>
</div>
<h4>Multiple choice</h4>
<div class="formbox">
<label title="Afficher les boutons de zoom">Afficher les boutons de zoom</label>
<div class="umap-multiplechoice by3">
<input type="radio" name="zoomControl" id="zoomControl.0" value="true" checked><label for="zoomControl.0">toujours</label>
<input type="radio" name="zoomControl" id="zoomControl.1" value="false"><label for="zoomControl.1">jamais</label>
<input type="radio" name="zoomControl" id="zoomControl.2" value="null"><label for="zoomControl.2">caché</label>
</div>
</div>
<h4>Select</h4>
<div class="formbox">
<label>Voulez-vous afficher un panneau latéral au chargement ?</label>
<select name="onLoadPanel">
<option value="none">Aucun</option>
<option value="caption">Légende</option>
<option value="databrowser">Explorateur : données</option>
</select>
</div>
<h4>You want it darker…</h4>
<div class="panel dark full on">
<h3><i class="icon icon-16 icon-settings" title=""></i><span>Titre avec icône</span></h3>
<details open="">
<summary><span>Options d'interface</span></summary>
<fieldset>
<form>
<div class="formbox">
<input type="checkbox" name="homeControl" data-ref="input" class="switch" id="homeControl">
<label title="Afficher le bouton de retour à l'accueil" for="homeControl">Afficher le bouton de retour à l'accueil</label>
</div>
<div class="formbox">
<label title="Afficher les boutons de zoom">Afficher les boutons de zoom</label>
<div class="umap-multiplechoice by3" data-ref="wrapper">
<input type="radio" name="zoomControl" id="zoomControl.0" value="true"><label for="zoomControl.0">toujours</label>
<input type="radio" name="zoomControl" id="zoomControl.1" value="false" checked><label for="zoomControl.1">jamais</label>
<input type="radio" name="zoomControl" id="zoomControl.2" value="null"><label for="zoomControl.2">caché</label>
</div>
</div>
<div class="formbox">
<label title="Voulez-vous afficher un panneau latéral au chargement ?">Voulez-vous afficher un panneau latéral au chargement ?</label>
<select name="onLoadPanel">
<option value="none">Aucun</option>
<option value="caption">Légende</option>
<option value="databrowser">Explorateur : données</option>
</select>
</div>
</form>
</fieldset>
</details>
<details open="">
<summary><span>Actions avancées</span></summary>
<fieldset>
<div class="button-bar half">
<button class="button" type="button">
<i class="icon icon-24 icon-delete"></i>Supprimer
</button>
<button class="button" type="button">
<i class="icon icon-24 icon-empty"></i>Effacer les données
</button>
<button class="button" type="button">
<i class="icon icon-24 icon-empty"></i>Supprimer les calques
</button>
<button class="button" type="button">
<i class="icon icon-24 icon-clone"></i>Cloner cette carte
</button>
<button class="button" type="button">
<i class="icon icon-24 icon-download"></i>Ouvrir le panneau de partage
</button>
</div>
</fieldset>
</details>
</div>
<h3>Colors</h3>
<div class="grid-container row colors-container"></div>
</div>
</div>
{% endblock maincontent %}
{% block bottom_js %}
{{ block.super }}
<script type="text/javascript">
const colorsContainer = document.querySelector('.colors-container')
const styles = getComputedStyle(document.querySelector('html'))
let html = ""
for (const style of styles) {
if (style.startsWith("--") && style.includes("color")) {
const value = styles.getPropertyValue(style)
html += `<div style="border-color: ${value};">${style}</div>`
}
}
const template = document.createElement('template')
template.innerHTML = html
colorsContainer.appendChild(template.content)
</script>
{% endblock bottom_js %}

View file

@ -205,6 +205,7 @@ urlpatterns += i18n_patterns(
)
urlpatterns += (
path("stats/", cache_page(60 * 60)(views.stats), name="stats"),
path("design_system/", views.design_system, name="design_system"),
path(
"favicon.ico",
cache_control(max_age=60 * 60 * 24, immutable=True, public=True)(

View file

@ -1410,6 +1410,13 @@ def stats(request):
)
class DesignSystem(TemplateView):
template_name = "umap/design_system.html"
design_system = DesignSystem.as_view()
@require_GET
@cache_control(max_age=60 * 60 * 24, immutable=True, public=True) # One day.
def webmanifest(request):