Approximate irregular polygons as sets of circles using differentiable rendering
Project description
Geomantic
TL;DR
A Python package to approximate an irregular polygon as a set of circles, with optional auto-detection of optimal circle count. Accepts standard polygon input, produces both raw circle data and a visualization, and is engineered with DevOps hygiene suitable for portfolio/demo use.
Installation
pip install geomantic
Quick Start
from geomantic import pack_polygon, visualize_packing
# Define a polygon (list of coordinate tuples)
polygon = [(0, 0), (2, 0), (2, 1), (0, 1)]
# Pack circles into the polygon (auto-detect optimal count)
circles = pack_polygon(polygon)
# Visualize the result
visualize_packing(polygon, circles)
For geographic data (lat/lon coordinates):
# Use projection for geographic coordinates
circles = pack_polygon(
polygon,
n=5, # Or None for auto-detection
use_projection=True # Ensures circles stay circular on Earth's surface
)
Architecture
Geomantic uses a modular architecture with four main components:
geomantic/
├── core.py # Main API: pack_polygon() orchestrates the pipeline
├── optimizer.py # PyTorch-based optimization engine
├── projection.py # Geographic coordinate transformations (WGS84 ↔ UTM)
└── visualization.py # Plotting and output utilities
Key modules:
-
core.py: Main entry point withpack_polygon()function. Handles input parsing, projection setup, normalization, and result formatting. -
optimizer.py: Contains the differentiable rendering optimization:CircleModel: PyTorch neural network with learnable circle positions and radiiDifferentiableRenderer: Rasterizes polygons to binary masksoptimize_circles(): Gradient descent optimization with IoU loss- Smart initialization samples circle positions inside the polygon
- Containment and repulsion penalties prevent circles from escaping or collapsing
-
projection.py: Coordinate transformation utilities:MetricProjector: WGS84 ↔ UTM transformations with auto-zone detectionMetricNormalizer: Scales coordinates to [0,1] normalized space for optimization
-
visualization.py: Visualization tools includingvisualize_packing()andprint_circle_summary()
Optimization pipeline:
Input Polygon → [Projection to UTM] → Normalization →
PyTorch Optimization (IoU + Containment + Repulsion) →
Denormalization → [Projection back to WGS84] → Circle Results
Project Goals
Problem Statement
Traditional geometric approximation methods struggle with irregular polygons, especially when physical interpretability matters. Geographic boundaries (ZIP codes, districts, protected areas) often need to be simplified for:
- Spatial analysis: Coverage estimation, service area planning, accessibility modeling
- Visualization: Reducing visual complexity while preserving spatial meaning
- Approximation: Replacing complex polygons with analytically tractable primitives
Existing solutions either produce poor approximations (convex hulls, bounding boxes) or lack geographic awareness (treating lat/lon as Cartesian coordinates produces distorted circles).
What Geomantic Solves
1. Physically Accurate Geographic Approximation
- Automatically projects WGS84 coordinates to UTM before optimization, ensuring circles remain circular on Earth's surface
- Handles latitude-dependent longitude scaling for correct distance calculations
- Eliminates the "stretched ellipse" problem common in naive lat/lon circle fitting
2. Smart Optimization with Constraints
- Uses differentiable rendering with PyTorch for gradient-based optimization (faster convergence than genetic algorithms)
- Containment penalties keep circles inside polygon boundaries (critical for concave shapes)
- Repulsion penalties prevent circle collapse, ensuring diverse coverage
- Auto-detection finds optimal circle count using elbow method on IoU loss
3. Production-Ready Engineering
- Type-safe codebase with full mypy compliance
- Comprehensive test coverage with pytest
- Pre-commit hooks for code quality (Black, flake8, mypy)
- CI/CD pipeline with multi-version Python testing (3.8-3.12)
- Installable via pip with minimal dependencies
Use Cases
- Urban planning: Approximate service areas (fire stations, schools) for quick distance calculations
- Ecology: Simplify habitat boundaries for circular buffer analysis
- Marketing: Convert trade areas or ZIP code boundaries to radial coverage zones
- Education: Demonstrate optimization techniques, differentiable rendering, and coordinate projections
- Data visualization: Replace complex polygons with simpler circular representations for cleaner maps
What Geomantic Is NOT
- Not a general-purpose polygon simplification tool (use Shapely's
simplify()for that) - Not optimized for real-time applications (optimization takes seconds to minutes depending on complexity)
- Not a commercial SaaS product (open-source library for integration into your own tools)
- Not a replacement for precise geometric operations (approximation introduces error by design)
User Stories
- As a Data Scientist, I want to fit circles to arbitrary polygons, so that I can demonstrate geometric optimization in presentations.
- As a developer, I want to import this as a Python library, so that I can integrate it into Jupyter notebooks or pipelines.
- As a hobbyist, I want to visualize the packing, so that I can experiment and tweak inputs for different shapes.
Functional Requirements
- Core Algorithm (Priority: High)
- Accept input polygon (list of coordinates or GeoJSON).
- Accept optional n; otherwise autodetect using suitable metric (e.g., elbow method).
- Grid-based differentiable rasterization for circle fitting.
- Compute suitable initial centroids.
- API/Interface (Priority: High)
- Python function/class interface.
- Output: Array of dictionaries, each with radius, centroid_x, centroid_y.
- Visualization (Priority: Medium)
- Function to generate an image (matplotlib, etc.) showing polygon outline and circles.
- Packaging (Priority: High)
- Installable via pip (or equivalent, e.g., Poetry support).
- Testing & Code Quality (Priority: High)
- Unit tests for inputs/outputs, edge cases, and algorithm steps.
- Automated linting and type-checking, e.g., with flake8/black/pylint and mypy.
- DevOps & Deployment (Priority: Medium)
- GitHub Actions to run tests/linting on commit/push.
- (Optional) Terraform/GCP scripts for potential future hosting.
User Experience
- User installs via pip.
- Imports in notebook/script: from geomantic import pack_polygon
- Calls main function, passing polygon and options.
- Receives output (list of circles), optionally calls viz function for output.
- Views resulting image in notebook or as file.
Technical Considerations
- Python-first; minimal non-Python dependencies.
- Codebase ready for CI (GitHub Actions).
- Focus on modularity/testability: split main algorithm from I/O and viz.
- Type hints and docstrings throughout.
- Testing: Use pytest; test basic input validation, algorithm output, and (if feasible) some image output.
Success Metrics
- All primary features covered by automated tests and CI.
- Can be imported and run in a clean virtualenv.
- Generates accurate results for at least 3 "typical" test polygons.
- README/docs clear enough for DS/engineering peers to use and understand.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file geomantic-0.1.0.tar.gz.
File metadata
- Download URL: geomantic-0.1.0.tar.gz
- Upload date:
- Size: 42.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
78deada7044760c75012c7f16f60e42f3c328a9fe18feea0c35c9569a17db008
|
|
| MD5 |
8457b5f0ed471fe6cff554e8e9a1e078
|
|
| BLAKE2b-256 |
36924395703bb2ac9cde619064abd561adcfbad5c61db7d88d6afb1bfdcd8839
|
Provenance
The following attestation bundles were made for geomantic-0.1.0.tar.gz:
Publisher:
publish.yml on horeilly/geomantic
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
geomantic-0.1.0.tar.gz -
Subject digest:
78deada7044760c75012c7f16f60e42f3c328a9fe18feea0c35c9569a17db008 - Sigstore transparency entry: 791351379
- Sigstore integration time:
-
Permalink:
horeilly/geomantic@5fa623116ec4f9ad3f5a010689bf8f2af4bf64f8 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/horeilly
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@5fa623116ec4f9ad3f5a010689bf8f2af4bf64f8 -
Trigger Event:
push
-
Statement type:
File details
Details for the file geomantic-0.1.0-py3-none-any.whl.
File metadata
- Download URL: geomantic-0.1.0-py3-none-any.whl
- Upload date:
- Size: 19.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b0c22006057eb3134b131583223444141be2c5c66f8ccee781a0d85ebe71ce18
|
|
| MD5 |
62a9c5160089cf4fa765edb1429acf02
|
|
| BLAKE2b-256 |
78270ff8c8b41e58736b6e26916eb4321f147ba7da5493917f329fa56cd2bffb
|
Provenance
The following attestation bundles were made for geomantic-0.1.0-py3-none-any.whl:
Publisher:
publish.yml on horeilly/geomantic
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
geomantic-0.1.0-py3-none-any.whl -
Subject digest:
b0c22006057eb3134b131583223444141be2c5c66f8ccee781a0d85ebe71ce18 - Sigstore transparency entry: 791351384
- Sigstore integration time:
-
Permalink:
horeilly/geomantic@5fa623116ec4f9ad3f5a010689bf8f2af4bf64f8 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/horeilly
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@5fa623116ec4f9ad3f5a010689bf8f2af4bf64f8 -
Trigger Event:
push
-
Statement type: