Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ jobs:

steps:
- uses: actions/checkout@v1
- name: Set up Python 3.7
- name: Set up Python 3.13
uses: actions/setup-python@v1
with:
python-version: 3.7
python-version: 3.13
- name: Install dependencies
run: |
python -m pip install --upgrade pip
Expand Down
16 changes: 8 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
# OsmToRoadGraph v.0.6.0
# OsmToRoadGraph v.0.7.0

![Build Status](https://github.com/AndGem/OsmToRoadGraph/workflows/Build%20Status/badge.svg?branch=master)
[![codecov](https://codecov.io/gh/AndGem/OsmToRoadGraph/branch/master/graph/badge.svg)](https://codecov.io/gh/AndGem/OsmToRoadGraph)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

- [OsmToRoadGraph v.0.6.0](#osmtoroadgraph-v060)
- [OsmToRoadGraph v.0.7.0](#osmtoroadgraph-v060)
- [Updates](#updates)
- [Introduction](#introduction)
- [Motivation](#motivation)
Expand All @@ -24,12 +24,12 @@

## Updates

**Changelog v.0.5.0 -> v.0.6.0c:**

- [x] Added bz2 support for input files
- [x] dropped Python <=3.6 support
- [x] fixed minor issues with v.0.6.0a
- [x] improve code quality from v.0.6.0b
**Changelog v.0.6.0c -> v.0.7.0:**

- [x] Upgraded to Python 3.13 requirement
- [x] pyproject.toml added
- [x] minor code improvements

## Introduction

Expand All @@ -53,7 +53,7 @@ As an additional feature, and to make interaction easier, since version 0.5 OsmT

### Requirements

- Python 3.7+/PyPy
- Python 3.13+/PyPy
- An OSM XML file
- [Optional: [networkx](https://networkx.github.io/) as dependency: `pip3 install networkx`]

Expand Down
6 changes: 3 additions & 3 deletions examples/pycgr-to-png/pycgr-to-png.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
from dataclasses import dataclass
from enum import Enum
import argparse
import sys
from dataclasses import dataclass
from enum import Enum

from PIL import Image, ImageDraw, ImageColor
import utm
from PIL import Image, ImageColor, ImageDraw


@dataclass
Expand Down
31 changes: 10 additions & 21 deletions examples/shortest-distances-drawing/shortest-distances-drawing.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,20 @@
import argparse
from collections import Counter
from functools import lru_cache
import json
import random
import sys
from collections import Counter
from functools import lru_cache

import networkx as nx
from PIL import Image, ImageDraw, ImageColor
from tqdm import tqdm
import utm
from PIL import Image, ImageColor, ImageDraw
from tqdm import tqdm


def load_graph(filename):
with open(filename, "r", encoding="utf-8") as f:
json_data = json.load(f)
G = nx.adjacency_graph(json_data)
return G
return nx.adjacency_graph(json_data)


def find_approximate_central_node(G):
Expand All @@ -29,9 +28,7 @@ def find_approximate_central_node(G):
node_distances += lengths

central_node = min(node_distances, key=node_distances.get)
print(
f"taking node with id {central_node} as central node, with summed distance: {node_distances[central_node]}"
)
print(f"taking node with id {central_node} as central node, with summed distance: {node_distances[central_node]}")
return central_node


Expand Down Expand Up @@ -127,12 +124,8 @@ def edge_length(_u, _v, data):

if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument(
"-f", "--file", dest="in_filename", type=str, help="input networkx JSON file"
)
parser.add_argument(
"-o", "--out", dest="out_filename", type=str, help="output png file"
)
parser.add_argument("-f", "--file", dest="in_filename", type=str, help="input networkx JSON file")
parser.add_argument("-o", "--out", dest="out_filename", type=str, help="output png file")
parser.add_argument(
"-c",
"--center",
Expand Down Expand Up @@ -181,11 +174,7 @@ def edge_length(_u, _v, data):
elif args.metric == "length":
metric = edge_length
else:
print(
f"Did not recognize --metric/-m option. Provided {args.metric}. Must either be 'travel-time' or 'length'"
)
print(f"Did not recognize --metric/-m option. Provided {args.metric}. Must either be 'travel-time' or 'length'")

lengths = nx.single_source_dijkstra_path_length(
G, start_node, cutoff=None, weight=metric
)
lengths = nx.single_source_dijkstra_path_length(G, start_node, cutoff=None, weight=metric)
draw_graph_on_map(G, lengths, output_filename=args.out_filename)
17 changes: 5 additions & 12 deletions graph/algorithms.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
from collections import deque
from typing import Deque, Set

from graph import graphfactory
from utils import timer

from graph.graph import Graph
from typing import Deque, Set
from utils import timer


def BFS(graph: Graph, s: int) -> Set[int]:
Expand All @@ -13,11 +12,7 @@ def BFS(graph: Graph, s: int) -> Set[int]:

while unvisited_nodes:
current_node = unvisited_nodes.popleft()
unseen_nodes = [
neighbor
for neighbor in graph.all_neighbors(current_node)
if neighbor not in seen_nodes
]
unseen_nodes = [neighbor for neighbor in graph.all_neighbors(current_node) if neighbor not in seen_nodes]
seen_nodes.update(unseen_nodes)
unvisited_nodes.extend(unseen_nodes)

Expand All @@ -40,14 +35,12 @@ def computeLCC(graph):
# determine largest connected components
lcc = max(found_nodes, key=len)

print(
f"\t LCC contains {len(lcc)} nodes (removed { len(graph.vertices) - len(lcc)} nodes)"
)
print(f"\t LCC contains {len(lcc)} nodes (removed { len(graph.vertices) - len(lcc)} nodes)")

return lcc


def computeLCCGraph(graph: Graph) -> Graph:
lcc = computeLCC(graph)
new_nodes = [graph.vertices[id] for id in lcc]
new_nodes = [graph.vertices[vertex_id] for vertex_id in lcc]
return graphfactory.build_graph_from_vertices_edges(new_nodes, graph.edges)
29 changes: 8 additions & 21 deletions graph/contract_graph.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
from collections import deque, defaultdict
from collections import defaultdict, deque
from dataclasses import replace
from typing import DefaultDict, List, Set, Tuple

from graph import graphfactory
from utils import timer

from graph.graph import Graph
from graph.graph_types import Vertex, Edge
from graph.graph_types import Edge, Vertex
from utils import timer


class ContractGraph:
Expand Down Expand Up @@ -47,9 +46,7 @@ def _find_new_edges(self) -> Set[Edge]:
for first_out_edge in out_edges:
start_node_id = node_id

edges_to_merge, final_node_id = self._find_edges_to_merge(
start_node_id, first_out_edge
)
edges_to_merge, final_node_id = self._find_edges_to_merge(start_node_id, first_out_edge)

if len(edges_to_merge) == 0:
continue
Expand All @@ -60,16 +57,8 @@ def _find_new_edges(self) -> Set[Edge]:
if edges_to_merge[0].backward:
# deduplication measure; if not for this for bidirectional edges, that are
# removed between intersections, 2 new edges would be created
smaller_node_id = (
start_node_id
if start_node_id < final_node_id
else final_node_id
)
bigger_node_id = (
start_node_id
if start_node_id > final_node_id
else final_node_id
)
smaller_node_id = start_node_id if start_node_id < final_node_id else final_node_id
bigger_node_id = start_node_id if start_node_id > final_node_id else final_node_id
if (smaller_node_id, bigger_node_id) in bidirectional_edges:
# already added this edge skip it
continue
Expand All @@ -92,9 +81,7 @@ def _find_new_edges(self) -> Set[Edge]:
new_edges.add(merged_edge)
return new_edges

def _find_edges_to_merge(
self, start_node_id: int, first_out_edge: Edge
) -> Tuple[List[Edge], int]:
def _find_edges_to_merge(self, start_node_id: int, first_out_edge: Edge) -> Tuple[List[Edge], int]:
# walk from start_node along first_out_edge until:
# i) another intersection node is found
# ii) an edge is encountered on the way that is different (different name, max_speed, ...)
Expand Down Expand Up @@ -127,7 +114,7 @@ def _find_edges_to_merge(

if len(next_out_edges) > 1:
# something is wrong.. this should have been filtered out by the intersection check
assert False
raise AssertionError()

next_out_edge = next_out_edges[0]
if self._is_not_same_edge(out_edge, next_out_edge):
Expand Down
6 changes: 3 additions & 3 deletions graph/convert_graph.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
try:
import contextlib

with contextlib.suppress(ImportError):
import networkx as nx
except ImportError:
pass


def convert_to_networkx(graph):
Expand Down
7 changes: 3 additions & 4 deletions graph/graph.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from graph.graph_types import Edge, Vertex
from typing import List

from graph.graph_types import Edge, Vertex


class Graph:
def __init__(self) -> None:
Expand Down Expand Up @@ -35,6 +36,4 @@ def edge_name(self, edge_id):
return f"{self.edges[edge_id].name}"

def all_neighbors(self, node_id: int) -> List[int]:
return list(
set(self.outneighbors[node_id]).union(set(self.inneighbors[node_id]))
)
return list(set(self.outneighbors[node_id]).union(set(self.inneighbors[node_id])))
23 changes: 8 additions & 15 deletions graph/graphfactory.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,19 @@
from dataclasses import replace
from typing import Dict, List, Tuple

from graph.graph import Graph
from graph.graph_types import Vertex, Edge, VertexData, EdgeData
from graph.graph_types import Edge, EdgeData, Vertex, VertexData
from osm.osm_types import OSMNode, OSMWay
from utils import geo_tools, timer

from typing import Dict, List, Tuple


@timer.timer
def build_graph_from_osm(nodes: Dict[int, OSMNode], ways: List[OSMWay]) -> Graph:
g = Graph()

# 1. create mapping to 0 based index nodes
node_ids = nodes.keys()
id_mapper = dict(zip(node_ids, range(len(node_ids))))
id_mapper = dict(zip(node_ids, range(len(node_ids)), strict=True))

# 2. add nodes and edges
_add_nodes(g, id_mapper, nodes)
Expand All @@ -34,20 +33,14 @@ def _add_edges(g: Graph, id_mapper: Dict[int, int], ways: List[OSMWay]) -> None:
for i in range(len(w.nodes) - 1):
s_id, t_id = id_mapper[w.nodes[i]], id_mapper[w.nodes[i + 1]]
s, t = g.vertices[s_id], g.vertices[t_id]
length = round(
geo_tools.distance(s.data.lat, s.data.lon, t.data.lat, t.data.lon), 2
)
data = EdgeData(
length=length, highway=w.highway, max_v=w.max_speed_int, name=w.name
)
length = round(geo_tools.distance(s.data.lat, s.data.lon, t.data.lat, t.data.lon), 2)
data = EdgeData(length=length, highway=w.highway, max_v=w.max_speed_int, name=w.name)
edge = Edge(s_id, t_id, w.forward, w.backward, data=data)
if w.forward and w.backward:
smaller, bigger = min(s_id, t_id), max(s_id, t_id)
if (smaller, bigger) in bidirectional_edges:
print(f"found duplicated bidirectional edge {(smaller, bigger)}")
print(
f"(osm ids {w.osm_id} and {bidirectional_edges[(smaller, bigger)]})... skipping one"
)
print(f"(osm ids {w.osm_id} and {bidirectional_edges[(smaller, bigger)]})... skipping one")
continue
bidirectional_edges[(smaller, bigger)] = w.osm_id

Expand All @@ -59,8 +52,8 @@ def build_graph_from_vertices_edges(vertices: List[Vertex], edges: List[Edge]) -
g = Graph()

# 1. add all nodes and create mapping to 0 based index nodes
vertex_ids = set(v.id for v in vertices)
id_mapper = dict(zip(vertex_ids, range(len(vertex_ids))))
vertex_ids = {v.id for v in vertices}
id_mapper = dict(zip(vertex_ids, range(len(vertex_ids)), strict=True))
for v in vertices:
g.add_node(Vertex(id_mapper[v.id], v.data))

Expand Down
13 changes: 6 additions & 7 deletions osm/read_osm.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import bz2
import xml.sax
from typing import Dict, List, Set, Tuple

from osm.osm_types import OSMNode, OSMWay
from osm.way_parser_helper import WayParserHelper
from osm.xml_handler import NodeHandler, WayHandler, PercentageFile
from osm.xml_handler import NodeHandler, PercentageFile, WayHandler
from utils import timer

from typing import Dict, List, Set, Tuple


@timer.timer
def read_file(osm_filename, configuration) -> Tuple[Dict[int, OSMNode], List[OSMWay]]:
Expand All @@ -31,10 +30,10 @@ def decompress_content(osm_filename):

if content_begin.startswith(magic_bz2):
print("identified bz2 compressed file.. decompressing")
f = bz2.open(osm_filename, "rb")
content = f.read()
print("done!")
return content
with bz2.open(osm_filename, "rb") as f:
content = f.read()
print("done!")
return content

print("no compression recognized!")
return None
Expand Down
4 changes: 1 addition & 3 deletions osm/sanitize_input.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,7 @@ def remove_adjacent_duplicates(nodes):
del ways[index]

# determine nodes that do not appear in any of the ways
nodes_to_remove = [
osm_id for osm_id in nodes.keys() if osm_id not in found_node_ids
]
nodes_to_remove = [osm_id for osm_id in nodes if osm_id not in found_node_ids]
# remove these nodes
for index in reversed(nodes_to_remove):
del nodes[index]
Expand Down
7 changes: 2 additions & 5 deletions osm/way_parser_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,7 @@ def parse_max_speed(self, osm_way: OSMWay) -> int:
return self.config.speed_limits[highway]

try:
max_speed = int(maximum_speed)
return max_speed
return int(maximum_speed)

except ValueError:
max_speed_str = maximum_speed.lower()
Expand Down Expand Up @@ -76,8 +75,6 @@ def parse_max_speed(self, osm_way: OSMWay) -> int:
max_speed = self.config.speed_limits[highway]
else:
max_speed = 30
print(
f"couldn't find a speed limit for highway type {highway}! Setting it to {max_speed}"
)
print(f"couldn't find a speed limit for highway type {highway}! Setting it to {max_speed}")

return max_speed
Loading