sgu-client 0.1.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.
@@ -0,0 +1,277 @@
1
+ Metadata-Version: 2.4
2
+ Name: sgu-client
3
+ Version: 0.1.0
4
+ Summary: A modern Python client library for accessing Geological Survey of Sweden groundwater data APIs with type safety and pandas integration
5
+ Author: Anders Retzner
6
+ Author-email: Anders Retzner <anders.retzner@gmail.com>
7
+ License-Expression: MIT
8
+ Requires-Dist: requests>=2.28.0
9
+ Requires-Dist: pydantic>=2.0.0
10
+ Requires-Dist: pandas>=1.5.0 ; extra == 'recommended'
11
+ Requires-Python: >=3.11
12
+ Provides-Extra: recommended
13
+ Description-Content-Type: text/markdown
14
+
15
+ # SGU Client
16
+
17
+ A modern Python client library for accessing Geological Survey of Sweden (SGU) groundwater data APIs with type safety and pandas integration.
18
+
19
+ [![Python Version](https://img.shields.io/badge/python-3.11+-blue.svg)](https://python.org)
20
+ [![PyPI version](https://badge.fury.io/py/sgu-client.svg)](https://badge.fury.io/py/sgu-client)
21
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
22
+ [![Code style: ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
23
+
24
+ > This package is not affiliated with or endorsed by SGU.
25
+
26
+ ## Features
27
+
28
+ - **Type-safe**: Full type hints with Pydantic validation
29
+ - **Pandas integration**: Convert data to DataFrames with optional pandas dependency
30
+ - **Friendly shortcuts**: Convenience functions to access stations by names, modeled levels by coordinates, and much more.
31
+
32
+ Get going with your analysis in just a few lines of code:
33
+
34
+ ```python
35
+ from sgu_client import SGUClient
36
+
37
+ with SGUClient() as client:
38
+ measurements = client.levels.observed.get_measurements_by_name(platsbeteckning="95_2")
39
+ df = measurements.to_dataframe()
40
+
41
+ # ... rest of your awesome workflow
42
+ ```
43
+
44
+ ## Installation
45
+
46
+ Using `uv`
47
+
48
+ ```bash
49
+ # basic installation
50
+ uv add sgu-client
51
+
52
+ # with pandas support for dataframe conversion
53
+ uv add "sgu-client[recommended]"
54
+ ```
55
+
56
+ ... or using `pip`
57
+
58
+ ```bash
59
+ # basic installation
60
+ pip install sgu-client
61
+
62
+ # with pandas support for dataframe conversion
63
+ pip install "sgu-client[recommended]"
64
+ ```
65
+
66
+ ## Usage
67
+
68
+ ### Initializing a client
69
+
70
+ ```python
71
+ from sgu_client import SGUClient
72
+
73
+ client = SGUClient()
74
+
75
+ # with custom configuration
76
+ from sgu_client import SGUConfig
77
+ config = SGUConfig(timeout=60, debug=True, max_retries=5)
78
+ client = SGUClient(config=config)
79
+
80
+ # as a context manager
81
+ with SGUClient() as client:
82
+ stations = client.levels.observed.get_stations(limit=10)
83
+ ```
84
+
85
+ ### Observed groundwater levels
86
+
87
+ Access real-time and historical groundwater monitoring data from SGU's network of observation stations.
88
+
89
+ ```python
90
+ from sgu_client import SGUClient
91
+ from datetime import UTC, datetime
92
+
93
+
94
+ with SGUClient() as client:
95
+ # basic API endpoint usage
96
+ stations = client.levels.observed.get_stations(limit=50)
97
+
98
+ # with OGC API filters, like bbox of southern Sweden
99
+ stations = client.levels.observed.get_stations(
100
+ bbox=[12.0, 55.0, 16.0, 58.0],
101
+ limit=100
102
+ )
103
+
104
+ # convenience function to get station by name
105
+ station = client.levels.observed.get_station_by_name(
106
+ platsbeteckning="95_2" # or obsplatsnamn="95_2"
107
+ )
108
+ # or multiple stations by names
109
+ stations = client.levels.observed.get_stations_by_names(
110
+ platsbeteckning=["95_2", "101_1"] # or obsplatsnamn=["Lagga_2", ...]
111
+ )
112
+
113
+ # convenience function to get measurements by station name
114
+ measurements = client.levels.observed.get_measurements_by_name(
115
+ platsbeteckning="95_2", # or obsplatsnamn="95_2"
116
+ limit=100
117
+ )
118
+ # or multiple stations by names
119
+ measurements = client.levels.observed.get_measurements_by_names(
120
+ platsbeteckning=["95_2", "101_1"], # or obsplatsnamn=["Lagga_2", ...]
121
+ limit=100
122
+ )
123
+
124
+ # filter by datetime for faster responses
125
+ tmin = datetime(2020, 1, 1, tzinfo=UTC)
126
+ tmax = datetime(2021, 1, 1, tzinfo=UTC)
127
+ measurements = client.levels.observed.get_measurements_by_names(
128
+ platsbeteckning=["95_2"], tmin=tmin, tmax=tmax, limit=10
129
+ )
130
+
131
+ # responses that create lists of features can be converted to pandas DataFrames
132
+ measurements = client.levels.observed.get_measurements_by_names(
133
+ platsbeteckning=["95_2", "101_1"], # or obsplatsnamn=["Lagga_2", ...]
134
+ limit=100
135
+ )
136
+ df = measurements.to_dataframe() # requires pandas
137
+ ```
138
+
139
+ ### Modeled groundwater levels
140
+
141
+ Access modeled groundwater levels from SGU-HYPE.
142
+
143
+ ```python
144
+ from sgu_client import SGUClient
145
+
146
+ with SGUClient() as client:
147
+ # basic API endpoint usage
148
+ areas = client.levels.modeled.get_areas(limit=10)
149
+
150
+ # with OGC API filters, like bbox of southern Sweden
151
+ areas = client.levels.modeled.get_areas(
152
+ bbox=[12.0, 55.0, 16.0, 58.0],
153
+ limit=20
154
+ )
155
+
156
+ # get a specific area by ID
157
+ area = client.levels.modeled.get_area("omraden.30125")
158
+ print(f"Area ID: {area.properties.omrade_id}")
159
+ print(f"Geometry: {area.geometry.type}")
160
+
161
+ # convenience function to get levels for a specific area
162
+ levels = client.levels.modeled.get_levels_by_area(30125, limit=10)
163
+ # or multiple areas by IDs
164
+ levels = client.levels.modeled.get_levels_by_areas(
165
+ area_ids=[30125, 30126],
166
+ limit=50
167
+ )
168
+
169
+ # convenience function to get levels by coordinates
170
+ levels = client.levels.modeled.get_levels_by_coords(
171
+ lat=57.7089,
172
+ lon=11.9746,
173
+ limit=10
174
+ )
175
+
176
+ # responses that create lists of features can be converted to pandas DataFrames
177
+ levels = client.levels.modeled.get_levels_by_areas(
178
+ area_ids=[30125, 30126],
179
+ limit=50
180
+ )
181
+ df = levels.to_dataframe() # requires pandas
182
+ ```
183
+
184
+ ### Working with Typed Data
185
+
186
+ All responses are fully typed with Pydantic models:
187
+
188
+ ```python
189
+ from sgu_client import SGUClient
190
+
191
+ client = SGUClient()
192
+
193
+ # Get stations with full type safety
194
+ stations = client.levels.observed.get_stations(limit=5)
195
+
196
+ for station in stations.features:
197
+ # All properties are typed and documented
198
+ print(f"Station: {station.properties.obsplatsnamn}")
199
+ print(f"Municipality: {station.properties.kommun}")
200
+ print(f"Coordinates: {station.geometry.coordinates}")
201
+
202
+ # Get measurements with automatic datetime parsing
203
+ measurements = client.levels.observed.get_measurements(limit=5)
204
+ for measurement in measurements.features:
205
+ props = measurement.properties
206
+ print(f"Date: {props.observation_date}") # Parsed datetime object
207
+ print(f"Level: {props.grundvattenniva_m_o_h} m above sea level")
208
+ ```
209
+
210
+ ## API Reference
211
+
212
+ ### SGUClient
213
+
214
+ The main client class providing access to all SGU APIs.
215
+
216
+ - `levels.observed` - Observed groundwater level measurements
217
+ - `levels.modeled` - Modeled groundwater levels from SGU-HYPE
218
+
219
+ ### Configuration
220
+
221
+ ```python
222
+ from sgu_client import SGUConfig
223
+
224
+ config = SGUConfig(
225
+ timeout=30, # Request timeout in seconds
226
+ max_retries=3, # Maximum retry attempts
227
+ debug=False # Enable debug logging
228
+ )
229
+ ```
230
+
231
+ ## Development
232
+
233
+ We recommend using [uv](https://github.com/astral-sh/uv) for development:
234
+
235
+ ```bash
236
+ # Clone the repository
237
+ git clone https://github.com/officialankan/sgu-client.git
238
+ cd sgu-client
239
+
240
+ # Install dependencies and sync environment
241
+ uv sync --all-extras
242
+
243
+ # Run tests
244
+ uv run pytest
245
+
246
+ # Format and lint code
247
+ uv run ruff format
248
+ uv run ruff check --fix
249
+ ```
250
+
251
+ ### Release Process
252
+
253
+ To release a new version:
254
+
255
+ 1. Create PR with your changes + version bump in `pyproject.toml`
256
+ 2. PR will be tested and published to TestPyPI for verification
257
+ 3. Once merged, the new version is automatically published to PyPI
258
+
259
+ ## Contributing
260
+
261
+ Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.
262
+
263
+ ## Roadmap
264
+
265
+ - [x] Initial release with observed and modeled groundwater levels
266
+ - [ ] Add example notebooks and tutorials
267
+ - [ ] Add support for groundwater quality API
268
+ - [ ] Add support for geological data API
269
+
270
+ ## License
271
+
272
+ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
273
+
274
+ ## Acknowledgments
275
+
276
+ - [Sveriges geologiska undersökning (SGU)](https://www.sgu.se/) for providing open access to groundwater data
277
+ - Built with [uv](https://github.com/astral-sh/uv), [Pydantic](https://pydantic.dev/), and [requests](https://requests.readthedocs.io/)
@@ -0,0 +1,263 @@
1
+ # SGU Client
2
+
3
+ A modern Python client library for accessing Geological Survey of Sweden (SGU) groundwater data APIs with type safety and pandas integration.
4
+
5
+ [![Python Version](https://img.shields.io/badge/python-3.11+-blue.svg)](https://python.org)
6
+ [![PyPI version](https://badge.fury.io/py/sgu-client.svg)](https://badge.fury.io/py/sgu-client)
7
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
8
+ [![Code style: ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
9
+
10
+ > This package is not affiliated with or endorsed by SGU.
11
+
12
+ ## Features
13
+
14
+ - **Type-safe**: Full type hints with Pydantic validation
15
+ - **Pandas integration**: Convert data to DataFrames with optional pandas dependency
16
+ - **Friendly shortcuts**: Convenience functions to access stations by names, modeled levels by coordinates, and much more.
17
+
18
+ Get going with your analysis in just a few lines of code:
19
+
20
+ ```python
21
+ from sgu_client import SGUClient
22
+
23
+ with SGUClient() as client:
24
+ measurements = client.levels.observed.get_measurements_by_name(platsbeteckning="95_2")
25
+ df = measurements.to_dataframe()
26
+
27
+ # ... rest of your awesome workflow
28
+ ```
29
+
30
+ ## Installation
31
+
32
+ Using `uv`
33
+
34
+ ```bash
35
+ # basic installation
36
+ uv add sgu-client
37
+
38
+ # with pandas support for dataframe conversion
39
+ uv add "sgu-client[recommended]"
40
+ ```
41
+
42
+ ... or using `pip`
43
+
44
+ ```bash
45
+ # basic installation
46
+ pip install sgu-client
47
+
48
+ # with pandas support for dataframe conversion
49
+ pip install "sgu-client[recommended]"
50
+ ```
51
+
52
+ ## Usage
53
+
54
+ ### Initializing a client
55
+
56
+ ```python
57
+ from sgu_client import SGUClient
58
+
59
+ client = SGUClient()
60
+
61
+ # with custom configuration
62
+ from sgu_client import SGUConfig
63
+ config = SGUConfig(timeout=60, debug=True, max_retries=5)
64
+ client = SGUClient(config=config)
65
+
66
+ # as a context manager
67
+ with SGUClient() as client:
68
+ stations = client.levels.observed.get_stations(limit=10)
69
+ ```
70
+
71
+ ### Observed groundwater levels
72
+
73
+ Access real-time and historical groundwater monitoring data from SGU's network of observation stations.
74
+
75
+ ```python
76
+ from sgu_client import SGUClient
77
+ from datetime import UTC, datetime
78
+
79
+
80
+ with SGUClient() as client:
81
+ # basic API endpoint usage
82
+ stations = client.levels.observed.get_stations(limit=50)
83
+
84
+ # with OGC API filters, like bbox of southern Sweden
85
+ stations = client.levels.observed.get_stations(
86
+ bbox=[12.0, 55.0, 16.0, 58.0],
87
+ limit=100
88
+ )
89
+
90
+ # convenience function to get station by name
91
+ station = client.levels.observed.get_station_by_name(
92
+ platsbeteckning="95_2" # or obsplatsnamn="95_2"
93
+ )
94
+ # or multiple stations by names
95
+ stations = client.levels.observed.get_stations_by_names(
96
+ platsbeteckning=["95_2", "101_1"] # or obsplatsnamn=["Lagga_2", ...]
97
+ )
98
+
99
+ # convenience function to get measurements by station name
100
+ measurements = client.levels.observed.get_measurements_by_name(
101
+ platsbeteckning="95_2", # or obsplatsnamn="95_2"
102
+ limit=100
103
+ )
104
+ # or multiple stations by names
105
+ measurements = client.levels.observed.get_measurements_by_names(
106
+ platsbeteckning=["95_2", "101_1"], # or obsplatsnamn=["Lagga_2", ...]
107
+ limit=100
108
+ )
109
+
110
+ # filter by datetime for faster responses
111
+ tmin = datetime(2020, 1, 1, tzinfo=UTC)
112
+ tmax = datetime(2021, 1, 1, tzinfo=UTC)
113
+ measurements = client.levels.observed.get_measurements_by_names(
114
+ platsbeteckning=["95_2"], tmin=tmin, tmax=tmax, limit=10
115
+ )
116
+
117
+ # responses that create lists of features can be converted to pandas DataFrames
118
+ measurements = client.levels.observed.get_measurements_by_names(
119
+ platsbeteckning=["95_2", "101_1"], # or obsplatsnamn=["Lagga_2", ...]
120
+ limit=100
121
+ )
122
+ df = measurements.to_dataframe() # requires pandas
123
+ ```
124
+
125
+ ### Modeled groundwater levels
126
+
127
+ Access modeled groundwater levels from SGU-HYPE.
128
+
129
+ ```python
130
+ from sgu_client import SGUClient
131
+
132
+ with SGUClient() as client:
133
+ # basic API endpoint usage
134
+ areas = client.levels.modeled.get_areas(limit=10)
135
+
136
+ # with OGC API filters, like bbox of southern Sweden
137
+ areas = client.levels.modeled.get_areas(
138
+ bbox=[12.0, 55.0, 16.0, 58.0],
139
+ limit=20
140
+ )
141
+
142
+ # get a specific area by ID
143
+ area = client.levels.modeled.get_area("omraden.30125")
144
+ print(f"Area ID: {area.properties.omrade_id}")
145
+ print(f"Geometry: {area.geometry.type}")
146
+
147
+ # convenience function to get levels for a specific area
148
+ levels = client.levels.modeled.get_levels_by_area(30125, limit=10)
149
+ # or multiple areas by IDs
150
+ levels = client.levels.modeled.get_levels_by_areas(
151
+ area_ids=[30125, 30126],
152
+ limit=50
153
+ )
154
+
155
+ # convenience function to get levels by coordinates
156
+ levels = client.levels.modeled.get_levels_by_coords(
157
+ lat=57.7089,
158
+ lon=11.9746,
159
+ limit=10
160
+ )
161
+
162
+ # responses that create lists of features can be converted to pandas DataFrames
163
+ levels = client.levels.modeled.get_levels_by_areas(
164
+ area_ids=[30125, 30126],
165
+ limit=50
166
+ )
167
+ df = levels.to_dataframe() # requires pandas
168
+ ```
169
+
170
+ ### Working with Typed Data
171
+
172
+ All responses are fully typed with Pydantic models:
173
+
174
+ ```python
175
+ from sgu_client import SGUClient
176
+
177
+ client = SGUClient()
178
+
179
+ # Get stations with full type safety
180
+ stations = client.levels.observed.get_stations(limit=5)
181
+
182
+ for station in stations.features:
183
+ # All properties are typed and documented
184
+ print(f"Station: {station.properties.obsplatsnamn}")
185
+ print(f"Municipality: {station.properties.kommun}")
186
+ print(f"Coordinates: {station.geometry.coordinates}")
187
+
188
+ # Get measurements with automatic datetime parsing
189
+ measurements = client.levels.observed.get_measurements(limit=5)
190
+ for measurement in measurements.features:
191
+ props = measurement.properties
192
+ print(f"Date: {props.observation_date}") # Parsed datetime object
193
+ print(f"Level: {props.grundvattenniva_m_o_h} m above sea level")
194
+ ```
195
+
196
+ ## API Reference
197
+
198
+ ### SGUClient
199
+
200
+ The main client class providing access to all SGU APIs.
201
+
202
+ - `levels.observed` - Observed groundwater level measurements
203
+ - `levels.modeled` - Modeled groundwater levels from SGU-HYPE
204
+
205
+ ### Configuration
206
+
207
+ ```python
208
+ from sgu_client import SGUConfig
209
+
210
+ config = SGUConfig(
211
+ timeout=30, # Request timeout in seconds
212
+ max_retries=3, # Maximum retry attempts
213
+ debug=False # Enable debug logging
214
+ )
215
+ ```
216
+
217
+ ## Development
218
+
219
+ We recommend using [uv](https://github.com/astral-sh/uv) for development:
220
+
221
+ ```bash
222
+ # Clone the repository
223
+ git clone https://github.com/officialankan/sgu-client.git
224
+ cd sgu-client
225
+
226
+ # Install dependencies and sync environment
227
+ uv sync --all-extras
228
+
229
+ # Run tests
230
+ uv run pytest
231
+
232
+ # Format and lint code
233
+ uv run ruff format
234
+ uv run ruff check --fix
235
+ ```
236
+
237
+ ### Release Process
238
+
239
+ To release a new version:
240
+
241
+ 1. Create PR with your changes + version bump in `pyproject.toml`
242
+ 2. PR will be tested and published to TestPyPI for verification
243
+ 3. Once merged, the new version is automatically published to PyPI
244
+
245
+ ## Contributing
246
+
247
+ Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.
248
+
249
+ ## Roadmap
250
+
251
+ - [x] Initial release with observed and modeled groundwater levels
252
+ - [ ] Add example notebooks and tutorials
253
+ - [ ] Add support for groundwater quality API
254
+ - [ ] Add support for geological data API
255
+
256
+ ## License
257
+
258
+ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
259
+
260
+ ## Acknowledgments
261
+
262
+ - [Sveriges geologiska undersökning (SGU)](https://www.sgu.se/) for providing open access to groundwater data
263
+ - Built with [uv](https://github.com/astral-sh/uv), [Pydantic](https://pydantic.dev/), and [requests](https://requests.readthedocs.io/)
@@ -0,0 +1,66 @@
1
+ [project]
2
+ name = "sgu-client"
3
+ version = "0.1.0"
4
+ description = "A modern Python client library for accessing Geological Survey of Sweden groundwater data APIs with type safety and pandas integration"
5
+ readme = "README.md"
6
+ authors = [{ name = "Anders Retzner", email = "anders.retzner@gmail.com" }]
7
+ license = "MIT"
8
+ requires-python = ">=3.11"
9
+ dependencies = ["requests>=2.28.0", "pydantic>=2.0.0"]
10
+
11
+ [build-system]
12
+ requires = ["uv_build>=0.8.14,<0.9.0"]
13
+ build-backend = "uv_build"
14
+
15
+ [project.optional-dependencies]
16
+ recommended = ["pandas>=1.5.0"]
17
+
18
+ [dependency-groups]
19
+ dev = [
20
+ "pytest-cov>=6.0.0",
21
+ "pytest>=8.4.1",
22
+ "ruff>=0.12.11",
23
+ ]
24
+
25
+ [tool.ruff]
26
+ line-length = 88
27
+ target-version = "py311"
28
+
29
+ [tool.ruff.lint]
30
+ select = [
31
+ "E", # pycodestyle errors
32
+ "W", # pycodestyle warnings
33
+ "F", # pyflakes
34
+ "I", # isort
35
+ "B", # flake8-bugbear
36
+ "C4", # flake8-comprehensions
37
+ "UP", # pyupgrade
38
+ "ARG", # flake8-unused-arguments
39
+ "SIM", # flake8-simplify
40
+ "TCH", # flake8-type-checking
41
+ "TID", # flake8-tidy-imports
42
+ "RUF", # Ruff-specific rules
43
+ ]
44
+ ignore = [
45
+ "E501", # line too long, handled by formatter
46
+ "B008", # do not perform function calls in argument defaults
47
+ "C901", # too complex
48
+ "W191", # indentation contains tabs
49
+ ]
50
+
51
+ [tool.ruff.format]
52
+ quote-style = "double"
53
+ indent-style = "space"
54
+ skip-magic-trailing-comma = false
55
+ line-ending = "auto"
56
+
57
+ [tool.ruff.lint.per-file-ignores]
58
+ "__init__.py" = ["F401"] # Allow unused imports in __init__.py files
59
+
60
+ [tool.ruff.lint.isort]
61
+ known-first-party = ["sgu_client"]
62
+
63
+
64
+ [tool.pytest.ini_options]
65
+ addopts = "--cov=src --cov-report=term-missing --cov-report=html --cov-branch"
66
+ testpaths = ["tests"]
@@ -0,0 +1,22 @@
1
+ """SGU Client - Python library for accessing SGU groundwater data."""
2
+
3
+ from .config import SGUConfig
4
+ from .exceptions import (
5
+ SGUAPIError,
6
+ SGUClientError,
7
+ SGUConnectionError,
8
+ SGUTimeoutError,
9
+ SGUValidationError,
10
+ )
11
+ from .sgu_client import SGUClient
12
+
13
+ __version__ = "0.1.0"
14
+ __all__ = [
15
+ "SGUAPIError",
16
+ "SGUClient",
17
+ "SGUClientError",
18
+ "SGUConfig",
19
+ "SGUConnectionError",
20
+ "SGUTimeoutError",
21
+ "SGUValidationError",
22
+ ]
@@ -0,0 +1,5 @@
1
+ """Client module for SGU API."""
2
+
3
+ from .base import BaseClient
4
+
5
+ __all__ = ["BaseClient"]