diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..2656162 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,88 @@ +name: Test + +on: + push: + branches: # on all branches except `typos` + - "**" + - "!typos" + paths-ignore: + - "docs/**" + - "examples/**" + - "data/**" + - ".git*" + - "README.md" + pull_request: + branches: + - "**" + schedule: # Every Monday at 04:00 UTC + - cron: "0 4 * * 1" + +jobs: + caching: + strategy: + matrix: + python-version: ["3.12"] + os: [ubuntu-latest] + fail-fast: false + env: + FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true + + defaults: + run: + shell: bash -elo pipefail {0} + + name: Cache for ${{ matrix.python-version }} on ${{ matrix.os }} + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@v6 + + - name: Get week number + run: echo "WEEK=$(date +'%V')" >> $GITHUB_ENV + + - name: Set up Python ${{ matrix.python-version }} + uses: mamba-org/setup-micromamba@v2 + with: + create-args: python=${{ matrix.python-version }} + environment-file: environment-dev.yml + cache-environment: true + cache-environment-key: W${{ env.WEEK }} + + run-tests: + needs: caching + strategy: + matrix: + python-version: ["3.12"] + os: [ubuntu-latest] + submodule: + #- { name: "io", pytest_args: "tests/test_io.py" } # To do + - { name: "utils", pytest_args: "tests/test_utils.py" } + #- { name: "viz", pytest_args: "tests/test_viz.py" } # To do + fail-fast: false + + name: ${{ matrix.submodule.name }} (${{ matrix.python-version }} on ${{ matrix.os }}) + runs-on: ${{ matrix.os }} + timeout-minutes: 60 + defaults: + run: + shell: bash -el {0} + + steps: + - uses: actions/checkout@v6 + + - name: Get week number + run: echo "WEEK=$(date +'%V')" >> $GITHUB_ENV + + - name: Set up Python ${{ matrix.python-version }} + uses: mamba-org/setup-micromamba@v2 + with: + create-args: python=${{ matrix.python-version }} + environment-file: environment-dev.yml + cache-environment: true + cache-environment-key: W${{ env.WEEK }} + + - name: Install bikenetlib + run: pip install --no-build-isolation --no-deps -e . + + - name: "Run tests ${{ matrix.submodule.name }}" + run: | + pytest ${{ matrix.submodule.pytest_args }} diff --git a/README.md b/README.md index fd3c9a6..a82be3f 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,10 @@ # Bike Net Kit / Bike Net Lib +[![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) +[![code style: prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg?style=flat-square)](https://github.com/prettier/prettier) +[![pre-commit](https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit&logoColor=white)](https://github.com/pre-commit/pre-commit) +[![Test](https://github.com/BikeNetKit/BikeNetLib/actions/workflows/test.yml/badge.svg?branch=main)](https://github.com/BikeNetKit/BikeNetLib/actions/workflows/test.yml) + > [!WARNING] > This repository is under heavy development and not yet functional. Do not use. diff --git a/bikenetlib/utils.py b/bikenetlib/utils.py index 3727619..90cddf5 100644 --- a/bikenetlib/utils.py +++ b/bikenetlib/utils.py @@ -236,58 +236,6 @@ def update_seed_points_with_existing_bike_network(seed_points_snapped, nodes_exn seed_points_snapped.set_index("osmid", drop=False, inplace=True) return seed_points_snapped -def get_grid_seed_points(edges, seed_point_spacing, principal_bearing): - """Get grid seed points for street network, rotated by principal bearing - - Adapted from: https://github.com/gboeing/osmnx-examples/blob/v0.11/notebooks/17-street-network-orientations.ipynb - - Parameters - ---------- - edges: geopandas.geodataframe.GeoDataFrame - The street network, in a projected coordinate reference system - seed_point_spacing: int - Distance between seed points, in meters - principal_bearing: float - Principal bearing (most common bearing of streets) - - Returns - ------- - seed_points: geopandas.geodataframe.GeoDataFrame - Seed points, rotated by principal bearing, to be snapped, in the same projected coordinate reference system as edges - """ - - # Rotate edges counter to the principal bearing - edges_temp = edges.copy() - edges_temp.geometry = edges_temp.geometry.rotate(principal_bearing, origin=(0, 0)) - - # Create grid - # get convex hull around edge area - hull = edges_temp.union_all().convex_hull - # get bounds of hull - xmin, ymin, xmax, ymax = hull.bounds - - # https://stackoverflow.com/questions/66010964/fastest-way-to-produce-a-grid-of-points-that-fall-within-a-polygon-or-shape - # Populate hull bbox with evenly spaced seeding points - points = [] - for x in np.arange(xmin, xmax, seed_point_spacing): - for y in np.arange(ymin, ymax, seed_point_spacing): - points.append(Point((round(x, 4), round(y, 4)))) - - # Keep only those seed points that are within the hull polygon - prep_polygon = prep(hull) - valid_points = [] - valid_points.extend(filter(prep_polygon.contains, points)) - - # store seed points in gdf - seed_points = gpd.GeoDataFrame({"geometry": valid_points}, crs=edges.crs) - - # Rotate points back using the principal bearing - seed_points.geometry = seed_points.geometry.rotate( - -1 * principal_bearing, origin=(0, 0) - ) - - return seed_points - def get_principal_bearing(G): """Determine the most common (principal) bearing, for the best grid orientation. diff --git a/tests/test_data/copenhagen.dbf b/tests/test_data/copenhagen.dbf new file mode 100644 index 0000000..101a0ec Binary files /dev/null and b/tests/test_data/copenhagen.dbf differ diff --git a/tests/test_data/copenhagen.prj b/tests/test_data/copenhagen.prj new file mode 100644 index 0000000..a30c00a --- /dev/null +++ b/tests/test_data/copenhagen.prj @@ -0,0 +1 @@ +GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137,298.257223563]],PRIMEM["Greenwich",0],UNIT["Degree",0.017453292519943295]] \ No newline at end of file diff --git a/tests/test_data/copenhagen.qpj b/tests/test_data/copenhagen.qpj new file mode 100644 index 0000000..5fbc831 --- /dev/null +++ b/tests/test_data/copenhagen.qpj @@ -0,0 +1 @@ +GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563,AUTHORITY["EPSG","7030"]],AUTHORITY["EPSG","6326"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4326"]] diff --git a/tests/test_data/copenhagen.shp b/tests/test_data/copenhagen.shp new file mode 100644 index 0000000..81dc6e6 Binary files /dev/null and b/tests/test_data/copenhagen.shp differ diff --git a/tests/test_data/copenhagen.shx b/tests/test_data/copenhagen.shx new file mode 100644 index 0000000..66e4f92 Binary files /dev/null and b/tests/test_data/copenhagen.shx differ diff --git a/tests/test_data/oelde_grid.gpkg b/tests/test_data/oelde_grid.gpkg new file mode 100644 index 0000000..d0d867f Binary files /dev/null and b/tests/test_data/oelde_grid.gpkg differ diff --git a/tests/test_data/oelde_growbikenet.gpkg b/tests/test_data/oelde_growbikenet.gpkg new file mode 100644 index 0000000..232c1a2 Binary files /dev/null and b/tests/test_data/oelde_growbikenet.gpkg differ diff --git a/tests/test_data/oelde_streets.gpkg b/tests/test_data/oelde_streets.gpkg new file mode 100644 index 0000000..9ed1ae4 Binary files /dev/null and b/tests/test_data/oelde_streets.gpkg differ diff --git a/tests/test_io.py b/tests/test_io.py new file mode 100644 index 0000000..b76dadd --- /dev/null +++ b/tests/test_io.py @@ -0,0 +1,13 @@ +import pytest +import osmnx as ox +import pandas as pd +import geopandas as gpd +from pandas.testing import assert_frame_equal +from bikenetlib.io import ( + prepare_network, + download_pois, + save_to_file, +) +from shapely.geometry import Point, LineString, MultiLineString + +# To do \ No newline at end of file diff --git a/tests/test_utils.py b/tests/test_utils.py new file mode 100644 index 0000000..de6f05f --- /dev/null +++ b/tests/test_utils.py @@ -0,0 +1,88 @@ +import pytest +import osmnx as ox +import pandas as pd +import geopandas as gpd +from pandas.testing import assert_frame_equal +from bikenetlib.utils import ( # List them in the order appearing in utils.py + intersects_properly, + get_principal_bearing, + filter_seed_points, +) +from shapely.geometry import Point, LineString, MultiLineString + + +# intersects_properly + +@pytest.fixture +def geom_1(): + linestring = LineString([(0, 0), (1, 1), (2, 2)]) + return linestring + + +@pytest.fixture +def geom_2(): + linestring = LineString([(3, 3), (4, 4), (5, 5)]) + return linestring + + +def test_intersects_properly(geom_1, geom_2): + assert intersects_properly(geom_1, geom_2) is False + + +# filter_seed_points + +@pytest.fixture +def seed_point_delta(): + return 500 + + +@pytest.fixture +def snapped_seed_points(): + d = { + "osmid": ["1", "2", "3"], + "geometry_generated": [Point(1000, 1000), Point(2000, 2000), Point(3000, 3000)], + } + gdf = gpd.GeoDataFrame(d, geometry="geometry_generated", crs="EPSG:3857") + gdf["geometry_osm"] = gpd.GeoSeries( + [Point(1001, 1001), Point(10000, 10000), Point(3001, 3001)], crs="EPSG:3857" + ) + return gdf + + +@pytest.fixture +def filtered_seed_points(): + d = {"osmid": ["1", "3"], "geometry": [Point(1001, 1001), Point(3001, 3001)]} + gdf = gpd.GeoDataFrame(d, geometry="geometry", crs="EPSG:3857") + gdf = gdf.set_index("osmid") + gdf["osmid"] = gdf.index + gdf = gdf.iloc[:, [1, 0]] + return gdf + + +def test_filter_seed_points( + snapped_seed_points, filtered_seed_points, seed_point_delta +): + assert_frame_equal( + filter_seed_points(snapped_seed_points, seed_point_delta), + filtered_seed_points, + check_dtype=False, + ) + + +# get_principal_bearing + +@pytest.fixture +def validation_streets(): + streets_nodes = gpd.read_file( + "./tests/test_data/oelde_streets.gpkg", layer="nodes" + ).set_index("osmid") + streets_edges = gpd.read_file( + "./tests/test_data/oelde_streets.gpkg", layer="edges" + ).set_index(["u", "v", "key"]) + streets = ox.convert.graph_from_gdfs(streets_nodes, streets_edges) + return streets + + +def test_get_principal_bearing(validation_streets): + assert get_principal_bearing(validation_streets) == 65.0 + diff --git a/tests/test_viz.py b/tests/test_viz.py new file mode 100644 index 0000000..17efb5f --- /dev/null +++ b/tests/test_viz.py @@ -0,0 +1 @@ +# To do \ No newline at end of file