Spaces:
Sleeping
Sleeping
File size: 8,100 Bytes
008947a 4fb118c 008947a | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 | # AGENTS.md - City Map Poster Generator
This file provides essential information for agentic coding agents working in the maptoposter repository.
## Project Overview
A Python-based web application that generates beautiful, minimalist map posters for any place in the world. The product UI is React/Vite over FastAPI, while OSMnx and Matplotlib power the map pipeline.
**Tech Stack:**
- Python 3.12+
- React + Vite (primary web UI)
- FastAPI (application/API layer)
- OSMnx (street network analysis)
- Matplotlib (map rendering)
- GeoPandas (geospatial data)
- Nominatim (geocoding)
## Build/Test/Lint Commands
### Environment Setup
```bash
# Install dependencies with uv
uv sync --all-groups
corepack pnpm --dir frontend install --frozen-lockfile
```
### Running the Application
```bash
# Start the API
uv run python app.py
# Start the frontend dev server in another terminal
corepack pnpm --dir frontend dev
# Or use the restart script (handles port conflicts)
bash restart.sh
# Development UI: http://localhost:5173
# Production-style UI/API: http://localhost:7860 after building frontend
```
### Running Tests
```bash
# Run the full pytest suite
uv run pytest
# Run individual test functions
uv run pytest test_logic.py -k "china_hierarchy or manual_coordinates"
```
### Development Utilities
```bash
# Verify city data integrity
uv run python verify_data.py
# Create map poster directly (CLI)
uv run python create_map_poster.py --city "Beijing" --country "China" --theme japanese_ink --output-format png
```
## Code Style Guidelines
### General Principles
- **Bilingual Support**: All user-facing text should support both English (en) and Chinese (cn)
- **File Encoding**: Use UTF-8 encoding with BOM for Chinese characters: `# -*- coding: utf-8 -*-`
- **Function Documentation**: Use docstrings for all public functions
- **Error Handling**: Provide meaningful error messages in both languages when possible
### Import Organization
```python
# Standard library imports first
import os
import json
import sys
from datetime import datetime
# Third-party imports next
from fastapi import FastAPI
import osmnx as ox
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
from geopy.geocoders import Nominatim
# Local imports last
from cities_data import get_countries, get_provinces, get_cities
from create_map_poster import generate_map_poster
```
### Naming Conventions
- **Files**: `snake_case.py` (e.g., `cities_data.py`, `test_logic.py`)
- **Variables/Functions**: `snake_case` (e.g., `get_available_themes()`, `motorway_color`)
- **Constants**: `UPPER_SNAKE_CASE` (e.g., `THEMES_DIR`, `LAYER_KEYS`)
- **Classes**: `PascalCase` (rare in this codebase, but follow convention if used)
### Type Hints
```python
def generate_output_filename(city: str, theme_name: str, output_format: str) -> str:
"""Generate unique output filename with city, theme, and datetime."""
pass
def get_theme_choices(lang: str = "en") -> list[tuple[str, str]]:
"""Return a list of (Display Name, Internal Name) tuples for themes."""
pass
```
### String Patterns
- **Language Keys**: Use `lang="en"` or `lang="cn"` parameters
- **Layer Constants**: Define parallel arrays for different languages
```python
LAYERS_EN = ["Motorway", "Primary Roads", "Secondary Roads", "Water", "Parks"]
LAYERS_CN = ["ι«ιε
¬θ·―", "δΈ»εΉ²ι", "欑干ι", "ζ°΄ε", "ε
¬ε"]
LAYER_KEYS = ["motorway", "primary", "secondary", "water", "parks"]
```
### Data Structures
- **City Hierarchy**: Use tuples for display/internal name pairs: `[("Display Name", "internal_name"), ...]`
- **Theme Files**: JSON format with descriptive names: `{"name": "Theme Name", "bg": "#FFFFFF", ...}`
- **Coordinates**: Use `(lat, lon)` tuple format
## Directory Structure
```
maptoposter/
βββ app.py # FastAPI + built frontend entry point
βββ Dockerfile # Hugging Face production container
βββ create_map_poster.py # Core poster generation logic
βββ cities_data.py # City/province/district data management
βββ test_logic.py # Test suite for data logic
βββ verify_data.py # Data integrity verification
βββ backend/ # FastAPI schemas and endpoints
βββ frontend/ # React/Vite editor
βββ docs/ # Deployment and maintenance guides
βββ .github/workflows/ # Validation and Space publishing
βββ src/maptoposter/ # Typed core, cache, renderer, resources
βββ themes/ # JSON theme configuration files
βββ fonts/ # Font files (Chinese and English)
βββ posters/ # Generated poster outputs
βββ public/ # Static assets and demo images
βββ restart.sh # Development restart script
```
## Common Patterns
### Language Support
```python
def translate(text: str, lang: str = "en") -> str:
"""Simple translation wrapper."""
translations = {
"en": {"City": "City", "Theme": "Theme"},
"cn": {"City": "εεΈ", "Theme": "δΈ»ι’"}
}
return translations.get(lang, {}).get(text, text)
```
### File Path Handling
```python
THEMES_DIR = "themes"
FONTS_DIR = "fonts"
POSTERS_DIR = "posters"
# Always check existence
if not os.path.exists(THEMES_DIR):
return []
```
### FastAPI Endpoint Patterns
```python
@app.get("/api/v1/health")
def health() -> dict[str, str]:
"""Return service readiness and version information."""
return {"status": "ok", "version": __version__}
```
## Testing Guidelines
### Test Structure
- Use pytest tests with focused fixtures and mocks
- Group related tests in modules/functions (e.g., `test_china_hierarchy()`)
- Test both data integrity and coordinate retrieval
- Include Chinese characters in test data
### Test Execution
```bash
# Run all tests
uv run pytest
# The exact count grows with the maintained suite; require zero failures.
```
## Error Handling
### Common Patterns
```python
# File not found handling
if not os.path.exists(font_path):
print(f"β Font not found: {font_path}")
continue
# API error handling
try:
result = geocoder.geocode(city)
except Exception as e:
print(f"β Geocoding failed for {city}: {e}")
return None
# Data validation
assert len(provinces) > 30, f"Expected >30 provinces, found {len(provinces)}"
```
## Performance Considerations
- **Caching**: OSMnx caching is enabled: `ox.settings.use_cache = True`
- **Large Cities**: Be aware that Beijing/Shanghai may have centering issues
- **Small Cities**: Some layers may be empty due to missing OSM data
- **Memory**: Clear matplotlib figures after generation to prevent memory leaks
## Font Management
The project supports both English and Chinese fonts:
- **English**: Goudy Old Style (Regular, Bold)
- **Chinese**: HYWenRunSongYunU.ttf
Always verify font availability before use:
```python
if os.path.exists(font_path):
font = FontProperties(fname=font_path)
else:
print(f"β Font not found: {font_path}")
font = None
```
## Theme Development
Themes are JSON files in the `themes/` directory with the following structure:
```json
{
"name": "Theme Display Name",
"description": "Brief description",
"bg": "#FFFFFF",
"text": "#000000",
"water": "#E0E0E0",
"parks": "#F0F0F0",
"road_motorway": "#FF0000",
"road_primary": "#333333",
"road_secondary": "#666666",
"road_tertiary": "#999999",
"road_residential": "#CCCCCC",
"road_default": "#888888"
}
```
## Development Notes
- **Port Management**: Default port is 7860. Use `restart.sh` to handle conflicts
- **Output Directory**: Generated posters go to `posters/` (auto-created if needed)
- **Debugging**: Use print statements with emoji indicators for important events
- **Code Organization**: Keep API routes in `backend/`, UI code in `frontend/`, and domain logic in `src/maptoposter/`
- **Deployment**: GitHub `main` is mirrored to the Docker Space only after CI validation
- **Dependencies**: The project uses `uv` for dependency management. Lock file is `uv.lock`
|