diff --git a/CHANGELOG.md b/CHANGELOG.md index e400e6e..1c7f2d0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,7 +13,8 @@ versions of pandas (>= 2.2), GeoPandas (>= 1.0), scikit-learn and statsmodels. ### Added - `crime_mapping.get_city_shape("Natal, RN, Brazil")` — thin wrapper around - `osmnx.geocode_to_gdf` returning the raw city shape. + `osmnx.geocode_to_gdf` returning the raw city shape; `osmnx` is now a core + dependency (the `osm` extra is kept, empty, for compatibility). - `examples/natal.ipynb`: an executed end-to-end walkthrough on Natal with synthetic data, also rendered in the documentation. - `PredictionPipeline.evaluate` accepts a list of scorings and returns a diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 06ae5fb..5afe947 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -8,7 +8,7 @@ Thanks for your interest in Predspot! Issues and pull requests are welcome. git clone https://github.com/adaj/predspot.git cd predspot python -m venv .venv && source .venv/bin/activate -pip install -e ".[dev,osm,contour]" +pip install -e ".[dev,contour,examples]" ``` ## Checks diff --git a/README.md b/README.md index a341d08..9fefa72 100644 --- a/README.md +++ b/README.md @@ -36,8 +36,14 @@ Install from PyPI (Python 3.10 or newer): ```bash pip install predspot # core -pip install "predspot[osm]" # + study areas from OpenStreetMap (osmnx) pip install "predspot[contour]" # + GeoJSON contour export (geojsoncontour) +pip install "predspot[examples]" # + jupyter, to run the example notebooks +``` + +Not on PyPI yet? Install straight from GitHub: + +```bash +pip install "git+https://github.com/adaj/predspot.git" ``` Basic usage example: @@ -81,7 +87,7 @@ mapping = QuadratCount(tfreq='W', grid=create_gridhexagonal(study_area_gdf, reso ### Study area from OpenStreetMap and synthetic data You do not need real data to try Predspot. Fetch a city boundary from -OpenStreetMap (`pip install "predspot[osm]"`) and generate synthetic events +OpenStreetMap and generate synthetic events with spatial hotspots and realistic temporal patterns (trend, annual cycle, day-of-week and hour-of-day profiles): @@ -276,7 +282,7 @@ From source, for development: ```bash git clone https://github.com/adaj/predspot.git cd predspot -pip install -e ".[dev,osm,contour]" +pip install -e ".[dev,contour,examples]" ruff check src tests # lint pytest # ~10 s ``` diff --git a/docs/getting-started/installation.md b/docs/getting-started/installation.md index ed42c27..96f10da 100644 --- a/docs/getting-started/installation.md +++ b/docs/getting-started/installation.md @@ -10,16 +10,17 @@ Optional extras add features that need heavier dependencies: | Extra | Installs | Enables | |-------|----------|---------| -| `osm` | [osmnx](https://osmnx.readthedocs.io) | [`load_study_area`][predspot.crime_mapping.load_study_area] — study areas from OpenStreetMap | | `contour` | [geojsoncontour](https://github.com/bartromgens/geojsoncontour) | [`contour_geojson`][predspot.utilities.contour_geojson] — GeoJSON contour export | +| `examples` | jupyter, nbconvert, ipykernel | Running the example notebooks | | `dev` | pytest, ruff, build, twine | Running the test suite and building the package | ```bash -pip install "predspot[osm,contour]" +pip install "predspot[contour,examples]" ``` The core dependencies — pandas, GeoPandas, Shapely, NumPy, SciPy, scikit-learn, -statsmodels and Matplotlib — are installed automatically. +statsmodels, Matplotlib and [osmnx](https://osmnx.readthedocs.io) (study areas +from OpenStreetMap) — are installed automatically. !!! tip "conda users" GeoPandas and its GEOS/PROJ stack install fine from PyPI wheels nowadays, @@ -31,7 +32,7 @@ statsmodels and Matplotlib — are installed automatically. ```bash git clone https://github.com/adaj/predspot.git cd predspot -pip install -e ".[dev,osm,contour]" +pip install -e ".[dev,contour,examples]" pytest ``` diff --git a/docs/getting-started/quickstart.md b/docs/getting-started/quickstart.md index b9750eb..d2c4bfe 100644 --- a/docs/getting-started/quickstart.md +++ b/docs/getting-started/quickstart.md @@ -8,7 +8,7 @@ synthetic data so you can run it as is. Every step is explained in depth in the Predspot needs the boundary of the region you are studying as a GeoDataFrame with a CRS. The easiest way is to fetch it from OpenStreetMap -(`pip install "predspot[osm]"`): +(osmnx is installed with Predspot): ```python from predspot import load_study_area diff --git a/docs/guide/data.md b/docs/guide/data.md index 137fad6..ba8e039 100644 --- a/docs/guide/data.md +++ b/docs/guide/data.md @@ -38,7 +38,7 @@ removed, but grid cells are only created where they intersect it. place with [osmnx](https://osmnx.readthedocs.io) (Nominatim) and returns its administrative boundary. Be specific — add the state and country — so that the first match is the boundary you want; `which_result` lets you - pick another match. Requires `pip install "predspot[osm]"`. + pick another match. osmnx is installed with Predspot. === "From a file" diff --git a/examples/build_natal_notebook.py b/examples/build_natal_notebook.py index b6e8863..578bb7f 100644 --- a/examples/build_natal_notebook.py +++ b/examples/build_natal_notebook.py @@ -37,7 +37,7 @@ def code(text): 6. forecast the next months and check how well the true hotspots are recovered. Every intermediate object is displayed so you can see exactly what flows -between the steps. Requirements: `pip install "predspot[osm]" matplotlib`. +between the steps. Requirements: `pip install predspot jupyter` (or `pip install "predspot[examples]"`). """) code(""" diff --git a/pyproject.toml b/pyproject.toml index 89b35b0..abded01 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -40,10 +40,17 @@ dependencies = [ "scikit-learn>=1.3", "statsmodels>=0.14", "matplotlib>=3.7", + "osmnx>=2.0", ] [project.optional-dependencies] contour = ["geojsoncontour>=0.4"] +osm = [] # kept for compatibility: osmnx is now a core dependency +examples = [ + "jupyter>=1.0", + "nbconvert>=7", + "ipykernel>=6", +] docs = [ "mkdocs>=1.6,<2", "mkdocs-material>=9.5,<10", diff --git a/src/predspot/crime_mapping.py b/src/predspot/crime_mapping.py index f2ccfa9..36a3608 100644 --- a/src/predspot/crime_mapping.py +++ b/src/predspot/crime_mapping.py @@ -94,8 +94,7 @@ def load_study_area(place, crs=WGS84, which_result=None): """ Fetch the boundary polygon of a place from OpenStreetMap. - Uses `osmnx `_ (optional dependency: - ``pip install predspot[osm]``) to geocode ``place`` with Nominatim and + Uses `osmnx `_ to geocode ``place`` with Nominatim and return its administrative boundary, ready to be used as the ``study_area`` of [`Dataset`][predspot.Dataset] or as the ``bbox`` of the ``create_grid*`` functions. @@ -122,7 +121,7 @@ def load_study_area(place, crs=WGS84, which_result=None): import osmnx as ox except ImportError as exc: raise ImportError( - "load_study_area requires the optional dependency `osmnx`: pip install predspot[osm]" + "load_study_area requires `osmnx` (installed with predspot): pip install osmnx" ) from exc logger.debug("Geocoding study area %r with osmnx", place) gdf = ox.geocode_to_gdf(place, which_result=which_result) @@ -145,7 +144,7 @@ def get_city_shape(place_query): A thin wrapper around ``osmnx.geocode_to_gdf`` that returns the raw Nominatim result. Prefer [`load_study_area`][predspot.crime_mapping.load_study_area] when you want the result validated (polygon geometry, tidy columns). - Requires the optional ``osmnx`` dependency (``pip install predspot[osm]``). + Uses ``osmnx``, installed with Predspot. Args: place_query (str): Name of the place, in a format Nominatim accepts, @@ -163,7 +162,7 @@ def get_city_shape(place_query): import osmnx as ox except ImportError as exc: raise ImportError( - "get_city_shape requires the optional dependency `osmnx`: pip install predspot[osm]" + "get_city_shape requires `osmnx` (installed with predspot): pip install osmnx" ) from exc return ox.geocode_to_gdf(place_query) diff --git a/tests/test_load_study_area.py b/tests/test_load_study_area.py index 92b7640..c15f2bc 100644 --- a/tests/test_load_study_area.py +++ b/tests/test_load_study_area.py @@ -56,7 +56,7 @@ def test_load_study_area_rejects_points(monkeypatch): def test_load_study_area_without_osmnx(monkeypatch): monkeypatch.setitem(sys.modules, "osmnx", None) - with pytest.raises(ImportError, match="predspot\\[osm\\]"): + with pytest.raises(ImportError, match="pip install osmnx"): load_study_area("Natal, Brazil") @@ -82,5 +82,5 @@ def test_get_city_shape_returns_raw_geocode(monkeypatch): def test_get_city_shape_without_osmnx(monkeypatch): monkeypatch.setitem(sys.modules, "osmnx", None) - with pytest.raises(ImportError, match="predspot\\[osm\\]"): + with pytest.raises(ImportError, match="pip install osmnx"): get_city_shape("Natal, RN, Brazil")