Skip to content

Commit 93daf05

Browse files
authored
Merge pull request #7 from josteinl/feature/add_swedish_coordinates
Add Swedish projections SWEREF99 with EPSG SRID 3006 to 3018
2 parents 98e1fe2 + 530f92f commit 93daf05

9 files changed

Lines changed: 715 additions & 437 deletions

File tree

.github/workflows/branch.yaml

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -44,12 +44,8 @@ jobs:
4444
poetry-version: ${{ matrix.poetry-version }}
4545
- name: Install dependencies
4646
run: poetry install
47-
- name: Run black
48-
run: poetry run black . --check
49-
# - name: Run isort
50-
# run: poetry run isort . --check-only --profile black
51-
# - name: Run flake8
52-
# run: poetry run flake8 .
47+
- name: Run ruff
48+
run: poetry run ruff check .
5349
- name: Run mypy
5450
run: poetry run mypy .
5551
- name: Run bandit

.github/workflows/release.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ jobs:
1010
fail-fast: false
1111
matrix:
1212
python-version: ['3.11']
13-
poetry-version: ['1.6.1']
13+
poetry-version: ['1.7.1']
1414
os: [ubuntu-latest]
1515
runs-on: ${{ matrix.os }}
1616
steps:

CHANGES.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
# NGI Python Coordinate Projector Package
22

3+
_2024-01-15_
4+
5+
Version 0.0.10
6+
7+
Add
8+
9+
- Swedish projections SWEREF99 with EPSG SRID 3006 to 3018.
10+
311
_2023-08-24_
412

513
Version 0.0.9

poetry.lock

Lines changed: 430 additions & 417 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ exclude = '''
1515

1616
[tool.poetry]
1717
name = "coordinate-projector"
18-
version = "0.0.9"
18+
version = "0.0.10"
1919
description = "Project points from one projection to another using pyproj"
2020
license = "MIT"
2121
authors = ["Helge Smebye", "Jostein Leira <jostein@leira.net>"]
@@ -38,10 +38,12 @@ packages = [
3838
line-length = 120
3939
src = ["src", "tests"]
4040

41+
[tool.ruff.per-file-ignores]
42+
"__init__.py" = ["F401"]
4143

4244
[tool.poetry.dependencies]
4345
python = ">=3.9,<4"
44-
pyproj = "^3.5.0"
46+
pyproj = "^3.6.1"
4547
python-dateutil = "^2.8.2"
4648
types-python-dateutil = "^2.8.19"
4749
timezonefinder = "*"

src/coordinate_projector/datetime_parser.py

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,18 @@
77
from dateutil import tz
88
from timezonefinder import TimezoneFinder
99

10-
from coordinate_projector import Projector
10+
from coordinate_projector.projector import Projector
1111

1212
projector = Projector()
1313

14-
_time_zone_finder = TimezoneFinder()
14+
_time_zone_finder: TimezoneFinder | None = None
1515

1616

1717
def ensure_tz(
18-
dt: Optional[datetime],
19-
longitude: Optional[float] = None,
20-
latitude: Optional[float] = None,
21-
srid: int = 4326,
18+
dt: Optional[datetime],
19+
longitude: Optional[float] = None,
20+
latitude: Optional[float] = None,
21+
srid: int = 4326,
2222
) -> Optional[datetime]:
2323
"""
2424
Return passed datetime dt enriched with timezone.
@@ -31,6 +31,8 @@ def ensure_tz(
3131
If no location or timezone is provided, then assume the passed datetime is
3232
recorded in the norwegian timezone.
3333
"""
34+
global _time_zone_finder
35+
3436
if not dt:
3537
return dt
3638

@@ -46,6 +48,9 @@ def ensure_tz(
4648
longitude, latitude = projector.transform(from_srid=srid, to_srid=4326, east=longitude, north=latitude)
4749

4850
# find timezone from position
51+
if not _time_zone_finder:
52+
_time_zone_finder = TimezoneFinder()
53+
4954
input_timezone = tz.gettz(_time_zone_finder.timezone_at(lng=longitude, lat=latitude))
5055
else:
5156
# Assume Norway
@@ -57,10 +62,10 @@ def ensure_tz(
5762

5863

5964
def datetime_to_json(
60-
dt: Optional[datetime],
61-
longitude: Optional[float] = None,
62-
latitude: Optional[float] = None,
63-
srid: int = 4326,
65+
dt: Optional[datetime],
66+
longitude: Optional[float] = None,
67+
latitude: Optional[float] = None,
68+
srid: int = 4326,
6469
) -> Optional[str]:
6570
"""
6671
Return passed datetime.datetime as json-formatted (iso-8601) string with UTC timezone. Sub-second time information

src/coordinate_projector/projections.json

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,123 @@
134134
"data": "+proj=utm +zone=36 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs"
135135
}
136136
},
137+
"3006": {
138+
"srid": 3006,
139+
"name": "SWEREF99 TM",
140+
"shortname": "SWEREF99 TM",
141+
"definition": {
142+
"name": "EPSG:3006",
143+
"data": "+proj=utm +zone=33 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs"
144+
}
145+
},
146+
"3007": {
147+
"srid": 3007,
148+
"name": "SWEREF99 12 00",
149+
"shortname": "SWEREF99 12 00",
150+
"definition": {
151+
"name": "EPSG:3007",
152+
"data": "+proj=tmerc +lat_0=0 +lon_0=12 +k=1 +x_0=150000 +y_0=0 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs"
153+
}
154+
},
155+
"3008": {
156+
"srid": 3008,
157+
"name": "SWEREF99 13 30",
158+
"shortname": "SWEREF99 13 30",
159+
"definition": {
160+
"name": "EPSG:3008",
161+
"data": "+proj=tmerc +lat_0=0 +lon_0=13.5 +k=1 +x_0=150000 +y_0=0 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs"
162+
}
163+
},
164+
"3009": {
165+
"srid": 3009,
166+
"name": "SWEREF99 15 00",
167+
"shortname": "SWEREF99 15 00",
168+
"definition": {
169+
"name": "EPSG:3009",
170+
"data": "+proj=tmerc +lat_0=0 +lon_0=15 +k=1 +x_0=150000 +y_0=0 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs"
171+
}
172+
},
173+
"3010":{
174+
"srid": 3010,
175+
"name": "SWEREF99 16 30",
176+
"shortname": "SWEREF99 16 30",
177+
"definition": {
178+
"name": "EPSG:3010",
179+
"data": "+proj=tmerc +lat_0=0 +lon_0=16.5 +k=1 +x_0=150000 +y_0=0 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs"
180+
}
181+
},
182+
"3011": {
183+
"srid": 3011,
184+
"name": "SWEREF99 18 00",
185+
"shortname": "SWEREF99 18 00",
186+
"definition": {
187+
"name": "EPSG:3011",
188+
"data": "+proj=tmerc +lat_0=0 +lon_0=18 +k=1 +x_0=150000 +y_0=0 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs"
189+
}
190+
},
191+
"3012": {
192+
"srid": 3012,
193+
"name": "SWEREF99 14 15",
194+
"shortname": "SWEREF99 14 15",
195+
"definition": {
196+
"name": "EPSG:3012",
197+
"data": "+proj=tmerc +lat_0=0 +lon_0=14.25 +k=1 +x_0=150000 +y_0=0 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs"
198+
}
199+
},
200+
"3013": {
201+
"srid": 3013,
202+
"name": "SWEREF99 15 45",
203+
"shortname": "SWEREF99 15 45",
204+
"definition": {
205+
"name": "EPSG:3013",
206+
"data": "+proj=tmerc +lat_0=0 +lon_0=15.75 +k=1 +x_0=150000 +y_0=0 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs"
207+
}
208+
},
209+
"3014": {
210+
"srid": 3014,
211+
"name": "SWEREF99 17 15",
212+
"shortname": "SWEREF99 17 15",
213+
"definition": {
214+
"name": "EPSG:3014",
215+
"data": "+proj=tmerc +lat_0=0 +lon_0=17.25 +k=1 +x_0=150000 +y_0=0 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs"
216+
}
217+
},
218+
"3015": {
219+
"srid": 3015,
220+
"name": "SWEREF99 18 45",
221+
"shortname": "SWEREF99 18 45",
222+
"definition": {
223+
"name": "EPSG:3015",
224+
"data": "+proj=tmerc +lat_0=0 +lon_0=18.75 +k=1 +x_0=150000 +y_0=0 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs"
225+
}
226+
},
227+
"3016": {
228+
"srid": 3016,
229+
"name": "SWEREF99 20 15",
230+
"shortname": "SWEREF99 20 15",
231+
"definition": {
232+
"name": "EPSG:3016",
233+
"data": "+proj=tmerc +lat_0=0 +lon_0=20.25 +k=1 +x_0=150000 +y_0=0 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs"
234+
}
235+
},
236+
"3017": {
237+
"srid": 3017,
238+
"name": "SWEREF99 21 45",
239+
"shortname": "SWEREF99 21 45",
240+
"definition": {
241+
"name": "EPSG:3017",
242+
"data": "+proj=tmerc +lat_0=0 +lon_0=21.75 +k=1 +x_0=150000 +y_0=0 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs"
243+
}
244+
},
245+
"3018": {
246+
"srid": 3018,
247+
"name": "SWEREF99 23 15",
248+
"shortname": "SWEREF99 23 15",
249+
"definition": {
250+
"name": "EPSG:3018",
251+
"data": "+proj=tmerc +lat_0=0 +lon_0=23.25 +k=1 +x_0=150000 +y_0=0 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs"
252+
}
253+
},
137254
"3857": {
138255
"srid": 3857,
139256
"name": "WGS 84 / Pseudo-Mercator",

tests/test_projector.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import pytest
22

3-
from coordinate_projector import Projector
3+
from coordinate_projector.projector import Projector
44
from data.coords import Coords
55
from data.coords import ProjSets
66

@@ -90,3 +90,6 @@ def test_get_supported_projections(self):
9090
assert "4326" in supported_projections
9191
assert "5130" in supported_projections
9292
assert "5105" in supported_projections
93+
assert "3006" in supported_projections
94+
assert "3010" in supported_projections
95+
assert "3018" in supported_projections

tests/test_sweref99.py

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
import pytest
2+
3+
from coordinate_projector.projector import Projector
4+
5+
6+
# Assumption that the SWEREF99 is the same as WGS84
7+
WGS84 = 4326
8+
SWEREF99TM = 3006
9+
SWEREF99_12_00 = 3007
10+
SWEREF99_13_30 = 3008
11+
SWEREF99_15_00 = 3009
12+
SWEREF99_16_30 = 3010
13+
SWEREF99_18_00 = 3011
14+
SWEREF99_14_15 = 3012
15+
SWEREF99_15_45 = 3013
16+
SWEREF99_17_15 = 3014
17+
SWEREF99_18_45 = 3015
18+
SWEREF99_20_15 = 3016
19+
SWEREF99_21_45 = 3017
20+
SWEREF99_23_15 = 3018
21+
22+
23+
@pytest.mark.parametrize(
24+
"latitude, longitude, to_srid, expected_northing, expected_easting",
25+
(
26+
(55, 12.75, SWEREF99TM, 6097106.672, 356083.438),
27+
(55, 14.25, SWEREF99TM, 6095048.642, 452024.069),
28+
(57, 12.75, SWEREF99TM, 6319636.937, 363331.554),
29+
(57, 19.5, SWEREF99TM, 6326392.707, 773251.054),
30+
(59, 11.25, SWEREF99TM, 6546096.724, 284626.066),
31+
(59, 19.5, SWEREF99TM, 6548757.206, 758410.519),
32+
(61, 12.75, SWEREF99TM, 6764877.311, 378323.440),
33+
(61, 18.75, SWEREF99TM, 6768593.345, 702745.127),
34+
(63, 12, SWEREF99TM, 6989134.048, 348083.148),
35+
(63, 19.5, SWEREF99TM, 6993565.630, 727798.671),
36+
(65, 13.5, SWEREF99TM, 7209293.753, 429270.201),
37+
(65, 21.75, SWEREF99TM, 7225449.115, 817833.405),
38+
(67, 16.5, SWEREF99TM, 7432168.174, 565398.458),
39+
(67, 24, SWEREF99TM, 7459745.672, 891298.142),
40+
(69, 21, SWEREF99TM, 7666089.698, 739639.195),
41+
(57, 11.25, SWEREF99_12_00, 6320164.077, 104421.390),
42+
(59, 11.25, SWEREF99_12_00, 6542910.921, 106894.103),
43+
(57, 12.75, SWEREF99_12_00, 6320164.077, 195578.610),
44+
(59, 12.75, SWEREF99_12_00, 6542910.921, 193105.897),
45+
(55, 12.75, SWEREF99_13_30, 6097487.637, 102004.871),
46+
(57, 12.75, SWEREF99_13_30, 6320164.077, 104421.390),
47+
(59, 12.75, SWEREF99_13_30, 6542910.921, 106894.103),
48+
(61, 12.75, SWEREF99_13_30, 6765725.847, 109420.005),
49+
(63, 12.75, SWEREF99_13_30, 6988606.198, 111996.020),
50+
(55, 14.25, SWEREF99_13_30, 6097487.637, 197995.129),
51+
(57, 14.25, SWEREF99_13_30, 6320164.077, 195578.610),
52+
(59, 14.25, SWEREF99_13_30, 6542910.921, 193105.897),
53+
(61, 14.25, SWEREF99_13_30, 6765725.847, 190579.995),
54+
(63, 12, SWEREF99_14_15, 6990379.288, 36003.367),
55+
(65, 13.5, SWEREF99_14_15, 7211548.993, 114619.001),
56+
(63, 15, SWEREF99_14_15, 6988606.198, 188003.980),
57+
(65, 15, SWEREF99_14_15, 7211548.993, 185380.999),
58+
# SWEREF 99 SWEREF 99 15 00
59+
(55, 14.25, SWEREF99_15_00, 6097487.637, 102004.871),
60+
(57, 14.25, SWEREF99_15_00, 6320164.077, 104421.390),
61+
(59, 14.25, SWEREF99_15_00, 6542910.921, 106894.103),
62+
(61, 14.25, SWEREF99_15_00, 6765725.847, 109420.005),
63+
(57, 15.75, SWEREF99_15_00, 6320164.077, 195578.610),
64+
(59, 15.75, SWEREF99_15_00, 6542910.921, 193105.897),
65+
(61, 15.75, SWEREF99_15_00, 6765725.847, 190579.995),
66+
# SWEREF 99 SWEREF 99 15 45
67+
(63, 15.00, SWEREF99_15_45, 6988606.198, 111996.020),
68+
(65, 15.00, SWEREF99_15_45, 7211548.993, 114619.001),
69+
(67, 15.00, SWEREF99_15_45, 7434550.943, 117285.739),
70+
(63, 16.50, SWEREF99_15_45, 6988606.198, 188003.980),
71+
(65, 16.50, SWEREF99_15_45, 7211548.993, 185380.999),
72+
(67, 16.50, SWEREF99_15_45, 7434550.943, 182714.261),
73+
# SWEREF 99 SWEREF 99 16 30
74+
(57, 15.75, SWEREF99_16_30, 6320164.077, 104421.390),
75+
(59, 15.75, SWEREF99_16_30, 6542910.921, 106894.103),
76+
(61, 15.75, SWEREF99_16_30, 6765725.847, 109420.005),
77+
(63, 15.75, SWEREF99_16_30, 6988606.198, 111996.020),
78+
(57, 17.25, SWEREF99_16_30, 6320164.077, 195578.610),
79+
(59, 17.25, SWEREF99_16_30, 6542910.921, 193105.897),
80+
(61, 17.25, SWEREF99_16_30, 6765725.847, 190579.995),
81+
(63, 17.25, SWEREF99_16_30, 6988606.198, 188003.980),
82+
# SWEREF 99 SWEREF 99 17 15
83+
(63, 16.50, SWEREF99_17_15, 6988606.198, 111996.020),
84+
(65, 16.50, SWEREF99_17_15, 7211548.993, 114619.001),
85+
(67, 16.50, SWEREF99_17_15, 7434550.943, 117285.739),
86+
(63, 18.00, SWEREF99_17_15, 6988606.198, 188003.980),
87+
(65, 18.00, SWEREF99_17_15, 7211548.993, 185380.999),
88+
(67, 18.00, SWEREF99_17_15, 7434550.943, 182714.261),
89+
# SWEREF 99 SWEREF 99 18 00
90+
(59, 17.25, SWEREF99_18_00, 6542910.921, 106894.103),
91+
(61, 17.25, SWEREF99_18_00, 6765725.847, 109420.005),
92+
(59, 18.75, SWEREF99_18_00, 6542910.921, 193105.897),
93+
(61, 18.75, SWEREF99_18_00, 6765725.847, 190579.995),
94+
# SWEREF 99 SWEREF 99 18 45
95+
(57, 18, SWEREF99_18_45, 6320164.077, 104421.390),
96+
(59, 18, SWEREF99_18_45, 6542910.921, 106894.103),
97+
(63, 18, SWEREF99_18_45, 6988606.198, 111996.020),
98+
(65, 18, SWEREF99_18_45, 7211548.993, 114619.001),
99+
(67, 18, SWEREF99_18_45, 7434550.943, 117285.739),
100+
(57, 19.5, SWEREF99_18_45, 6320164.077, 195578.610),
101+
(59, 19.5, SWEREF99_18_45, 6542910.921, 193105.897),
102+
(63, 19.5, SWEREF99_18_45, 6988606.198, 188003.980),
103+
(65, 19.5, SWEREF99_18_45, 7211548.993, 185380.999),
104+
(67, 19.5, SWEREF99_18_45, 7434550.943, 182714.261),
105+
# SWEREF 99 SWEREF 99 20 15
106+
(63, 19.5, SWEREF99_20_15, 6988606.198, 111996.020),
107+
(65, 19.5, SWEREF99_20_15, 7211548.993, 114619.001),
108+
(67, 19.5, SWEREF99_20_15, 7434550.943, 117285.739),
109+
(69, 19.5, SWEREF99_20_15, 7657608.465, 119992.964),
110+
(65, 21, SWEREF99_20_15, 7211548.993, 185380.999),
111+
(67, 21, SWEREF99_20_15, 7434550.943, 182714.261),
112+
(69, 21, SWEREF99_20_15, 7657608.465, 180007.036),
113+
# SWEREF 99 SWEREF 99 21 45
114+
(65, 21, SWEREF99_21_45, 7211548.993, 114619.001),
115+
(67, 21, SWEREF99_21_45, 7434550.943, 117285.739),
116+
(65, 22.5, SWEREF99_21_45, 7211548.993, 185380.999),
117+
(67, 22.5, SWEREF99_21_45, 7434550.943, 182714.261),
118+
# SWEREF 99 SWEREF 99 23 15
119+
(65, 22.5, SWEREF99_23_15, 7211548.993, 114619.001),
120+
(67, 22.5, SWEREF99_23_15, 7434550.943, 117285.739),
121+
(65, 24, SWEREF99_23_15, 7211548.993, 185380.999),
122+
(67, 24, SWEREF99_23_15, 7434550.943, 182714.261),
123+
),
124+
)
125+
def test_sweref99(latitude, longitude, to_srid, expected_northing, expected_easting):
126+
"""
127+
Test data above taken from:
128+
https://www.lantmateriet.se/contentassets/a7ddfc3b7821498da8b55cd3f71b5150/kontrollpunkter_sweref99tm.pdf
129+
https://www.lantmateriet.se/contentassets/a7ddfc3b7821498da8b55cd3f71b5150/kontrollpunkter_sweref99proj.pdf
130+
"""
131+
projector = Projector()
132+
easting, northing = projector.transform(from_srid=WGS84, to_srid=to_srid, east=longitude, north=latitude)
133+
assert easting == pytest.approx(expected_easting)
134+
assert northing == pytest.approx(expected_northing)

0 commit comments

Comments
 (0)