@zwaarcontrast/ol-graticule-heeresgitter
v2.2.0
Published
WWII Wehrmacht map reference grids for ol-graticule: Deutsches Heeresgitter (DHG, Bessel + 6° Gauß-Krüger) and Heeresmeldenetz (HMN) letter cells.
Maintainers
Readme
@zwaarcontrast/ol-graticule-heeresgitter
WWII Wehrmacht map reference grids for
@zwaarcontrast/ol-graticule.
Renders the two superimposed grids printed on the Deutsche Heereskarte sheet series from 1942 onwards:
- Deutsches Heeresgitter (DHG), the black metric kilometre grid used for artillery and navigation. Gauß-Krüger transverse Mercator on the Bessel 1841 ellipsoid, 6° strips numbered by Kennziffer, scale factor 1.0 on the central meridian, false easting 500 000 m.
- Heeresmeldenetz (HMN), the orange letter-cell overprint used for
verbal position reporting up the chain of command. 150 km
Großquadrate subdivided into 6 km Kleinquadrate (letter pairs
AA..ZZwithIskipped), 2 km Meldetrapeze (1..9), 1 km Arbeitstrapeze (a..d), and an optional 100 m tenths suffix.
Both grids share the DHG projection, so HMN cells snap to the kilometre lattice.

Live demo: https://zwaarcontrast.nl/ol-graticule/ol-graticule-heeresgitter/
Install
npm install \
@zwaarcontrast/ol-graticule \
@zwaarcontrast/ol-graticule-projected \
@zwaarcontrast/ol-graticule-heeresgitter \
ol proj4Peers: ol ^10, @zwaarcontrast/ol-graticule,
@zwaarcontrast/ol-graticule-projected. The DHG projection is registered
via proj4 on first use; all 60 zones share the same definition with only
the central meridian and +towgs84 parameters changing.
Usage
Stack both grid systems to reproduce the sheet appearance: a fine black km grid with the orange HMN cells layered on top.
import Map from 'ol/Map';
import View from 'ol/View';
import { UniversalGraticule } from '@zwaarcontrast/ol-graticule';
import {
DhgGridSystem,
HmnGridSystem,
} from '@zwaarcontrast/ol-graticule-heeresgitter';
const dhg = new DhgGridSystem();
const hmn = new HmnGridSystem();
map.addLayer(new UniversalGraticule({
gridSystem: dhg,
style: { strokeColor: '#222', edgeLabel: true },
}));
map.addLayer(new UniversalGraticule({
gridSystem: hmn,
style: { strokeColor: '#d97706', cellLabel: true },
}));You can render either grid on its own. DHG covers the full validity
envelope at every zoom (zone outlines at small scales, full km grid once
you zoom past maxRenderResolution). HMN only renders inside its
configured resolution band.
The two grids on a sheet
A Wehrmacht sheet of the Deutsche Heereskarte standard (post-1942) typically carries both grids superimposed:
| Grid | Drawn in | Spacing | Purpose | |---|---|---|---| | DHG Deutsches Heeresgitter | black, fine line | 1 km on 1:50 000 (= 2 cm on paper) | metric coordinates for artillery, navigation | | HMN Heeresmeldenetz | orange overprint | 6 km letter cells | verbal position reporting up the chain of command |
Both are derived from the same projection, so HMN cells align with DHG kilometre lines.
DHG projection spec
From the Planheft Schweiz (OKH g 23/1, 16 March 1944), page C 1:
| Parameter | Value | |---|---| | Reference ellipsoid | Bessel 1841 | | Projection | Gauß-Krüger (transverse Mercator) | | Strip width | 6° | | Scale factor on CM | 1.0 (no Maßstabsreduktion, unlike UTM's 0.9996) | | False easting | 500 000 m | | False northing | 0 (Hochwert = meridian arc from equator) | | Strip overlap | 30' on each side beyond the nominal 6° | | Datum | each national triangulation preserved, no common adjustment |
Zone numbering (Kennziffer) follows n = (L_m + 3°) / 6° where L_m
is the central meridian in degrees east of Greenwich. The inverse is
L_m = n × 6° − 3°:
| Kennziffer | CM | Coverage | |---|---|---| | 1 | 3° E | 0°–6° E | | 5 | 27° E | 24°–30° E | | 6 | 33° E | 30°–36° E (Kolosjoki) | | 14 | 81° E | 78°–84° E (easternmost zone of the validity envelope) | | 55 | 33° W | 36°–30° W (westernmost zone) | | 59 | 9° W | 12°–6° W | | 60 | 3° W | 6° W–0° |
Eastings on map sheets are written with the Kennziffer prepended:
e.g. 5600 on the Owrutsch sheet = zone 5, Rechtswert 600 000 m. Inline
grid ticks may show only the last two km digits (short form).
Validity envelope
The Planheft Schweiz prints an explicit world-coverage plate titled Streifen des Deutschen Heeresgitters und Einteilung der Weltkarte 1:1.000.000 (page C 2). It enumerates the operational extent rectangle:
| Axis | From | To | |---|---|---| | Kennziffer | 55 (CM 33° W) | 14 (CM 81° E) | | Longitude (strip edges) | −36° | +84° | | IMW row | SI (32° S) | R (68°–72° N) | | Latitude | −32° | +72° |
Anything outside this rectangle was out of scope for the DHG (no Planheft, no triangulation), so the renderer uses these four numbers as its hard clip envelope.
IMW 1:1 000 000 sheet indexing
The DHG strip number doubles as a sheet-index column on the Internationale Weltkarte 1:1 000 000 (IMW) layout that the German Heereskarte series inherits, with a fixed offset:
- IMW columns 1–60 (180° W eastwards) number the same 6° strips but are
offset by 30:
IMW_col = DHG_strip + 30 (mod 60). - IMW rows: A, B, …, V (no I-skip at the row-letter level), so row
Rcovers latitudes 68°–72° N. - A sheet code like
R-36-X-West-7decodes as: IMW row R × IMW column 36 (= DHG strip 6) → subdivision X → west half → 1:50 000 sheet 7.
HMN hierarchy
Two historical variants of the HMN existed: a geographic one (Luftwaffe favoured, with named Großtrapeze) and a planar one (Heer favoured, keyed off DHG metres). This package implements the planar variant, which is what's printed on the Deutsche Heereskarte sheets:
| Level | Size | Labelling | Origin |
|---|---|---|---|
| Großquadrat | 150 km × 150 km | implicit, identified by sheet | intersection of CM with integer × 150 km Northing |
| Kleinquadrat | 6 km × 6 km | letter pair AA..ZZ (25 letters, I skipped) | NW corner of containing Großquadrat |
| Meldetrapez | 2 km × 2 km | 1..9 (3 × 3, NW→SE row-major) | NW corner of Kleinquadrat |
| Arbeitstrapez | 1 km × 1 km | a..d (2 × 2, NW→SE row-major) | NW corner of Meldetrapez |
| (tenths) | 100 m precision | 2 digits, tenths east + tenths north | SW corner of Arbeitstrapez |
Letter-pair labelling rule
- 25-letter alphabet:
A B C D E F G H J K L M N O P Q R S T U V W X Y Z(noI). - First letter = column, ascending W → E within the Großquadrat
(
Aat west,Zat east). - Second letter = row, ascending N → S (
Aat north,Zat south).
Sub-cell layout
Meldetrapez (2 km, inside Kleinquadrat) Arbeitstrapez (1 km, inside Meldetrapez)
1 2 3 a b
4 5 6 c d
7 8 9Both grids are row-major from the NW corner of the parent cell. The optional tenths suffix counts (east, north) from the SW corner of the Arbeitstrapez at 100 m precision.
Canonical reference grammar:
([A-HJ-Z][A-HJ-Z]) ?([1-9])([a-d])?( ?\d{2})?.
Großquadrat repetition and disambiguation
Within a single Großquadrat each letter pair is unique. AA..ZZ
repeats every 150 km in both axes, though, so a complete report includes
the sheet number (or the Großquadrat designator) to resolve which 150 km
tile. Along 6° strip boundaries the labelling produces Restquadrate,
cut-off cells where the strip ends mid-cell.
The parseHmn API therefore requires either an explicit grossquadrat
or a near location for disambiguation.
DHG options
| Option | Type | Default | What it does |
|---|---|---|---|
| zoneBoundary | 'tiled' | 'overlap' | 'single' | 'tiled' | Behaviour at 6° zone seams. tiled cuts hard at each meridian. overlap re-draws the 30' overlap band like wartime sheets that straddle a strip. single only renders the zone nearest the viewport centre. |
| labelForm | 'long' | 'short' | 'long' | long prints the Kennziffer-prefixed full km value ("5600"). short prints only the last two digits ("00"). Wartime corners use long form; inline ticks use short. |
| maxRenderResolution | number (m/px) | 2000 | Above this, only zone outlines + Kennziffer labels render. |
| overviewLabelMaxResolution | number (m/px) | 6000 | Strip-boundary lines always render, but Kennziffer labels are gated to avoid clutter. |
| targetScreenPx | number | 80 | Target pixel spacing between adjacent grid lines; drives the 1/2/6/30/150 km interval ladder. |
| densificationPoints | number | 60 | Vertices per grid line for non-affine view projections. |
| datumShift | DatumShift | Potsdam | Override the WGS 84 → Bessel-Potsdam Helmert transform. |
HMN options
| Option | Type | Default | What it does |
|---|---|---|---|
| maxDepth | 2 | 3 | 4 | 4 | 2 = Kleinquadrat only, 3 = + Meldetrapez, 4 = + Arbeitstrapez. |
| maxRenderResolution | number (m/px) | 300 | HMN hides itself at smaller scales (DHG carries the metric grid). |
| targetScreenPx | number | 80 | Pixel size at which a level subdivides into the next. |
| zoneBoundary | 'tiled' | 'overlap' | 'single' | 'tiled' | Same semantics as DhgGridSystem. |
| datumShift | DatumShift | Potsdam | Override the datum shift. |
| densificationPoints | number | 60 | Vertices per grid line. |
Encoding and parsing references
DHG: (lat, lon) to printed kilometre labels
import { encodeDhg, encodeDhgText, formatEasting, formatNorthing }
from '@zwaarcontrast/ol-graticule-heeresgitter';
const coord = encodeDhg([69.5, 30.0]); // Kolosjoki NW corner
// -> { kennziffer: 6, easting: 383038.., northing: 7715567.. }
formatEasting(coord); // -> "6383" (long form)
formatEasting(coord, { form: 'short' }); // -> "83" (inline tick)
formatNorthing(coord); // -> "7715"
encodeDhgText([52.0, 28 + 20 / 60]); // Owrutsch NW corner
// -> "5591 5763" (zone 5, ~91 km east of CM 27°E, Hochwert ~5763 km)
// The "5600" label printed near the corner of the Owrutsch sheet is the
// 600 km grid line east of the corner; that line crosses 52°N at
// approximately 28.463°E:
encodeDhgText([52.0, 28.463]); // -> "5600 5763"Input order is [latitude, longitude]. encodeDhg picks the zone whose
central meridian is nearest the longitude; pass an explicit kennziffer
to force a specific zone (useful when straddling a boundary).
HMN: (lat, lon) to letter-cell reference
import { encodeHmn, formatHmn } from '@zwaarcontrast/ol-graticule-heeresgitter';
// The Hadres sheet prints "PE 1b 52" as a worked Meldung; it resolves
// to approximately 48.509°N / 16.156°E:
const ref = encodeHmn([48.509, 16.156]);
// ref.canonical -> "PE 1b 52"
// ref.grossquadrat -> { kennziffer: 3, gx: 0, gy: 35 }
// ref.bbox -> [16.155.., 48.508.., 16.156.., 48.509..] (100 m cell)
encodeHmn([48.509, 16.156], { depth: 2 }).canonical; // -> "PE"
encodeHmn([48.509, 16.156], { depth: 3 }).canonical; // -> "PE 1"
encodeHmn([48.509, 16.156], { depth: 4 }).canonical; // -> "PE 1b"Reverse: parse an HMN reference back to a coordinate
Because AA..ZZ repeats every 150 km Großquadrat, parsing needs
disambiguation. Pass either an explicit grossquadrat, or a near
location and the library picks the closest matching Großquadrat:
import { parseHmn } from '@zwaarcontrast/ol-graticule-heeresgitter';
parseHmn('PE 1b 52', { near: [48.6, 16.1] });
// -> { canonical: 'PE 1b 52',
// kleinquadrat: 'PE', meldetrapez: 1, arbeitstrapez: 'b',
// tenths: [5, 2], depth: 5,
// grossquadrat: { kennziffer: 3, gx: 0, gy: 35 },
// bbox: [16.155.., 48.508.., 16.156.., 48.509..],
// center: [48.509.., 16.156..] }
parseHmn('JQ 4d 24', { near: [69.5, 30.0] }); // Kolosjoki river forkLenient input: case-insensitive, optional whitespace, optional sub-cell parts.
Primary-source validation
The renderer and the encode / decode helpers are anchored to wartime Deutsche Heereskarte sheets held by the UC Berkeley Library (digital collection, record 105643), and to the explicit DHG specification in the Planheft Schweiz (OKH g 23/1, 16 March 1944, pages C 1–C 3).
Kolosjoki, Norwegen / Finnland 1:50 000 sheet R-36-X-West-7, October 1943
Carries both grids. The black fine grid is the DHG km lattice; the orange overprint is the HMN.

DHG check at the NW corner labelled 30°00' E / 69°30' N:
- Easting label
83(short form, last two km digits) → DHG easting 500 000 − 3° × 111.32 × cos(69.5°) ≈ 383 142 m ✓ - Northing label
7715→ Bessel meridian arc to 69°30' N ≈ 7 715 km ✓
HMN check on the top-left orange cell labelled FP:
- Column F = index 5 → 380 ≤ easting < 386 km — contains 383 km (= 30° E) ✓
- Row P = index 14 (with I-skip) → 7 710 ≤ northing < 7 716 km — contains 7 715 km (= 69°30' N) ✓
- Implicit Großquadrat: NW corner at (E = 350 km, N = 7 800 km), the 52nd 150 km row north of the equator, west of CM 33° E. The FP cell falls inside it ✓
The printed legend on the sheet gives JQ 4d Flußgabel oder JQ 4d 24
as a sample report, where Flußgabel ("river fork") substitutes for the
100 m tenths suffix.
Hadres, Alpen- und Donau-Reichsgaue 1:50 000 Blatt 4558 Ost
Letter cells PA, QA, OB, PB, QB, OC, PC, QC of the orange
HMN overprint, with the black DHG km grid underneath:

Carries the explicit HMN subdivision legend printed in the margin:

The face of the sheet repeats the subdivision diagram with PE shown
as a 3 × 3 grid of digits 1..9 and Meldetrapez 1 further split
into the 2 × 2 grid a b / c d:

This is the canonical row-major layout the encoder produces.
Owrutsch, Osteuropa 1:300 000 Zusammendruck V52/W50 Owrutsch-Tscherkassy
Confirms the DHG at a different theatre and a different scale, with no
HMN overprint (1:300 000 was too small for the orange grid). The NW
corner is labelled 28°20' E / 52° N:
- Northing label
5760→ Bessel meridian arc to 52° N ≈ 5 763 km ✓ - Easting label
5600→ zone-prefixed (5| 600 000 m), at 1°20' E from CM 27° E ≈ 92 km east of CM → 592 km Rechtswert. The label sits on the 600 km grid line itself ✓
Embenskij Post, Osteuropa 1:300 000 Zusammendruck II/49–III/47
Confirms the same projection over the Caspian steppe, a 6th theatre tag on the system.

Planheft Schweiz, p. C 2: the world-coverage plate
The Streifen des Deutschen Heeresgitters und Einteilung der Weltkarte 1:1.000.000 plate enumerates the system's operational rectangle: zones 55 (CM 33° W) to 14 (CM 81° E), latitude band 32° S to 72° N. The renderer uses these four numbers as its hard clip envelope.

Theatres
The Planheft Schweiz cross-references the full set of sister Planhefte. All use the same DHG. Only local material differs: source maps, height datums, place-name conventions, country-specific triangulation notes.
| Merkblatt | Theatre | |---|---| | g 23/1 | Schweiz | | g 23/2 | Fennoskandien (incl. the Kolosjoki sheet above) | | 33/10 | Italien | | 33/11 | Belgien | | 33/11a/b | Südosteuropa (Balkan, north & south) | | 33/12 | Übersichten Ost | | 33/15 | Niederlande | | 34/17 | Luxemburg | | 34/18 | Großbritannien | | 34/24 | Mittelmeergebiet | | 34/25 | Spanien und Portugal | | 34/27 | Afrika | | 34/29 | Vorderasien | | 34/31a/b/c | Osteuropa (Baltikum, ex-Polen, Rußland; incl. Owrutsch + Embenskij) | | 34/32 | Dänemark / Island / Färöer / Grönland | | 34/33 | Frankreich |
Because the projection and the letter-cell rule are universal, this package needs no theatre-specific factory functions, in contrast to the Modified British System which has one factory per theatre.
The Planheft notes that each national triangulation kept its own
astronomical origin, so the WGS 84 → Bessel transform varies slightly
per region (typically 50–150 m residuals at the border between two
triangulations). The package uses a single global Helmert (Potsdam
datum) and lets you override per call via the datumShift option.
What this package doesn't implement
- Geographic HMN with named Großtrapeze (Luftwaffe variant). Not
printed on the Heereskarte sheets we have. The Luftwaffe used the
Gradnetzmeldeverfahren (GNMV); for that, see
@zwaarcontrast/ol-graticule-luftwaffe-planquadrat. - UTM-REF / Deutscher Heeresblattschnitt, the 1944 prototype that
later became MGRS. Covered by
@zwaarcontrast/ol-graticule-mgrs. - DRG (Deutsches Reichsgitter), the civilian 3°-strip predecessor. Different grid; could be a future sibling package if anyone needs it.
Credits and attribution
Map sheet images
All four sheet reproductions in this README, plus the Planheft Schweiz plate, are courtesy of the UC Berkeley Library, German WWII Captured Maps digital collection: https://digicoll.lib.berkeley.edu/record/105643. If you reuse the images, please cite Berkeley.
Sources for the encoding rules
- Planheft Schweiz (OKH g 23/1, 16 March 1944), pages C 1–C 3: the explicit DHG projection specification (Bessel 1841, 6° Gauß-Krüger, scale factor 1.0, false easting 500 000 m, Kennziffer numbering, strip overlap, world-coverage rectangle).
- Buchroithner & Pfahlbusch, Geodetic grids in authoritative maps: new findings about the origin of the UTM Grid, Cartography & GIS (2016): HMN hierarchy and the UTM-REF lineage that eventually became MGRS.
- Powell & Mühr, Capturing the Complex Histories of German World War II Captured Maps (UC Berkeley Library): provenance and cataloguing context for the captured-map collection.
- Sample sheets that anchor the implementation: Norwegen / Finnland 1:50 000 R-36-X-West-7 Kolosjoki (October 1943); Alpen- und Donau-Reichsgaue 1:50 000 Blatt 4558 Ost Hadres; Osteuropa 1:300 000 V52/W50 Owrutsch-Tscherkassy; Osteuropa 1:300 000 Zusammendruck II/49–III/47 Embenskij Post – Osero Koschkar-Ata.
License
MIT. See LICENSE.
