{"id":25254275,"url":"https://github.com/trafficgcn/osmnx_adjacency_matrix_for_graph_convolutional_networks","last_synced_at":"2025-10-27T01:31:09.924Z","repository":{"id":173108709,"uuid":"598328479","full_name":"TrafficGCN/osmnx_adjacency_matrix_for_graph_convolutional_networks","owner":"TrafficGCN","description":"Creating an Adjacency Matrix Using the Dijkstra Algorithm for Graph Convolutional Networks GCNs","archived":false,"fork":false,"pushed_at":"2024-03-31T13:47:59.000Z","size":49218,"stargazers_count":13,"open_issues_count":0,"forks_count":0,"subscribers_count":3,"default_branch":"main","last_synced_at":"2024-04-01T12:40:25.524Z","etag":null,"topics":["adjacency-matrix","data-science","dijkstra","dijkstra-algorithm","gcn","graph","graph-algorithms","graph-convolutional-networks","matrix","metrla","open-street-map","optimal-route","osm","osmnx","python","traffic","traffic-analysis","traffic-congestion"],"latest_commit_sha":null,"homepage":"https://thomasafink.com/","language":"Jupyter Notebook","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/TrafficGCN.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null}},"created_at":"2023-02-06T21:57:24.000Z","updated_at":"2024-03-31T13:48:02.000Z","dependencies_parsed_at":null,"dependency_job_id":"58687a7f-68c3-4ab0-9da9-acae0df9f182","html_url":"https://github.com/TrafficGCN/osmnx_adjacency_matrix_for_graph_convolutional_networks","commit_stats":null,"previous_names":["thomasafink/osmnx_adjacency_matrix_for_graph_convolutional_networks","trafficgcn/osmnx_adjacency_matrix_for_graph_convolutional_networks"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TrafficGCN%2Fosmnx_adjacency_matrix_for_graph_convolutional_networks","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TrafficGCN%2Fosmnx_adjacency_matrix_for_graph_convolutional_networks/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TrafficGCN%2Fosmnx_adjacency_matrix_for_graph_convolutional_networks/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TrafficGCN%2Fosmnx_adjacency_matrix_for_graph_convolutional_networks/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/TrafficGCN","download_url":"https://codeload.github.com/TrafficGCN/osmnx_adjacency_matrix_for_graph_convolutional_networks/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":238418235,"owners_count":19468869,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["adjacency-matrix","data-science","dijkstra","dijkstra-algorithm","gcn","graph","graph-algorithms","graph-convolutional-networks","matrix","metrla","open-street-map","optimal-route","osm","osmnx","python","traffic","traffic-analysis","traffic-congestion"],"created_at":"2025-02-12T05:31:39.664Z","updated_at":"2025-10-27T01:30:59.908Z","avatar_url":"https://github.com/TrafficGCN.png","language":"Jupyter Notebook","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Creating an Adjacency Matrix Using the Dijkstra Algorithm for Graph Convolutional Networks GCNs\n\n\u003cb\u003eFor more datasets view: https://github.com/ThomasAFink/40_cities_osmnx_adjacency_matrices_for_graph_convolutional_networks/\u003c/b\u003e\n\n### Citations\n\n1. For the Los Angeles metr-la and Santa Clara pems-bay datasets cite: Kwak, Semin. (2020). PEMS-BAY and METR-LA in csv [Data set]. Zenodo. https://doi.org/10.5281/zenodo.5146275\n\n2. Weather sensors are provided by University of Utah Department of Atmospheric Sciences https://mesowest.utah.edu/\n\n3. Bicycle sensors from the City of Munich Opendata Portal: https://opendata.muenchen.de/dataset/raddauerzaehlstellen-muenchen/resource/211e882d-fadd-468a-bf8a-0014ae65a393?view_id=11a47d6c-0bc1-4bfa-93ea-126089b59c3d\n\n4. OpenStreetMap https://www.openstreetmap.org/ must also be referenced because the matrices where calculated using OpenStreetMap.\n5. If you use any of the Maps you must reference both OpenStreetMap and Mapbox https://www.mapbox.com/.\n\n### Introduction\n\nAlright so you have a list of geocoordinates (for example maybe street sensors) and you want to create an adjacency matrix to feed into your PyTorch or Tensorflow GCN model such as the A3T-GCN. This tutorial uses OSMnx to measure the distance of optimal paths between each of the geocoordinates for each geocoordinate. The distances are saved to a CSV file as an adjacency matrix. The distance matrix in this tutorial is very basic, but you could go even further by capturing more spatial information such as capturing the shape of the paths between the geocoordinates. The adjacency matrix is used to structure the unstructured graph data.\n\n![image](https://user-images.githubusercontent.com/53316058/217098922-fb6fb157-20dd-443f-8faa-217122097361.png)\n\n### Repo Structure\nA brief file structure overview of the repository is provided. The adjacency_matrix.py is in the root directory. The data folder houses a list of geocoordinates in a csv file. For this example six bicycle sensors where selected from the City of Munich's opendata portal. For a GCN of course it is better for a graph to be larger than 50 sensors. The graph distances are saved as a matrix in the output folder.\n\n\n\n    /\n    dijkstra_distance_matrix.py\n    dijkstra_distance_matrix_multi_processing.py\n\n    - / data / munich /\n    munich_bicycle_sensors.csv\n\n    - / output / munich /\n    bicycle_adjacency_matrix.csv\n    \nThe munich_bicycle_sensors.csv includes the following target geocoordinates.\n\n    DETEKTOR_ID,  LATITUDE,    LONGITUDE\n    Arnulf,       48.14205,    11.55534\n    Kreuther,     48.12194,    11.62417\n    Olympia,      48.16887,    11.55005\n    Hirsch,       48.14438,    11.51794\n    Margareten,   48.12032,    11.53599\n    Erhardt,      48.13192,    11.58469\n    \n### Prerequisites\nBefore jumping into the code the following requirements and packages are needed to run the code:\n\n    Python 3.10.6\n    pip3 install osmnx==0.16.1\n    pip3 install shapely==1.8.0\n    pip3 install scipy\n    pip3 install networkx\n    pip3 install pandas\n    \nFirst the packages that were just installed are imported into our file adjacency_matrix.py\n\n    import os\n    import numpy as np\n    from itertools import islice\n    import pandas as pd\n    import osmnx as ox\n    import networkx as nx\n\n### Code\nThen we setup the OSMNX cache configuration is an optional method to store maps from OSM. This is especially resourceful for storing larger maps as it requires fewer requests to the api. For plotting a handful of paths this really does not impact processing time significantly.\n\n    # Using the cache accelerates processing for a large map\n    ox.config(log_console=True, use_cache=True)\n\nNext we can set the location of our map to confine its perimeter.\n\n    place = 'Munich, Bavaria, Germany'\n\nThe mode of transport is set to bike. Drive captures the road network used by automobiles. Bike capture the path network used by bicycles. Walk capture the walkway network used by pedestrians. Finding an optimal walkway is usually too resource intensive when processing due to the fact that their are many more nodes on the walking graph\n\n    # 'drive', 'bike', 'walk'\n    mode = 'bike'\n\nFinally the graph can be requested and downloaded from the OSMnx api. The graph perimeters are set in the parameters and passed to the api request. The graph mode and place are also passed along. ONLY DOWNLOAD THE GRAPH ONCE TO AVOID REQUEST LIMITS!!\n\n    graph = ox.graph_from_place(place, network_type = mode)    \n\nNext a function is created which saves our optimal path distance matrix as the CSV file in our output folder from the list of geocoordinates in our data folder. This function takes our dataframe as a parameter.\n\n    ##### Interface to OSMNX    \n    def generate_adjacency_matrix(df):\n    \nTo save the distance between every sensor with every other sensor we create an adjacency matrix.\n\n        # Create the adjacency matrix\n        matrix = [[\"DETEKTOR_ID_X, DETEKTOR_ID_Y, DISTANCE\"]]\n    \nWe need to iterate through all the detectors for each detector in our dataframe.\n\n        i=0;\n            for detector in islice(df.iterrows(), 0, len(list(df.DETEKTOR_ID))):\n                j=0;\n                for each_detector in df.iterrows():\n\nNow that we have requested the graph from OSMnx api we can also request or path. We need the coordinates from the current sensor and the coordinates to the destination sensor. Remember we are inside a double for loop looping through sensor by sensor for each sensor.\n\n                    # coordinates from the current sensor\n                    start_latlng = (float(detector[\"LATITUDE\"]), float(detector[\"LONGITUDE\"]))\n                    # coordinates belonging to the destination sensor\n                    end_latlng = (float(each_detector[\"LATITUDE\"]), float(each_detector[\"LONGITUDE\"]))\n\n\nThen we find the nearest nodes to the coordinates on the graph we requested from OSMnx.\n\n                    # find the nearest node to the current sensor\n                    orig_node = ox.get_nearest_node(graph, start_latlng)\n                    # find the nearest node to the destination sensor\n                    dest_node = ox.get_nearest_node(graph, end_latlng)\n\n\nUsing the Dijkstra method we find the shortest distance between the two sensors or points. Another method is the bellman-ford algorithm.\n\n                    #find the shortest path method dijkstra or bellman-ford\n                    shortest_route_distance = nx.shortest_path_length(graph, orig_node,dest_node, weight=\"length\", method=\"dijkstra\")\n\nFinally we then append the distance to the adjacency matrix we created outside of the for loop. This would be a good place to console log the matrix to keep track of progress.\n\n                    matrix.append([str(detector[\"DETEKTOR_ID\"]) + \",\" + str(each_detector[\"DETEKTOR_ID\"]) + \",\" + str(float(shortest_route_distance))])\n\n\nAfter the distances between all the sensors have been found and added to the adjacency matrix using the nested for loop, the matrix is then converted into a numpy array.\n\n        matrix = np.array(matrix)\n        matrix = np.asarray(matrix)\n\nFinally the matrix is saved as a csv in the output folder as bicycle_adjacency_matrix.csv\n\n        # Save the dijkstra rad sensors distance matrix\n        np.savetxt(OS_PATH + \"output/bicycle_adjacency_matrix.csv\", matrix, delimiter=\",\", fmt='%s')\n\n\n\u003chr /\u003e\n\n\nFirst in the main part of the script a list of target geocoordinates are fetched from the data folder in the munich_bicycle_sensors.csv file and loaded into python using pandas' dataframe method. The geocoordinates are then formatted as geocoordinates using geopandas.\n\n    # Data import path\n    OS_PATH = os.path.dirname(os.path.realpath('__file__'))\n    SENSORS_CSV   = OS_PATH + '/data/munich_bicycle_sensors.csv'\n\n    # Data Import Path\n    df = pd.read_csv(SENSORS_CSV)\n\n    # Keep only relevant columns\n    df = df.loc[:, (\"DETEKTOR_ID\",\"LATITUDE\", \"LONGITUDE\")]\n\n    # Remove missing geocoordinates\n    df.dropna(subset=['LATITUDE'], how='all', inplace=True)\n    df.dropna(subset=['LONGITUDE'], how='all', inplace=True)\n\n    # Remove missing sensor ids\n    df.dropna(subset=['DETEKTOR_ID'], how='all', inplace=True)\n\nFinally our generate_adjacency_matrix function is called after getting the optimal paths.\n\n    generate_adjacency_matrix(df)\n\nThe result is presented below:\n\n    DETEKTOR_ID_X, DETEKTOR_ID_Y, DISTANCE\n    Arnulf,        Arnulf,        0.0\n    Arnulf,        Kreuther,      6360.067999999998\n    Arnulf,        Olympia,       3548.8400000000006\n    Arnulf,        Hirsch,        3364.7529999999997\n    Arnulf,        Margareten,    3658.5860000000002\n    Arnulf,        Erhardt,       2916.509\n    Kreuther,      Arnulf,        6403.099000000001\n    Kreuther,      Kreuther,      0.0\n    Kreuther,      Olympia,       9011.475000000002\n    Kreuther,      Hirsch,        9707.515000000003\n    Kreuther,      Margareten,    8099.6449999999995\n    Kreuther,      Erhardt,       3523.4769999999994\n    Olympia,       Arnulf,        3542.883\n    Olympia,       Kreuther,      9088.311999999993\n    Olympia,       Olympia,       0.0\n    Olympia,       Hirsch,        4825.775000000002\n    Olympia,       Margareten,    7074.653000000002\n    Olympia,       Erhardt,       5913.148000000002\n    Hirsch,        Arnulf,        3302.0229999999992\n    Hirsch,        Kreuther,      9655.762999999995\n    Hirsch,        Olympia,       4833.914000000002\n    Hirsch,        Hirsch,        0.0\n    Hirsch,        Margareten,    4234.665\n    Hirsch,        Erhardt,       6212.2040000000015\n    Margareten,    Arnulf,        3646.659999999999\n    Margareten,    Kreuther,      8178.820999999996\n    Margareten,    Olympia,       6458.455999999998\n    Margareten,    Hirsch,        4202.3730000000005\n    Margareten,    Margareten,    0.0\n    Margareten,    Erhardt,       4780.81\n    Erhardt,       Arnulf,        2913.113999999999\n    Erhardt,       Kreuther,      3536.422\n    Erhardt,       Olympia,       5837.165999999999\n    Erhardt,       Hirsch,        6217.530000000001\n    Erhardt,       Margareten,    4748.426999999998\n    Erhardt,       Erhardt,       0.0\n\u003cimg src=\"https://github.com/ThomasAFink/osmnx_adjacency_matrix_for_graph_convolutional_networks/blob/main/output/munich/munich_dijkstra_map_bike_small.jpg?raw=true\" width=\"450\" align=\"right\"\u003e\n\nA more complex adjacency may capture the entire shape of the paths between the points instead of just the distance. This would increase the accuracy of a GCN model as you capturing more spatial information. This can also be done with OSMnx. I reference how to capture an entire path in an array plotting the Dijkstra paths in my other tutorial.\n\nThe OSMnx solution is great for data science if you need static graph data to test an academic GCN model. For a dynamic graph or a software solution it would be better to use a proprietary vendor such as a Google or Apple Maps to calculate your distances with better efficiency.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftrafficgcn%2Fosmnx_adjacency_matrix_for_graph_convolutional_networks","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftrafficgcn%2Fosmnx_adjacency_matrix_for_graph_convolutional_networks","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftrafficgcn%2Fosmnx_adjacency_matrix_for_graph_convolutional_networks/lists"}