|
1 | 1 | { |
2 | 2 | "cells": [ |
| 3 | + { |
| 4 | + "cell_type": "markdown", |
| 5 | + "metadata": {}, |
| 6 | + "source": [ |
| 7 | + "## Path Finding in the City of Austin\n", |
| 8 | + "\n", |
| 9 | + "This notebook demonstrates pathfinding along the city of Austin street network using Xarray-spatial's `pathfinding` module.\n", |
| 10 | + "The a_star_search function provides the shortest path between any two points." |
| 11 | + ] |
| 12 | + }, |
| 13 | + { |
| 14 | + "cell_type": "markdown", |
| 15 | + "metadata": {}, |
| 16 | + "source": [ |
| 17 | + "#### Setup:\n", |
| 18 | + "\n", |
| 19 | + "First, we'll need to import some packages: these include the basic array manipulation ones, \n", |
| 20 | + "as well as some geospatial-focused ones.\n", |
| 21 | + "We'll also grab a few datashader functions for easy rendering." |
| 22 | + ] |
| 23 | + }, |
3 | 24 | { |
4 | 25 | "cell_type": "code", |
5 | 26 | "execution_count": null, |
|
10 | 31 | "import numpy as np\n", |
11 | 32 | "import pandas as pd\n", |
12 | 33 | "import geopandas\n", |
| 34 | + "import spatialpandas\n", |
13 | 35 | "\n", |
14 | 36 | "import datashader as ds\n", |
15 | 37 | "from datashader.transfer_functions import shade, stack, dynspread, set_background\n", |
|
21 | 43 | "cell_type": "markdown", |
22 | 44 | "metadata": {}, |
23 | 45 | "source": [ |
24 | | - "## Load data\n", |
| 46 | + "### Load data\n", |
25 | 47 | "\n", |
| 48 | + "Now, we're ready to load up the data and transform it into a format we ccan work with.\n", |
26 | 49 | "The road network used in this example notebook can be downloaded from:\n", |
27 | 50 | "\n", |
28 | 51 | "https://data.austintexas.gov/Locations-and-Maps/Street-Centerline/m5w3-uea6" |
29 | 52 | ] |
30 | 53 | }, |
31 | 54 | { |
32 | | - "cell_type": "code", |
33 | | - "execution_count": null, |
| 55 | + "cell_type": "markdown", |
34 | 56 | "metadata": {}, |
35 | | - "outputs": [], |
36 | 57 | "source": [ |
37 | | - "streets = geopandas.read_file('zip://./data/Street_Centerline.zip')\n", |
38 | | - "streets = streets.to_crs({'init': 'epsg:4326'})\n", |
39 | | - "\n", |
40 | | - "xs = []\n", |
41 | | - "ys = []\n", |
42 | | - "for s in streets.geometry.values:\n", |
43 | | - " try:\n", |
44 | | - " coords = s.coords.xy\n", |
45 | | - " xs += coords[0].tolist()\n", |
46 | | - " ys += coords[1].tolist()\n", |
47 | | - " \n", |
48 | | - " xs.append(np.nan)\n", |
49 | | - " ys.append(np.nan)\n", |
50 | | - " except:\n", |
51 | | - " continue\n", |
52 | | - " \n", |
53 | | - "street_df = pd.DataFrame(dict(x=xs, y=ys))" |
| 58 | + "We'll start by opening the shapefile, transforming the crs (coordinate reference system) to the commonly-used longitude/latitude, \n", |
| 59 | + "and, after a quick clean-up, transforming it to a spatialpandas GeoDataFrame.\n", |
| 60 | + "\n", |
| 61 | + "Now our data is ready to be aggregated to an xarray DataArray raster." |
54 | 62 | ] |
55 | 63 | }, |
56 | 64 | { |
|
59 | 67 | "metadata": {}, |
60 | 68 | "outputs": [], |
61 | 69 | "source": [ |
62 | | - "street_df" |
| 70 | + "streets = geopandas.read_file('./data/Street_Centerline.zip')\n", |
| 71 | + "streets = streets.to_crs('EPSG:4326')\n", |
| 72 | + "streets = streets.explode('geometry').reset_index(drop=True)\n", |
| 73 | + "streets_spd = spatialpandas.GeoDataFrame(streets, geometry='geometry')" |
63 | 74 | ] |
64 | 75 | }, |
65 | 76 | { |
66 | 77 | "cell_type": "markdown", |
67 | 78 | "metadata": {}, |
68 | 79 | "source": [ |
69 | | - "## Define study area (find range of x and y)" |
| 80 | + "### Define study area (find range of x and y) and aggregate:\n", |
| 81 | + "\n", |
| 82 | + "To finish off our set-up:\n", |
| 83 | + "- We'll define a study area, with xmin, xmax, ymin, and ymax; this set the x, y coordinates we'll be using in our aggregate.\n", |
| 84 | + "- We'll set up a datashader Canvas object, which provides an easy frame for setting up a new raster and aggregating data to it.\n", |
| 85 | + "- Finally, we'll aggregate the streets data into a lines raster with Canvas.line.\n", |
| 86 | + "\n", |
| 87 | + "- We also set up the start and goal point (x, y) coordinates, and set up a DataFrame and aggregation for visualization.\n", |
| 88 | + "\n", |
| 89 | + "Some shading and stacking of all of this displays our complete setup below." |
70 | 90 | ] |
71 | 91 | }, |
72 | 92 | { |
|
94 | 114 | "cvs = ds.Canvas(plot_width=W, plot_height=H,\n", |
95 | 115 | " x_range=xrange, y_range=yrange)\n", |
96 | 116 | "\n", |
97 | | - "street_agg = cvs.line(street_df, x='x', y='y').astype(int)\n", |
| 117 | + "street_agg = cvs.line(streets_spd, geometry='geometry')\n", |
98 | 118 | "street_shaded = dynspread(shade(street_agg, cmap=['salmon']))\n", |
99 | 119 | "\n", |
100 | 120 | "# Pick two locations\n", |
|
112 | 132 | "cell_type": "markdown", |
113 | 133 | "metadata": {}, |
114 | 134 | "source": [ |
115 | | - "## Shortest path using A* from start location to goal location" |
| 135 | + "### Shortest path using A* from start location to goal location\n", |
| 136 | + "\n", |
| 137 | + "Now, we can do some pathfinding:\n", |
| 138 | + "\n", |
| 139 | + "In `a_star_search`, we'll input the Austin city streets lines aggregate we built above, the start and goal point coordinates, and barriers:\n", |
| 140 | + " - Barriers defines all non-crossable points in the raster: for our streets raster, this includes all non-street areas, all of which have 0 set as their value. \n", |
| 141 | + "\n", |
| 142 | + "We've also set `snap-start` and `snap-goal` to `True`: this helps ensure the start and goal points are set correctly.\n", |
| 143 | + "\n", |
| 144 | + "The result is a the shortest path al\n", |
| 145 | + " " |
116 | 146 | ] |
117 | 147 | }, |
118 | 148 | { |
|
0 commit comments