All Projects → matejker → everystreet

matejker / everystreet

Licence: MIT License
An algorithm finding #everystreet route on Open Street Map (OSMnx)

Programming Languages

Jupyter Notebook
11667 projects
python
139335 projects - #7 most used programming language

Projects that are alternatives of or similar to everystreet

Erdos
modular and modern graph-theory algorithms framework in Java
Stars: ✭ 104 (+141.86%)
Mutual labels:  graph-algorithms, graph-theory
jsgraph
Deprecated: Use the @encapsule/arccore package that includes the graph library
Stars: ✭ 42 (-2.33%)
Mutual labels:  graph-algorithms, graph-theory
Libgrape Lite
🍇 A C++ library for parallel graph processing 🍇
Stars: ✭ 169 (+293.02%)
Mutual labels:  graph-algorithms, graph-theory
Modal logic
Final Year Masters Project: modal logic solver tableaux
Stars: ✭ 16 (-62.79%)
Mutual labels:  graph-algorithms, graph-theory
kaliningraph
🕸️ Graphs, finite fields and discrete dynamical systems in Kotlin
Stars: ✭ 62 (+44.19%)
Mutual labels:  graph-algorithms, graph-theory
Networkx
Network Analysis in Python
Stars: ✭ 10,057 (+23288.37%)
Mutual labels:  graph-algorithms, graph-theory
graphs
Graph algorithms written in Go
Stars: ✭ 60 (+39.53%)
Mutual labels:  graph-algorithms, graph-theory
Grafatko
An app for creating and visualizing graphs and graph-related algorithms.
Stars: ✭ 22 (-48.84%)
Mutual labels:  graph-algorithms, graph-theory
Differentia.js
No longer being supported or maintained. A Graph Theory & Data Structure Library for JavaScript.
Stars: ✭ 13 (-69.77%)
Mutual labels:  graph-algorithms, graph-theory
networkx-guide
We here are very big fans of NetworkX as a graph library and its comprehensive set of graph algorithms. For many though, working with NetworkX involves a steep learning curve. This guide is designed as an aid for beginners and experienced users to find specific tips and explore the world of complex networks.
Stars: ✭ 28 (-34.88%)
Mutual labels:  graph-algorithms, graph-theory
Graphs.jl
An optimized graphs package for the Julia programming language
Stars: ✭ 197 (+358.14%)
Mutual labels:  graph-algorithms, graph-theory
grblas
Python wrapper around GraphBLAS
Stars: ✭ 22 (-48.84%)
Mutual labels:  graph-algorithms, graph-theory
Lightgraphs.jl
An optimized graphs package for the Julia programming language
Stars: ✭ 611 (+1320.93%)
Mutual labels:  graph-algorithms, graph-theory
D3graphtheory
💥 Interactive and colorful 🎨 graph theory tutorials made using d3.js ⚡️
Stars: ✭ 1,364 (+3072.09%)
Mutual labels:  graph-algorithms, graph-theory
Graph
Graph algorithms and data structures
Stars: ✭ 431 (+902.33%)
Mutual labels:  graph-algorithms, graph-theory
jgrapht
Master repository for the JGraphT project
Stars: ✭ 2,259 (+5153.49%)
Mutual labels:  graph-algorithms, graph-theory
Graph-Algorithms
Everything you need to know about graph theory to ace a technical interview 🔥
Stars: ✭ 87 (+102.33%)
Mutual labels:  graph-algorithms, graph-theory
LightGraphs.jl
An optimized graphs package for the Julia programming language
Stars: ✭ 680 (+1481.4%)
Mutual labels:  graph-algorithms, graph-theory
instance-watcher
Get notified for Instances mistakenly left running across all AWS regions for specific AWS Account
Stars: ✭ 90 (+109.3%)
Mutual labels:  running
osm-wikidata
Match OSM entities with Wikidata items
Stars: ✭ 72 (+67.44%)
Mutual labels:  openstreetmap

#everystreet algorithm

Over the COVID-19 lockdown many runners and cyclist found themselves captured within their cities. Some of them ended up running or spinning endless hours on virtual races such as Zwift, but some as e.g. Mark Beaumont [1] decided to join #everystreet challenge. Every street is a challenge originated by Rickey Gates [2, 3] who run every single street in city of San Francisco in fall 2018 which took him 46 days and run 1,303 miles.

Inspired by Mark Beaumont who did this challenge over the lockdown in Edinburgh, place where I spend the lockdown, and literally everyone else who managed to accomplish this challenge (I would have never had the patience and motivation to run that much in the city) I said to myself that I am a mathematician and software engineer more then a runner or cyclist. So, I ask myself, what is the optimal route? Is there any algorithm which can generate such route?

Every street challenge

Rules of every street challenge are run or cycle every single street of given (metropolitan) area which is usually a city or a neighborhood. Till this point the rules are simple and clear, but the problem is the street definition. How do we define a street? Do we include pedestrian paths, parks or motorways? For simplicity, we consider a street network of edges and nodes (interception of two or more edges and dead-end roads) accessible by car*. Assuming that runner can run oneway roads in both direction, we do not consider road direction. In order to find such network we used Open Street Map API [4].

Chinese postman problem

Finding a route for #everystreet challenge is basically a well-known problem of the chinese postman, called after Chinese mathematician Kuan Mei-Ko. (Also known as Postman Tour or Route Inspection Problem) The problem is to find the shortest closed path (or circuit) such that visits every edge of a (closed and undirected) graph.

from libs.tools import *
from libs.graph_route import plot_graph_route

import networkx as nx
import osmnx as ox
import matplotlib.pyplot as plt

from network import Network
from network.algorithms import hierholzer

We used OSMnx as a source for geographical data. As an example, we chose an Edinburgh neighborhood of the Grange. In order to avoid heavy traffic we specify OSMnx query and limit the highway selection.

CUSTOM_FILTER = (
    '["highway"]["area"!~"yes"]["highway"!~"bridleway|bus_guideway|bus_stop|construction|'
    'cycleway|elevator|footway|motorway|motorway_junction|motorway_link|escalator|proposed|'
    'construction|platform|raceway|rest_area|path|service"]["access"!~"customers|no|private"]'
    '["public_transport"!~"platform"]["fee"!~"yes"]["foot"!~"no"]["service"!~"drive-through|'
    'driveway|parking_aisle"]["toll"!~"yes"]'
)

location = "The Grange, Edinburgh, Scotland"
org_graph = ox.graph_from_place(location, custom_filter=CUSTOM_FILTER)

""" Simplifying the original directed multi-graph to undirected, so we can go both 
    ways in one way streets """
graph = ox.utils_graph.get_undirected(org_graph)
fig, ax = ox.plot_graph(graph, node_zorder=2, node_color="k", bgcolor="w")

Algorithm

In this work, we used algorithm proposed by Edmonds, J. and Johnson [5], which states as follow:

  1. Find all nodes with odd degree
  2. Calculate the sortest distance between all odd-degree nodes
  3. Create a complete weighted graph of all odd-degree nodes, as weights we use distances from step 2.
  4. Find minimal matching in the complete weighted graph
  5. Add matched pairs into original graph
  6. Find the Eulerian circuit using Hierholzer [10] algorithm

👉 For more details see the theoretical summary.

# Finds the odd degree nodes and minimal matching
odd_degree_nodes = get_odd_degree_nodes(graph)
pair_weights = get_shortest_distance_for_odd_degrees(graph, odd_degree_nodes)
matched_edges_with_weights = min_matching(pair_weights)

Result of minimal matching plotted into original graph (red edges).

fig, ax = plt.subplots(figsize=(8, 8), facecolor='black', frameon=False)
for v, u, w in matched_edges_with_weights:
    x = graph.nodes[v]["x"], graph.nodes[u]["x"]
    y = graph.nodes[v]["y"], graph.nodes[u]["y"]
    ax.plot(x, y, c='red', alpha=0.3)
    ax.scatter(x, y, c='red', edgecolor="none")

fig, ax = ox.plot_graph(graph, node_zorder=2, node_color='g', bgcolor='k', ax=ax)

Counting the final_path with Hierholzer algorithm and plotting on map. As we can see all edges were visited.

# List all edges of the extended graph including original edges and edges from minimal matching
single_edges = [(u, v) for u, v, k in graph.edges]
added_edges = get_shortest_paths(graph, matched_edges_with_weights)
edges = map_osmnx_edges2integers(graph, single_edges + added_edges)

# Finds the Eulerian path
network = Network(len(graph.nodes), edges, weighted=True)
eulerian_path = hierholzer(network)
converted_eulerian_path = convert_integer_path2osmnx_nodes(eulerian_path, graph.nodes())
double_edge_heap = get_double_edge_heap(org_graph)

# Finds the final path with edge IDs
final_path = convert_path(graph, converted_eulerian_path, double_edge_heap)

fig, ax = plot_graph_route(org_graph, final_path, route_linewidth=6, node_size=0, bgcolor="w", route_alpha=0.2, route_color="w")```

In order to see how the runner should accomplish the route on the map, we created a simple GIF.

for i, e in enumerate(final_path, start=1):
    fig, ax = plot_graph_route(org_graph, final_path[:i], route_linewidth=6, node_size=0, bgcolor="w", route_alpha=0.2)
    ax.set_title(location)
    fig.savefig(f"/img_{i}.png", dpi=120, bbox_inches="tight")

The Grange route

If you would like to generate GPX file with the route, follow Issue #6 (comment)

Usage

In order to run it locally you need to install it on Python +3.8:

pip install -r requirements.txt  # Using pip
poetry install  # or by using Poetry

Feel more that free to use, modify and copy the code, just follow the licence and cite it:

@misc{Kerekrety2020,
  author = {Kerekrety, M},
  title = {#everystreet algorithm},
  year = {2020},
  publisher = {GitHub},
  journal = {GitHub repository},
  howpublished = {\url{https://github.com/matejker/everystreet}}
}

Related readings

References

[1] Beaumont M. (2020), Strava Profile, https://www.strava.com/athletes/8288853
[2] Gates R. (2019), Every Single Street with Rickey Gates, https://www.everysinglestreet.com/why
[3] Turner K. (2019), Every Single Street, Strava stories, https://blog.strava.com/every-single-street-17484/
[4] Open Street Map (2020), http://openstreetmap.org
[5] Edmonds, J. and Johnson, E.L (1973), Matching, Euler tours and the Chinese postman. Mathematical Programming 5, 88124 https://doi.org/10.1007/BF01580113
[6] Bondy J. A. and Murty U. S. R. (1976), Graph theory with applications, ISBN 0333177916
[7] Diestel. R, (2005), Graph Theory Graduate Texts in Mathematics, Springer
[8] Erben, M., (1652), Knigsberg 1651, https://en.wikipedia.org/wiki/Knigsberg#/media/File:Image- Koenigsberg, Map by Merian-Erben 1652.jpg
[9] Euler L. (1741), Commentarii academiae scientiarum Petropolitanae, Vol- ume 8, pp. 128-140. https://scholarlycommons.pacific.edu/euler-works/53/
[10] Fleischner H. (2016), Algorithms in Graph Theory, https://www.dbai.tuwien.ac.at/staff/kronegger/misc/ AlgorithmsInGraphTheory Script.pdf
[11] Cormen, T. H. (2001), Introduction to algorithms, Cambridge, Mass: MIT Press.
[12] Galil Z. (1986), Efficient algorithms for finding maximum matching in graphs, https://www.semanticscholar.org/paper/Efficient-algorithms-for- finding-maximum-matching-Galil/ef1b31b4728615a52e3b8084379a4897 b8e526ea
[13] Edmonds J. (2008), Weighted maximum matching in general graphs, http://jorisvr.nl/files/graphmatching/20130407/mwmatching.py

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].