This is the first releas of dpg-map, an AI coded dearpygui map widget built due to a lack of time to build it myself. It features multi-widget abilities, with layers, overlays etc
7.5 KiB
Getting Started
This guide walks through installing dpg-map, creating a map widget, adding overlays, updating
the map from runtime code, and configuring tile providers and caches.
Requirements
dpg-map currently targets Python >=3.14 and uses:
- Dear PyGui for the UI
- Pillow for decoding tile images
- Requests for tile HTTP requests
- Platformdirs for the default disk cache location
The project is managed with uv.
Install From This Repository
For development inside this repository:
uv sync
uv run python -c "import dpg_map as dpgm; print(dpgm.list_providers())"
For another local app using this checkout:
cd /path/to/your-app
uv add --editable /home/hector/projects/dpg-map
Then import it as:
import dpg_map as dpgm
Configure The Package
Configure package-wide defaults before creating maps:
dpgm.configure(
user_agent="my-product/1.0 contact@example.com",
cache_dir=".tile-cache",
memory_cache_max_tiles=512,
disk_cache_max_bytes=2_000_000_000,
)
Set an application-specific user_agent when using OpenStreetMap tiles. If no user agent is
configured, dpg-map emits a runtime warning and uses a fallback package user agent.
Create A Map
map_widget(...) is a Dear PyGui context manager. It creates a child window containing the map
drawlist and installs internal render and input handlers.
from typing import Any
import dearpygui.dearpygui as _dpg
import dpg_map as dpgm
dpg: Any = _dpg
dpgm.configure(user_agent="getting-started/0.1 contact@example.com")
dpg.create_context()
dpg.create_viewport(title="dpg-map", width=900, height=600)
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("start", lat=47.9029, lon=1.9093, label="Start")
dpg.setup_dearpygui()
dpg.show_viewport()
dpg.start_dearpygui()
dpg.destroy_context()
map_widget(...) must be called on the GUI thread inside an active Dear PyGui context.
Tags And Map Scope
Each map has a tag. Runtime calls should pass map_tag when there may be more than one map:
dpgm.set_center(47.9029, 1.9093, map_tag="live-map")
dpgm.update_marker("vehicle", lat=47.9030, lon=1.9098, map_tag="live-map")
Inside a with dpgm.map_widget(...) block, overlay creation can omit map_tag because the map is
the current map context:
with dpgm.map_widget(tag="live-map"):
dpgm.add_marker("vehicle", lat=47.9029, lon=1.9093)
Use explicit map_tag in callbacks, worker threads, and apps with multiple maps.
Add Overlays
Markers:
dpgm.add_marker(
"vehicle",
lat=47.9029,
lon=1.9093,
label="Vehicle",
show_label=True,
color=(255, 80, 80, 255),
radius=6,
map_tag="map",
)
Polylines:
dpgm.add_polyline(
"route",
points=[(47.9029, 1.9093), (47.9050, 1.9150), (47.9080, 1.9180)],
color=(80, 180, 255, 255),
thickness=3,
map_tag="map",
)
Trajectories:
dpgm.add_trajectory(
"track",
points=[],
color=(255, 180, 60, 255),
thickness=3,
show_points=True,
point_stride=10,
map_tag="map",
)
Coordinates are (lat, lon) pairs in degrees. Latitude must be between -90 and 90;
longitude must be between -180 and 180.
Runtime Updates
Runtime public calls are intended to be safe from background threads. They update logical state and enqueue GUI-thread work instead of calling Dear PyGui directly.
dpgm.update_marker("vehicle", lat=current_lat, lon=current_lon, map_tag="map")
dpgm.update_trajectory("track", points=tuple(track_points), map_tag="map")
Overlay updates do not reset map center or zoom.
View Control
dpgm.set_center(47.9029, 1.9093, map_tag="map")
dpgm.set_zoom(15, map_tag="map")
dpgm.set_view(center=(47.9029, 1.9093), zoom=15, map_tag="map")
dpgm.fit_bounds(((47.89, 1.89), (47.92, 1.93)), map_tag="map")
set_zoom(...) and set_view(...) clamp zoom to the active provider range.
Coordinate conversion helpers use map-local screen coordinates:
lat, lon = dpgm.screen_to_latlon(120, 80, map_tag="map")
x, y = dpgm.latlon_to_screen(47.9029, 1.9093, map_tag="map")
Layers
Overlays belong to named layers. Layers can be shown, hidden, cleared, and ordered with
z_index.
dpgm.add_layer("vehicles", z_index=80, map_tag="map")
dpgm.add_marker("car-1", lat=47.9, lon=1.9, layer="vehicles", map_tag="map")
dpgm.hide_layer("vehicles", map_tag="map")
dpgm.show_layer("vehicles", map_tag="map")
dpgm.clear_layer("vehicles", map_tag="map")
Default layers include default, markers, lines, and trajectories.
Tile Providers
OpenStreetMap is registered by default as osm.
Custom providers use XYZ URL templates. Required template fields are {z}, {x}, and {y}.
Optional fields are {s} for subdomain, {r} for retina suffix, and {ext} for file extension.
provider = dpgm.TileProvider(
name="carto-light",
url_template="https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png",
subdomains=("a", "b", "c", "d"),
attribution="(c) OpenStreetMap contributors (c) CARTO",
file_extension="png",
)
dpgm.register_provider(provider)
dpgm.set_provider("carto-light", map_tag="map")
Provider switching preserves overlays and center, clamps zoom to the new provider range, and invalidates stale tile loads from the previous provider.
Cache
dpg-map uses:
- A memory cache for decoded runtime tiles
- A persistent provider-namespaced disk cache
stats = dpgm.get_cache_stats(map_tag="map")
print(stats.memory_tiles, stats.disk_bytes, stats.disk_path)
dpgm.clear_memory_cache(map_tag="map")
dpgm.clear_disk_cache(provider="osm")
clear_memory_cache(...) is routed through the renderer command queue so Dear PyGui textures are
deleted on the GUI thread. clear_disk_cache(...) can clear all providers, one provider, or a
map-scoped cache directory.
Sizing
The map widget is a Dear PyGui child window containing a measured drawlist.
Common sizing modes:
width=-1,height=-1: fill available space where Dear PyGui supports it- Positive
widthandheight: fixed requested size autosize_x=Trueorautosize_y=True: pass Dear PyGui autosize flags to the child- Hidden layouts preserve the last non-zero measured size until visible again
Examples:
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
Multiple Maps
Multiple maps can be used in the same Dear PyGui app. Give each map a distinct tag and pass
map_tag for runtime updates.
with dpgm.map_widget(tag="live-map", center=(47.9, 1.9), zoom=15, width=-1, height=300):
dpgm.add_marker("vehicle", lat=47.9, lon=1.9)
with dpgm.map_widget(tag="recap-map", center=(47.9, 1.9), zoom=13, width=-1, height=300):
dpgm.add_trajectory("lap", points=[])
dpgm.update_marker("vehicle", lat=47.901, lon=1.902, map_tag="live-map")
dpgm.update_trajectory("lap", points=lap_points, map_tag="recap-map")
Pan, zoom, tile rendering, overlays, providers, and caches are isolated per map unless you choose to share global configuration.
Diagnostics
Use get_map_debug_state(...) and get_cache_stats(...) while developing or troubleshooting:
debug = dpgm.get_map_debug_state(map_tag="map")
stats = dpgm.get_cache_stats(map_tag="map")
These snapshots are intended for diagnostics and logging, not as a stable serialization format.