step 8: harden docs and prepare rebuilt beta

This commit is contained in:
2026-05-23 10:47:34 +02:00
parent d0ba8c4218
commit 50e38e18ee
17 changed files with 653 additions and 65 deletions

181
README.md
View File

@@ -1,53 +1,176 @@
# dpg-map
`dpg-map` is a Dear PyGui map widget package under rebuild.
`dpg-map` is a Dear PyGui map widget for XYZ raster tiles and geographic overlays.
The Step 6 overlay rendering layer is in place:
The rebuilt beta exposes a stable public import:
```python
import dpg_map as dpgm
```
## Install
Use `uv` for development and dependency management:
```bash
uv sync
uv run pytest
```
From another local project, add this package as an editable dependency:
```bash
uv add --editable ../dpg-map
uv run python -c "import dpg_map as dpgm; print(dpgm.list_providers())"
```
## Basic Map
```python
from typing import Any
import dearpygui.dearpygui as _dpg
import dpg_map as dpgm
dpg: Any = _dpg
dpgm.configure(user_agent="my-app/0.1 contact@example.com")
dpg.create_context()
dpg.create_viewport(title="Map", width=1000, height=700)
with dpg.window(label="Map", width=-1, height=-1):
with dpgm.map_widget(tag="map", center=(47.9029, 1.9093), zoom=15, width=-1, height=-1):
dpgm.add_marker("vehicle", lat=47.9029, lon=1.9093, label="Vehicle")
dpg.setup_dearpygui()
dpg.show_viewport()
dpg.start_dearpygui()
dpg.destroy_context()
```
## Sizing
The widget is a Dear PyGui `child_window` containing a measured drawlist. The child keeps the requested Dear PyGui sizing intent while the drawlist uses concrete measured pixels.
Supported sizing modes:
- `width=0` and `height=0` keep Dear PyGui default sizing.
- `width=-1` and `height=-1` fill available space where Dear PyGui supports it.
- Positive dimensions request fixed sizes.
- `autosize_x` and `autosize_y` are passed to the child window.
- Hidden layouts preserve the last non-zero measured size until visible again.
Examples:
```bash
uv run python examples/sizing_window.py
uv run python examples/sizing_child.py
uv run python examples/sizing_table.py
uv run python examples/hidden_tab.py
```
## Live Updates
Runtime overlay updates are safe to call from background threads. They update logical state and enqueue renderer commands; Dear PyGui drawing, texture, handler, and viewport calls stay on the GUI thread.
Live marker update:
```python
dpgm.add_marker("vehicle", lat=47.9029, lon=1.9093, map_tag="map")
dpgm.update_marker("vehicle", lat=current_lat, lon=current_lon, map_tag="map")
```
Live trajectory update:
```python
dpgm.add_trajectory("track", points=[], map_tag="map")
dpgm.update_trajectory("track", points=tuple(points), map_tag="map")
```
Stress examples:
```bash
uv run python examples/markers_live_thread.py
uv run python examples/trajectory_live_thread.py
```
## Providers
OpenStreetMap is registered as `osm` by default. Custom XYZ providers can be registered and selected at runtime:
```python
provider = dpgm.TileProvider(
name="custom",
url_template="https://example.com/{z}/{x}/{y}.png",
attribution="Tiles (c) Example",
)
dpgm.register_provider(provider)
with dpgm.map_widget(tag="map", center=(47.9029, 1.9093), zoom=15):
dpgm.add_marker("vehicle", lat=47.9029, lon=1.9093)
dpgm.update_marker("vehicle", lat=47.9030, lon=1.9094, map_tag="map")
dpgm.set_provider("custom", map_tag="map")
```
Implemented so far:
Provider switching preserves overlays and center, clamps zoom to the new provider range, increments the tile generation, and ignores stale tile results from the previous provider.
- public package exports
- tile provider definitions and registry
- Web Mercator projection helpers
- thread-safe logical map state and map registry
- command queue with coalescing for overlay and view updates
- logical marker, polyline, trajectory, and layer models
- persistent disk cache paths, metadata, scanning, pruning, and clearing
- Dear PyGui `child_window` + measured-size `drawlist` widget shell
- GUI-thread frame pump that drains commands, manages textures, and draws raster tiles
- sizing helpers that preserve the last non-zero size across hidden layouts
- asynchronous tile workers that read disk cache, fetch HTTP, and decode images
- memory cache with visible-tile protection and GUI-thread texture deletion
- measured-rectangle mouse interaction for left-drag panning and wheel zooming
- programmatic view commands and screen/latitude-longitude conversion helpers
- marker, polyline, and trajectory rendering on a draw layer separate from tiles
- background-thread overlay updates that coalesce without resetting center or zoom
Example:
Examples:
```bash
uv run python examples/custom_provider.py
```
## Cache
Tiles use an in-memory cache and a persistent provider-namespaced disk cache.
```python
dpgm.configure(
user_agent="my-app/0.1 contact@example.com",
cache_dir=".tile-cache",
memory_cache_max_tiles=512,
disk_cache_max_bytes=2_000_000_000,
)
stats = dpgm.get_cache_stats(map_tag="map")
dpgm.clear_memory_cache(map_tag="map")
dpgm.clear_disk_cache(provider="osm")
```
`disk_cache_max_bytes=None` disables the disk size limit. Memory cache clears are routed through the renderer command queue so texture deletion happens on the GUI thread. Disk cache clears can target all providers or a single provider namespace.
Cache example:
```bash
uv run python examples/cache_stress.py
```
## OpenStreetMap Usage
The default OpenStreetMap provider requires attribution and should use an application-specific `User-Agent`.
```python
dpgm.configure(user_agent="my-product/1.0 contact@example.com")
```
If no user agent is configured, `dpg-map` emits a runtime warning and falls back to a package user agent. Applications are responsible for displaying provider attribution in accordance with provider terms; the renderer draws the provider attribution text on the map.
## Thread-Safety Contract
Public runtime functions are intended to be callable from non-GUI threads unless explicitly documented otherwise. They acquire map locks briefly, update logical state, and/or enqueue commands.
Thread-safe runtime areas include:
- view updates: `set_center`, `set_zoom`, `set_view`, `fit_bounds`
- overlay updates: `add_marker`, `update_marker`, `update_trajectory`, `delete_overlay`
- layer updates: `add_layer`, `show_layer`, `hide_layer`, `clear_layer`
- provider/cache updates: `set_provider`, `clear_memory_cache`, `clear_disk_cache`
`map_widget(...)` creates Dear PyGui items and must be used on the GUI thread inside an active Dear PyGui context.
## Examples
```bash
uv run python examples/basic_map.py
uv run python examples/sizing_window.py
uv run python examples/sizing_child.py
uv run python examples/sizing_table.py
uv run python examples/hidden_tab.py
uv run python examples/cache_stress.py
uv run python examples/custom_provider.py
uv run python examples/markers_live_thread.py
uv run python examples/trajectory_live_thread.py
```