Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 36 additions & 2 deletions kmm/positions/read_kmm2.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@
pattern2 = re.compile(r"CMAST")

# Norway ("Banenor") kmm2 files are identified by this token in the VER header
# line. They store WGS84 latitude/longitude in the coordinate columns instead of
# SWEREF99 TM northing/easting.
# line. Older ones store WGS84 latitude/longitude in the coordinate columns
# instead of SWEREF99 TM northing/easting; newer ones, under the same header,
# store projected northing/easting already.
norway_header_token = "NorKmmToKmm"
tm = projections.make_transverse_mercator("SWEREF_99_TM")

Expand Down Expand Up @@ -53,6 +54,20 @@
)


def is_degrees(dataframe):
"""
The same converter version (``NorKmmToKmm2_1.03``) writes either WGS84
degrees or projected metres into the coordinate columns, so the header
cannot tell them apart; the values can. A latitude is at most 90, while a
northing anywhere in Scandinavia is in the millions. The projected files
use zone 33 ETRS89 coordinates, which SWEREF99 TM shares its projection
with, so they pass through unchanged.
"""
northing = dataframe["northing"].abs()
easting = dataframe["easting"].abs()
return bool(northing.median() <= 90 and easting.median() <= 180)


def latlon_to_sweref(dataframe):
"""
Norway kmm2 files store WGS84 latitude/longitude in the coordinate columns
Expand All @@ -62,6 +77,8 @@ def latlon_to_sweref(dataframe):
"""
if len(dataframe) == 0 or not {"northing", "easting"}.issubset(dataframe.columns):
return dataframe
if not is_degrees(dataframe):
return dataframe

latitude = dataframe["northing"].to_numpy()
longitude = dataframe["easting"].to_numpy()
Expand Down Expand Up @@ -166,3 +183,20 @@ def test_norway_latlon_to_sweref():
)
assert abs(latitude - 59.9100025) < 1e-4
assert abs(longitude - 10.7545870) < 1e-4


def test_norway_projected_coordinates_pass_through():
# Newer Norway files carry the same VER header but already store projected
# northing/easting. Converting those as degrees produced garbage; they must
# land on the same place as the lat/lon fixture (same four positions).
projected = read_kmm2(Path("tests/norway_projected.kmm2"))
degrees = read_kmm2(Path("tests/norway.kmm2"))
assert len(projected) == 4
assert np.allclose(projected["northing"], degrees["northing"], atol=1.0)
assert np.allclose(projected["easting"], degrees["easting"], atol=1.0)

latitude, longitude = tm.grid_to_geodetic(
projected["northing"].iloc[0], projected["easting"].iloc[0]
)
assert abs(latitude - 59.9100025) < 1e-4
assert abs(longitude - 10.7545870) < 1e-4
5 changes: 5 additions & 0 deletions tests/norway_projected.kmm2
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
VER NorKmmToKmm2_1.03 SplSetup NO_NORM
POS 23060200 10 0.0 200.0 ? 3.0 0.0 0.0 0.0 6649001.564084955 262665.94670750335 0.0 5.0 0.0 2026-05-19T02:59:50.0000000+02:00
POS 23060300 10 0.0 201.0 ? 3.0 0.0 0.0 0.0 6649001.118494233 262666.7812106609 0.0 5.0 0.0 2026-05-19T02:59:51.0000000+02:00
POS 23060400 10 0.0 202.0 ? 3.0 0.0 0.0 0.0 6649000.615133018 262667.6301772379 0.0 5.0 0.0 2026-05-19T02:59:52.0000000+02:00
POS 23060500 10 0.0 203.0 ? 3.0 0.0 0.0 0.0 6649000.143033189 262668.4762151675 0.0 5.0 0.0 2026-05-19T02:59:53.0000000+02:00
Loading