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.
- duva/Kommun_Sweref99TM.DAT +0 -0
- duva/Kommun_Sweref99TM.ID +0 -0
- duva/Kommun_Sweref99TM.IND +0 -0
- duva/Kommun_Sweref99TM.MAP +0 -0
- duva/Kommun_Sweref99TM.TAB +15 -0
- duva/RegSO_2025.gpkg +0 -0
- duva/__init__.py +222 -0
- duva-0.1.0.dist-info/METADATA +5 -0
- duva-0.1.0.dist-info/RECORD +11 -0
- duva-0.1.0.dist-info/WHEEL +5 -0
- duva-0.1.0.dist-info/top_level.txt +1 -0
|
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,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 @@
|
|
|
1
|
+
duva
|