{ "cells": [ { "cell_type": "markdown", "id": "0", "metadata": {}, "source": [ "# Usage | 1. Seed points\n", "This notebook explains the seed points concept behind growbikenet with illustrative examples. \n", "Parameters covered: `seed_point_type`, `seed_point_linking`, `street_network_file`, `seed_point_grid_spacing`, `seed_point_file`, `seed_point_tags`" ] }, { "cell_type": "markdown", "id": "abe2efc9-2585-46ea-93a2-508c82f5e47c", "metadata": {}, "source": [ "We start every Usage notebook with the standard way of importing growbikenet:" ] }, { "cell_type": "code", "execution_count": null, "id": "f1b588a9-1942-4de4-bf60-4a4743762d79", "metadata": {}, "outputs": [], "source": [ "import growbikenet as gbn" ] }, { "cell_type": "markdown", "id": "926b7d89-3579-4bff-b5dd-e712f47ff64d", "metadata": {}, "source": [ "## Network construction through linking seed points" ] }, { "cell_type": "markdown", "id": "0918baaf-88cc-4480-91ec-6e5485eaae31", "metadata": {}, "source": [ "Growbikenet is based on the idea of linking seed points, inspired by the Dutch CROW Design manual for bicycle traffic. These are points in a city that shall be connected by protected bicycle infrastructure, yielding edges in a bicycle network. The choice and placement of seed points is crucial. To build a well-covering bicycle network, seed points should cover most of the city. By default, in growbikenet seed points are arbitrary, city-spanning points on a grid, snapped to the street network, but it is also possible to assign actual points of interests such as railway stations, schools, or parks, see the end of this notebook. The `seed_point_type` parameter is controlling the type of seed points chosen." ] }, { "cell_type": "markdown", "id": "b7c633fb-d3f4-44fd-b00d-72c510dccc37", "metadata": {}, "source": [ "![Seed points and triangulation](_static/seedpoints.jpg)" ] }, { "cell_type": "markdown", "id": "6d17b116-c6ad-4275-9b4e-28135c1ac126", "metadata": {}, "source": [ "Once seed points are defined, they need to be linked. The type of linking is controlled by the `seed_point_linking` parameter. By default, growbikenet triangulates or quadrangulates the seed points automatically, building triangle or square-shaped links between the seed points. Once the linking process is done (triangulation or quadrangulation), an abstract, unrouted network between seed points is established, see the figure above. Growbikenet then builds a bicycle network on this network's edges, routed on the street network. Run the cells below for live examples of this process." ] }, { "cell_type": "markdown", "id": "a0ee86e2-964a-42a8-a467-263b0586ac4f", "metadata": {}, "source": [ "## Save street network locally" ] }, { "cell_type": "markdown", "id": "c8c304b0-82f0-49d9-962d-91f4b5f5f618", "metadata": {}, "source": [ "Here we work with Barcelona. When running growbikenet, it uses OSMnx to fetch street network data from [OSM](https://www.openstreetmap.org/). OSMnx has data caching implemented, so when running it multiple times on the same city, it will load the cached json data. Unfortunately, loading json is slow. We therefore first download and save the (undirected) street network once locally, under `Barcelona_streets.gpkg`, so we can load that file fast into growbikenet whenever we will use it (because gpkg files are loaded fast):" ] }, { "cell_type": "code", "execution_count": null, "id": "27459560-6747-4d21-9ef2-5683b756ce41", "metadata": {}, "outputs": [], "source": [ "import osmnx as ox\n", "g = ox.graph_from_place(\"Barcelona\", network_type='drive')\n", "ox.io.save_graph_geopackage(g.to_undirected(), \"Barcelona_streets.gpkg\")" ] }, { "cell_type": "markdown", "id": "3", "metadata": {}, "source": [ "## Triangulation versus quadrangulation" ] }, { "cell_type": "markdown", "id": "21e549cd-c76e-4728-9bff-e6b021d51a60", "metadata": {}, "source": [ "This section explores the utility of the `seed_point_type` and `seed_point_linking` parameters. By default, growbikenet detects automatically the optimal type and spacing of the seed point grid, and of the seed point linking process for a given city: \n", "\n", "1. Many European cities like Berlin or London have very irregular, organic street networks. On these networks, growbikenet constructs by default a triangular grid (`seed_point_type=='grid_triangle'`) that it naturally triangulates (`seed_point_linking=='triangulate_delaunay'`).\n", "2. Some cities like Prague or Budapest have some square grid elements. On these networks, growbikenet constructs by default a square grid (`seed_point_type=='grid_square'`) that it triangulates (`seed_point_linking=='triangulate_delaunay'`).\n", "3. Many US cities, and some European cities, like Manhattan or Barcelona, have ample grid elements in their street networks. On these networks, growbikenet constructs by default a square grid (`seed_point_type=='grid_square'`) that it quadrangulates (`seed_point_linking=='quadrangulate'`).\n", "\n", "See below the quadrangulated square grid result for Barcelona, loaded in from the downloaded file via the `street_network_file` parameter:" ] }, { "cell_type": "code", "execution_count": null, "id": "4", "metadata": {}, "outputs": [], "source": [ "edges_ranked = gbn.growbikenet(\"Barcelona\",\n", " street_network_file=\"Barcelona_streets.gpkg\",)" ] }, { "cell_type": "markdown", "id": "bcd2409f-bac6-4e7c-89e9-d8c229eb36d2", "metadata": {}, "source": [ "Explore the results interactively:" ] }, { "cell_type": "code", "execution_count": null, "id": "fac59916-d789-4e4d-8415-81dd962e8fbb", "metadata": {}, "outputs": [], "source": [ "edges_ranked.explore(tiles=\"CartoDB Positron\", \n", " style_kwds={\"weight\": 3, \"color\": \"#096a51\"},)" ] }, { "cell_type": "markdown", "id": "ce454aed-de04-4fa2-b30c-0b9fc3d7cf7b", "metadata": {}, "source": [ "Compare this to the output if we were to instruct growbikenet to instead triangulate on the square grid instead of quadrangulate. It features some ugly zig-zags:" ] }, { "cell_type": "code", "execution_count": null, "id": "aadcd2a9-b552-4948-916b-e622e2cafb5f", "metadata": {}, "outputs": [], "source": [ "edges_ranked = gbn.growbikenet(\"Barcelona\",\n", " seed_point_linking ='triangulate_delaunay',\n", " street_network_file=\"Barcelona_streets.gpkg\",)" ] }, { "cell_type": "code", "execution_count": null, "id": "6f23e8db-5a59-487c-9d38-863dbd8feb85", "metadata": {}, "outputs": [], "source": [ "edges_ranked.explore(tiles=\"CartoDB Positron\",\n", " style_kwds={\"weight\": 3, \"color\": \"#096a51\"},)" ] }, { "cell_type": "markdown", "id": "827dc2b4-0494-441a-9764-67ac69f408db", "metadata": {}, "source": [ "Under the hood, growbikenet also automatically adapts the spacing of the seed point grid." ] }, { "cell_type": "markdown", "id": "a670932a-ac7d-40d1-b0ac-f473eb7b83c1", "metadata": {}, "source": [ "## Spacing of the seed point grid" ] }, { "cell_type": "markdown", "id": "10a3af8b-eaa9-4d28-90e7-9a3bb77f2fad", "metadata": {}, "source": [ "In the above network outputs, observe how the grid is more finegrained in the quadrangulation than in the triangulation. This is because by default, `seed_point_grid_spacing` is set to `auto`, leading growbikenet to automatically adjust the grid spacing to ensure that any point in the city is always within 500m of the network, on average less than 200m (under perfect conditions). Concretely, the spacing of seed points is every 1707m in the triangulation case, but ony 1000m in the quadrangulation case." ] }, { "attachments": {}, "cell_type": "markdown", "id": "f7c53c73-4a82-4850-a4cb-5a95571569b9", "metadata": {}, "source": [ "Let's get back to the nice quadrangulated square grid of Barcelona. As a big part of the city has a square grid structure, each 3x3 block could be considered as a \"superblock\", with a bike network going around it. Let us now try to choose a spacing manually that will create this network:" ] }, { "cell_type": "code", "execution_count": null, "id": "ded3664c-c373-46ea-8d6d-99fe91a89ee7", "metadata": {}, "outputs": [], "source": [ "edges_ranked = gbn.growbikenet(\"Barcelona\",\n", " seed_point_grid_spacing=530,\n", " street_network_file=\"Barcelona_streets.gpkg\",)" ] }, { "cell_type": "markdown", "id": "82300baf-b6af-466f-b3ca-7d4c316b5c01", "metadata": {}, "source": [ "A spacing of 530m is tighter, but fits the \"superblocks\" nicer - zoom into the map to see the details:" ] }, { "cell_type": "code", "execution_count": null, "id": "80412346-610a-47fe-b99d-f61fe3815e19", "metadata": {}, "outputs": [], "source": [ "edges_ranked.explore(tiles=\"CartoDB Positron\",\n", " style_kwds={\"weight\": 3, \"color\": \"#096a51\"},)" ] }, { "cell_type": "markdown", "id": "e0f6293a-bd9d-40e3-b8b4-f57440bb4353", "metadata": {}, "source": [ "## Preset seed points" ] }, { "cell_type": "markdown", "id": "7a1a7e21-9e63-4cbe-ba9b-d4e817595d87", "metadata": {}, "source": [ "Apart from connecting street intersection seed points from an arbitrary grid, growbikenet also supports connecting seed points that are actual points of interests like rail stations, schools, or parks." ] }, { "cell_type": "markdown", "id": "4d7aefe3-918b-4afd-9093-cafb7b4791fb", "metadata": {}, "source": [ "### Rail" ] }, { "cell_type": "code", "execution_count": null, "id": "56449a4a-4272-4673-aff4-f9fb8f13e84f", "metadata": {}, "outputs": [], "source": [ "edges_ranked = gbn.growbikenet(\"Barcelona\",\n", " seed_point_type=\"rail\",\n", " street_network_file=\"Barcelona_streets.gpkg\",)\n", "edges_ranked.explore(tiles=\"CartoDB Positron\",\n", " style_kwds={\"weight\": 3, \"color\": \"#096a51\"})" ] }, { "cell_type": "markdown", "id": "e9d9275c-de3b-450b-b15c-ffcca522731e", "metadata": {}, "source": [ "### School" ] }, { "cell_type": "code", "execution_count": null, "id": "051e872f-77a2-4f3e-a7cd-924361328997", "metadata": {}, "outputs": [], "source": [ "edges_ranked = gbn.growbikenet(\"Barcelona\",\n", " seed_point_type=\"school\",\n", " street_network_file=\"Barcelona_streets.gpkg\",)\n", "edges_ranked.explore(tiles=\"CartoDB Positron\",\n", " style_kwds={\"weight\": 3, \"color\": \"#096a51\"})" ] }, { "cell_type": "markdown", "id": "6268b0de-f58a-438b-8335-962ef2b84fe1", "metadata": {}, "source": [ "### Park" ] }, { "cell_type": "code", "execution_count": null, "id": "4cd2eb66-b05d-4385-95b1-5d3bc1213dad", "metadata": {}, "outputs": [], "source": [ "edges_ranked = gbn.growbikenet(\"Barcelona\",\n", " seed_point_type=\"park\",\n", " street_network_file=\"Barcelona_streets.gpkg\",)\n", "edges_ranked.explore(tiles=\"CartoDB Positron\",\n", " style_kwds={\"weight\": 3, \"color\": \"#096a51\"})" ] }, { "cell_type": "markdown", "id": "843a8e50-9d3a-4bb3-80f0-5fd25d184254", "metadata": {}, "source": [ "## Custom seed points" ] }, { "cell_type": "markdown", "id": "5436df48-2ef6-4514-a73a-4023d96362a6", "metadata": {}, "source": [ "### File" ] }, { "cell_type": "markdown", "id": "c9d14324-8ef6-4583-8039-1e943273590b", "metadata": {}, "source": [ "Seed points can be completely custom, for example loaded from file. Here, we first save the coordinates of a bunch of touristic spots in Barcelona as gpkg file, to then load it to construct a bicycle network around them:" ] }, { "cell_type": "code", "execution_count": null, "id": "1cc42f4a-8f9f-4827-be56-aefd5819f9f9", "metadata": {}, "outputs": [], "source": [ "import pandas as pd\n", "import geopandas as gpd\n", "df = pd.DataFrame(\n", " {\n", " \"Latitude\": [41.403653, 41.378289, 41.361902, 41.413509, 41.409700, 41.399190, 41.436646, 41.391843, 41.395832],\n", " \"Longitude\": [2.173703, 2.192716, 2.158304, 2.153073, 2.126480, 2.212746, 2.191520, 2.165109, 2.112801],\n", " }\n", ")\n", "gdf = gpd.GeoDataFrame(\n", " df, geometry=gpd.points_from_xy(df.Longitude, df.Latitude), crs=\"EPSG:4326\"\n", ")\n", "gdf.to_file(\"Barcelona_touristic_spots.gpkg\")" ] }, { "cell_type": "code", "execution_count": null, "id": "4ba7836a-69d7-4505-9784-905eeeba5a1c", "metadata": {}, "outputs": [], "source": [ "edges_ranked = gbn.growbikenet(\"Barcelona\",\n", " seed_point_type=\"file\",\n", " seed_point_file=\"Barcelona_touristic_spots.gpkg\",\n", " street_network_file=\"Barcelona_streets.gpkg\",)\n", "edges_ranked.explore(tiles=\"CartoDB Positron\",\n", " style_kwds={\"weight\": 3, \"color\": \"#096a51\"})" ] }, { "cell_type": "markdown", "id": "40304418-2d06-43a5-a734-0cb12c4ffec4", "metadata": {}, "source": [ "### Tags" ] }, { "cell_type": "markdown", "id": "815ab76e-ee65-4c52-8bb2-8857b20027eb", "metadata": {}, "source": [ "Finally, we are able to use arbitrary OSM tags. For example, to create a bike network between all ice cream shops and other places of worship:" ] }, { "cell_type": "code", "execution_count": null, "id": "a6028257-c8b2-4224-ba3f-daea8d6d1542", "metadata": {}, "outputs": [], "source": [ "edges_ranked = gbn.growbikenet(\"Barcelona\",\n", " seed_point_type=\"tags\",\n", " seed_point_tags={\"amenity\": [\"ice_cream\", \"place_of_worship\"]},\n", " street_network_file=\"Barcelona_streets.gpkg\",)\n", "edges_ranked.explore(tiles=\"CartoDB Positron\",\n", " style_kwds={\"weight\": 3, \"color\": \"#096a51\"})" ] }, { "cell_type": "markdown", "id": "81c6b696-4c18-4874-b387-71f73253104e", "metadata": {}, "source": [ "Note that under the hood, the seed point types `rail`, `school`, and `park` are just preset tags. For example, instead of selecting railway stations via `seed_point_type=\"rail\"`, they could be selected equivalently via `seed_point_type=\"tags\"` and `seed_point_tags={\"railway\": [\"station\", \"halt\"]}`." ] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.12.13" }, "widgets": { "application/vnd.jupyter.widget-state+json": { "state": {}, "version_major": 2, "version_minor": 0 } } }, "nbformat": 4, "nbformat_minor": 5 }