toolsos 0.2.8__tar.gz → 0.3.1__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.
Files changed (33) hide show
  1. {toolsos-0.2.8 → toolsos-0.3.1}/PKG-INFO +2 -1
  2. {toolsos-0.2.8 → toolsos-0.3.1}/pyproject.toml +2 -1
  3. {toolsos-0.2.8 → toolsos-0.3.1}/src/toolsos/geo.py +28 -2
  4. {toolsos-0.2.8 → toolsos-0.3.1}/src/toolsos/helpers.py +18 -5
  5. toolsos-0.3.1/src/toolsos/huisstijl/maps/choropleth.py +78 -0
  6. {toolsos-0.2.8 → toolsos-0.3.1}/src/toolsos.egg-info/PKG-INFO +2 -1
  7. {toolsos-0.2.8 → toolsos-0.3.1}/src/toolsos.egg-info/SOURCES.txt +3 -1
  8. {toolsos-0.2.8 → toolsos-0.3.1}/src/toolsos.egg-info/requires.txt +1 -0
  9. toolsos-0.3.1/tests/test_tables.py +56 -0
  10. {toolsos-0.2.8 → toolsos-0.3.1}/README.md +0 -0
  11. {toolsos-0.2.8 → toolsos-0.3.1}/setup.cfg +0 -0
  12. {toolsos-0.2.8 → toolsos-0.3.1}/src/toolsos/__init__.py +0 -0
  13. {toolsos-0.2.8 → toolsos-0.3.1}/src/toolsos/cbs_tools.py +0 -0
  14. {toolsos-0.2.8 → toolsos-0.3.1}/src/toolsos/create_tables.py +0 -0
  15. {toolsos-0.2.8 → toolsos-0.3.1}/src/toolsos/database/database_connection.py +0 -0
  16. {toolsos-0.2.8 → toolsos-0.3.1}/src/toolsos/database/database_transfer.py +0 -0
  17. {toolsos-0.2.8 → toolsos-0.3.1}/src/toolsos/download.py +0 -0
  18. {toolsos-0.2.8 → toolsos-0.3.1}/src/toolsos/huisstijl/__init__.py +0 -0
  19. {toolsos-0.2.8 → toolsos-0.3.1}/src/toolsos/huisstijl/colors.py +0 -0
  20. {toolsos-0.2.8 → toolsos-0.3.1}/src/toolsos/huisstijl/graphs/__init__.py +0 -0
  21. {toolsos-0.2.8 → toolsos-0.3.1}/src/toolsos/huisstijl/graphs/bargraph.py +0 -0
  22. {toolsos-0.2.8 → toolsos-0.3.1}/src/toolsos/huisstijl/graphs/graph_styles.py +0 -0
  23. {toolsos-0.2.8 → toolsos-0.3.1}/src/toolsos/huisstijl/graphs/linegraph.py +0 -0
  24. {toolsos-0.2.8 → toolsos-0.3.1}/src/toolsos/huisstijl/graphs/piegraph.py +0 -0
  25. {toolsos-0.2.8 → toolsos-0.3.1}/src/toolsos/huisstijl/graphs/styler.py +0 -0
  26. {toolsos-0.2.8 → toolsos-0.3.1}/src/toolsos/huisstijl/maps/__init__.py +0 -0
  27. {toolsos-0.2.8 → toolsos-0.3.1}/src/toolsos/huisstijl/tables/__init__.py +0 -0
  28. {toolsos-0.2.8 → toolsos-0.3.1}/src/toolsos/huisstijl/tables/table_helpers.py +0 -0
  29. {toolsos-0.2.8 → toolsos-0.3.1}/src/toolsos/huisstijl/tables/table_styles.py +0 -0
  30. {toolsos-0.2.8 → toolsos-0.3.1}/src/toolsos/huisstijl/tables/tables.py +0 -0
  31. {toolsos-0.2.8 → toolsos-0.3.1}/src/toolsos/polars_helpers.py +0 -0
  32. {toolsos-0.2.8 → toolsos-0.3.1}/src/toolsos.egg-info/dependency_links.txt +0 -0
  33. {toolsos-0.2.8 → toolsos-0.3.1}/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.2.8
3
+ Version: 0.3.1
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.2.8"
9
+ version = "0.3.1"
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,8 +1,10 @@
1
1
  from __future__ import annotations
2
2
 
3
+ from pathlib import Path
3
4
  from typing import Any, Union
4
5
 
5
6
  import geopandas as gpd
7
+ import pandas as pd
6
8
  import requests
7
9
 
8
10
 
@@ -45,12 +47,36 @@ def get_geo_json(
45
47
 
46
48
 
47
49
  def get_geo_dataframe(
48
- level: str, year: Union[int, Any], with_water: bool = False, mra: bool = False
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,
49
56
  ):
50
- return gpd.read_file(
57
+
58
+ gdf = gpd.GeoDataFrame.from_features(
51
59
  get_geo_json(level=level, year=year, with_water=with_water, mra=mra)
52
60
  )
53
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
+
54
80
 
55
81
  def extract_name_code_table(geo_json: dict[str, str]) -> dict[str, str]:
56
82
  """_summary_
@@ -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
- start_l = [f"{start_label} {bins[1]}"]
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]}" for i in range(1, len(bins) - 2)
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 pd.cut(x, bins=bins, labels=start_l + inbetween_labels + end_l) # type: ignore
52
+ return categorized
@@ -0,0 +1,78 @@
1
+ from typing import TYPE_CHECKING, Any, Iterable, Optional
2
+
3
+ import geopandas as gpd
4
+ import matplotlib.pyplot as plt
5
+ from matplotlib import axis
6
+ from matplotlib.colors import ListedColormap
7
+ from matplotlib.patches import Patch
8
+
9
+
10
+ def plot_choropleth(
11
+ gdf: gpd.GeoDataFrame,
12
+ column: str,
13
+ colors: Iterable[str],
14
+ column_label: Optional[str] = False,
15
+ column_label_kwargs: Any = None,
16
+ legend_title: Optional[str] = None,
17
+ edgecolor: str = "grey",
18
+ figsize: tuple[int, int] = (10, 8),
19
+ bbox_to_anchor: tuple[int, int] = (-0.05, 0),
20
+ ax: Optional[axis.Axis] = None,
21
+ ):
22
+ _ax = ax
23
+
24
+ # plotten
25
+ if not ax:
26
+ fig, ax = plt.subplots(figsize=figsize)
27
+
28
+ gdf.plot(
29
+ column=column,
30
+ cmap=ListedColormap(colors),
31
+ ax=ax,
32
+ categorical=True,
33
+ edgecolor=edgecolor,
34
+ linewidth=0.2,
35
+ )
36
+
37
+ labels = gdf["categorieen"].cat.categories
38
+
39
+ if not column_label_kwargs:
40
+ column_label_kwargs = {}
41
+
42
+ if column_label:
43
+ for x, y, label in zip(
44
+ gdf.geometry.representative_point().x,
45
+ gdf.geometry.representative_point().y,
46
+ gdf[column_label],
47
+ ):
48
+ ax.annotate(
49
+ label,
50
+ xy=(x, y),
51
+ xytext=(0, 0),
52
+ textcoords="offset points",
53
+ **column_label_kwargs,
54
+ )
55
+
56
+ # Create custom legend using the 'colors' and 'labels' variables, with a grey border
57
+ legend_elements = [
58
+ Patch(facecolor=color, label=label, edgecolor="#b0b0b0")
59
+ for color, label in zip(colors, labels)
60
+ ]
61
+
62
+ # Move the legend to the bottom left outside the plot
63
+ ax.legend(
64
+ handles=legend_elements,
65
+ bbox_to_anchor=bbox_to_anchor,
66
+ loc="lower left",
67
+ title=legend_title,
68
+ frameon=False,
69
+ )
70
+
71
+ # lat/lon uit
72
+ ax.set_axis_off()
73
+
74
+ # laten zien
75
+ plt.tight_layout()
76
+
77
+ if not _ax:
78
+ return fig, ax
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: toolsos
3
- Version: 0.2.8
3
+ Version: 0.3.1
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
@@ -10,6 +10,7 @@ pyreadstat
10
10
  pyyaml
11
11
  requests
12
12
  sqlalchemy
13
+ geopandas
13
14
 
14
15
  [dev]
15
16
  black
@@ -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