Skip to content

Latest commit

 

History

2 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

MGRS Grid Generator

A Python tool for generating Military Grid Reference System (MGRS) grid squares as GeoJSON — optimized for MapTiler vector tiling and military/defence map overlays.

Features

  • Multi-precision grids — GZD, 100km, 10km, 1km, 100m, 10m, 1m
  • Adaptive parallelism — Single-thread for small areas, ProcessPoolExecutor for large
  • Zone exceptions — Norway (band V) and Svalbard (band X) handled correctly
  • Deduplication — Automatic removal of duplicate cells at zone boundaries
  • Three output layers — Polygons, label points (--labels), grid lines (--lines)
  • Enriched properties — MGRS components, centroid coords, precision, UTM data
  • GeoJSON validation — Output validated before writing, warnings on malformed features
  • Antimeridian support — Bounding boxes crossing ±180° longitude handled correctly
  • Area lookup--area bavaria auto-resolves bounding box for countries, regions, and states
  • Coordinate rounding — 7 decimal places (~1cm) for smaller file sizes

Installation

pip install -r requirements.txt
pip install -e .

# For development (mypy, pytest):
pip install -e ".[dev]"

Dependencies

Package Purpose
mgrs MGRS coordinate conversion
shapely Geometry operations
pyproj CRS transformations

CLI Usage

python -m src.cli.main <command> [options]

generate — Generate Grid Zones or Tiles

# Generate all GZD zones
python -m src.cli.main generate -l GZD -o gzd.geojson

# Generate 10km grid for a bounding box
python -m src.cli.main generate -l 10km -b 12.0 48.5 19.0 51.1 -o cz_10km.geojson

# Generate with companion layers
python -m src.cli.main generate -l 1km -b 14.0 50.0 15.0 51.0 \
    -o prague.geojson --labels --lines
# Outputs: prague.geojson, prague_labels.geojson, prague_lines.geojson

# Auto-resolve bounding box by place name (country, region, state, etc.)
python -m src.cli.main generate -l 10km --area bavaria -o bavaria_10km.geojson
python -m src.cli.main generate -l GZD --area japan -o japan_gzd.geojson
python -m src.cli.main generate -l 10km --area texas -o texas_10km.geojson

# Filter specific UTM zones
python -m src.cli.main generate -l GZD --zone 33 34 -o zones.geojson

# Antimeridian crossing (wraps around 180°)
python -m src.cli.main generate -l GZD -b 170 50 -170 60 -o antimeridian.geojson

# Verbose mode
python -m src.cli.main generate -l GZD -o gzd.geojson -v

CLI Flags

Flag Description
-l, --level Grid precision: GZD, 100km, 10km, 1km, 100m, 10m, 1m
-b, --bbox Bounding box: MIN_LON MIN_LAT MAX_LON MAX_LAT
--area Place name to auto-resolve bbox — country, region, state (e.g. bavaria, texas, CZ)
-o, --output Output file path (default: stdout)
--labels Write separate label-points GeoJSON
--lines Write separate grid-lines GeoJSON
--zone Filter by UTM zone number(s)
-v, --verbose Enable debug logging

Other Commands

python -m src.cli.main point 14.42 50.08    # Get MGRS zone for a coordinate
python -m src.cli.main bbox bavaria          # Look up bounding box for an area
python -m src.cli.main list                  # List all 1,197 grid zones

Python API

Geocoding (bbox lookup)

from src.geocoding import lookup_area_bbox

# Look up bounding box for any area (requires MAPTILER_API_KEY env var)
bbox = lookup_area_bbox("bavaria")      # (8.9764, 47.2701, 13.8396, 50.5647)
bbox = lookup_area_bbox("japan")        # (122.9338, 24.0456, 153.9869, 45.5227)
bbox = lookup_area_bbox("CZ")           # (12.0905, 48.5519, 18.8593, 51.0557)

Grid Generation

from shapely.geometry import box
from src.grid.generator import (
    GridGenerator,
    generate_gzd_features,
    generate_label_points,
    generate_grid_lines,
    build_feature_collection,
)

# Generate GZD features
features = generate_gzd_features()                          # All 1,197 zones
filtered = generate_gzd_features(bbox=box(14, 50, 15, 51))  # Filtered

# Generate grid cells (auto-deduplicated at zone boundaries)
gen = GridGenerator(level_meters=1000)
features = gen.generate_grid(box(14.0, 50.0, 15.0, 51.0))

# Companion layers
labels = generate_label_points(features)   # Point features for label placement
lines  = generate_grid_lines(features)     # LineString features for grid edges

# FeatureCollection with metadata
fc = build_feature_collection(features, level="1KM", bbox=(14.0, 50.0, 15.0, 51.0))

Zone Geometry

from src.geometry.zones import get_utm_zone, get_mgrs_grid_zone, get_zone_geometry

zone_num, hemisphere = get_utm_zone(50.08, 14.42)   # (33, 'N')
gzd = get_mgrs_grid_zone(50.08, 14.42)              # "33U"
geom = get_zone_geometry(33, 'U')                    # Shapely Polygon

Coordinate Transformation

from src.transformation.transformer import UTMTransformer

transformer = UTMTransformer(zone_num=33, latitude=50.0)
easting, northing = transformer.transform_point_to_utm(14.42, 50.08)
lon, lat = transformer.transform_point_to_wgs84(easting, northing)

Grid Precision Levels

Level Cell Size MGRS Digits NATO Use Case
GZD ~6° × 8° Theater-level planning, logistics corridors
100km 100 × 100 km 0 + 0 Operational-level planning, area of operations (AO)
10km 10 × 10 km 1 + 1 Tactical planning, brigade/battalion sectors
1km 1 × 1 km 2 + 2 Mission planning — fire support, patrol routes, objectives
100m 100 × 100 m 3 + 3 Precision targeting, CAS (close air support)
10m 10 × 10 m 4 + 4 Point targets, IED markers, POI
1m 1 × 1 m 5 + 5 Maximum precision targeting

Feature Properties

Grid Cell

{
  "type": "Feature",
  "id": "33UVR",
  "properties": {
    "mgrs": "33UVR",
    "gzd": "33U",
    "sqid": "VR",
    "zone_number": 33,
    "band": "U",
    "easting": "",
    "northing": "",
    "utm_easting": 400000,
    "utm_northing": 5500000,
    "precision_meters": 100000,
    "center_lon": 14.5006602,
    "center_lat": 50.2748925
  }
}

FeatureCollection Metadata

{
  "type": "FeatureCollection",
  "bbox": [14.0, 50.0, 15.0, 51.0],
  "properties": {
    "generated_at": "2026-02-08T10:00:00+00:00",
    "generator": "mgrs-grid",
    "level": "1KM",
    "feature_count": 1234
  }
}

Zone Exceptions

Norway — Band V (56–64°N)

Zone 31V narrowed to 0–3°E, Zone 32V widened to 3–12°E.

Svalbard — Band X (72–84°N)

Band X is 12° tall (not 8°). Zones 32X, 34X, 36X don't exist — their space is absorbed by odd-numbered zones (31X, 33X, 35X, 37X). Total GZD count: 1,197.


Architecture

src/
├── _compat.py               # Centralized path setup
├── cli/main.py              # CLI with subcommands
├── config/settings.py       # Grid level enums and lookups
├── geometry/zones.py        # UTM zone calculations, band logic
├── grid/
│   ├── generator.py         # Grid cells, label points, grid lines
│   └── tiling.py            # Zone iteration utilities
└── transformation/
    └── transformer.py       # pyproj UTM ↔ WGS84 transforms
tests/                       # pytest suite (56 tests)

Module Reference

Module Key Exports
geometry.zones get_utm_zone, get_zone_geometry, get_all_zone_boundaries, LAT_BANDS
grid.generator GridGenerator, generate_gzd_features, generate_label_points, generate_grid_lines, build_feature_collection
grid.tiling iter_zones, get_all_zones, count_zones
transformation.transformer UTMTransformer, get_utm_crs
config.settings GridLevel, PRECISION_MAP, LEVEL_NAME_MAP

Performance

Grid generation uses adaptive parallelism:

  • ≤4 zones: Single-threaded (avoids ~200ms process spawn overhead)
  • >4 zones: ProcessPoolExecutor with workers capped at zone count

Testing

python -m pytest tests/ -v    # 56 tests
python -m mypy src/           # Type checking (0 errors)

License

MIT — see LICENSE for details.

About

Generate Military Grid Reference System (MGRS) grids as GeoJSON — multi-precision, seamless tiling, NATO-ready

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages