Metadata-Version: 2.5
Name: osmimage
Version: 0.1.0
Summary: Render an OpenStreetMap tile image from latitude, longitude and zoom, returning a Pillow image ready for further drawing.
Project-URL: Homepage, https://github.com/AngLaboratory/osmimage
Project-URL: Source, https://github.com/AngLaboratory/osmimage
Project-URL: Issues, https://github.com/AngLaboratory/osmimage/issues
Author-email: AngLaboratory <anglaboratory@gmail.com>
License-Expression: MIT
License-File: LICENSE
Keywords: basemap,gis,map,openstreetmap,osm,pillow,static-map,tiles,web-mercator,지도,타일
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Science/Research
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Multimedia :: Graphics
Classifier: Topic :: Scientific/Engineering :: GIS
Classifier: Typing :: Typed
Requires-Python: >=3.9
Requires-Dist: pillow>=9.0
Requires-Dist: requests>=2.25
Description-Content-Type: text/markdown

# osmimage

Render an [OpenStreetMap](https://www.openstreetmap.org/) tile image from a
latitude, longitude and zoom level, and get a [Pillow](https://python-pillow.org/)
image back. Draw your own routes, markers or labels on top of it.

<p align="center">
  <img src="https://raw.githubusercontent.com/AngLaboratory/osmimage/main/assets/example_route.png" width="360" alt="A route drawn on an OSM map with start/end markers">
</p>

## Install

```bash
pip install osmimage
```

## Quick start

```python
from osmimage import OSMImage

m = OSMImage(width=400, height=300)

# lat/lon/zoom -> PIL.Image (RGBA)
img = m.render(lat=37.5665, lon=126.9780, zoom=13)
img.save("seoul.png")
```

<p align="center">
  <img src="https://raw.githubusercontent.com/AngLaboratory/osmimage/main/assets/example_basic.png" width="400" alt="An OSM map centered on Seoul">
</p>

## Fitting many points

Give `fit_zoom` a list of `(lat, lon)` points and it returns the tightest zoom
at which they all fit the canvas. Pair it with `center_of` to get the center to
render around.

```python
from osmimage import OSMImage

points = [(37.5665, 126.9780), (37.5700, 126.9820), (37.5740, 126.9790)]

m = OSMImage(400, 400)
zoom = m.fit_zoom(points)          # -> int, e.g. 15
lat, lon = m.center_of(points)     # -> (lat, lon) mid-point of the bounds
img = m.render(lat, lon, zoom)
img.save("area.png")
```

<p align="center">
  <img src="https://raw.githubusercontent.com/AngLaboratory/osmimage/main/assets/example_area.png" width="400" alt="An area fitted to a set of points">
</p>

## Drawing a route and markers

`draw_polyline` and `draw_marker` draw straight onto the rendered map from
geographic coordinates, anti-aliased. For anything custom, `to_pixel()` still
converts a coordinate to a pixel position so you can use `PIL.ImageDraw`.

```python
from osmimage import OSMImage

points = [
    (37.5758, 126.9768), (37.5769, 126.9770), (37.5780, 126.9773),
    (37.5790, 126.9778), (37.5799, 126.9786), (37.5805, 126.9797),
]

m = OSMImage(400, 400)
m.render(*m.center_of(points), m.fit_zoom(points))

m.draw_polyline(points, color="#3396FF", width=6)
m.draw_marker(*points[0],  radius=7, color="#2ECC71", outline="white", outline_width=2)
m.draw_marker(*points[-1], radius=7, color="#F23F5C", outline="white", outline_width=2)

m.image.save("route.png")
```

(That's the image at the top of this page.)

## Caching and reliability

Pass `cache_dir` to reuse tiles across runs (recommended, and kinder to the
tile server). Tile downloads are retried with backoff and fetched in parallel.

```python
m = OSMImage(400, 400, cache_dir=".tile_cache", max_workers=8)
```

For full control, build a `TileSource` yourself:

```python
from osmimage import OSMImage, TileSource

src = TileSource(
    "https://tile.openstreetmap.org/{z}/{x}/{y}.png",
    cache_dir=".tile_cache",
    max_retries=3,
    backoff=0.5,
)
m = OSMImage(400, 400, tile_source=src)
```

## API

### `OSMImage(width=300, height=300, *, tile_source=None, cache_dir=None, max_workers=8)`

- `render(lat, lon, zoom) -> PIL.Image.Image` — composite covering tiles onto
  a `width x height` canvas centered on the coordinate (tiles fetched in
  parallel). Returns an RGBA image.
- `draw_polyline(points, *, color="#3388FF", width=4, supersample=4) -> Image`
  — anti-aliased line through `(lat, lon)` points on the last render.
- `draw_marker(lat, lon, *, radius=6, color="#F23F5C", outline=None, outline_width=0, supersample=4) -> Image`
  — anti-aliased filled circle at a coordinate.
- `fit_zoom(points, *, padding=5, min_zoom=0, max_zoom=19) -> int` — the
  tightest zoom (0-19) at which all `(lat, lon)` points fit the canvas.
- `center_of(points) -> (lat, lon)` — mid-point of the bounds of `points`.
- `to_pixel(lat, lon) -> (x, y)` — pixel position of a coordinate on the last
  rendered image. Call `render()` first.
- `image` — the most recently rendered image (or `None`).

### `TileSource(url_template=..., *, tile_size=256, user_agent=..., timeout=10.0, session=None, cache_dir=None, max_retries=2, backoff=0.5)`

Fetches tiles with an in-memory + optional on-disk cache, retry/backoff, and a
parallel `get_tiles(coords, max_workers=8)` batch fetch. `clear_cache()` drops
the in-memory cache (the on-disk cache is left intact).

## Tile usage policy

The default tile server is the public OpenStreetMap one. Its use is subject to
the [OSM tile usage policy](https://operations.osmfoundation.org/policies/tiles/):
send a descriptive `User-Agent`, avoid bulk downloading (use `cache_dir`), and
for anything beyond light use, point at your own tile server.

Map data © OpenStreetMap contributors.

## License

MIT
