ml2drender 0.1.0__cp311-abi3-win_amd64.whl

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.
ml2drender/__init__.py ADDED
@@ -0,0 +1,5 @@
1
+ from .ml2drender import *
2
+
3
+ __doc__ = ml2drender.__doc__
4
+ if hasattr(ml2drender, "__all__"):
5
+ __all__ = ml2drender.__all__
@@ -0,0 +1,71 @@
1
+ """MapLibre style 2D renderer (pure Rust, no GPU).
2
+
3
+ The renderer is sans-IO: it asks for the resources it needs (TileJSON, GeoJSON, sprites, tiles,
4
+ glyph ranges) and the host loads them. :func:`render` runs that loop with a ``load`` callable;
5
+ :class:`Renderer` exposes the steps, for hosts that load concurrently.
6
+ """
7
+
8
+ from collections.abc import Callable
9
+
10
+ class View:
11
+ """A Web Mercator camera.
12
+
13
+ ``center`` is (lng, lat) in degrees, ``zoom`` is the MapLibre zoom (the world is
14
+ ``512 * 2**zoom`` CSS pixels wide), ``bearing`` is in degrees clockwise. The image is
15
+ ``width`` x ``height`` CSS pixels, times ``pixel_ratio`` in pixels. ``margin`` CSS pixels are
16
+ drawn around it and cropped, so that labels near the edge are placed as in a larger map.
17
+ """
18
+
19
+ def __init__(
20
+ self,
21
+ center: tuple[float, float],
22
+ zoom: float,
23
+ width: int,
24
+ height: int,
25
+ bearing: float = 0.0,
26
+ pixel_ratio: float = 1.0,
27
+ margin: int = 0,
28
+ ) -> None: ...
29
+ @staticmethod
30
+ def tile(z: int, x: int, y: int, tile_size: int = 256) -> View:
31
+ """The XYZ tile ``z/x/y`` of the Web Mercator grid, ``tile_size`` CSS pixels square."""
32
+ @property
33
+ def size(self) -> tuple[int, int]:
34
+ """Output width and height in pixels."""
35
+
36
+ class Renderer:
37
+ """A style (JSON string) fixed at creation, drawn for a view once the resources it needs are provided.
38
+
39
+ Raises ``RuntimeError`` on an invalid style. Not thread-safe: use it from the thread that created it.
40
+ """
41
+
42
+ def __init__(self, style: str, view: View) -> None: ...
43
+ def set_view(self, view: View) -> None:
44
+ """Point the renderer at another camera, keeping the resources and tiles it has."""
45
+ def needs(self) -> list[str]:
46
+ """URLs still needed before :meth:`render`.
47
+
48
+ Resources come in stages, each found from the last (style sources, then tiles, then glyph
49
+ ranges), so repeat :meth:`needs` and :meth:`provide` until this is empty.
50
+ """
51
+ def provide(self, url: str, data: bytes | None) -> None:
52
+ """The bytes of ``url``, or None for a missing resource (like HTTP 404: a missing vector
53
+ tile is empty, a missing raster tile is replaced by its parent)."""
54
+ def render(self) -> bytes:
55
+ """Non-premultiplied RGBA8 pixels, row by row (see :attr:`View.size`). Raises ``RuntimeError`` while resources are still needed."""
56
+
57
+ def render(style: str, view: View, load: Callable[[str], bytes | None]) -> bytes:
58
+ """Non-premultiplied RGBA8 pixels of ``style`` (JSON string) in ``view``.
59
+
60
+ Each resource is loaded with ``load(url)``, which returns its bytes, or None when it is
61
+ missing (like HTTP 404). Exceptions raised by ``load`` propagate.
62
+ """
63
+
64
+ def encode_png(rgba: bytes, width: int, height: int) -> bytes:
65
+ """Lossless PNG of RGBA8 pixels."""
66
+
67
+ def encode_webp(rgba: bytes, width: int, height: int) -> bytes:
68
+ """Lossless WebP of RGBA8 pixels."""
69
+
70
+ def encode_jpeg(rgba: bytes, width: int, height: int, quality: int = 90) -> bytes:
71
+ """JPEG of RGBA8 pixels at ``quality`` (1–100), flattened over black."""
Binary file
ml2drender/py.typed ADDED
File without changes
@@ -0,0 +1,94 @@
1
+ Metadata-Version: 2.4
2
+ Name: ml2drender
3
+ Version: 0.1.0
4
+ Classifier: Programming Language :: Rust
5
+ Classifier: Programming Language :: Python :: Implementation :: CPython
6
+ Classifier: Topic :: Scientific/Engineering :: GIS
7
+ Classifier: Typing :: Typed
8
+ Summary: MapLibre style 2D renderer in pure Rust, no GPU
9
+ License: BSD-3-Clause
10
+ Requires-Python: >=3.11
11
+ Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
12
+ Project-URL: Demo, https://kanahiro.github.io/ml2drender/
13
+ Project-URL: Documentation, https://github.com/Kanahiro/ml2drender/tree/main/crates/python
14
+ Project-URL: Homepage, https://github.com/Kanahiro/ml2drender
15
+
16
+ # ml2drender (Python)
17
+
18
+ Python bindings of [ml2drender](https://github.com/Kanahiro/ml2drender), a 2D map renderer for MapLibre styles written in pure Rust. It needs no GPU and links no C libraries.
19
+
20
+ ```sh
21
+ pip install ml2drender
22
+ ```
23
+
24
+ Wheels are built for Linux (x86-64, arm64; glibc), macOS (x86-64, arm64) and Windows (x86-64), for CPython 3.11 and later (abi3).
25
+
26
+ ## Usage
27
+
28
+ The renderer does no I/O. It asks for the resources a style needs (TileJSON, GeoJSON, sprites, tiles, glyph ranges) and you load them. `render` runs that loop with a `load` function:
29
+
30
+ ```python
31
+ import json
32
+ import urllib.error
33
+ import urllib.request
34
+
35
+ import ml2drender
36
+
37
+
38
+ def load(url):
39
+ try:
40
+ with urllib.request.urlopen(url) as response:
41
+ return response.read()
42
+ except urllib.error.HTTPError as e:
43
+ if e.code == 404:
44
+ return None # missing: an empty vector tile, a raster tile replaced by its parent
45
+ raise
46
+
47
+
48
+ style = load("https://demotiles.maplibre.org/style.json").decode()
49
+
50
+ view = ml2drender.View.tile(5, 28, 12, tile_size=512)
51
+ # or a camera: ml2drender.View((139.767, 35.681), zoom=5, width=512, height=512, bearing=0)
52
+ rgba = ml2drender.render(style, view, load)
53
+ width, height = view.size
54
+ open("tile.png", "wb").write(ml2drender.encode_png(rgba, width, height))
55
+ ```
56
+
57
+ Resource URLs reach `load` exactly as the style writes them, so make `glyphs`, `sprite` and TileJSON URLs absolute. `load` should return `None` only for a missing resource; raise for other failures (the exception propagates out of `render`).
58
+
59
+ To load concurrently (for example with asyncio or a thread pool), drive the steps yourself. Resources come in stages, each found from the last, so repeat until nothing is needed:
60
+
61
+ ```python
62
+ from concurrent.futures import ThreadPoolExecutor
63
+
64
+ renderer = ml2drender.Renderer(style, view)
65
+ with ThreadPoolExecutor(16) as pool:
66
+ while urls := renderer.needs():
67
+ for url, data in zip(urls, pool.map(load, urls)):
68
+ renderer.provide(url, data)
69
+ rgba = renderer.render()
70
+ ```
71
+
72
+ A `Renderer` keeps what it has loaded: `set_view` points it at another camera and the next `needs` asks only for what is missing.
73
+
74
+ ## API
75
+
76
+ | | |
77
+ |---|---|
78
+ | `View(center, zoom, width, height, bearing=0.0, pixel_ratio=1.0, margin=0)` | A Web Mercator camera. `center` is (lng, lat) in degrees; `zoom` is the MapLibre zoom (the world is `512 * 2**zoom` CSS pixels wide); `bearing` is degrees clockwise. The image is `width` x `height` CSS pixels, times `pixel_ratio` in pixels. `margin` CSS pixels are drawn around it and cropped, so labels near the edge are placed as in a larger map. |
79
+ | `View.tile(z, x, y, tile_size=256)` | The XYZ tile `z/x/y`, `tile_size` CSS pixels square. |
80
+ | `View.size` | `(width, height)` of the output in pixels. |
81
+ | `render(style, view, load)` | Non-premultiplied RGBA8 pixels (`bytes`, row by row) of `style` (a JSON string) in `view`. |
82
+ | `Renderer(style, view)` | The steps of `render`: `needs() -> list[str]`, `provide(url, data: bytes \| None)`, `render() -> bytes`, `set_view(view)`. Not thread-safe. |
83
+ | `encode_png(rgba, width, height)` | Lossless PNG. |
84
+ | `encode_webp(rgba, width, height)` | Lossless WebP. |
85
+ | `encode_jpeg(rgba, width, height, quality=90)` | JPEG, flattened over black. |
86
+
87
+ Invalid styles and rendering errors raise `RuntimeError`. Type hints and docstrings ship with the package (`ml2drender.pyi`).
88
+
89
+ The Python bindings draw Web Mercator views only. Projected CRSs (PROJJSON), reprojected sources for MapLibre GL JS and resource fetching are in the [JavaScript package](https://github.com/Kanahiro/ml2drender#javascript-api).
90
+
91
+ ## License
92
+
93
+ BSD-3-Clause
94
+
@@ -0,0 +1,8 @@
1
+ ml2drender/__init__.py,sha256=URlnh07Ip95698-oNZYP4LyEGwL0DJ-QP6wUGTTElEM,123
2
+ ml2drender/__init__.pyi,sha256=-zYVbyD-aRzKWmULgZM1kM8Puo7mY2JZSCKypayoWXQ,3194
3
+ ml2drender/ml2drender.pyd,sha256=_eFkQds6PTPmM7Y3kkURfD0KoFrL28U-v07QlVPJgNc,3078144
4
+ ml2drender/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
+ ml2drender-0.1.0.dist-info/METADATA,sha256=SoHebX35ZM0X_7MY2Puyp-jpMKNZvOhGRLJzDzuQJt0,4340
6
+ ml2drender-0.1.0.dist-info/WHEEL,sha256=DRguzO_JHkJTcrXDweuHyM66sVwM00caBYZ3J_zMn0w,96
7
+ ml2drender-0.1.0.dist-info/sboms/ml2drender-python.cyclonedx.json,sha256=n3pAQBr1b5ZtdQsrVCpvgpNSGe788f07vPJG08JoqCo,61129
8
+ ml2drender-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: maturin (1.16.0)
3
+ Root-Is-Purelib: false
4
+ Tag: cp311-abi3-win_amd64