Skip to content

Commit 5e7f2aa

Browse files
committed
Simplify README: more concise, less presumptive, better examples
- Remove verbose prose and prescriptive language - Lead with quick start example - Add practical examples section with common use cases - Streamline API reference to essentials - Add notes section for quick reference - Keep all important information but make it scannable
1 parent bb1c465 commit 5e7f2aa

1 file changed

Lines changed: 103 additions & 91 deletions

File tree

README.md

Lines changed: 103 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -1,168 +1,180 @@
11
# mlnative
22

33
[![PyPI version](https://badge.fury.io/py/mlnative.svg)](https://pypi.org/project/mlnative/)
4-
[![License](https://img.shields.io/badge/license-Apache%202.0-blue.svg)](https://github.com/adonm/mlnative/blob/main/LICENSE)
4+
[![License](https://img.shields.io/badge/license-Apache%202.0-blue.svg)](LICENSE)
55

6-
> **⚠️ Warning: This is an alpha release. The API may change significantly. Not recommended for production use.**
6+
Render static map images from Python using MapLibre Native.
77

8-
Simple Python wrapper for MapLibre GL Native using a native Rust renderer.
8+
**Platform:** Linux x64, ARM64
9+
**Python:** 3.12+
910

10-
A grug-brained library for rendering static map images with minimal complexity.
11-
12-
## Features
13-
14-
- **Simple API**: One class, minimal methods, zero confusion
15-
- **Native Performance**: Rust backend with MapLibre Native C++ core
16-
- **Built-in Defaults**: Uses OpenFreeMap Liberty style by default - no configuration needed
17-
- **Address Support**: Built-in geocoding with geopy for rendering addresses directly
18-
- **Geometry Support**: Shapely integration for bounds fitting and GeoJSON
19-
20-
## Installation
11+
## Quick Start
2112

2213
```bash
2314
pip install mlnative
2415
```
2516

26-
Platform-specific wheels include the native renderer binary:
27-
- Linux x86_64, aarch64
28-
29-
## Quick Start
30-
3117
```python
3218
from mlnative import Map
3319

34-
# Render a map - uses OpenFreeMap Liberty style by default
3520
with Map(512, 512) as m:
36-
png_bytes = m.render(center=[-122.4, 37.8], zoom=12)
37-
38-
with open("map.png", "wb") as f:
39-
f.write(png_bytes)
21+
png = m.render(center=[-122.4, 37.8], zoom=12)
22+
open("map.png", "wb").write(png)
4023
```
4124

42-
## Rendering from Addresses
25+
## Features
26+
27+
- **Zero config** - Works out of the box with OpenFreeMap tiles
28+
- **HiDPI support** - `pixel_ratio=2` for sharp retina displays
29+
- **Batch rendering** - Efficiently render hundreds of maps
30+
- **Address geocoding** - Built-in support via geopy
31+
- **Custom markers** - Add GeoJSON points, lines, polygons
32+
33+
## Examples
34+
35+
### Render from address
4336

4437
```python
4538
from mlnative import Map
4639
from geopy.geocoders import ArcGIS
4740

48-
# Geocode an address and render
4941
geolocator = ArcGIS()
5042
location = geolocator.geocode("Sydney Opera House")
5143

5244
with Map(512, 512) as m:
5345
png = m.render(
5446
center=[location.longitude, location.latitude],
55-
zoom=15 # Good for landmark/building view
47+
zoom=15
5648
)
57-
open("sydney.png", "wb").write(png)
5849
```
5950

60-
## Custom Styles
61-
62-
While OpenFreeMap Liberty is the default, you can override it:
51+
### Fit bounds to show area
6352

6453
```python
65-
# OpenFreeMap styles
66-
m.load_style("https://tiles.openfreemap.org/styles/liberty")
67-
m.load_style("https://tiles.openfreemap.org/styles/positron")
68-
m.load_style("https://tiles.openfreemap.org/styles/dark")
54+
from mlnative import Map, feature_collection, point
55+
56+
# Show multiple locations
57+
markers = feature_collection([
58+
point(-122.4194, 37.7749), # SF
59+
point(-122.2712, 37.8044), # Oakland
60+
])
61+
62+
with Map(800, 600) as m:
63+
# Load style as dict to modify it
64+
style = {"version": 8, ...} # your style
65+
m.load_style(style)
66+
m.set_geojson("markers", markers)
67+
68+
# Fit map to show all markers
69+
center, zoom = m.fit_bounds(
70+
(-122.5, 37.7, -122.2, 37.9), # xmin, ymin, xmax, ymax
71+
padding=50
72+
)
73+
png = m.render(center=center, zoom=zoom)
74+
```
6975

70-
# MapLibre demo tiles (good for testing)
71-
m.load_style("https://demotiles.maplibre.org/style.json")
76+
### Batch render multiple views
7277

73-
# Or load from dict
74-
m.load_style({"version": 8, "sources": {...}, "layers": [...]})
78+
```python
79+
views = [
80+
{"center": [0, 0], "zoom": 1},
81+
{"center": [-122.4, 37.8], "zoom": 12},
82+
{"center": [151.2, -33.9], "zoom": 10, "bearing": 45},
83+
]
84+
85+
with Map(512, 512) as m:
86+
pngs = m.render_batch(views) # Returns list of PNG bytes
7587
```
7688

77-
## API Reference
89+
### HiDPI rendering
7890

79-
### Map Class
91+
```python
92+
# Retina/HiDPI display (2x resolution)
93+
with Map(512, 512, pixel_ratio=2) as m:
94+
png = m.render(center=[0, 0], zoom=5)
95+
# Image is 1024x1024, text appears sharp
96+
```
8097

81-
**`Map(width, height, pixel_ratio=1.0)`**
98+
## API Reference
8299

83-
Create a new map renderer.
100+
### Map(width, height, pixel_ratio=1.0)
84101

85-
- `width`: Image width in pixels (1-4096)
86-
- `height`: Image height in pixels (1-4096)
87-
- `pixel_ratio`: Pixel ratio for high-DPI rendering
102+
Create map renderer. Context manager ensures cleanup.
88103

89-
**`render(center, zoom, bearing=0, pitch=0)`**
104+
### render(center, zoom, bearing=0, pitch=0)
90105

91-
Render the map to PNG bytes. Uses OpenFreeMap Liberty style by default.
106+
Render single view. Returns PNG bytes.
92107

93-
- `center`: `[longitude, latitude]` list
94-
- `zoom`: Zoom level (0-24)
108+
- `center`: `[longitude, latitude]`
109+
- `zoom`: 0-24
95110
- `bearing`: Rotation in degrees (0-360)
96111
- `pitch`: Tilt in degrees (0-85)
97112

98-
**`render_batch(views)`**
113+
### render_batch(views)
99114

100115
Render multiple views efficiently.
101116

102-
**`fit_bounds(bounds, padding=0, max_zoom=24)`**
117+
```python
118+
views = [
119+
{"center": [lon, lat], "zoom": z},
120+
{"center": [lon, lat], "zoom": z, "geojson": {"markers": {...}}},
121+
]
122+
```
123+
124+
### fit_bounds(bounds, padding=0, max_zoom=24)
103125

104-
Calculate center/zoom to fit bounds. Returns `(center, zoom)`.
126+
Calculate center/zoom to fit bounding box.
105127

106128
```python
107-
bounds = (-122.6, 37.7, -122.3, 37.9) # (xmin, ymin, xmax, ymax)
108-
center, zoom = m.fit_bounds(bounds)
129+
center, zoom = m.fit_bounds((xmin, ymin, xmax, ymax))
109130
png = m.render(center=center, zoom=zoom)
110131
```
111132

112-
**`set_geojson(source_id, geojson)`**
133+
### set_geojson(source_id, geojson)
113134

114-
Update GeoJSON source data (requires style loaded as dict).
135+
Update GeoJSON source in style (requires dict style, not URL).
115136

116137
```python
117-
from shapely import Point
118-
m.set_geojson("markers", Point(-122.4, 37.8))
138+
m.set_geojson("markers", {"type": "FeatureCollection", "features": [...]})
119139
```
120140

121-
**`load_style(style)`**
122-
123-
Load custom style (URL, file path, or dict). Call before render if not using default.
141+
### load_style(style)
124142

125-
### GeoJSON Helpers
143+
Load custom style (URL, file path, or dict).
126144

127145
```python
128-
from mlnative import point, feature_collection, from_coordinates, from_latlng
129-
130-
# From coordinates (lng, lat)
131-
fc = from_coordinates([(-122.4, 37.8), (-74.0, 40.7)])
146+
# OpenFreeMap styles
147+
m.load_style("https://tiles.openfreemap.org/styles/liberty")
148+
m.load_style("https://tiles.openfreemap.org/styles/positron")
132149

133-
# From GPS coordinates (lat, lng)
134-
fc = from_latlng([(37.8, -122.4), (40.7, -74.0)])
150+
# MapLibre demo
151+
m.load_style("https://demotiles.maplibre.org/style.json")
135152

136-
# From shapely
137-
from shapely import MultiPoint, Point
138-
fc = feature_collection(MultiPoint([Point(-122.4, 37.8), Point(-74.0, 40.7)]))
153+
# Custom style dict
154+
m.load_style({"version": 8, "sources": {...}, "layers": [...]})
139155
```
140156

141-
## Examples
157+
## GeoJSON Helpers
142158

143-
See `examples/` directory:
144-
- `basic.py` - Simple usage
145-
- `address_rendering.py` - Render from addresses
146-
- `fastapi_server.py` - Static maps API
147-
148-
## Supported Platforms
149-
150-
- Linux x86_64, aarch64
159+
```python
160+
from mlnative import point, feature_collection, from_coordinates, from_latlng
151161

152-
> **Note:** macOS and Windows support limited due to upstream MapLibre Native build constraints.
162+
# Create point
163+
sf = point(-122.4194, 37.7749, {"name": "San Francisco"})
153164

154-
## Development
165+
# From coordinate tuples
166+
fc = from_coordinates([(-122.4, 37.8), (-74.0, 40.7)])
155167

156-
Requires Python 3.12+, Rust 1.70+, and uv.
168+
# From GPS (lat, lng) order
169+
fc = from_latlng([(37.8, -122.4), (40.7, -74.0)])
170+
```
157171

158-
```bash
159-
# Setup
160-
uv pip install -e ".[dev,web]"
161-
cd rust && cargo build --release
172+
## Notes
162173

163-
# Run tests
164-
just test
165-
```
174+
- **Default style**: OpenFreeMap Liberty (no configuration needed)
175+
- **GeoJSON updates**: Requires style loaded as dict, not URL
176+
- **pixel_ratio**: Higher values = larger image, same geographic area
177+
- **Platform**: Linux only (macOS/Windows builds disabled due to upstream issues)
166178

167179
## License
168180

0 commit comments

Comments
 (0)