duva 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
duva-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,5 @@
1
+ Metadata-Version: 2.4
2
+ Name: duva
3
+ Version: 0.1.0
4
+ Requires-Python: >=3.12
5
+ Requires-Dist: geopandas>=1.0.0
duva-0.1.0/README.md ADDED
@@ -0,0 +1,39 @@
1
+ # duva 🕊️
2
+
3
+ Reverse geocode Swedish coordinates to RegSO areas.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ pip install duva
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ```python
14
+ from duva import duva, duva_many, duva_df, duva_from_kod
15
+
16
+ # Single coordinate (WGS84)
17
+ duva(x=17.88, y=59.35)
18
+ # {'regsonamn': 'Blackeberg', 'kommunnamn': 'Stockholm', 'lansnamn': 'Stockholms län', ...}
19
+
20
+ # As object
21
+ result = duva(x=17.88, y=59.35, as_object=True)
22
+ result.regsonamn # 'Blackeberg'
23
+
24
+ # List of coordinates
25
+ duva_many([(17.88, 59.35), (18.07, 59.28)])
26
+
27
+ # Enrich a Polars DataFrame
28
+ duva_df(df, x_col="lon", y_col="lat")
29
+
30
+ # Lookup by RegSO code
31
+ duva_from_kod("0180R009")
32
+ ```
33
+
34
+ ## Notes
35
+
36
+ - Input coordinates must be WGS84 (standard GPS)
37
+ - Raises `ValueError` for invalid coordinates or coordinates outside Sweden
38
+ - Returns `not_on_land=True` for water areas, `is_baltic=True` for international water
39
+ - Based on SCB's RegSO 2025 and municipality boundaries
Binary file
Binary file
Binary file
Binary file
@@ -0,0 +1,15 @@
1
+ !table
2
+ !version 300
3
+ !charset WindowsLatin1
4
+
5
+ Definition Table
6
+ Type NATIVE Charset "WindowsLatin1"
7
+ Fields 2
8
+ KnKod Char (4) Index 1 ;
9
+ KnNamn Char (15) Index 2 ;
10
+ begin_metadata
11
+ "\MapInfo" = ""
12
+ "\MapInfo\TableID" = "6b951346-0ed0-4fdf-9893-79398f968eb8"
13
+ "\MapInfo\ParentTableID" = "5505f6c7-db00-4b1a-a344-db38bbddbb57"
14
+ "\IsReadOnly" = "FALSE"
15
+ end_metadata
Binary file
@@ -0,0 +1,222 @@
1
+ from pathlib import Path
2
+
3
+ import geopandas as gpd
4
+ import polars as pl
5
+ from shapely.geometry import Point
6
+
7
+ _GPKG_PATH = Path(__file__).parent / "RegSO_2025.gpkg"
8
+ _KOMMUN_PATH = Path(__file__).parent / "Kommun_Sweref99TM.TAB"
9
+
10
+ _GDF: gpd.GeoDataFrame = gpd.read_file(_GPKG_PATH).drop(columns=["objectid", "geometry"])
11
+ _GDF_GEO: gpd.GeoDataFrame = gpd.read_file(_GPKG_PATH)[["regsokod", "geometry"]]
12
+ _KOMMUN_GEO: gpd.GeoDataFrame = gpd.read_file(_KOMMUN_PATH)[["KnKod", "KnNamn", "geometry"]].set_crs("EPSG:3006", allow_override=True)
13
+ _KOD_INDEX: dict[str, dict] = {
14
+ row["regsokod"]: row
15
+ for row in _GDF.to_dict(orient="records")
16
+ }
17
+
18
+ _LAN: dict[str, str] = {
19
+ "01": "Stockholms län",
20
+ "03": "Uppsala län",
21
+ "04": "Södermanlands län",
22
+ "05": "Östergötlands län",
23
+ "06": "Jönköpings län",
24
+ "07": "Kronobergs län",
25
+ "08": "Kalmar län",
26
+ "09": "Gotlands län",
27
+ "10": "Blekinge län",
28
+ "12": "Skåne län",
29
+ "13": "Hallands län",
30
+ "14": "Västra Götalands län",
31
+ "17": "Värmlands län",
32
+ "18": "Örebro län",
33
+ "19": "Västmanlands län",
34
+ "20": "Dalarnas län",
35
+ "21": "Gävleborgs län",
36
+ "22": "Västernorrlands län",
37
+ "23": "Jämtlands län",
38
+ "24": "Västerbottens län",
39
+ "25": "Norrbottens län",
40
+ }
41
+
42
+ _LON_MIN, _LAT_MIN, _LON_MAX, _LAT_MAX = 10.59, 55.13, 24.18, 69.06
43
+
44
+
45
+ class RegSO:
46
+ def __init__(
47
+ self,
48
+ not_on_land: bool,
49
+ is_baltic: bool = False,
50
+ objektidentitet: str | None = None,
51
+ objekttyp: str | None = None,
52
+ regsokod: str | None = None,
53
+ regsonamn: str | None = None,
54
+ lanskod: str | None = None,
55
+ lansnamn: str | None = None,
56
+ kommunkod: str | None = None,
57
+ kommunnamn: str | None = None,
58
+ version: str | None = None,
59
+ ansvarig_organisation: str | None = None,
60
+ referensdatum: str | None = None,
61
+ ) -> None:
62
+ self.not_on_land = not_on_land
63
+ self.is_baltic = is_baltic
64
+ self.objektidentitet = objektidentitet
65
+ self.objekttyp = objekttyp
66
+ self.regsokod = regsokod
67
+ self.regsonamn = regsonamn
68
+ self.lanskod = lanskod
69
+ self.lansnamn = lansnamn
70
+ self.kommunkod = kommunkod
71
+ self.kommunnamn = kommunnamn
72
+ self.version = version
73
+ self.ansvarig_organisation = ansvarig_organisation
74
+ self.referensdatum = referensdatum
75
+
76
+ def __repr__(self) -> str:
77
+ return (
78
+ f"RegSO(regsokod={self.regsokod!r}, regsonamn={self.regsonamn!r}, "
79
+ f"kommunkod={self.kommunkod!r}, kommunnamn={self.kommunnamn!r}, "
80
+ f"lansnamn={self.lansnamn!r}, not_on_land={self.not_on_land}, "
81
+ f"is_baltic={self.is_baltic})"
82
+ )
83
+
84
+
85
+ def _validate(x: float, y: float) -> None:
86
+ if not (-180 <= x <= 180):
87
+ raise ValueError(f"Invalid longitude: {x}. Must be between -180 and 180.")
88
+ if not (-90 <= y <= 90):
89
+ raise ValueError(f"Invalid latitude: {y}. Must be between -90 and 90.")
90
+ if not (_LON_MIN <= x <= _LON_MAX and _LAT_MIN <= y <= _LAT_MAX):
91
+ raise ValueError(f"Coordinates ({x}, {y}) are outside Sweden.")
92
+
93
+
94
+ def _lookup_kommun(point_3006: gpd.GeoDataFrame) -> tuple[str | None, str | None]:
95
+ match = _KOMMUN_GEO[_KOMMUN_GEO.contains(point_3006.geometry.iloc[0])]
96
+ if match.empty:
97
+ return None, None
98
+ return match.iloc[0]["KnKod"], match.iloc[0]["KnNamn"]
99
+
100
+
101
+ def _lookup_point(x: float, y: float) -> dict:
102
+ point = gpd.GeoDataFrame(
103
+ geometry=[Point(x, y)],
104
+ crs="EPSG:4326",
105
+ ).to_crs("EPSG:3006")
106
+
107
+ match = _GDF_GEO[_GDF_GEO.contains(point.geometry.iloc[0])]
108
+
109
+ if match.empty:
110
+ kommunkod, kommunnamn = _lookup_kommun(point)
111
+ return {
112
+ "not_on_land": True,
113
+ "is_baltic": kommunkod is None,
114
+ "kommunkod": kommunkod,
115
+ "kommunnamn": kommunnamn,
116
+ }
117
+
118
+ row = dict(_KOD_INDEX[match.iloc[0]["regsokod"]])
119
+ km = _KOMMUN_GEO[_KOMMUN_GEO["KnKod"] == row["kommunkod"]]
120
+ row["kommunnamn"] = km.iloc[0]["KnNamn"] if not km.empty else None
121
+ row["lansnamn"] = _LAN.get(row["lanskod"])
122
+ row["not_on_land"] = False
123
+ row["is_baltic"] = False
124
+ return row
125
+
126
+
127
+ def duva(x: float, y: float, as_object: bool = False) -> dict | RegSO:
128
+ _validate(x, y)
129
+ row = _lookup_point(x, y)
130
+ return RegSO(**row) if as_object else row
131
+
132
+
133
+ def duva_many(
134
+ coords: list[tuple[float, float]],
135
+ as_object: bool = False,
136
+ ) -> list[dict | RegSO]:
137
+ results = []
138
+ for x, y in coords:
139
+ try:
140
+ _validate(x, y)
141
+ row = _lookup_point(x, y)
142
+ except ValueError as e:
143
+ row = {"error": str(e), "not_on_land": None}
144
+ results.append(RegSO(**row) if as_object else row)
145
+ return results
146
+
147
+
148
+ def duva_df(
149
+ df: pl.DataFrame,
150
+ x_col: str = "lon",
151
+ y_col: str = "lat",
152
+ ) -> pl.DataFrame:
153
+ pdf = df.to_pandas()
154
+ valid = pdf[x_col].notna() & pdf[y_col].notna()
155
+
156
+ gdf = gpd.GeoDataFrame(
157
+ pdf[valid][[x_col, y_col]].copy(),
158
+ geometry=gpd.points_from_xy(
159
+ pdf.loc[valid, x_col].astype(float),
160
+ pdf.loc[valid, y_col].astype(float),
161
+ ),
162
+ crs="EPSG:4326",
163
+ ).to_crs("EPSG:3006")
164
+
165
+ joined_regso = gpd.sjoin(gdf, _GDF_GEO, how="left", predicate="within")
166
+ joined_kommun = gpd.sjoin(gdf, _KOMMUN_GEO, how="left", predicate="within")
167
+
168
+ regsokod_list: list[str | None] = [None] * len(pdf)
169
+ regsonamn_list: list[str | None] = [None] * len(pdf)
170
+ kommunkod_list: list[str | None] = [None] * len(pdf)
171
+ kommunnamn_list: list[str | None] = [None] * len(pdf)
172
+ lanskod_list: list[str | None] = [None] * len(pdf)
173
+ lansnamn_list: list[str | None] = [None] * len(pdf)
174
+ not_on_land_list: list[bool] = [False] * len(pdf)
175
+ is_baltic_list: list[bool] = [False] * len(pdf)
176
+
177
+ for idx in pdf[valid].index:
178
+ regso_match = joined_regso[joined_regso.index == idx]
179
+ kommun_match = joined_kommun[joined_kommun.index == idx]
180
+
181
+ kod = regso_match["regsokod"].iloc[0] if len(regso_match) else None
182
+ if kod and not isinstance(kod, float):
183
+ meta = _KOD_INDEX.get(kod, {})
184
+ regsokod_list[idx] = kod
185
+ regsonamn_list[idx] = meta.get("regsonamn")
186
+ lanskod_list[idx] = meta.get("lanskod")
187
+ lansnamn_list[idx] = _LAN.get(meta.get("lanskod", ""))
188
+ kommunkod_list[idx] = meta.get("kommunkod")
189
+ km = _KOMMUN_GEO[_KOMMUN_GEO["KnKod"] == kommunkod_list[idx]]
190
+ kommunnamn_list[idx] = km.iloc[0]["KnNamn"] if not km.empty else None
191
+ else:
192
+ not_on_land_list[idx] = True
193
+ if len(kommun_match):
194
+ kk = kommun_match["KnKod"].iloc[0]
195
+ kn = kommun_match["KnNamn"].iloc[0]
196
+ kommunkod_list[idx] = None if isinstance(kk, float) else kk
197
+ kommunnamn_list[idx] = None if isinstance(kn, float) else kn
198
+ is_baltic_list[idx] = kommunkod_list[idx] is None
199
+
200
+ return df.with_columns([
201
+ pl.Series("regsokod", regsokod_list, dtype=pl.String),
202
+ pl.Series("regsonamn", regsonamn_list, dtype=pl.String),
203
+ pl.Series("kommunkod", kommunkod_list, dtype=pl.String),
204
+ pl.Series("kommunnamn", kommunnamn_list, dtype=pl.String),
205
+ pl.Series("lanskod", lanskod_list, dtype=pl.String),
206
+ pl.Series("lansnamn", lansnamn_list, dtype=pl.String),
207
+ pl.Series("not_on_land", not_on_land_list, dtype=pl.Boolean),
208
+ pl.Series("is_baltic", is_baltic_list, dtype=pl.Boolean),
209
+ ])
210
+
211
+
212
+ def duva_from_kod(kod: str, as_object: bool = False) -> dict | RegSO | None:
213
+ row = _KOD_INDEX.get(kod)
214
+ if row is None:
215
+ return None
216
+ row = dict(row)
217
+ km = _KOMMUN_GEO[_KOMMUN_GEO["KnKod"] == row["kommunkod"]]
218
+ row["kommunnamn"] = km.iloc[0]["KnNamn"] if not km.empty else None
219
+ row["lansnamn"] = _LAN.get(row["lanskod"])
220
+ row["not_on_land"] = False
221
+ row["is_baltic"] = False
222
+ return RegSO(**row) if as_object else row
@@ -0,0 +1,5 @@
1
+ Metadata-Version: 2.4
2
+ Name: duva
3
+ Version: 0.1.0
4
+ Requires-Python: >=3.12
5
+ Requires-Dist: geopandas>=1.0.0
@@ -0,0 +1,18 @@
1
+ README.md
2
+ pyproject.toml
3
+ duva/Kommun_Sweref99TM.DAT
4
+ duva/Kommun_Sweref99TM.ID
5
+ duva/Kommun_Sweref99TM.IND
6
+ duva/Kommun_Sweref99TM.MAP
7
+ duva/Kommun_Sweref99TM.TAB
8
+ duva/RegSO_2025.gpkg
9
+ duva/__init__.py
10
+ duva.egg-info/PKG-INFO
11
+ duva.egg-info/SOURCES.txt
12
+ duva.egg-info/dependency_links.txt
13
+ duva.egg-info/requires.txt
14
+ duva.egg-info/top_level.txt
15
+ tests/test_duva.py
16
+ tests/test_duva_df.py
17
+ tests/test_duva_from_kod.py
18
+ tests/test_duva_many.py
@@ -0,0 +1 @@
1
+ geopandas>=1.0.0
@@ -0,0 +1 @@
1
+ duva
@@ -0,0 +1,21 @@
1
+ [project]
2
+ name = "duva"
3
+ version = "0.1.0"
4
+ requires-python = ">=3.12"
5
+ dependencies = [
6
+ "geopandas>=1.0.0",
7
+ ]
8
+
9
+ [build-system]
10
+ requires = ["setuptools>=68"]
11
+ build-backend = "setuptools.build_meta"
12
+
13
+ [tool.setuptools.packages.find]
14
+ where = ["."]
15
+ include = ["duva*"]
16
+
17
+ [tool.setuptools.package-data]
18
+ duva = ["*.gpkg", "*.TAB", "*.DAT", "*.ID", "*.IND", "*.MAP"]
19
+
20
+ [tool.pytest.ini_options]
21
+ testpaths = ["tests"]
duva-0.1.0/setup.cfg ADDED
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,84 @@
1
+ import pytest
2
+ from duva import duva, RegSO
3
+
4
+ BLACKEBERG = (17.88, 59.35)
5
+ GAMLA_ENSKEDE = (18.07, 59.28)
6
+ BALTIC = (20.5, 57.5)
7
+
8
+
9
+ def test_returns_dict_by_default():
10
+ result = duva(*BLACKEBERG)
11
+ assert isinstance(result, dict)
12
+
13
+
14
+ def test_returns_regso_object():
15
+ result = duva(*BLACKEBERG, as_object=True)
16
+ assert isinstance(result, RegSO)
17
+
18
+
19
+ def test_correct_regsonamn():
20
+ result = duva(*BLACKEBERG)
21
+ assert result["regsonamn"] == "Blackeberg"
22
+
23
+
24
+ def test_correct_kommunkod():
25
+ result = duva(*BLACKEBERG)
26
+ assert result["kommunkod"] == "0180"
27
+
28
+
29
+ def test_correct_kommunnamn():
30
+ result = duva(*BLACKEBERG)
31
+ assert result["kommunnamn"] == "Stockholm"
32
+
33
+
34
+ def test_correct_lansnamn():
35
+ result = duva(*BLACKEBERG)
36
+ assert result["lansnamn"] == "Stockholms län"
37
+
38
+
39
+ def test_not_on_land_false_for_land():
40
+ result = duva(*BLACKEBERG)
41
+ assert result["not_on_land"] is False
42
+
43
+
44
+ def test_is_baltic_false_for_land():
45
+ result = duva(*BLACKEBERG)
46
+ assert result["is_baltic"] is False
47
+
48
+
49
+ def test_not_on_land_true_for_water():
50
+ result = duva(*BALTIC)
51
+ assert result["not_on_land"] is True
52
+
53
+
54
+ def test_is_baltic_true_for_baltic():
55
+ result = duva(*BALTIC)
56
+ assert result["is_baltic"] is True
57
+
58
+
59
+ def test_water_has_no_regsonamn():
60
+ result = duva(*BALTIC)
61
+ assert result.get("regsonamn") is None
62
+
63
+
64
+ def test_object_attributes_match_dict():
65
+ d = duva(*GAMLA_ENSKEDE)
66
+ obj = duva(*GAMLA_ENSKEDE, as_object=True)
67
+ assert obj.regsonamn == d["regsonamn"]
68
+ assert obj.kommunkod == d["kommunkod"]
69
+ assert obj.lansnamn == d["lansnamn"]
70
+
71
+
72
+ def test_invalid_longitude_raises():
73
+ with pytest.raises(ValueError, match="longitude"):
74
+ duva(x=200.0, y=59.0)
75
+
76
+
77
+ def test_invalid_latitude_raises():
78
+ with pytest.raises(ValueError, match="latitude"):
79
+ duva(x=17.0, y=100.0)
80
+
81
+
82
+ def test_outside_sweden_raises():
83
+ with pytest.raises(ValueError, match="outside Sweden"):
84
+ duva(x=10.0, y=50.0)
@@ -0,0 +1,92 @@
1
+ import polars as pl
2
+ import pytest
3
+ from duva import duva_df
4
+
5
+
6
+ @pytest.fixture
7
+ def sample_df() -> pl.DataFrame:
8
+ return pl.DataFrame({
9
+ "lon": [17.88, 20.5, 18.07, None],
10
+ "lat": [59.35, 57.5, 59.28, None],
11
+ })
12
+
13
+
14
+ def test_returns_polars_dataframe(sample_df: pl.DataFrame):
15
+ result = duva_df(sample_df)
16
+ assert isinstance(result, pl.DataFrame)
17
+
18
+
19
+ def test_adds_regsonamn_column(sample_df: pl.DataFrame):
20
+ result = duva_df(sample_df)
21
+ assert "regsonamn" in result.columns
22
+
23
+
24
+ def test_adds_kommunkod_column(sample_df: pl.DataFrame):
25
+ result = duva_df(sample_df)
26
+ assert "kommunkod" in result.columns
27
+
28
+
29
+ def test_adds_kommunnamn_column(sample_df: pl.DataFrame):
30
+ result = duva_df(sample_df)
31
+ assert "kommunnamn" in result.columns
32
+
33
+
34
+ def test_adds_lanskod_column(sample_df: pl.DataFrame):
35
+ result = duva_df(sample_df)
36
+ assert "lanskod" in result.columns
37
+
38
+
39
+ def test_adds_lansnamn_column(sample_df: pl.DataFrame):
40
+ result = duva_df(sample_df)
41
+ assert "lansnamn" in result.columns
42
+
43
+
44
+ def test_adds_not_on_land_column(sample_df: pl.DataFrame):
45
+ result = duva_df(sample_df)
46
+ assert "not_on_land" in result.columns
47
+
48
+
49
+ def test_adds_is_baltic_column(sample_df: pl.DataFrame):
50
+ result = duva_df(sample_df)
51
+ assert "is_baltic" in result.columns
52
+
53
+
54
+ def test_row_count_unchanged(sample_df: pl.DataFrame):
55
+ result = duva_df(sample_df)
56
+ assert len(result) == len(sample_df)
57
+
58
+
59
+ def test_correct_regsonamn_for_land(sample_df: pl.DataFrame):
60
+ result = duva_df(sample_df)
61
+ assert result["regsonamn"][0] == "Blackeberg"
62
+
63
+
64
+ def test_not_on_land_false_for_land(sample_df: pl.DataFrame):
65
+ result = duva_df(sample_df)
66
+ assert result["not_on_land"][0] is False
67
+
68
+
69
+ def test_not_on_land_true_for_baltic(sample_df: pl.DataFrame):
70
+ result = duva_df(sample_df)
71
+ assert result["not_on_land"][1] is True
72
+
73
+
74
+ def test_is_baltic_true_for_baltic(sample_df: pl.DataFrame):
75
+ result = duva_df(sample_df)
76
+ assert result["is_baltic"][1] is True
77
+
78
+
79
+ def test_null_coords_produce_null_regso(sample_df: pl.DataFrame):
80
+ result = duva_df(sample_df)
81
+ assert result["regsonamn"][3] is None
82
+
83
+
84
+ def test_custom_column_names():
85
+ df = pl.DataFrame({"x": [17.88], "y": [59.35]})
86
+ result = duva_df(df, x_col="x", y_col="y")
87
+ assert result["regsonamn"][0] == "Blackeberg"
88
+
89
+
90
+ def test_lansnamn_correct(sample_df: pl.DataFrame):
91
+ result = duva_df(sample_df)
92
+ assert result["lansnamn"][0] == "Stockholms län"
@@ -0,0 +1,62 @@
1
+ from duva import duva_from_kod, RegSO
2
+
3
+ BLACKEBERG_KOD = "0180R009"
4
+ INVALID_KOD = "INVALID"
5
+
6
+
7
+ def test_returns_dict_by_default():
8
+ result = duva_from_kod(BLACKEBERG_KOD)
9
+ assert isinstance(result, dict)
10
+
11
+
12
+ def test_returns_regso_object():
13
+ result = duva_from_kod(BLACKEBERG_KOD, as_object=True)
14
+ assert isinstance(result, RegSO)
15
+
16
+
17
+ def test_correct_regsonamn():
18
+ result = duva_from_kod(BLACKEBERG_KOD)
19
+ assert result["regsonamn"] == "Blackeberg"
20
+
21
+
22
+ def test_correct_kommunkod():
23
+ result = duva_from_kod(BLACKEBERG_KOD)
24
+ assert result["kommunkod"] == "0180"
25
+
26
+
27
+ def test_correct_kommunnamn():
28
+ result = duva_from_kod(BLACKEBERG_KOD)
29
+ assert result["kommunnamn"] == "Stockholm"
30
+
31
+
32
+ def test_correct_lansnamn():
33
+ result = duva_from_kod(BLACKEBERG_KOD)
34
+ assert result["lansnamn"] == "Stockholms län"
35
+
36
+
37
+ def test_not_on_land_false():
38
+ result = duva_from_kod(BLACKEBERG_KOD)
39
+ assert result["not_on_land"] is False
40
+
41
+
42
+ def test_is_baltic_false():
43
+ result = duva_from_kod(BLACKEBERG_KOD)
44
+ assert result["is_baltic"] is False
45
+
46
+
47
+ def test_invalid_kod_returns_none():
48
+ result = duva_from_kod(INVALID_KOD)
49
+ assert result is None
50
+
51
+
52
+ def test_invalid_kod_as_object_returns_none():
53
+ result = duva_from_kod(INVALID_KOD, as_object=True)
54
+ assert result is None
55
+
56
+
57
+ def test_object_attributes_match_dict():
58
+ d = duva_from_kod(BLACKEBERG_KOD)
59
+ obj = duva_from_kod(BLACKEBERG_KOD, as_object=True)
60
+ assert obj.regsonamn == d["regsonamn"]
61
+ assert obj.kommunkod == d["kommunkod"]
62
+ assert obj.lansnamn == d["lansnamn"]
@@ -0,0 +1,64 @@
1
+ from duva import duva_many, RegSO
2
+
3
+ BLACKEBERG = (17.88, 59.35)
4
+ BALTIC = (20.5, 57.5)
5
+ OUTSIDE_SWEDEN = (10.0, 50.0)
6
+ INVALID_LON = (200.0, 59.0)
7
+
8
+
9
+ def test_returns_list():
10
+ result = duva_many([BLACKEBERG])
11
+ assert isinstance(result, list)
12
+
13
+
14
+ def test_returns_correct_length():
15
+ coords = [BLACKEBERG, BALTIC, OUTSIDE_SWEDEN]
16
+ result = duva_many(coords)
17
+ assert len(result) == 3
18
+
19
+
20
+ def test_returns_dicts_by_default():
21
+ result = duva_many([BLACKEBERG, BALTIC])
22
+ assert all(isinstance(r, dict) for r in result)
23
+
24
+
25
+ def test_returns_regso_objects():
26
+ result = duva_many([BLACKEBERG, BALTIC], as_object=True)
27
+ assert all(isinstance(r, RegSO) for r in result)
28
+
29
+
30
+ def test_correct_result_for_land():
31
+ result = duva_many([BLACKEBERG])
32
+ assert result[0]["regsonamn"] == "Blackeberg"
33
+
34
+
35
+ def test_water_returns_not_on_land():
36
+ result = duva_many([BALTIC])
37
+ assert result[0]["not_on_land"] is True
38
+
39
+
40
+ def test_invalid_coords_returns_error_key():
41
+ result = duva_many([OUTSIDE_SWEDEN])
42
+ assert "error" in result[0]
43
+
44
+
45
+ def test_invalid_coords_not_on_land_is_none():
46
+ result = duva_many([OUTSIDE_SWEDEN])
47
+ assert result[0]["not_on_land"] is None
48
+
49
+
50
+ def test_mixed_coords():
51
+ result = duva_many([BLACKEBERG, BALTIC, OUTSIDE_SWEDEN])
52
+ assert result[0]["regsonamn"] == "Blackeberg"
53
+ assert result[1]["not_on_land"] is True
54
+ assert "error" in result[2]
55
+
56
+
57
+ def test_empty_list():
58
+ result = duva_many([])
59
+ assert result == []
60
+
61
+
62
+ def test_invalid_lon_returns_error():
63
+ result = duva_many([INVALID_LON])
64
+ assert "error" in result[0]