Metadata-Version: 2.4
Name: canwxdb
Version: 0.1.0
Summary: Python client for the canwxdb Canadian weather observations API
Project-URL: Homepage, https://wxdb.ca
Project-URL: Repository, https://github.com/neuroblaze/canwxdb-py
Project-URL: Bug Tracker, https://github.com/neuroblaze/canwxdb-py/issues
License: MIT
Requires-Python: >=3.8
Requires-Dist: requests>=2.20
Provides-Extra: all
Requires-Dist: geopandas>=0.9; extra == 'all'
Requires-Dist: pandas>=1.0; extra == 'all'
Requires-Dist: shapely>=1.7; extra == 'all'
Requires-Dist: tqdm>=4.0; extra == 'all'
Provides-Extra: geo
Requires-Dist: geopandas>=0.9; extra == 'geo'
Requires-Dist: shapely>=1.7; extra == 'geo'
Provides-Extra: pandas
Requires-Dist: pandas>=1.0; extra == 'pandas'
Provides-Extra: progress
Requires-Dist: tqdm>=4.0; extra == 'progress'
Description-Content-Type: text/markdown

# canwxdb-py

Python client for the [canwxdb](https://wxdb.ca) API — historical Canadian weather observations from Environment and Climate Change Canada, queryable by geography and time range.

> **Community project — not affiliated with or endorsed by Environment and Climate Change Canada.**

## Installation

```bash
pip install canwxdb
```

Optional extras for richer output formats and progress bars:

```bash
pip install canwxdb[pandas]      # DataFrame support
pip install canwxdb[geo]         # GeoDataFrame support (includes pandas)
pip install canwxdb[progress]    # tqdm progress bars
pip install canwxdb[all]         # everything above
```

## Quick start

```python
from canwxdb import Client

client = Client()
```

### Look up a station

```python
station = client.station(52)
# {'type': 'Feature', 'geometry': ..., 'properties': {'name': 'ESQUIMALT HARBOUR', ...}}
```

### Find stations near a location

```python
# By lat/lon + radius
stations = client.stations(lat=48.43, lon=-123.37, radius_km=50)

# By Canadian postal code
stations = client.stations(postal_code="V8W1A1", radius_km=25)

# By bounding box  (min_lon, min_lat, max_lon, max_lat)
stations = client.stations(bbox=(-124.5, 48.3, -123.0, 49.0))
```

### Fetch observations

```python
# Daily observations for a single station
obs = client.daily(station_id=52, start="2024-01-01", end="2024-12-31")

# Hourly observations
obs = client.hourly(station_id=52, start="2024-07-01", end="2024-07-31")

# Monthly observations
obs = client.monthly(station_id=52, start="1990-01-01", end="2024-12-31")
```

All three methods accept the same geographic filters as `stations()`.

Date arguments accept ISO 8601 strings, `datetime.date`, or `datetime.datetime` objects.

### Get a pandas DataFrame

```python
df = client.daily(
    station_id=52,
    start="2024-01-01",
    end="2024-12-31",
    format="dataframe",
)

df.head()
#         date  station_id  mean_temp_c  ...  latitude  longitude
# 0 2024-01-01          52         -1.2  ...     48.43    -123.43
# 1 2024-01-02          52          0.5  ...     48.43    -123.43
```

### Get a GeoDataFrame (GeoPandas)

```python
gdf = client.daily(
    postal_code="V8W1A1",
    radius_km=100,
    start="2023-01-01",
    end="2023-12-31",
    format="geodataframe",
)

# Plot mean temperature across stations
gdf.plot(column="mean_temp_c", legend=True, figsize=(10, 6))
```

The GeoDataFrame uses WGS-84 (EPSG:4326) and has a `Point` geometry column built from each station's coordinates.

### Progress bars (recommended for large queries)

For multi-page queries — especially in Jupyter notebooks — pass `show_progress=True` to see live row counts. The library automatically uses `tqdm.notebook` inside Jupyter and plain `tqdm` elsewhere.

```python
# In a Jupyter notebook this renders a rich notebook-style progress bar.
df = client.daily(
    bbox=(-124.5, 48.3, -123.0, 49.0),
    start="2015-01-01",
    end="2024-12-31",
    format="dataframe",
    show_progress=True,
)
# Fetching: 45000 rows [00:18, 2490 rows/s]
```

### Limit pages for exploratory queries

When exploring a new dataset, use `max_pages` to avoid accidentally fetching millions of rows:

```python
# Just peek at the first page
df = client.hourly(
    bbox=(-80, 43, -76, 44),
    start="2020-01-01",
    end="2023-12-31",
    format="dataframe",
    max_pages=1,
)
```

### Using as a context manager

```python
with Client() as client:
    df = client.daily(station_id=52, start="2024-01-01", end="2024-12-31",
                      format="dataframe")
```

### Connecting to a custom endpoint

```python
client = Client(base_url="https://my-private-instance.example.com")
```

## Output formats

| `format=` | Return type | Requires |
|---|---|---|
| `"geojson"` (default) | `dict` (GeoJSON FeatureCollection) | — |
| `"dataframe"` | `pandas.DataFrame` | `pandas` |
| `"geodataframe"` | `geopandas.GeoDataFrame` | `geopandas`, `shapely` |

## Error handling

```python
from canwxdb import Client, WxDBNotFoundError, WxDBRateLimitError

client = Client()

try:
    station = client.station(99999)
except WxDBNotFoundError:
    print("Station not found")

# The client automatically retries 429 responses with exponential back-off.
# WxDBRateLimitError is raised only if all retries are exhausted.
```

| Exception | Raised when |
|---|---|
| `WxDBNotFoundError` | HTTP 404 |
| `WxDBRateLimitError` | HTTP 429 after all retries |
| `WxDBHTTPError` | Any other non-2xx response |
| `WxDBError` | Base class for all of the above |

## Data source

Weather data is sourced from [Environment and Climate Change Canada's open data](https://climate.weather.gc.ca/) under the [Government of Canada Open Government Licence](https://open.canada.ca/en/open-government-licence-canada).
