Skip to main content

Convert 2D density histograms into minimal, weighted NetworkX graphs

Project description

Road Vectorizer

Convert 2D density histograms into minimal, weighted NetworkX graphs.

Given a density map (e.g. GPS trace heatmap, traffic density raster), the package extracts the road centrelines and returns a clean undirected graph where each edge follows the actual road path and carries a density-based weight.

Full city road network (69 nodes, 90 edges)

Full city overlay

Partial road network (17 nodes, 19 edges) — coverage: 37.9%

Partial city overlay


Installation

pip install numpy scipy scikit-image networkx matplotlib

No separate install needed — just clone the repo and import:

from road_vectorizer import build_graph, compute_road_coverage, plot_graph_overlay

Quick Start

import numpy as np
from road_vectorizer import build_graph, plot_graph_overlay

density = np.load("my_density_map.npy")

G = build_graph(density)
plot_graph_overlay(density, G)

API Reference

build_graph(density_map, **kwargs) → nx.Graph

Convert a 2D density histogram into a weighted undirected graph.

Parameter Type Default Description
density_map np.ndarray required 2D array of non-negative density values
threshold float | None None Binarization threshold. None → Otsu auto-threshold
dilate_radius int 0 Dilation before skeletonization (helps connect fragmented roads)
prune_length int 0 Max length of dead-end spurs to remove (pixels)
merge_distance int 5 Cluster nodes within this many pixels into one

Returns: nx.Graph with:

  • Nodes: pos = (col, row) for plotting
  • Edges: weight (mean density), max_density, length (pixels), path (list of (row, col) coordinates)
G = build_graph(density, threshold=0.3, prune_length=5, merge_distance=5)

compute_road_coverage(full_graph, partial_graph, tolerance=2) → dict

Compute what fraction of a full road network is present in a partial one.

For each edge in the full graph, checks how many of its path pixels lie within tolerance pixels of any path pixel in the partial graph.

Parameter Type Default Description
full_graph nx.Graph required Reference graph (from full density map)
partial_graph nx.Graph required Graph from partial density map (some roads removed)
tolerance int 2 Pixel tolerance for fuzzy matching (L∞ distance)

Returns: dict with:

  • "coverage" — overall fraction [0, 1]
  • "edges" — per-edge breakdown (u, v, length, covered_pixels, edge_coverage)
full_graph = build_graph(full_density, threshold=0.3)
partial_graph = build_graph(partial_density, threshold=0.3)

result = compute_road_coverage(full_graph, partial_graph, tolerance=2)
print(f"Coverage: {result['coverage']:.1%}")  # e.g. "Coverage: 37.9%"

plot_graph_overlay(density_map, graph, **kwargs) → Axes

Draw the density map with the extracted graph overlaid.

Parameter Type Default Description
density_map np.ndarray required Original 2D density histogram
graph nx.Graph required Graph from build_graph
edge_cmap str "winter" Colormap for weight-based edge colouring
color_edges_by_weight bool True Colour edges by their weight attribute
edge_color str | None None Fixed colour override for all edges
node_color str "#00e5ff" Node colour
node_size int 40 Node marker size
edge_width float 2.0 Edge line width
save_path str | None None Save figure to this path
show bool True Call plt.show()
plot_graph_overlay(density, G, save_path="overlay.png", show=False, edge_cmap="plasma")

Pipeline

density_map
    │
    ▼
┌─────────────┐     Otsu or manual threshold
│  Binarize   │────────────────────────────────▶  binary mask
└─────────────┘
    │
    ▼
┌──────────────┐    skimage.morphology.skeletonize
│ Skeletonize  │───────────────────────────────▶  1 px wide centrelines
└──────────────┘
    │
    ▼
┌─────────────┐     pixels with ≠ 2 neighbours
│ Find Nodes  │────────────────────────────────▶  junctions + endpoints
└─────────────┘
    │
    ▼
┌──────────────┐    walk skeleton between nodes
│ Trace Edges  │───────────────────────────────▶  raw edges + density
└──────────────┘
    │
    ▼
┌──────────────┐    1. Euclidean clustering of nearby nodes
│  Simplify    │    2. Contract degree-2 chain nodes
│              │    3. Prune dead-end spurs
└──────────────┘
    │
    ▼
  nx.Graph

Examples

Example Description
examples/example_usage.py Simple 200×200 synthetic map (5 roads)
examples/complex_example.py 400×400 map with grid, roundabout, curves
examples/city_example.py 600×600 organic city network + coverage demo
python examples/city_example.py

Dependencies

  • Python ≥ 3.10
  • numpy
  • scipy
  • scikit-image
  • networkx
  • matplotlib

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

road_vectorizer-0.1.0.tar.gz (11.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

road_vectorizer-0.1.0-py3-none-any.whl (12.7 kB view details)

Uploaded Python 3

File details

Details for the file road_vectorizer-0.1.0.tar.gz.

File metadata

  • Download URL: road_vectorizer-0.1.0.tar.gz
  • Upload date:
  • Size: 11.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.5

File hashes

Hashes for road_vectorizer-0.1.0.tar.gz
Algorithm Hash digest
SHA256 0686eec7733e9f2fd9357a35dd7f7ed7139fb6b43493d11f7f9aa8c8762728e6
MD5 0cc267102adf89ee1485f03c0bee9e68
BLAKE2b-256 dbb7bb8fcd1bc89f53d4738201945aba30248e6285b0a09018d067cfb787a806

See more details on using hashes here.

File details

Details for the file road_vectorizer-0.1.0-py3-none-any.whl.

File metadata

File hashes

Hashes for road_vectorizer-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 1360ec400df8f3e221b1ba351a9862b8e86898edeecd61f2fece65ba1af140e6
MD5 88ea339283262954c54abae202748774
BLAKE2b-256 d63e8ca878b90dc431770aaa011002e553c09b855663a78541684df61a1c1fb4

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page