bcdata 0.10.2__tar.gz → 0.13.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.
- {bcdata-0.10.2/bcdata.egg-info → bcdata-0.13.0}/PKG-INFO +57 -48
- {bcdata-0.10.2 → bcdata-0.13.0}/README.md +37 -26
- bcdata-0.13.0/pyproject.toml +55 -0
- bcdata-0.13.0/src/bcdata/__init__.py +27 -0
- {bcdata-0.10.2 → bcdata-0.13.0/src}/bcdata/bc2pg.py +43 -75
- {bcdata-0.10.2 → bcdata-0.13.0/src}/bcdata/bcdc.py +46 -59
- {bcdata-0.10.2 → bcdata-0.13.0/src}/bcdata/cli.py +39 -20
- {bcdata-0.10.2 → bcdata-0.13.0/src}/bcdata/database.py +25 -16
- {bcdata-0.10.2 → bcdata-0.13.0/src}/bcdata/wcs.py +2 -8
- {bcdata-0.10.2 → bcdata-0.13.0/src}/bcdata/wfs.py +69 -41
- {bcdata-0.10.2 → bcdata-0.13.0/src/bcdata.egg-info}/PKG-INFO +57 -48
- bcdata-0.13.0/src/bcdata.egg-info/SOURCES.txt +21 -0
- bcdata-0.13.0/src/bcdata.egg-info/requires.txt +13 -0
- {bcdata-0.10.2 → bcdata-0.13.0}/tests/test_bc2pg.py +33 -12
- {bcdata-0.10.2 → bcdata-0.13.0}/tests/test_bcdc.py +31 -2
- {bcdata-0.10.2 → bcdata-0.13.0}/tests/test_wcs.py +3 -6
- {bcdata-0.10.2 → bcdata-0.13.0}/tests/test_wfs.py +22 -16
- bcdata-0.10.2/CHANGES.txt +0 -298
- bcdata-0.10.2/bcdata/__init__.py +0 -18
- bcdata-0.10.2/bcdata.egg-info/SOURCES.txt +0 -25
- bcdata-0.10.2/bcdata.egg-info/not-zip-safe +0 -1
- bcdata-0.10.2/bcdata.egg-info/requires.txt +0 -13
- bcdata-0.10.2/pyproject.toml +0 -16
- bcdata-0.10.2/requirements.txt +0 -8
- bcdata-0.10.2/setup.py +0 -55
- {bcdata-0.10.2 → bcdata-0.13.0}/LICENSE.txt +0 -0
- {bcdata-0.10.2 → bcdata-0.13.0}/setup.cfg +0 -0
- {bcdata-0.10.2 → bcdata-0.13.0/src}/bcdata.egg-info/dependency_links.txt +0 -0
- {bcdata-0.10.2 → bcdata-0.13.0/src}/bcdata.egg-info/entry_points.txt +0 -0
- {bcdata-0.10.2 → bcdata-0.13.0/src}/bcdata.egg-info/top_level.txt +0 -0
- {bcdata-0.10.2 → bcdata-0.13.0}/tests/test_cli.py +0 -0
|
@@ -1,36 +1,34 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: bcdata
|
|
3
|
-
Version: 0.
|
|
4
|
-
Summary:
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
Keywords: gis geospatial data BC DataBC download "Britsh Columbia"
|
|
10
|
-
Classifier: Development Status :: 3 - Alpha
|
|
11
|
-
Classifier: Intended Audience :: Developers
|
|
12
|
-
Classifier: Intended Audience :: Science/Research
|
|
13
|
-
Classifier: Operating System :: OS Independent
|
|
3
|
+
Version: 0.13.0
|
|
4
|
+
Summary: Download open data, monitor and report on changes
|
|
5
|
+
Author-email: Simon Norris <snorris@hillcrestgeo.ca>
|
|
6
|
+
Project-URL: Homepage, https://github.com/smnorris/bcdata
|
|
7
|
+
Project-URL: Issues, https://github.com/smnorris/bcdata
|
|
8
|
+
Classifier: Development Status :: 1 - Planning
|
|
14
9
|
Classifier: Programming Language :: Python :: 3.9
|
|
15
10
|
Classifier: Programming Language :: Python :: 3.10
|
|
16
11
|
Classifier: Programming Language :: Python :: 3.11
|
|
17
12
|
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
-
Classifier:
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
19
15
|
Classifier: Topic :: Scientific/Engineering :: GIS
|
|
16
|
+
Classifier: Operating System :: OS Independent
|
|
17
|
+
Requires-Python: >=3.9
|
|
20
18
|
Description-Content-Type: text/markdown
|
|
21
19
|
License-File: LICENSE.txt
|
|
22
|
-
Requires-Dist: geoalchemy2
|
|
23
|
-
Requires-Dist: geopandas
|
|
24
|
-
Requires-Dist: owslib
|
|
25
|
-
Requires-Dist: psycopg2-binary
|
|
26
|
-
Requires-Dist: rasterio
|
|
27
|
-
Requires-Dist: requests
|
|
28
|
-
Requires-Dist: sqlalchemy
|
|
29
|
-
Requires-Dist: stamina
|
|
20
|
+
Requires-Dist: geoalchemy2
|
|
21
|
+
Requires-Dist: geopandas
|
|
22
|
+
Requires-Dist: owslib
|
|
23
|
+
Requires-Dist: psycopg2-binary
|
|
24
|
+
Requires-Dist: rasterio
|
|
25
|
+
Requires-Dist: requests
|
|
26
|
+
Requires-Dist: sqlalchemy
|
|
27
|
+
Requires-Dist: stamina
|
|
30
28
|
Provides-Extra: test
|
|
31
|
-
Requires-Dist: pytest
|
|
29
|
+
Requires-Dist: pytest; extra == "test"
|
|
32
30
|
Requires-Dist: pre-commit; extra == "test"
|
|
33
|
-
Requires-Dist:
|
|
31
|
+
Requires-Dist: requests-mock; extra == "test"
|
|
34
32
|
|
|
35
33
|
# bcdata
|
|
36
34
|
|
|
@@ -43,7 +41,9 @@ There is a [wealth of British Columbia geographic information available as open
|
|
|
43
41
|
data](https://catalogue.data.gov.bc.ca/dataset?download_audience=Public),
|
|
44
42
|
but direct file download urls are not available and the syntax to accesss WFS via `ogr2ogr` and/or `curl/wget` can be awkward.
|
|
45
43
|
|
|
46
|
-
This
|
|
44
|
+
This tool attempts to simplify downloads of BC geographic data and smoothly integrate with PostGIS and Python GIS tools like `geopandas`, `fiona` and `rasterio`. As noted above, the module is an interface only to geographic data [avialable via WFS/WCS](https://bcgov.github.io/data-publication/pages/tips_tricks_webservices.html) - to work with other BC open data, use the [bcdata R package](https://github.com/bcgov/bcdata).
|
|
45
|
+
|
|
46
|
+
Note that this tool depends on BC Open Geospatial Web Services API at [openmaps.gov.bc.ca](https://openmaps.gov.bc.ca/geo/pub/ows?service=WFS&request=Getcapabilities) and the [BC Data Catalogue API](https://catalogue.data.gov.bc.ca/dataset/bc-data-catalogue-api). If downloads are failing, consult the [BCGov Data Systems and Services uptime monitor](https://uptime.com/statuspage/bcgov-dss).
|
|
47
47
|
|
|
48
48
|
**Disclaimer**
|
|
49
49
|
*It is the user's responsibility to check the licensing for any downloads, data are generally licensed as [OGL-BC](http://www2.gov.bc.ca/gov/content/governments/about-the-bc-government/databc/open-data/open-government-license-bc)*
|
|
@@ -153,25 +153,30 @@ Usage: bcdata bc2pg [OPTIONS] DATASET
|
|
|
153
153
|
|
|
154
154
|
Download a DataBC WFS layer to postgres
|
|
155
155
|
|
|
156
|
-
$ bcdata bc2pg
|
|
156
|
+
$ bcdata bc2pg whse_imagery_and_base_maps.gsr_airports_svw
|
|
157
157
|
|
|
158
158
|
Options:
|
|
159
|
-
-db, --db_url TEXT
|
|
160
|
-
|
|
161
|
-
--table TEXT
|
|
162
|
-
--schema TEXT
|
|
163
|
-
--geometry_type TEXT
|
|
164
|
-
--query TEXT
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
-
|
|
169
|
-
|
|
170
|
-
-
|
|
171
|
-
-
|
|
172
|
-
-
|
|
173
|
-
-
|
|
174
|
-
--
|
|
159
|
+
-db, --db_url TEXT Target database url, defaults to
|
|
160
|
+
$DATABASE_URL environment variable if set
|
|
161
|
+
--table TEXT Destination table name
|
|
162
|
+
--schema TEXT Destination schema name
|
|
163
|
+
--geometry_type TEXT Spatial type of geometry column
|
|
164
|
+
--query TEXT A valid CQL or ECQL query
|
|
165
|
+
--bounds TEXT Bounds: "left bottom right top" or "[left,
|
|
166
|
+
bottom, right, top]". Coordinates are BC
|
|
167
|
+
Albers (default) or --bounds_crs
|
|
168
|
+
--bounds-crs, --bounds_crs TEXT
|
|
169
|
+
CRS of provided bounds
|
|
170
|
+
-c, --count INTEGER Total number of features to load
|
|
171
|
+
-k, --primary_key TEXT Primary key of dataset
|
|
172
|
+
-s, --sortby TEXT Name of sort field
|
|
173
|
+
-e, --schema_only Create empty table from catalogue schema
|
|
174
|
+
-a, --append Append to existing table
|
|
175
|
+
-r, --refresh Truncate from existing table before load
|
|
176
|
+
-t, --no_timestamp Do not log download to bcdata.log
|
|
177
|
+
-v, --verbose Increase verbosity.
|
|
178
|
+
-q, --quiet Decrease verbosity.
|
|
179
|
+
--help Show this message and exit.
|
|
175
180
|
```
|
|
176
181
|
|
|
177
182
|
#### cat
|
|
@@ -332,7 +337,7 @@ Describe a dataset. Note that if we know the id of a dataset, we can use that ra
|
|
|
332
337
|
|
|
333
338
|
The JSON output can be manipulated with [jq](https://stedolan.github.io/jq/). For example, to show only the fields available in the dataset:
|
|
334
339
|
|
|
335
|
-
$ bcdata info bc-airports | jq '.schema
|
|
340
|
+
$ bcdata info bc-airports | jq '.schema'
|
|
336
341
|
{
|
|
337
342
|
"CUSTODIAN_ORG_DESCRIPTION": "string",
|
|
338
343
|
"BUSINESS_CATEGORY_CLASS": "string",
|
|
@@ -360,6 +365,11 @@ Save a layer to a geopackage in BC Albers:
|
|
|
360
365
|
|
|
361
366
|
Note that this will not work if the source data has mixed geometry types.
|
|
362
367
|
|
|
368
|
+
Load to parquet on s3:
|
|
369
|
+
|
|
370
|
+
$ bcdata dump bc-airports \
|
|
371
|
+
| ogr2ogr -f Parquet /vsis3/$BUCKET/airports.parquet -s_srs EPSG:4326 -t_srs EPSG:3005 /vsistdin/
|
|
372
|
+
|
|
363
373
|
Load data to postgres and run a spatial query:
|
|
364
374
|
|
|
365
375
|
$ bcdata bc2pg bc-airports \
|
|
@@ -406,13 +416,12 @@ Load data to postgres and run a spatial query:
|
|
|
406
416
|
|
|
407
417
|
Create virtualenv and install `bcdata` in development mode:
|
|
408
418
|
|
|
409
|
-
$
|
|
410
|
-
$
|
|
411
|
-
$
|
|
412
|
-
|
|
413
|
-
(
|
|
414
|
-
(
|
|
415
|
-
(bcdata_env)$ py.test
|
|
419
|
+
$ git clone git@github.com:smnorris/bcdata.git
|
|
420
|
+
$ cd bcdata
|
|
421
|
+
$ python -m venv .venv
|
|
422
|
+
$ source .venv/bin/activate
|
|
423
|
+
(.venv)$ pip install -e .[test]
|
|
424
|
+
(.venv)$ py.test
|
|
416
425
|
|
|
417
426
|
|
|
418
427
|
## Other implementations
|
|
@@ -9,7 +9,9 @@ There is a [wealth of British Columbia geographic information available as open
|
|
|
9
9
|
data](https://catalogue.data.gov.bc.ca/dataset?download_audience=Public),
|
|
10
10
|
but direct file download urls are not available and the syntax to accesss WFS via `ogr2ogr` and/or `curl/wget` can be awkward.
|
|
11
11
|
|
|
12
|
-
This
|
|
12
|
+
This tool attempts to simplify downloads of BC geographic data and smoothly integrate with PostGIS and Python GIS tools like `geopandas`, `fiona` and `rasterio`. As noted above, the module is an interface only to geographic data [avialable via WFS/WCS](https://bcgov.github.io/data-publication/pages/tips_tricks_webservices.html) - to work with other BC open data, use the [bcdata R package](https://github.com/bcgov/bcdata).
|
|
13
|
+
|
|
14
|
+
Note that this tool depends on BC Open Geospatial Web Services API at [openmaps.gov.bc.ca](https://openmaps.gov.bc.ca/geo/pub/ows?service=WFS&request=Getcapabilities) and the [BC Data Catalogue API](https://catalogue.data.gov.bc.ca/dataset/bc-data-catalogue-api). If downloads are failing, consult the [BCGov Data Systems and Services uptime monitor](https://uptime.com/statuspage/bcgov-dss).
|
|
13
15
|
|
|
14
16
|
**Disclaimer**
|
|
15
17
|
*It is the user's responsibility to check the licensing for any downloads, data are generally licensed as [OGL-BC](http://www2.gov.bc.ca/gov/content/governments/about-the-bc-government/databc/open-data/open-government-license-bc)*
|
|
@@ -119,25 +121,30 @@ Usage: bcdata bc2pg [OPTIONS] DATASET
|
|
|
119
121
|
|
|
120
122
|
Download a DataBC WFS layer to postgres
|
|
121
123
|
|
|
122
|
-
$ bcdata bc2pg
|
|
124
|
+
$ bcdata bc2pg whse_imagery_and_base_maps.gsr_airports_svw
|
|
123
125
|
|
|
124
126
|
Options:
|
|
125
|
-
-db, --db_url TEXT
|
|
126
|
-
|
|
127
|
-
--table TEXT
|
|
128
|
-
--schema TEXT
|
|
129
|
-
--geometry_type TEXT
|
|
130
|
-
--query TEXT
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
-
|
|
135
|
-
|
|
136
|
-
-
|
|
137
|
-
-
|
|
138
|
-
-
|
|
139
|
-
-
|
|
140
|
-
--
|
|
127
|
+
-db, --db_url TEXT Target database url, defaults to
|
|
128
|
+
$DATABASE_URL environment variable if set
|
|
129
|
+
--table TEXT Destination table name
|
|
130
|
+
--schema TEXT Destination schema name
|
|
131
|
+
--geometry_type TEXT Spatial type of geometry column
|
|
132
|
+
--query TEXT A valid CQL or ECQL query
|
|
133
|
+
--bounds TEXT Bounds: "left bottom right top" or "[left,
|
|
134
|
+
bottom, right, top]". Coordinates are BC
|
|
135
|
+
Albers (default) or --bounds_crs
|
|
136
|
+
--bounds-crs, --bounds_crs TEXT
|
|
137
|
+
CRS of provided bounds
|
|
138
|
+
-c, --count INTEGER Total number of features to load
|
|
139
|
+
-k, --primary_key TEXT Primary key of dataset
|
|
140
|
+
-s, --sortby TEXT Name of sort field
|
|
141
|
+
-e, --schema_only Create empty table from catalogue schema
|
|
142
|
+
-a, --append Append to existing table
|
|
143
|
+
-r, --refresh Truncate from existing table before load
|
|
144
|
+
-t, --no_timestamp Do not log download to bcdata.log
|
|
145
|
+
-v, --verbose Increase verbosity.
|
|
146
|
+
-q, --quiet Decrease verbosity.
|
|
147
|
+
--help Show this message and exit.
|
|
141
148
|
```
|
|
142
149
|
|
|
143
150
|
#### cat
|
|
@@ -298,7 +305,7 @@ Describe a dataset. Note that if we know the id of a dataset, we can use that ra
|
|
|
298
305
|
|
|
299
306
|
The JSON output can be manipulated with [jq](https://stedolan.github.io/jq/). For example, to show only the fields available in the dataset:
|
|
300
307
|
|
|
301
|
-
$ bcdata info bc-airports | jq '.schema
|
|
308
|
+
$ bcdata info bc-airports | jq '.schema'
|
|
302
309
|
{
|
|
303
310
|
"CUSTODIAN_ORG_DESCRIPTION": "string",
|
|
304
311
|
"BUSINESS_CATEGORY_CLASS": "string",
|
|
@@ -326,6 +333,11 @@ Save a layer to a geopackage in BC Albers:
|
|
|
326
333
|
|
|
327
334
|
Note that this will not work if the source data has mixed geometry types.
|
|
328
335
|
|
|
336
|
+
Load to parquet on s3:
|
|
337
|
+
|
|
338
|
+
$ bcdata dump bc-airports \
|
|
339
|
+
| ogr2ogr -f Parquet /vsis3/$BUCKET/airports.parquet -s_srs EPSG:4326 -t_srs EPSG:3005 /vsistdin/
|
|
340
|
+
|
|
329
341
|
Load data to postgres and run a spatial query:
|
|
330
342
|
|
|
331
343
|
$ bcdata bc2pg bc-airports \
|
|
@@ -372,13 +384,12 @@ Load data to postgres and run a spatial query:
|
|
|
372
384
|
|
|
373
385
|
Create virtualenv and install `bcdata` in development mode:
|
|
374
386
|
|
|
375
|
-
$
|
|
376
|
-
$
|
|
377
|
-
$
|
|
378
|
-
|
|
379
|
-
(
|
|
380
|
-
(
|
|
381
|
-
(bcdata_env)$ py.test
|
|
387
|
+
$ git clone git@github.com:smnorris/bcdata.git
|
|
388
|
+
$ cd bcdata
|
|
389
|
+
$ python -m venv .venv
|
|
390
|
+
$ source .venv/bin/activate
|
|
391
|
+
(.venv)$ pip install -e .[test]
|
|
392
|
+
(.venv)$ py.test
|
|
382
393
|
|
|
383
394
|
|
|
384
395
|
## Other implementations
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "bcdata"
|
|
7
|
+
dynamic = ["version"]
|
|
8
|
+
readme = "README.md"
|
|
9
|
+
license = {file = "LICENSE"}
|
|
10
|
+
authors = [
|
|
11
|
+
{name="Simon Norris", email="snorris@hillcrestgeo.ca"},
|
|
12
|
+
]
|
|
13
|
+
description = "Download open data, monitor and report on changes"
|
|
14
|
+
requires-python = ">=3.9"
|
|
15
|
+
classifiers = [
|
|
16
|
+
"Development Status :: 1 - Planning",
|
|
17
|
+
"Programming Language :: Python :: 3.9",
|
|
18
|
+
"Programming Language :: Python :: 3.10",
|
|
19
|
+
"Programming Language :: Python :: 3.11",
|
|
20
|
+
"Programming Language :: Python :: 3.12",
|
|
21
|
+
"Programming Language :: Python :: 3.13",
|
|
22
|
+
"License :: OSI Approved :: MIT License",
|
|
23
|
+
"Topic :: Scientific/Engineering :: GIS",
|
|
24
|
+
"Operating System :: OS Independent"
|
|
25
|
+
]
|
|
26
|
+
dependencies = [
|
|
27
|
+
"geoalchemy2",
|
|
28
|
+
"geopandas",
|
|
29
|
+
"owslib",
|
|
30
|
+
"psycopg2-binary",
|
|
31
|
+
"rasterio",
|
|
32
|
+
"requests",
|
|
33
|
+
"sqlalchemy",
|
|
34
|
+
"stamina"
|
|
35
|
+
]
|
|
36
|
+
|
|
37
|
+
[project.optional-dependencies]
|
|
38
|
+
test = [
|
|
39
|
+
"pytest",
|
|
40
|
+
"pre-commit",
|
|
41
|
+
"requests-mock"
|
|
42
|
+
]
|
|
43
|
+
|
|
44
|
+
[project.scripts]
|
|
45
|
+
bcdata = "bcdata.cli:cli"
|
|
46
|
+
|
|
47
|
+
[project.urls]
|
|
48
|
+
Homepage = "https://github.com/smnorris/bcdata"
|
|
49
|
+
Issues = "https://github.com/smnorris/bcdata"
|
|
50
|
+
|
|
51
|
+
[tool.setuptools.dynamic]
|
|
52
|
+
version = {attr = "bcdata.__version__"}
|
|
53
|
+
|
|
54
|
+
[tool.ruff]
|
|
55
|
+
line-length = 100
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import requests
|
|
2
|
+
|
|
3
|
+
from .bc2pg import bc2pg as bc2pg
|
|
4
|
+
from .bcdc import get_table_definition as get_table_definition
|
|
5
|
+
from .bcdc import get_table_name as get_table_name
|
|
6
|
+
from .wcs import get_dem as get_dem
|
|
7
|
+
from .wfs import define_requests as define_requests
|
|
8
|
+
from .wfs import get_count as get_count
|
|
9
|
+
from .wfs import get_data as get_data
|
|
10
|
+
from .wfs import get_features as get_features
|
|
11
|
+
from .wfs import get_sortkey as get_sortkey
|
|
12
|
+
from .wfs import list_tables as list_tables
|
|
13
|
+
from .wfs import validate_name as validate_name
|
|
14
|
+
|
|
15
|
+
PRIMARY_KEY_DB_URL = "https://raw.githubusercontent.com/smnorris/bcdata/main/data/primary_keys.json"
|
|
16
|
+
|
|
17
|
+
# BCDC does not indicate which column in the schema is the primary key.
|
|
18
|
+
# In this absence, bcdata maintains its own dictionary of {table: primary_key},
|
|
19
|
+
# served via github. Retrieve the dict with this function"""
|
|
20
|
+
response = requests.get(PRIMARY_KEY_DB_URL)
|
|
21
|
+
if response.status_code == 200:
|
|
22
|
+
primary_keys = response.json()
|
|
23
|
+
else:
|
|
24
|
+
raise Exception(f"Failed to download primary key database at {PRIMARY_KEY_DB_URL}")
|
|
25
|
+
primary_keys = {}
|
|
26
|
+
|
|
27
|
+
__version__ = "0.13.0"
|
|
@@ -1,12 +1,6 @@
|
|
|
1
|
-
import json
|
|
2
1
|
import logging
|
|
3
|
-
import os
|
|
4
2
|
|
|
5
|
-
import geopandas as gpd
|
|
6
3
|
import numpy
|
|
7
|
-
import stamina
|
|
8
|
-
from geoalchemy2 import Geometry
|
|
9
|
-
import requests
|
|
10
4
|
from shapely.geometry.linestring import LineString
|
|
11
5
|
from shapely.geometry.multilinestring import MultiLineString
|
|
12
6
|
from shapely.geometry.multipoint import MultiPoint
|
|
@@ -34,19 +28,6 @@ SUPPORTED_TYPES = [
|
|
|
34
28
|
]
|
|
35
29
|
|
|
36
30
|
|
|
37
|
-
def get_primary_keys():
|
|
38
|
-
"""download primary key data file"""
|
|
39
|
-
response = requests.get(bcdata.PRIMARY_KEY_DB_URL)
|
|
40
|
-
if response.status_code == 200:
|
|
41
|
-
primary_keys = response.json()
|
|
42
|
-
else:
|
|
43
|
-
log.warning(
|
|
44
|
-
f"Failed to download primary key database at {bcdata.PRIMARY_KEY_DB_URL}"
|
|
45
|
-
)
|
|
46
|
-
primary_keys = {}
|
|
47
|
-
return primary_keys
|
|
48
|
-
|
|
49
|
-
|
|
50
31
|
def bc2pg( # noqa: C901
|
|
51
32
|
dataset,
|
|
52
33
|
db_url,
|
|
@@ -54,12 +35,15 @@ def bc2pg( # noqa: C901
|
|
|
54
35
|
schema=None,
|
|
55
36
|
geometry_type=None,
|
|
56
37
|
query=None,
|
|
38
|
+
bounds=None,
|
|
39
|
+
bounds_crs="EPSG:3005",
|
|
57
40
|
count=None,
|
|
58
41
|
sortby=None,
|
|
59
42
|
primary_key=None,
|
|
60
43
|
timestamp=True,
|
|
61
44
|
schema_only=False,
|
|
62
45
|
append=False,
|
|
46
|
+
refresh=False,
|
|
63
47
|
):
|
|
64
48
|
"""Request table definition from bcdc and replicate in postgres"""
|
|
65
49
|
if schema_only and append:
|
|
@@ -72,6 +56,16 @@ def bc2pg( # noqa: C901
|
|
|
72
56
|
if table:
|
|
73
57
|
table_name = table.lower()
|
|
74
58
|
|
|
59
|
+
# take note if geometry type is specified as option
|
|
60
|
+
geometry_type_opt = geometry_type
|
|
61
|
+
|
|
62
|
+
# do not promote to multi if singlepart specified,
|
|
63
|
+
# otherwise default to always promoting to multipart in case mixed types are returned
|
|
64
|
+
if geometry_type_opt and geometry_type_opt.upper() in ["POINT", "LINE", "POLYGON"]:
|
|
65
|
+
promote_to_multi = False
|
|
66
|
+
else:
|
|
67
|
+
promote_to_multi = True
|
|
68
|
+
|
|
75
69
|
# connect to target db
|
|
76
70
|
db = Database(db_url)
|
|
77
71
|
|
|
@@ -82,6 +76,8 @@ def bc2pg( # noqa: C901
|
|
|
82
76
|
urls = bcdata.define_requests(
|
|
83
77
|
dataset,
|
|
84
78
|
query=query,
|
|
79
|
+
bounds=bounds,
|
|
80
|
+
bounds_crs=bounds_crs,
|
|
85
81
|
count=count,
|
|
86
82
|
sortby=sortby,
|
|
87
83
|
crs="epsg:3005",
|
|
@@ -101,19 +97,13 @@ def bc2pg( # noqa: C901
|
|
|
101
97
|
table_definition = bcdata.get_table_definition(dataset)
|
|
102
98
|
|
|
103
99
|
if not table_definition["schema"]:
|
|
104
|
-
raise ValueError(
|
|
105
|
-
"Cannot create table, schema details not found via bcdc api"
|
|
106
|
-
)
|
|
100
|
+
raise ValueError("Cannot create table, schema details not found via bcdc api")
|
|
107
101
|
|
|
108
102
|
# if geometry type is not provided, determine type by making the first request
|
|
109
103
|
if not geometry_type:
|
|
110
|
-
df = WFS.make_requests(
|
|
111
|
-
[urls[0]], as_gdf=True, crs="epsg:3005", lowercase=True
|
|
112
|
-
)
|
|
104
|
+
df = WFS.make_requests(dataset=dataset, urls=[urls[0]], as_gdf=True, crs="epsg:3005", lowercase=True)
|
|
113
105
|
geometry_type = df.geom_type.unique()[0] # keep only the first type
|
|
114
|
-
if numpy.any(
|
|
115
|
-
df.has_z.unique()[0]
|
|
116
|
-
): # geopandas does not include Z in geom_type string
|
|
106
|
+
if numpy.any(df.has_z.unique()[0]): # geopandas does not include Z in geom_type string
|
|
117
107
|
geometry_type = geometry_type + "Z"
|
|
118
108
|
|
|
119
109
|
# if geometry type is still not populated try the last request
|
|
@@ -121,15 +111,14 @@ def bc2pg( # noqa: C901
|
|
|
121
111
|
if not geometry_type:
|
|
122
112
|
if not urls[-1] == urls[0]:
|
|
123
113
|
df_temp = WFS.make_requests(
|
|
124
|
-
|
|
114
|
+
dataset=dataset,
|
|
115
|
+
urls=[urls[-1]],
|
|
125
116
|
as_gdf=True,
|
|
126
117
|
crs="epsg:3005",
|
|
127
118
|
lowercase=True,
|
|
128
119
|
silent=True,
|
|
129
120
|
)
|
|
130
|
-
geometry_type = df_temp.geom_type.unique()[
|
|
131
|
-
0
|
|
132
|
-
] # keep only the first type
|
|
121
|
+
geometry_type = df_temp.geom_type.unique()[0] # keep only the first type
|
|
133
122
|
if numpy.any(
|
|
134
123
|
df_temp.has_z.unique()[0]
|
|
135
124
|
): # geopandas does not include Z in geom_type string
|
|
@@ -143,9 +132,8 @@ def bc2pg( # noqa: C901
|
|
|
143
132
|
raise ValueError("Geometry type {geometry_type} is not supported")
|
|
144
133
|
|
|
145
134
|
# if primary key is not supplied, use default (if present in list)
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
primary_key = primary_keys[dataset.lower()]
|
|
135
|
+
if not primary_key and dataset.lower() in bcdata.primary_keys:
|
|
136
|
+
primary_key = bcdata.primary_keys[dataset.lower()]
|
|
149
137
|
|
|
150
138
|
# fail if specified primary key is not in the table
|
|
151
139
|
if primary_key and primary_key.upper() not in [
|
|
@@ -161,6 +149,7 @@ def bc2pg( # noqa: C901
|
|
|
161
149
|
table_name,
|
|
162
150
|
table_definition["schema"],
|
|
163
151
|
geometry_type,
|
|
152
|
+
promote_to_multi,
|
|
164
153
|
table_definition["comments"],
|
|
165
154
|
primary_key,
|
|
166
155
|
)
|
|
@@ -168,9 +157,7 @@ def bc2pg( # noqa: C901
|
|
|
168
157
|
|
|
169
158
|
# check if column provided in sortby option is present in dataset
|
|
170
159
|
if sortby and sortby.lower() not in column_names:
|
|
171
|
-
raise ValueError(
|
|
172
|
-
f"Specified sortby column {sortby} is not present in {dataset}"
|
|
173
|
-
)
|
|
160
|
+
raise ValueError(f"Specified sortby column {sortby} is not present in {dataset}")
|
|
174
161
|
|
|
175
162
|
# load the data
|
|
176
163
|
if not schema_only:
|
|
@@ -178,9 +165,7 @@ def bc2pg( # noqa: C901
|
|
|
178
165
|
for n, url in enumerate(urls):
|
|
179
166
|
# if first url not downloaded above when checking geom type, do now
|
|
180
167
|
if df is None:
|
|
181
|
-
df = WFS.make_requests(
|
|
182
|
-
[url], as_gdf=True, crs="epsg:3005", lowercase=True
|
|
183
|
-
)
|
|
168
|
+
df = WFS.make_requests(dataset=dataset, urls=[url], as_gdf=True, crs="epsg:3005", lowercase=True)
|
|
184
169
|
# tidy the resulting dataframe
|
|
185
170
|
df = df.rename_geometry("geom")
|
|
186
171
|
# lowercasify
|
|
@@ -193,23 +178,20 @@ def bc2pg( # noqa: C901
|
|
|
193
178
|
df_nulls = df_nulls.drop(columns=["geom"])
|
|
194
179
|
# remove rows with null geometry from geodataframe
|
|
195
180
|
df = df[df["geom"].notna()]
|
|
196
|
-
#
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
MultiPolygon([feature]) if isinstance(feature, Polygon) else feature
|
|
211
|
-
for feature in df["geom"]
|
|
212
|
-
]
|
|
181
|
+
# promote to multipart
|
|
182
|
+
if promote_to_multi:
|
|
183
|
+
df["geom"] = [
|
|
184
|
+
MultiPoint([feature]) if isinstance(feature, Point) else feature
|
|
185
|
+
for feature in df["geom"]
|
|
186
|
+
]
|
|
187
|
+
df["geom"] = [
|
|
188
|
+
MultiLineString([feature]) if isinstance(feature, LineString) else feature
|
|
189
|
+
for feature in df["geom"]
|
|
190
|
+
]
|
|
191
|
+
df["geom"] = [
|
|
192
|
+
MultiPolygon([feature]) if isinstance(feature, Polygon) else feature
|
|
193
|
+
for feature in df["geom"]
|
|
194
|
+
]
|
|
213
195
|
|
|
214
196
|
# run the load in two parts, one with geoms, one with no geoms
|
|
215
197
|
log.info(f"Writing {dataset} to database as {schema_name}.{table_name}")
|
|
@@ -224,22 +206,8 @@ def bc2pg( # noqa: C901
|
|
|
224
206
|
df = None
|
|
225
207
|
|
|
226
208
|
# once load complete, note date/time of load completion in bcdata.log
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
db.
|
|
230
|
-
"""CREATE SCHEMA IF NOT EXISTS bcdata;
|
|
231
|
-
CREATE TABLE IF NOT EXISTS bcdata.log (
|
|
232
|
-
table_name text PRIMARY KEY,
|
|
233
|
-
latest_download timestamp WITH TIME ZONE
|
|
234
|
-
);
|
|
235
|
-
"""
|
|
236
|
-
)
|
|
237
|
-
db.execute(
|
|
238
|
-
"""INSERT INTO bcdata.log (table_name, latest_download)
|
|
239
|
-
SELECT %s as table_name, NOW() as latest_download
|
|
240
|
-
ON CONFLICT (table_name) DO UPDATE SET latest_download = NOW();
|
|
241
|
-
""",
|
|
242
|
-
(schema_name + "." + table_name,),
|
|
243
|
-
)
|
|
209
|
+
# do not log refreshes here, they called by cli once loaded to target table
|
|
210
|
+
if timestamp and not refresh:
|
|
211
|
+
db.log(schema_name, table_name)
|
|
244
212
|
|
|
245
213
|
return schema_name + "." + table_name
|