toolsos 0.2.7__tar.gz → 0.3.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.
- {toolsos-0.2.7 → toolsos-0.3.0}/PKG-INFO +2 -1
- {toolsos-0.2.7 → toolsos-0.3.0}/pyproject.toml +2 -1
- {toolsos-0.2.7 → toolsos-0.3.0}/src/toolsos/geo.py +35 -0
- {toolsos-0.2.7 → toolsos-0.3.0}/src/toolsos/helpers.py +18 -5
- toolsos-0.3.0/src/toolsos/huisstijl/maps/choropleth.py +72 -0
- {toolsos-0.2.7 → toolsos-0.3.0}/src/toolsos/huisstijl/tables/tables.py +9 -1
- {toolsos-0.2.7 → toolsos-0.3.0}/src/toolsos.egg-info/PKG-INFO +2 -1
- {toolsos-0.2.7 → toolsos-0.3.0}/src/toolsos.egg-info/SOURCES.txt +3 -1
- {toolsos-0.2.7 → toolsos-0.3.0}/src/toolsos.egg-info/requires.txt +1 -0
- toolsos-0.3.0/tests/test_tables.py +56 -0
- {toolsos-0.2.7 → toolsos-0.3.0}/README.md +0 -0
- {toolsos-0.2.7 → toolsos-0.3.0}/setup.cfg +0 -0
- {toolsos-0.2.7 → toolsos-0.3.0}/src/toolsos/__init__.py +0 -0
- {toolsos-0.2.7 → toolsos-0.3.0}/src/toolsos/cbs_tools.py +0 -0
- {toolsos-0.2.7 → toolsos-0.3.0}/src/toolsos/create_tables.py +0 -0
- {toolsos-0.2.7 → toolsos-0.3.0}/src/toolsos/database/database_connection.py +0 -0
- {toolsos-0.2.7 → toolsos-0.3.0}/src/toolsos/database/database_transfer.py +0 -0
- {toolsos-0.2.7 → toolsos-0.3.0}/src/toolsos/download.py +0 -0
- {toolsos-0.2.7 → toolsos-0.3.0}/src/toolsos/huisstijl/__init__.py +0 -0
- {toolsos-0.2.7 → toolsos-0.3.0}/src/toolsos/huisstijl/colors.py +0 -0
- {toolsos-0.2.7 → toolsos-0.3.0}/src/toolsos/huisstijl/graphs/__init__.py +0 -0
- {toolsos-0.2.7 → toolsos-0.3.0}/src/toolsos/huisstijl/graphs/bargraph.py +0 -0
- {toolsos-0.2.7 → toolsos-0.3.0}/src/toolsos/huisstijl/graphs/graph_styles.py +0 -0
- {toolsos-0.2.7 → toolsos-0.3.0}/src/toolsos/huisstijl/graphs/linegraph.py +0 -0
- {toolsos-0.2.7 → toolsos-0.3.0}/src/toolsos/huisstijl/graphs/piegraph.py +0 -0
- {toolsos-0.2.7 → toolsos-0.3.0}/src/toolsos/huisstijl/graphs/styler.py +0 -0
- {toolsos-0.2.7 → toolsos-0.3.0}/src/toolsos/huisstijl/maps/__init__.py +0 -0
- {toolsos-0.2.7 → toolsos-0.3.0}/src/toolsos/huisstijl/tables/__init__.py +0 -0
- {toolsos-0.2.7 → toolsos-0.3.0}/src/toolsos/huisstijl/tables/table_helpers.py +0 -0
- {toolsos-0.2.7 → toolsos-0.3.0}/src/toolsos/huisstijl/tables/table_styles.py +0 -0
- {toolsos-0.2.7 → toolsos-0.3.0}/src/toolsos/polars_helpers.py +0 -0
- {toolsos-0.2.7 → toolsos-0.3.0}/src/toolsos.egg-info/dependency_links.txt +0 -0
- {toolsos-0.2.7 → toolsos-0.3.0}/src/toolsos.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.2
|
|
2
2
|
Name: toolsos
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: OS tools
|
|
5
5
|
Author-email: OS <d.schmitz@amsterdam.nl>
|
|
6
6
|
Keywords: tools,Onderzoek & Statistiek
|
|
@@ -26,6 +26,7 @@ Requires-Dist: pyreadstat; extra == "all"
|
|
|
26
26
|
Requires-Dist: pyyaml; extra == "all"
|
|
27
27
|
Requires-Dist: requests; extra == "all"
|
|
28
28
|
Requires-Dist: sqlalchemy; extra == "all"
|
|
29
|
+
Requires-Dist: geopandas; extra == "all"
|
|
29
30
|
|
|
30
31
|
# Tools Onderzoek & Statistiek
|
|
31
32
|
|
|
@@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta"
|
|
|
6
6
|
|
|
7
7
|
[project]
|
|
8
8
|
name = "toolsos"
|
|
9
|
-
version = "0.
|
|
9
|
+
version = "0.3.0"
|
|
10
10
|
description = "OS tools"
|
|
11
11
|
readme = "README.md"
|
|
12
12
|
authors = [{ name = "OS", email = "d.schmitz@amsterdam.nl" }]
|
|
@@ -47,6 +47,7 @@ all = [
|
|
|
47
47
|
"pyyaml",
|
|
48
48
|
"requests",
|
|
49
49
|
"sqlalchemy",
|
|
50
|
+
"geopandas"
|
|
50
51
|
]
|
|
51
52
|
|
|
52
53
|
# [project.urls]
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
from pathlib import Path
|
|
3
4
|
from typing import Any, Union
|
|
4
5
|
|
|
6
|
+
import geopandas as gpd
|
|
7
|
+
import pandas as pd
|
|
5
8
|
import requests
|
|
6
9
|
|
|
7
10
|
|
|
@@ -43,6 +46,38 @@ def get_geo_json(
|
|
|
43
46
|
return json
|
|
44
47
|
|
|
45
48
|
|
|
49
|
+
def get_geo_dataframe(
|
|
50
|
+
level: str,
|
|
51
|
+
year: Union[int, Any],
|
|
52
|
+
with_water: bool = False,
|
|
53
|
+
mra: bool = False,
|
|
54
|
+
from_crs=4326,
|
|
55
|
+
to_crs=28992,
|
|
56
|
+
):
|
|
57
|
+
|
|
58
|
+
gdf = gpd.GeoDataFrame.from_features(
|
|
59
|
+
get_geo_json(level=level, year=year, with_water=with_water, mra=mra)
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
gdf = gdf.set_crs(from_crs).to_crs(to_crs)
|
|
63
|
+
|
|
64
|
+
return gdf
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def merge_data_to_gdf(
|
|
68
|
+
data: str, gdf: gpd.GeoDataFrame, key_data: str = "code", key_gdf: str = "code"
|
|
69
|
+
) -> gpd.GeoDataFrame:
|
|
70
|
+
p = Path(data)
|
|
71
|
+
if p.suffix == ".csv":
|
|
72
|
+
df = pd.read_csv(data)
|
|
73
|
+
elif p.suffix in [".xlsx", ".xls"]:
|
|
74
|
+
df = pd.read_excel(data)
|
|
75
|
+
else:
|
|
76
|
+
raise ValueError(f"File type not supported: {p.suffix}")
|
|
77
|
+
|
|
78
|
+
return gdf.merge(df, how="left", left_on=key_data, right_on=key_gdf)
|
|
79
|
+
|
|
80
|
+
|
|
46
81
|
def extract_name_code_table(geo_json: dict[str, str]) -> dict[str, str]:
|
|
47
82
|
"""_summary_
|
|
48
83
|
|
|
@@ -24,16 +24,29 @@ def os_cut(
|
|
|
24
24
|
start_label: str = "lager dan",
|
|
25
25
|
end_label: str = "en hoger",
|
|
26
26
|
add_edge: Optional[int] = None,
|
|
27
|
-
sep: str = "
|
|
27
|
+
sep: str = " tot ",
|
|
28
|
+
na_value: Optional[str] = None,
|
|
29
|
+
unit: Optional[str] = None,
|
|
28
30
|
) -> pd.Series:
|
|
29
31
|
# Add non_overlap to left edge/boundary
|
|
30
32
|
if not add_edge:
|
|
31
33
|
add_edge = 0
|
|
32
34
|
|
|
33
|
-
|
|
35
|
+
if not unit:
|
|
36
|
+
unit = ""
|
|
37
|
+
|
|
38
|
+
start_l = [f"{start_label} {bins[1]}{unit}"]
|
|
34
39
|
inbetween_labels = [
|
|
35
|
-
f"{bins[i] + add_edge}{sep}{bins[i+1]}"
|
|
40
|
+
f"{bins[i] + add_edge}{unit}{sep}{bins[i+1]}{unit}"
|
|
41
|
+
for i in range(1, len(bins) - 2)
|
|
36
42
|
]
|
|
37
|
-
end_l = [f"{bins[-2]} {end_label}"]
|
|
43
|
+
end_l = [f"{bins[-2]}{unit} {end_label}"]
|
|
44
|
+
|
|
45
|
+
labels = start_l + inbetween_labels + end_l
|
|
46
|
+
|
|
47
|
+
categorized = pd.cut(x, bins=bins, labels=labels, include_lowest=True) # type: ignore
|
|
48
|
+
|
|
49
|
+
if na_value:
|
|
50
|
+
categorized = categorized.cat.add_categories(na_value).fillna(na_value)
|
|
38
51
|
|
|
39
|
-
return
|
|
52
|
+
return categorized
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
from typing import Any, Iterable, Optional
|
|
2
|
+
|
|
3
|
+
import geopandas as gpd
|
|
4
|
+
import matplotlib.pyplot as plt
|
|
5
|
+
from matplotlib.colors import ListedColormap
|
|
6
|
+
from matplotlib.patches import Patch
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def plot_choropleth(
|
|
10
|
+
gdf: gpd.GeoDataFrame,
|
|
11
|
+
column: str,
|
|
12
|
+
colors: Iterable[str],
|
|
13
|
+
column_label: Optional[str] = False,
|
|
14
|
+
column_label_kwargs: Any = None,
|
|
15
|
+
legend_title: Optional[str] = None,
|
|
16
|
+
edgecolor: str = "grey",
|
|
17
|
+
figsize: tuple[int, int] = (10, 8),
|
|
18
|
+
bbox_to_anchor: tuple[int, int] = (-0.05, 0),
|
|
19
|
+
):
|
|
20
|
+
# plotten
|
|
21
|
+
fig, ax = plt.subplots(figsize=figsize)
|
|
22
|
+
|
|
23
|
+
gdf.plot(
|
|
24
|
+
column=column,
|
|
25
|
+
cmap=ListedColormap(colors),
|
|
26
|
+
ax=ax,
|
|
27
|
+
categorical=True,
|
|
28
|
+
edgecolor=edgecolor,
|
|
29
|
+
linewidth=0.2,
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
labels = gdf["categorieen"].cat.categories
|
|
33
|
+
|
|
34
|
+
if not column_label_kwargs:
|
|
35
|
+
column_label_kwargs = {}
|
|
36
|
+
|
|
37
|
+
if column_label:
|
|
38
|
+
for x, y, label in zip(
|
|
39
|
+
gdf.geometry.representative_point().x,
|
|
40
|
+
gdf.geometry.representative_point().y,
|
|
41
|
+
gdf[column_label],
|
|
42
|
+
):
|
|
43
|
+
ax.annotate(
|
|
44
|
+
label,
|
|
45
|
+
xy=(x, y),
|
|
46
|
+
xytext=(0, 0),
|
|
47
|
+
textcoords="offset points",
|
|
48
|
+
**column_label_kwargs,
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
# Create custom legend using the 'colors' and 'labels' variables, with a grey border
|
|
52
|
+
legend_elements = [
|
|
53
|
+
Patch(facecolor=color, label=label, edgecolor="#b0b0b0")
|
|
54
|
+
for color, label in zip(colors, labels)
|
|
55
|
+
]
|
|
56
|
+
|
|
57
|
+
# Move the legend to the bottom left outside the plot
|
|
58
|
+
ax.legend(
|
|
59
|
+
handles=legend_elements,
|
|
60
|
+
bbox_to_anchor=bbox_to_anchor,
|
|
61
|
+
loc="lower left",
|
|
62
|
+
title=legend_title,
|
|
63
|
+
frameon=False,
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
# lat/lon uit
|
|
67
|
+
ax.set_axis_off()
|
|
68
|
+
|
|
69
|
+
# laten zien
|
|
70
|
+
plt.tight_layout()
|
|
71
|
+
|
|
72
|
+
return fig, ax
|
|
@@ -278,7 +278,14 @@ def get_cols_id_with_pattern(df: pd.DataFrame, pattern: str) -> list[int]:
|
|
|
278
278
|
Returns:
|
|
279
279
|
list[int]: list with column indices matching pattern
|
|
280
280
|
"""
|
|
281
|
-
|
|
281
|
+
|
|
282
|
+
if isinstance(df.columns, pd.MultiIndex):
|
|
283
|
+
# Use the lowest level in the multi-index column
|
|
284
|
+
return [
|
|
285
|
+
idx for idx, col in enumerate(df.columns) if re.findall(pattern, col[-1])
|
|
286
|
+
]
|
|
287
|
+
else:
|
|
288
|
+
return [idx for idx, col in enumerate(df.columns) if re.findall(pattern, col)]
|
|
282
289
|
|
|
283
290
|
|
|
284
291
|
def get_string_cols_ids(df: pd.DataFrame) -> list[int]:
|
|
@@ -702,6 +709,7 @@ def format_worksheet(
|
|
|
702
709
|
"The number of widths defined in column_widths should be equal to the number of columsn in the dataframe"
|
|
703
710
|
)
|
|
704
711
|
col_widths = column_widths
|
|
712
|
+
|
|
705
713
|
elif (autofit_columns == "column_names") or (autofit_columns is None):
|
|
706
714
|
col_widths = get_max_col_widths(df)
|
|
707
715
|
elif autofit_columns == "all_data":
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.2
|
|
2
2
|
Name: toolsos
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: OS tools
|
|
5
5
|
Author-email: OS <d.schmitz@amsterdam.nl>
|
|
6
6
|
Keywords: tools,Onderzoek & Statistiek
|
|
@@ -26,6 +26,7 @@ Requires-Dist: pyreadstat; extra == "all"
|
|
|
26
26
|
Requires-Dist: pyyaml; extra == "all"
|
|
27
27
|
Requires-Dist: requests; extra == "all"
|
|
28
28
|
Requires-Dist: sqlalchemy; extra == "all"
|
|
29
|
+
Requires-Dist: geopandas; extra == "all"
|
|
29
30
|
|
|
30
31
|
# Tools Onderzoek & Statistiek
|
|
31
32
|
|
|
@@ -23,7 +23,9 @@ src/toolsos/huisstijl/graphs/linegraph.py
|
|
|
23
23
|
src/toolsos/huisstijl/graphs/piegraph.py
|
|
24
24
|
src/toolsos/huisstijl/graphs/styler.py
|
|
25
25
|
src/toolsos/huisstijl/maps/__init__.py
|
|
26
|
+
src/toolsos/huisstijl/maps/choropleth.py
|
|
26
27
|
src/toolsos/huisstijl/tables/__init__.py
|
|
27
28
|
src/toolsos/huisstijl/tables/table_helpers.py
|
|
28
29
|
src/toolsos/huisstijl/tables/table_styles.py
|
|
29
|
-
src/toolsos/huisstijl/tables/tables.py
|
|
30
|
+
src/toolsos/huisstijl/tables/tables.py
|
|
31
|
+
tests/test_tables.py
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import openpyxl
|
|
2
|
+
import pandas as pd
|
|
3
|
+
import pytest
|
|
4
|
+
|
|
5
|
+
from toolsos.huisstijl.tables.tables import write_table, write_table_from_dict
|
|
6
|
+
|
|
7
|
+
DATA = [["Noord", 1, 0.25], ["Zuid", 3, 0.75]]
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
COLUMNS = ["stadsdeel", "bewoners", "perc_bewoners"]
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@pytest.fixture(scope="class")
|
|
14
|
+
def workbook(data=DATA, columns=COLUMNS):
|
|
15
|
+
df1 = pd.DataFrame(data=DATA, columns=COLUMNS)
|
|
16
|
+
|
|
17
|
+
filename = "temp/singe_table.xlsx"
|
|
18
|
+
write_table(
|
|
19
|
+
df1, file=filename, header_row=0, perc_pattern="perc_bewoners", style="new"
|
|
20
|
+
)
|
|
21
|
+
return openpyxl.load_workbook(filename)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class TestTable:
|
|
25
|
+
@pytest.fixture(autouse=True)
|
|
26
|
+
def setup(self, workbook):
|
|
27
|
+
self.workbook = workbook
|
|
28
|
+
self.ws = workbook.active
|
|
29
|
+
|
|
30
|
+
def test_data(self, data=DATA):
|
|
31
|
+
rows = [[c.value for c in row] for row in self.ws.iter_rows(2)]
|
|
32
|
+
|
|
33
|
+
assert all(src == written for src, written in zip(DATA, rows))
|
|
34
|
+
|
|
35
|
+
def test_data_header(self):
|
|
36
|
+
header = [[c.value for c in row] for row in self.ws.iter_rows(2)]
|
|
37
|
+
|
|
38
|
+
assert all(src == written for src, written in zip(DATA, header))
|
|
39
|
+
|
|
40
|
+
def test_background_color_header(self):
|
|
41
|
+
header_color = [
|
|
42
|
+
[c.fill.fgColor.value for c in row] for row in self.ws.iter_rows(max_row=1)
|
|
43
|
+
]
|
|
44
|
+
|
|
45
|
+
assert all([[c == "004699" for c in row] for row in header_color])
|
|
46
|
+
|
|
47
|
+
# def test_text_color_header(self):
|
|
48
|
+
# header_text_color = [[c.font.color.value for in row] in self.ws]
|
|
49
|
+
|
|
50
|
+
def test_formatting(self):
|
|
51
|
+
perc_values = [
|
|
52
|
+
[c.number_format for c in row]
|
|
53
|
+
for row in self.ws.iter_rows(min_row=2, min_col=3)
|
|
54
|
+
]
|
|
55
|
+
|
|
56
|
+
all([[c == "'0.0%'" for c in row] for row in perc_values])
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|