duva 0.1.0__py3-none-any.whl

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.
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
duva/RegSO_2025.gpkg ADDED
Binary file
duva/__init__.py ADDED
@@ -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,11 @@
1
+ duva/Kommun_Sweref99TM.DAT,sha256=VzBbh1Sz4w-ndeejhU47o2HWihDyibApC0FEv-Zhx20,5898
2
+ duva/Kommun_Sweref99TM.ID,sha256=d7-Em-UYu4Pb8IAj83tlYB1wcIHIzNTQRrSjMWMtGRM,1160
3
+ duva/Kommun_Sweref99TM.IND,sha256=wqTDSTNVNo3Y8so69kTggvRqYDRATDzupKM8M2S7fsA,14336
4
+ duva/Kommun_Sweref99TM.MAP,sha256=987nFltE4tMe1bho2PriFMe-kE3F1GzhHDPmVOzg3aQ,119808
5
+ duva/Kommun_Sweref99TM.TAB,sha256=uK7sGxeh0KZF1LqQvLuL0tOjPLH8b3ZN5j_dtb3UXHA,379
6
+ duva/RegSO_2025.gpkg,sha256=Y8JrmzRCPinM9ZRGBQYQove5akbY1vo8FO0CZV0TXJw,33775616
7
+ duva/__init__.py,sha256=1Gfm4Tt2QmMdJEFmzu0d6A73ZOHjJnghfhj5Ox90-WE,7997
8
+ duva-0.1.0.dist-info/METADATA,sha256=bwwEtFJ5pcp3ryAHimtAshfdd8vCz3ua6NDebXlv7AU,104
9
+ duva-0.1.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
10
+ duva-0.1.0.dist-info/top_level.txt,sha256=B-Qs16mbm9sV31q-mamZ4ol93NnUDvXIRPr78iLpbjQ,5
11
+ duva-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (82.0.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1 @@
1
+ duva