gla-data 0.2.2__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.
- gla_data-0.3.0/PKG-INFO +165 -0
- gla_data-0.3.0/README.md +139 -0
- {gla_data-0.2.2 → gla_data-0.3.0}/pyproject.toml +36 -3
- gla_data-0.3.0/src/gla_data/__init__.py +60 -0
- gla_data-0.2.2/src/gla_data/addressbase.py → gla_data-0.3.0/src/gla_data/_addressbase.py +17 -98
- gla_data-0.3.0/src/gla_data/_aerial_imagery.py +161 -0
- gla_data-0.3.0/src/gla_data/_bbox.py +59 -0
- {gla_data-0.2.2 → gla_data-0.3.0}/src/gla_data/_cache.py +1 -1
- {gla_data-0.2.2 → gla_data-0.3.0}/src/gla_data/_lbsm.py +12 -42
- gla_data-0.3.0/src/gla_data/_london.py +54 -0
- {gla_data-0.2.2 → gla_data-0.3.0}/src/gla_data/_ons.py +15 -5
- gla_data-0.3.0/src/gla_data/_postgres.py +416 -0
- gla_data-0.3.0/src/gla_data/_s3.py +268 -0
- {gla_data-0.2.2 → gla_data-0.3.0}/src/gla_data/_schemas.py +21 -50
- gla_data-0.3.0/src/gla_data/_tree_canopy.py +296 -0
- gla_data-0.3.0/src/gla_data/addressbase.py +129 -0
- gla_data-0.3.0/src/gla_data/addressbase_ngd.py +305 -0
- gla_data-0.3.0/src/gla_data/aerial_imagery.py +375 -0
- {gla_data-0.2.2 → gla_data-0.3.0}/src/gla_data/catalogue.py +36 -4
- {gla_data-0.2.2 → gla_data-0.3.0}/src/gla_data/census.py +1 -2
- gla_data-0.3.0/src/gla_data/cli.py +211 -0
- gla_data-0.3.0/src/gla_data/config.py +180 -0
- gla_data-0.3.0/src/gla_data/explore.py +139 -0
- gla_data-0.3.0/src/gla_data/front_gardens.py +77 -0
- {gla_data-0.2.2 → gla_data-0.3.0}/src/gla_data/lbsm.py +5 -4
- gla_data-0.3.0/src/gla_data/mastermap.py +208 -0
- {gla_data-0.2.2 → gla_data-0.3.0}/src/gla_data/transforms.py +83 -48
- gla_data-0.3.0/src/gla_data/tree_canopy.py +209 -0
- {gla_data-0.2.2 → gla_data-0.3.0}/tests/test_addressbase.py +40 -6
- gla_data-0.3.0/tests/test_addressbase_ngd.py +248 -0
- gla_data-0.3.0/tests/test_aerial_imagery.py +355 -0
- {gla_data-0.2.2 → gla_data-0.3.0}/tests/test_aggregate.py +34 -0
- {gla_data-0.2.2 → gla_data-0.3.0}/tests/test_boundaries.py +1 -3
- {gla_data-0.2.2 → gla_data-0.3.0}/tests/test_catalogue.py +1 -1
- gla_data-0.3.0/tests/test_cli.py +101 -0
- gla_data-0.3.0/tests/test_config.py +87 -0
- gla_data-0.3.0/tests/test_conventions.py +206 -0
- {gla_data-0.2.2 → gla_data-0.3.0}/tests/test_crosswalk.py +32 -0
- gla_data-0.3.0/tests/test_explore.py +93 -0
- gla_data-0.3.0/tests/test_front_gardens.py +63 -0
- gla_data-0.3.0/tests/test_london.py +26 -0
- gla_data-0.3.0/tests/test_mastermap.py +279 -0
- gla_data-0.3.0/tests/test_postgres.py +282 -0
- gla_data-0.3.0/tests/test_s3.py +254 -0
- gla_data-0.3.0/tests/test_tree_canopy.py +361 -0
- gla_data-0.2.2/.copier-answers.yml +0 -13
- gla_data-0.2.2/.env.example +0 -43
- gla_data-0.2.2/.github/workflows/ci.yml +0 -35
- gla_data-0.2.2/.github/workflows/publish.yml +0 -43
- gla_data-0.2.2/PKG-INFO +0 -105
- gla_data-0.2.2/README.md +0 -87
- gla_data-0.2.2/notebooks/addressbase_postgres_setup.ipynb +0 -182
- gla_data-0.2.2/pixi.lock +0 -12984
- gla_data-0.2.2/pixi.toml +0 -41
- gla_data-0.2.2/src/gla_data/__init__.py +0 -22
- gla_data-0.2.2/src/gla_data/_postgres.py +0 -141
- gla_data-0.2.2/src/gla_data/cli.py +0 -59
- gla_data-0.2.2/src/gla_data/config.py +0 -62
- gla_data-0.2.2/tests/test_cli.py +0 -17
- gla_data-0.2.2/tests/test_postgres.py +0 -143
- gla_data-0.2.2/uv.lock +0 -763
- {gla_data-0.2.2 → gla_data-0.3.0}/.gitignore +0 -0
- {gla_data-0.2.2 → gla_data-0.3.0}/src/gla_data/_arcgis.py +0 -0
- {gla_data-0.2.2 → gla_data-0.3.0}/src/gla_data/_londondatastore.py +0 -0
- {gla_data-0.2.2 → gla_data-0.3.0}/src/gla_data/_ukds.py +0 -0
- {gla_data-0.2.2 → gla_data-0.3.0}/src/gla_data/geography.py +0 -0
- {gla_data-0.2.2 → gla_data-0.3.0}/src/gla_data/py.typed +0 -0
- {gla_data-0.2.2 → gla_data-0.3.0}/tests/__init__.py +0 -0
- {gla_data-0.2.2 → gla_data-0.3.0}/tests/conftest.py +0 -0
- {gla_data-0.2.2 → gla_data-0.3.0}/tests/test_census_dwellings.py +0 -0
- {gla_data-0.2.2 → gla_data-0.3.0}/tests/test_geography_lookup.py +0 -0
- {gla_data-0.2.2 → gla_data-0.3.0}/tests/test_gis_layer.py +0 -0
- {gla_data-0.2.2 → gla_data-0.3.0}/tests/test_lbsm.py +0 -0
- {gla_data-0.2.2 → gla_data-0.3.0}/tests/test_londondatastore.py +0 -0
gla_data-0.3.0/PKG-INFO
ADDED
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: gla-data
|
|
3
|
+
Version: 0.3.0
|
|
4
|
+
Summary: Shared tools for loading, transforming, and visualising the GLA datasets used in City Data analysis.
|
|
5
|
+
Author-email: Conor Dempsey <conor.dempsey@london.gov.uk>
|
|
6
|
+
License: MIT
|
|
7
|
+
Requires-Python: >=3.12
|
|
8
|
+
Requires-Dist: beautifulsoup4>=4.12
|
|
9
|
+
Requires-Dist: boto3>=1.35
|
|
10
|
+
Requires-Dist: botocore>=1.35
|
|
11
|
+
Requires-Dist: filelock>=3.12
|
|
12
|
+
Requires-Dist: geopandas>=1.0
|
|
13
|
+
Requires-Dist: numpy>=1.26
|
|
14
|
+
Requires-Dist: pandas>=2.2
|
|
15
|
+
Requires-Dist: pandera>=0.32.1
|
|
16
|
+
Requires-Dist: pyarrow>=24.0.0
|
|
17
|
+
Requires-Dist: pydantic-settings
|
|
18
|
+
Requires-Dist: pydantic>=2.0
|
|
19
|
+
Requires-Dist: python-dotenv
|
|
20
|
+
Requires-Dist: requests>=2.32
|
|
21
|
+
Requires-Dist: rich>=13.0
|
|
22
|
+
Requires-Dist: shapely>=2.0
|
|
23
|
+
Requires-Dist: sqlalchemy>=2.0
|
|
24
|
+
Requires-Dist: typer>=0.26.8
|
|
25
|
+
Description-Content-Type: text/markdown
|
|
26
|
+
|
|
27
|
+
# gla-data
|
|
28
|
+
|
|
29
|
+
Shared tools for loading, transforming, and visualising the GLA datasets used in City Data analysis.
|
|
30
|
+
|
|
31
|
+
## Installation
|
|
32
|
+
|
|
33
|
+
### CLI
|
|
34
|
+
|
|
35
|
+
To install `gla-data` globally so the `gla-data` CLI is available anywhere in your terminal:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
uv tool install gla-data
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
This requires [uv](https://docs.astral.sh/uv/) to be installed.
|
|
42
|
+
|
|
43
|
+
### pip
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
pip install gla-data
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### pixi
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
pixi add gla-data
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Pixi pulls geospatial dependencies (geopandas, pyproj, etc.) from conda-forge, which provides reliable pre-built binaries across all platforms and Python versions.
|
|
56
|
+
|
|
57
|
+
## Usage
|
|
58
|
+
|
|
59
|
+
### Python API
|
|
60
|
+
|
|
61
|
+
```python
|
|
62
|
+
import gla_data
|
|
63
|
+
|
|
64
|
+
# Load 2021 LSOA boundaries for London
|
|
65
|
+
lsoa_gdf = gla_data.load_boundaries(geography='lsoa', year=2021)
|
|
66
|
+
|
|
67
|
+
# Load census dwelling counts
|
|
68
|
+
dwellings_2021 = gla_data.load_census_dwellings(year=2021)
|
|
69
|
+
dwellings_2011 = gla_data.load_census_dwellings(year=2011)
|
|
70
|
+
|
|
71
|
+
# Crosswalk 2011 dwelling counts onto 2021 LSOA boundaries
|
|
72
|
+
dwellings_2011_xw = gla_data.crosswalk(
|
|
73
|
+
dwellings_2011,
|
|
74
|
+
from_year=2011,
|
|
75
|
+
to_year=2021,
|
|
76
|
+
value_cols=['dwellings_total'],
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
# Load an OA → LSOA geography lookup
|
|
80
|
+
lookup = gla_data.load_geography_lookup(year=2021, smallest_geography='oa')
|
|
81
|
+
|
|
82
|
+
# Aggregate OA-level data up to LSOA level
|
|
83
|
+
lsoa_totals = gla_data.aggregate(
|
|
84
|
+
df,
|
|
85
|
+
from_geography='oa',
|
|
86
|
+
to_geography='lsoa',
|
|
87
|
+
value_cols=['count'],
|
|
88
|
+
year=2021,
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
# OS MasterMap buildings, with heights, in one 1km grid square plus a 100m
|
|
92
|
+
# margin (GLA only; needs the database settings below)
|
|
93
|
+
buildings = gla_data.load_mastermap_topography(
|
|
94
|
+
bbox=(530000, 180000, 531000, 181000),
|
|
95
|
+
buffer_m=100,
|
|
96
|
+
descriptive_group='Building',
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
# GLA tree canopy in the same square plus a 100m margin. The first call
|
|
100
|
+
# downloads the data (about 270MB) and builds a local file from it, which
|
|
101
|
+
# takes several minutes; later calls read that file.
|
|
102
|
+
canopy = gla_data.load_tree_canopy(
|
|
103
|
+
bbox=(530000, 180000, 531000, 181000),
|
|
104
|
+
buffer_m=100,
|
|
105
|
+
)
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### CLI
|
|
109
|
+
|
|
110
|
+
The package installs a `gla-data` command. Run `catalogue` to list all available datasets:
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
gla-data catalogue
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
This prints a table of every dataset, the function used to load it, a description, and its access tier (`public`, `registered`, `licensed` or `internal`; see Configuration for the last three).
|
|
117
|
+
|
|
118
|
+
## Configuration
|
|
119
|
+
|
|
120
|
+
Public datasets need no setup. Datasets held inside the GLA (the GIS team's Postgres database and S3 buckets) need settings that are not included in this package: connection details, table names and bucket names. GLA staff can get the values from the City Intelligence Data Science team.
|
|
121
|
+
|
|
122
|
+
### Where to put settings
|
|
123
|
+
|
|
124
|
+
Every setting is named `GLA_DATA_<NAME>` and can be set in any of these places. Higher in the list wins:
|
|
125
|
+
|
|
126
|
+
1. An environment variable, e.g. `GLA_DATA_BLUESKY_IMAGERY_BUCKET`.
|
|
127
|
+
2. A `.env` file in the folder you run your code from (a project's own settings).
|
|
128
|
+
3. Your user-level settings file, `~/.config/gla-data/.env` (on Windows, `C:\Users\<you>\.config\gla-data\.env`). Use this for settings shared by all your projects, such as database connections and bucket names. Set `GLA_DATA_USER_ENV_FILE` to use a different path.
|
|
129
|
+
|
|
130
|
+
A settings file holds one setting per line:
|
|
131
|
+
|
|
132
|
+
```
|
|
133
|
+
GLA_DATA_BLUESKY_IMAGERY_BUCKET=<bucket name>
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
A misspelt `GLA_DATA_` setting in a settings file raises an error rather than being ignored. Other keys in a project's `.env` are left alone.
|
|
137
|
+
|
|
138
|
+
### Settings for GLA-internal datasets
|
|
139
|
+
|
|
140
|
+
| Setting | Needed for |
|
|
141
|
+
|---|---|
|
|
142
|
+
| `GLA_DATA_GIS_DB_HOST`, `_PORT`, `_NAME`, `_USER`, `_PASSWORD` | Anything from the GIS team's Postgres database (`source="postgres"`, `load_gis_layer`, `load_front_gardens`, `load_mastermap_topography`, `gla-data db ...`). Use a read-only login. |
|
|
143
|
+
| `GLA_DATA_GIS_DB_SCHEMA` | Optional: the AddressBase schema, if not the default search path. |
|
|
144
|
+
| `GLA_DATA_ADDRESSBASE_BLPU_TABLE`, `_CLASSIFICATION_TABLE` | `load_addressbase(source="postgres")`, if the tables are not named `blpu` / `classification`. |
|
|
145
|
+
| `GLA_DATA_GISAPDATA_<LAYER>_TABLE` | Each `gisapdata` layer used by `load_boundaries(source="postgres")`, `load_gis_layer` and `load_front_gardens`. |
|
|
146
|
+
| `GLA_DATA_GISAPDATA_MASTERMAP_TOPO_2024_TABLE` | `load_mastermap_topography` (2024 snapshot). |
|
|
147
|
+
| `GLA_DATA_BLUESKY_IMAGERY_BUCKET` | Bluesky aerial imagery (`build_imagery_tile_index`, `download_imagery_tiles`). |
|
|
148
|
+
| `GLA_DATA_BLUESKY_IMAGERY_DIR` | Optional: folder for the imagery tile index and downloads (default `data/bluesky_imagery`). Tiles are 256MB each. |
|
|
149
|
+
| `GLA_DATA_AWS_PROFILE`, `GLA_DATA_AWS_REGION` | Optional: pin the AWS profile or region used for S3. |
|
|
150
|
+
| `GLA_DATA_TREE_CANOPY_DIR` | Optional: folder for the tree canopy download and the GeoPackage built from it (default `data/tree_canopy`). Point every project at the same folder so the data is downloaded once. |
|
|
151
|
+
|
|
152
|
+
AWS credentials are never set here. gla-data uses the standard AWS sources: an AWS profile (`~/.aws`), `AWS_*` environment variables, or the machine's own role on GLA cloud machines. Keep database passwords in your user-level file, never in a project's repository.
|
|
153
|
+
|
|
154
|
+
## Development
|
|
155
|
+
|
|
156
|
+
```bash
|
|
157
|
+
git clone <repo-url>
|
|
158
|
+
cd gla-data
|
|
159
|
+
pixi install
|
|
160
|
+
|
|
161
|
+
pixi run test # run tests
|
|
162
|
+
pixi run lint # check code style
|
|
163
|
+
pixi run format # auto-format
|
|
164
|
+
pixi run typecheck # type checking
|
|
165
|
+
```
|
gla_data-0.3.0/README.md
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
# gla-data
|
|
2
|
+
|
|
3
|
+
Shared tools for loading, transforming, and visualising the GLA datasets used in City Data analysis.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
### CLI
|
|
8
|
+
|
|
9
|
+
To install `gla-data` globally so the `gla-data` CLI is available anywhere in your terminal:
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
uv tool install gla-data
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
This requires [uv](https://docs.astral.sh/uv/) to be installed.
|
|
16
|
+
|
|
17
|
+
### pip
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
pip install gla-data
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
### pixi
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
pixi add gla-data
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Pixi pulls geospatial dependencies (geopandas, pyproj, etc.) from conda-forge, which provides reliable pre-built binaries across all platforms and Python versions.
|
|
30
|
+
|
|
31
|
+
## Usage
|
|
32
|
+
|
|
33
|
+
### Python API
|
|
34
|
+
|
|
35
|
+
```python
|
|
36
|
+
import gla_data
|
|
37
|
+
|
|
38
|
+
# Load 2021 LSOA boundaries for London
|
|
39
|
+
lsoa_gdf = gla_data.load_boundaries(geography='lsoa', year=2021)
|
|
40
|
+
|
|
41
|
+
# Load census dwelling counts
|
|
42
|
+
dwellings_2021 = gla_data.load_census_dwellings(year=2021)
|
|
43
|
+
dwellings_2011 = gla_data.load_census_dwellings(year=2011)
|
|
44
|
+
|
|
45
|
+
# Crosswalk 2011 dwelling counts onto 2021 LSOA boundaries
|
|
46
|
+
dwellings_2011_xw = gla_data.crosswalk(
|
|
47
|
+
dwellings_2011,
|
|
48
|
+
from_year=2011,
|
|
49
|
+
to_year=2021,
|
|
50
|
+
value_cols=['dwellings_total'],
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
# Load an OA → LSOA geography lookup
|
|
54
|
+
lookup = gla_data.load_geography_lookup(year=2021, smallest_geography='oa')
|
|
55
|
+
|
|
56
|
+
# Aggregate OA-level data up to LSOA level
|
|
57
|
+
lsoa_totals = gla_data.aggregate(
|
|
58
|
+
df,
|
|
59
|
+
from_geography='oa',
|
|
60
|
+
to_geography='lsoa',
|
|
61
|
+
value_cols=['count'],
|
|
62
|
+
year=2021,
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
# OS MasterMap buildings, with heights, in one 1km grid square plus a 100m
|
|
66
|
+
# margin (GLA only; needs the database settings below)
|
|
67
|
+
buildings = gla_data.load_mastermap_topography(
|
|
68
|
+
bbox=(530000, 180000, 531000, 181000),
|
|
69
|
+
buffer_m=100,
|
|
70
|
+
descriptive_group='Building',
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
# GLA tree canopy in the same square plus a 100m margin. The first call
|
|
74
|
+
# downloads the data (about 270MB) and builds a local file from it, which
|
|
75
|
+
# takes several minutes; later calls read that file.
|
|
76
|
+
canopy = gla_data.load_tree_canopy(
|
|
77
|
+
bbox=(530000, 180000, 531000, 181000),
|
|
78
|
+
buffer_m=100,
|
|
79
|
+
)
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### CLI
|
|
83
|
+
|
|
84
|
+
The package installs a `gla-data` command. Run `catalogue` to list all available datasets:
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
gla-data catalogue
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
This prints a table of every dataset, the function used to load it, a description, and its access tier (`public`, `registered`, `licensed` or `internal`; see Configuration for the last three).
|
|
91
|
+
|
|
92
|
+
## Configuration
|
|
93
|
+
|
|
94
|
+
Public datasets need no setup. Datasets held inside the GLA (the GIS team's Postgres database and S3 buckets) need settings that are not included in this package: connection details, table names and bucket names. GLA staff can get the values from the City Intelligence Data Science team.
|
|
95
|
+
|
|
96
|
+
### Where to put settings
|
|
97
|
+
|
|
98
|
+
Every setting is named `GLA_DATA_<NAME>` and can be set in any of these places. Higher in the list wins:
|
|
99
|
+
|
|
100
|
+
1. An environment variable, e.g. `GLA_DATA_BLUESKY_IMAGERY_BUCKET`.
|
|
101
|
+
2. A `.env` file in the folder you run your code from (a project's own settings).
|
|
102
|
+
3. Your user-level settings file, `~/.config/gla-data/.env` (on Windows, `C:\Users\<you>\.config\gla-data\.env`). Use this for settings shared by all your projects, such as database connections and bucket names. Set `GLA_DATA_USER_ENV_FILE` to use a different path.
|
|
103
|
+
|
|
104
|
+
A settings file holds one setting per line:
|
|
105
|
+
|
|
106
|
+
```
|
|
107
|
+
GLA_DATA_BLUESKY_IMAGERY_BUCKET=<bucket name>
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
A misspelt `GLA_DATA_` setting in a settings file raises an error rather than being ignored. Other keys in a project's `.env` are left alone.
|
|
111
|
+
|
|
112
|
+
### Settings for GLA-internal datasets
|
|
113
|
+
|
|
114
|
+
| Setting | Needed for |
|
|
115
|
+
|---|---|
|
|
116
|
+
| `GLA_DATA_GIS_DB_HOST`, `_PORT`, `_NAME`, `_USER`, `_PASSWORD` | Anything from the GIS team's Postgres database (`source="postgres"`, `load_gis_layer`, `load_front_gardens`, `load_mastermap_topography`, `gla-data db ...`). Use a read-only login. |
|
|
117
|
+
| `GLA_DATA_GIS_DB_SCHEMA` | Optional: the AddressBase schema, if not the default search path. |
|
|
118
|
+
| `GLA_DATA_ADDRESSBASE_BLPU_TABLE`, `_CLASSIFICATION_TABLE` | `load_addressbase(source="postgres")`, if the tables are not named `blpu` / `classification`. |
|
|
119
|
+
| `GLA_DATA_GISAPDATA_<LAYER>_TABLE` | Each `gisapdata` layer used by `load_boundaries(source="postgres")`, `load_gis_layer` and `load_front_gardens`. |
|
|
120
|
+
| `GLA_DATA_GISAPDATA_MASTERMAP_TOPO_2024_TABLE` | `load_mastermap_topography` (2024 snapshot). |
|
|
121
|
+
| `GLA_DATA_BLUESKY_IMAGERY_BUCKET` | Bluesky aerial imagery (`build_imagery_tile_index`, `download_imagery_tiles`). |
|
|
122
|
+
| `GLA_DATA_BLUESKY_IMAGERY_DIR` | Optional: folder for the imagery tile index and downloads (default `data/bluesky_imagery`). Tiles are 256MB each. |
|
|
123
|
+
| `GLA_DATA_AWS_PROFILE`, `GLA_DATA_AWS_REGION` | Optional: pin the AWS profile or region used for S3. |
|
|
124
|
+
| `GLA_DATA_TREE_CANOPY_DIR` | Optional: folder for the tree canopy download and the GeoPackage built from it (default `data/tree_canopy`). Point every project at the same folder so the data is downloaded once. |
|
|
125
|
+
|
|
126
|
+
AWS credentials are never set here. gla-data uses the standard AWS sources: an AWS profile (`~/.aws`), `AWS_*` environment variables, or the machine's own role on GLA cloud machines. Keep database passwords in your user-level file, never in a project's repository.
|
|
127
|
+
|
|
128
|
+
## Development
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
git clone <repo-url>
|
|
132
|
+
cd gla-data
|
|
133
|
+
pixi install
|
|
134
|
+
|
|
135
|
+
pixi run test # run tests
|
|
136
|
+
pixi run lint # check code style
|
|
137
|
+
pixi run format # auto-format
|
|
138
|
+
pixi run typecheck # type checking
|
|
139
|
+
```
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "gla-data"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.3.0"
|
|
4
4
|
description = "Shared tools for loading, transforming, and visualising the GLA datasets used in City Data analysis."
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
license = { text = "MIT" }
|
|
@@ -10,12 +10,20 @@ dependencies = [
|
|
|
10
10
|
"pandas>=2.2",
|
|
11
11
|
"geopandas>=1.0",
|
|
12
12
|
"requests>=2.32",
|
|
13
|
+
"pydantic>=2.0",
|
|
13
14
|
"pydantic-settings",
|
|
15
|
+
"python-dotenv",
|
|
16
|
+
"shapely>=2.0",
|
|
14
17
|
"pyarrow>=24.0.0",
|
|
15
18
|
"typer>=0.26.8",
|
|
19
|
+
"rich>=13.0",
|
|
16
20
|
"pandera>=0.32.1",
|
|
17
21
|
"sqlalchemy>=2.0",
|
|
18
22
|
"beautifulsoup4>=4.12",
|
|
23
|
+
"boto3>=1.35",
|
|
24
|
+
"botocore>=1.35",
|
|
25
|
+
"filelock>=3.12",
|
|
26
|
+
"numpy>=1.26",
|
|
19
27
|
# Postgres driver (psycopg2) is a conda-only dep here (see pixi.toml) to
|
|
20
28
|
# avoid pip/conda installing it twice in the pixi env, matching how
|
|
21
29
|
# openpyxl (needed by census.py) is handled — a plain `pip install
|
|
@@ -30,6 +38,16 @@ gla-data = "gla_data.cli:main"
|
|
|
30
38
|
requires = ["hatchling"]
|
|
31
39
|
build-backend = "hatchling.build"
|
|
32
40
|
|
|
41
|
+
# The package is published on public PyPI while the repo is private, so the
|
|
42
|
+
# source distribution lists exactly what ships (pyproject.toml is always
|
|
43
|
+
# included). Everything else stays out: internal docs (DESIGN.md, docs/),
|
|
44
|
+
# notebooks, CI workflows, .env.example, Claude files, lock files.
|
|
45
|
+
[tool.hatch.build.targets.sdist]
|
|
46
|
+
only-include = ["src", "tests", "README.md"]
|
|
47
|
+
|
|
48
|
+
[tool.hatch.build.targets.wheel]
|
|
49
|
+
packages = ["src/gla_data"]
|
|
50
|
+
|
|
33
51
|
[tool.pytest.ini_options]
|
|
34
52
|
testpaths = ["tests"]
|
|
35
53
|
addopts = "-v --tb=short"
|
|
@@ -43,7 +61,22 @@ source = ["src"]
|
|
|
43
61
|
|
|
44
62
|
|
|
45
63
|
[tool.ruff.lint]
|
|
46
|
-
|
|
64
|
+
# T20: no print() in library code; BLE: no blind `except Exception`;
|
|
65
|
+
# C90: cyclomatic complexity limit (see mccabe below); D: numpy-style
|
|
66
|
+
# docstrings (this package's standard, decided 2026-10-19).
|
|
67
|
+
select = ["E", "F", "I", "UP", "B", "T20", "BLE", "C90", "D"]
|
|
68
|
+
|
|
69
|
+
[tool.ruff.lint.pydocstyle]
|
|
70
|
+
convention = "numpy"
|
|
71
|
+
|
|
72
|
+
[tool.ruff.lint.mccabe]
|
|
73
|
+
max-complexity = 10
|
|
74
|
+
|
|
75
|
+
[tool.ruff.lint.per-file-ignores]
|
|
76
|
+
"tests/**" = ["T20", "D"]
|
|
77
|
+
|
|
78
|
+
[tool.deptry]
|
|
79
|
+
known_first_party = ["gla_data"]
|
|
47
80
|
|
|
48
81
|
[tool.mypy]
|
|
49
82
|
strict = true
|
|
@@ -56,7 +89,7 @@ disallow_untyped_defs = false
|
|
|
56
89
|
disallow_incomplete_defs = false
|
|
57
90
|
|
|
58
91
|
[tool.bumpversion]
|
|
59
|
-
current_version = "0.
|
|
92
|
+
current_version = "0.3.0"
|
|
60
93
|
commit = true
|
|
61
94
|
tag = true
|
|
62
95
|
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"""Shared tools for loading and transforming GLA datasets.
|
|
2
|
+
|
|
3
|
+
Every public function is importable from the top level, e.g.
|
|
4
|
+
``gla_data.load_boundaries``; ``gla_data.catalogue()`` lists the datasets.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from importlib.metadata import version
|
|
8
|
+
|
|
9
|
+
from gla_data.addressbase import (
|
|
10
|
+
assign_area_code,
|
|
11
|
+
build_uprn_net_change_from_ngd,
|
|
12
|
+
compute_uprn_net_change,
|
|
13
|
+
load_addressbase,
|
|
14
|
+
load_ngd_addressbase,
|
|
15
|
+
)
|
|
16
|
+
from gla_data.aerial_imagery import (
|
|
17
|
+
build_imagery_tile_index,
|
|
18
|
+
download_imagery_tiles,
|
|
19
|
+
get_imagery_tile_path,
|
|
20
|
+
load_imagery_tile_index,
|
|
21
|
+
remove_imagery_tiles,
|
|
22
|
+
)
|
|
23
|
+
from gla_data.catalogue import catalogue
|
|
24
|
+
from gla_data.census import load_census_dwellings
|
|
25
|
+
from gla_data.explore import describe_table, list_schemas, list_tables
|
|
26
|
+
from gla_data.front_gardens import load_front_gardens
|
|
27
|
+
from gla_data.geography import load_boundaries, load_geography_lookup, load_gis_layer
|
|
28
|
+
from gla_data.lbsm import load_lbsm
|
|
29
|
+
from gla_data.mastermap import load_mastermap_topography
|
|
30
|
+
from gla_data.transforms import aggregate, crosswalk
|
|
31
|
+
from gla_data.tree_canopy import download_tree_canopy, load_tree_canopy
|
|
32
|
+
|
|
33
|
+
__version__ = version("gla-data")
|
|
34
|
+
__all__ = [
|
|
35
|
+
"load_boundaries",
|
|
36
|
+
"load_geography_lookup",
|
|
37
|
+
"load_gis_layer",
|
|
38
|
+
"load_census_dwellings",
|
|
39
|
+
"crosswalk",
|
|
40
|
+
"aggregate",
|
|
41
|
+
"catalogue",
|
|
42
|
+
"load_addressbase",
|
|
43
|
+
"compute_uprn_net_change",
|
|
44
|
+
"load_ngd_addressbase",
|
|
45
|
+
"assign_area_code",
|
|
46
|
+
"build_uprn_net_change_from_ngd",
|
|
47
|
+
"load_lbsm",
|
|
48
|
+
"list_schemas",
|
|
49
|
+
"list_tables",
|
|
50
|
+
"describe_table",
|
|
51
|
+
"load_front_gardens",
|
|
52
|
+
"build_imagery_tile_index",
|
|
53
|
+
"load_imagery_tile_index",
|
|
54
|
+
"download_imagery_tiles",
|
|
55
|
+
"get_imagery_tile_path",
|
|
56
|
+
"remove_imagery_tiles",
|
|
57
|
+
"load_mastermap_topography",
|
|
58
|
+
"load_tree_canopy",
|
|
59
|
+
"download_tree_canopy",
|
|
60
|
+
]
|
|
@@ -1,44 +1,31 @@
|
|
|
1
|
-
"""
|
|
2
|
-
|
|
3
|
-
AddressBase Premium is a ``licensed`` dataset (see DESIGN.md's data access tiers) —
|
|
4
|
-
``load_addressbase`` is the raw loader, not yet implemented pending GLA's S3
|
|
5
|
-
location for the extract. ``compute_uprn_net_change`` is a pure transform that
|
|
6
|
-
works on any already-loaded BLPU-shaped DataFrame, so it is fully testable today
|
|
7
|
-
against synthetic fixtures without AddressBase access itself, and composes with
|
|
8
|
-
``load_addressbase``'s output once that lands.
|
|
9
|
-
|
|
10
|
-
Ported from an exploratory notebook (external reference, not part of this
|
|
11
|
-
package) that produced this same net-change-by-OA-by-financial-year table for
|
|
12
|
-
the housing_projections project. That notebook's first approach computed a
|
|
13
|
-
*cumulative* running UPRN count per area and diffed it year-over-year — wrong
|
|
14
|
-
wherever the underlying data was processed in row-chunks rather than as one
|
|
15
|
-
geographically complete set, since a chunk boundary could split one area's
|
|
16
|
-
history across chunks. The event-based approach here (sum +1/-1 events within
|
|
17
|
-
each financial year directly, no cumulative step) sidesteps that failure mode
|
|
18
|
-
structurally: call ``compute_uprn_net_change`` once per geographic scope (e.g.
|
|
19
|
-
once per borough, not on arbitrary row partitions of a larger file) and there is
|
|
20
|
-
no cross-call state for a chunk boundary to corrupt.
|
|
21
|
-
"""
|
|
1
|
+
"""Shared AddressBase logic used by both routes (Premium in Postgres, NGD).
|
|
22
2
|
|
|
23
|
-
|
|
3
|
+
The residential classification rules, the financial-year convention and the
|
|
4
|
+
net-change transform. Public names are re-exported from ``addressbase``.
|
|
5
|
+
"""
|
|
24
6
|
|
|
25
|
-
import geopandas as gpd
|
|
26
7
|
import pandas as pd
|
|
27
8
|
|
|
28
|
-
from gla_data import _postgres
|
|
29
9
|
from gla_data._schemas import addressbase_net_change_schema
|
|
30
|
-
from gla_data.config import settings
|
|
31
10
|
|
|
32
|
-
# AddressBase
|
|
33
|
-
# RC01 (residential car park) and RG*
|
|
11
|
+
# AddressBase classification scheme (shared by AddressBase Premium and NGD):
|
|
12
|
+
# residential codes start with 'R'. RC01 (residential car park) and RG*
|
|
13
|
+
# (garage) are excluded — not living space.
|
|
34
14
|
_RESIDENTIAL_PREFIX = "R"
|
|
35
15
|
_EXCLUDED_PREFIXES = ("RC01", "RG")
|
|
36
16
|
|
|
37
|
-
# AddressBase
|
|
38
|
-
#
|
|
17
|
+
# AddressBase coverage is unreliable before this financial year in past uses
|
|
18
|
+
# of this data (the source notebook dropped 2007/2008 for the same reason).
|
|
39
19
|
_MIN_FINANCIAL_YEAR = 2009
|
|
40
20
|
|
|
41
21
|
|
|
22
|
+
def _is_residential_classification(code: pd.Series) -> pd.Series:
|
|
23
|
+
"""Boolean mask: residential classification code, excluding car parks/garages."""
|
|
24
|
+
is_residential = code.str.startswith(_RESIDENTIAL_PREFIX)
|
|
25
|
+
is_excluded = code.str.startswith(_EXCLUDED_PREFIXES)
|
|
26
|
+
return is_residential & ~is_excluded
|
|
27
|
+
|
|
28
|
+
|
|
42
29
|
def _filter_residential_uprns(classification: pd.DataFrame) -> pd.Series:
|
|
43
30
|
"""Return the deduplicated set of residential UPRNs.
|
|
44
31
|
|
|
@@ -59,9 +46,7 @@ def _filter_residential_uprns(classification: pd.DataFrame) -> pd.Series:
|
|
|
59
46
|
with multiple qualifying classification rows.
|
|
60
47
|
"""
|
|
61
48
|
code = classification["classification_code"]
|
|
62
|
-
|
|
63
|
-
is_excluded = code.str.startswith(_EXCLUDED_PREFIXES)
|
|
64
|
-
residential = classification.loc[is_residential & ~is_excluded, "uprn"]
|
|
49
|
+
residential = classification.loc[_is_residential_classification(code), "uprn"]
|
|
65
50
|
return residential.drop_duplicates()
|
|
66
51
|
|
|
67
52
|
|
|
@@ -176,69 +161,3 @@ def compute_uprn_net_change(
|
|
|
176
161
|
|
|
177
162
|
addressbase_net_change_schema(area_col).validate(result)
|
|
178
163
|
return result
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
def load_addressbase(
|
|
182
|
-
source: Literal["postgres", "s3", "local"] = "postgres",
|
|
183
|
-
table: str | None = None,
|
|
184
|
-
) -> gpd.GeoDataFrame | pd.DataFrame:
|
|
185
|
-
"""Load raw AddressBase Premium records for London.
|
|
186
|
-
|
|
187
|
-
GLA's AddressBase Premium extract lives in Postgres/PostGIS, so
|
|
188
|
-
``source="postgres"`` is implemented; ``"s3"`` and ``"local"`` are not,
|
|
189
|
-
yet.
|
|
190
|
-
|
|
191
|
-
**This currently returns the raw configured table as-is** — no column
|
|
192
|
-
renaming, residential filtering, or schema validation. The real
|
|
193
|
-
table/column names are still being confirmed against the live database
|
|
194
|
-
(see ``notebooks/addressbase_postgres_setup.ipynb``, which reflects the
|
|
195
|
-
schema and exports it for that purpose). Once confirmed, this will be
|
|
196
|
-
tightened to return a validated, BLPU-shaped GeoDataFrame ready to pass
|
|
197
|
-
directly into ``compute_uprn_net_change``.
|
|
198
|
-
|
|
199
|
-
Parameters
|
|
200
|
-
----------
|
|
201
|
-
source:
|
|
202
|
-
``"postgres"`` (implemented, default), ``"s3"`` or ``"local"``
|
|
203
|
-
(not yet implemented).
|
|
204
|
-
table:
|
|
205
|
-
Postgres table to read. Defaults to the configured
|
|
206
|
-
``GLA_DATA_ADDRESSBASE_BLPU_TABLE`` (see ``.env.example``).
|
|
207
|
-
Only used when ``source="postgres"``.
|
|
208
|
-
|
|
209
|
-
Returns
|
|
210
|
-
-------
|
|
211
|
-
gpd.GeoDataFrame or pd.DataFrame
|
|
212
|
-
Whatever is in the configured table, unmodified. GeoDataFrame if a
|
|
213
|
-
geometry column is present, otherwise a plain DataFrame.
|
|
214
|
-
|
|
215
|
-
Raises
|
|
216
|
-
------
|
|
217
|
-
NotImplementedError
|
|
218
|
-
For ``source="s3"`` or ``source="local"``.
|
|
219
|
-
EnvironmentError
|
|
220
|
-
For ``source="postgres"``, if required
|
|
221
|
-
``GLA_DATA_GIS_DB_*`` settings are missing — see
|
|
222
|
-
``.env.example``.
|
|
223
|
-
ValueError
|
|
224
|
-
For an unrecognised ``source``.
|
|
225
|
-
|
|
226
|
-
Access tier: ``licensed`` (OS AddressBase Premium via GLA's own licence).
|
|
227
|
-
"""
|
|
228
|
-
if source == "postgres":
|
|
229
|
-
engine = _postgres.get_engine()
|
|
230
|
-
table = table or settings.addressbase_blpu_table
|
|
231
|
-
return _postgres.read_table(engine, table, schema=settings.gis_db_schema)
|
|
232
|
-
if source == "s3":
|
|
233
|
-
raise NotImplementedError(
|
|
234
|
-
"load_addressbase(source='s3') is not yet implemented — GLA's "
|
|
235
|
-
"AddressBase Premium extract currently lives in Postgres, not "
|
|
236
|
-
"S3. Use source='postgres' instead."
|
|
237
|
-
)
|
|
238
|
-
if source == "local":
|
|
239
|
-
raise NotImplementedError(
|
|
240
|
-
"load_addressbase(source='local') is not yet implemented."
|
|
241
|
-
)
|
|
242
|
-
raise ValueError(
|
|
243
|
-
f"source={source!r} is not supported. Choose from: 'postgres', 's3', 'local'."
|
|
244
|
-
)
|