{ "cells": [ { "cell_type": "markdown", "id": "dcbd90e7", "metadata": {}, "source": [ "# Spatial Projection\n", "\n", "This notebook covers projecting point and linear geometries onto a linearly referenced network." ] }, { "cell_type": "code", "execution_count": 1, "id": "f8809f43", "metadata": { "execution": { "iopub.execute_input": "2026-05-29T16:43:07.685678Z", "iopub.status.busy": "2026-05-29T16:43:07.684729Z", "iopub.status.idle": "2026-05-29T16:43:08.282633Z", "shell.execute_reply": "2026-05-29T16:43:08.282155Z" } }, "outputs": [], "source": [ "import linref as lr\n", "\n", "# Load roadways dataset with LRS pre-configured\n", "roadways = lr.datasets.load('roadways', set_lrs=True)" ] }, { "cell_type": "markdown", "id": "c9977419", "metadata": {}, "source": [ "## Projecting Point Geometries\n", "\n", "When you have point data with only spatial coordinates (no route or milepost information), you can project those points onto your linearly referenced network. This is useful for integrating external data sources like GPS points, sensor locations, or crash data that hasn't been linearly referenced yet.\n", "\n", "First, we'll create some sample point geometries to simulate data without LRS information:" ] }, { "cell_type": "code", "execution_count": 2, "id": "bb7ff384", "metadata": { "execution": { "iopub.execute_input": "2026-05-29T16:43:08.284539Z", "iopub.status.busy": "2026-05-29T16:43:08.284348Z", "iopub.status.idle": "2026-05-29T16:43:08.299801Z", "shell.execute_reply": "2026-05-29T16:43:08.299368Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Points after projection (now have route and loc columns):\n", " sign_id sign_type route loc project_distance\n", "0 1 Speed Limit US-101 1.519231 0.098058\n", "1 2 Stop SR-1 4.026450 0.127467\n", "2 3 Yield I-5 6.038462 0.114354\n" ] } ], "source": [ "import geopandas as gpd\n", "from shapely.geometry import Point\n", "\n", "# First, ensure roadways have M-enabled geometries for projection\n", "roadways_spatial = roadways.lr.add_geom_m()\n", "\n", "# Create sample point features (e.g., sign locations) with only X/Y coordinates\n", "# These points are near the roadway network but don't have route or milepost info\n", "points = gpd.GeoDataFrame({\n", " 'sign_id': [1, 2, 3],\n", " 'sign_type': ['Speed Limit', 'Stop', 'Yield'],\n", " 'geometry': [\n", " Point(1.5, 0.4), # Near US-101\n", " Point(4.0, 6.1), # Near SR-1\n", " Point(6.0, 11.8) # Near I-5\n", " ]\n", "}, crs=roadways.crs)\n", "\n", "# Project points onto the roadway network\n", "# buffer=2 searches for roads within 2 coordinate units of each point\n", "projected = roadways_spatial.lr.project(points, buffer=2, nearest=True, dropna=True)\n", "\n", "print(\"Points after projection (now have route and loc columns):\")\n", "print(projected[['sign_id', 'sign_type', 'route', 'loc', 'project_distance']])" ] }, { "cell_type": "markdown", "id": "05b107fb", "metadata": {}, "source": [ "## Projecting Linear Geometries\n", "\n", "When you need to match linear geometries from one dataset to another (e.g., conflating road data from different sources), you can use `parallel_project_hausdorff()`. This function uses Hausdorff distance to find the best geometric match between linear features.\n", "\n", "This is useful for:\n", "- Conflating road inventories from different agencies\n", "- Matching GPS traces to a road network\n", "- Integrating pavement surveys onto a master road system\n", "\n", "Here's an example of creating alternative road segments and projecting them onto our master roadway network:" ] }, { "cell_type": "code", "execution_count": 3, "id": "cc26964e", "metadata": { "execution": { "iopub.execute_input": "2026-05-29T16:43:08.301290Z", "iopub.status.busy": "2026-05-29T16:43:08.301063Z", "iopub.status.idle": "2026-05-29T16:43:08.324595Z", "shell.execute_reply": "2026-05-29T16:43:08.323821Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Segments after projection (now have route, beg, and end from our LRS):\n", " segment_id data_source route beg end\n", "0 A County US-101 1.000000 2.50000\n", "1 B County SR-1 3.985852 6.00123\n" ] } ], "source": [ "from linref.ext.spatial import parallel_project_hausdorff\n", "from shapely.geometry import LineString\n", "\n", "# Create sample linear segments from another source (e.g., county road inventory)\n", "# These have geometries but no route IDs or mileposts from our system\n", "other_segments = gpd.GeoDataFrame({\n", " 'segment_id': ['A', 'B'],\n", " 'data_source': ['County', 'County'],\n", " 'geometry': [\n", " LineString([(1.0, 0.2), (3.0, 0.6)]), # Should match US-101\n", " LineString([(4.0, 5.9), (6.0, 6.4)]) # Should match SR-1\n", " ]\n", "}, crs=roadways.crs)\n", "\n", "# Project these segments onto our master roadway network\n", "# The function finds the best geometric match using Hausdorff distance\n", "projected_segments = parallel_project_hausdorff(\n", " target=roadways_spatial, # Our master roadway network with M-geometries\n", " projected=other_segments, # Segments to be matched\n", " buffer=2, # Search within 2 coordinate units of segment endpoints\n", " max_distance=1, # Only accept matches with similarity < 1 unit\n", " match=1, # Return best match only\n", " densify=0.1 # Add vertices for accurate matching\n", ")\n", "\n", "print(\"Segments after projection (now have route, beg, and end from our LRS):\")\n", "print(projected_segments[['segment_id', 'data_source', 'route', 'beg', 'end']])" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "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.3" } }, "nbformat": 4, "nbformat_minor": 5 }