Files
dpg-map/docs/GETTING_STARTED.md
Hector van der Aa 6990d011ff Release v1.0.0
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
2026-06-08 13:44:32 +02:00

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 width and height: fixed requested size
  • autosize_x=True or autosize_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.