Highlight a shortest path
Click one city, then another, and the page paints the shortest road route between them with graphtyDijkstra, pathTo and a .path class.
The knuth-miles dataset holds 128 North American cities, with the road miles between every pair in each edge's weight. The page drops edges over 300 miles, so a trip crosses several cities and some pairs have no route. It needs a <div id="cy"> with a height, a <p id="status"> and a bundler such as Vite.
import cytoscape from "cytoscape";
import graphtyCytoscape from "@graphty/cytoscape-extensions";
cytoscape.use(graphtyCytoscape);
const status = document.getElementById("status");
const cy = cytoscape({
container: document.getElementById("cy"),
style: [
{ selector: "node", style: { width: 6, height: 6, "background-color": "#999" } },
{ selector: "edge", style: { width: 1, "line-color": "#ddd" } },
{ selector: "node.path", style: { width: 10, height: 10, "background-color": "#d22" } },
{ selector: "edge.path", style: { width: 4, "line-color": "#d22", "z-index": 1 } },
],
});
cy.graphtyDataset("knuth-miles").then(() => {
cy.edges("[weight > 300]").remove();
cy.nodes().positions((n) => ({ x: n.data("longitude") * 20, y: -n.data("latitude") * 20 }));
cy.fit();
status.textContent = "Click a city to start.";
});
let root = null;
cy.on("tap", "node", (event) => {
const node = event.target;
if (root === null) {
cy.elements().removeClass("path");
root = node.addClass("path");
status.textContent = `From ${root.id()}: click a second city.`;
return;
}
const paths = cy.elements().graphtyDijkstra({ root, weight: "weight" });
const miles = paths.distanceTo(node);
if (miles === Infinity) {
status.textContent = `No chain of short roads joins ${root.id()} and ${node.id()}.`;
} else {
const path = paths.pathTo(node).addClass("path");
status.textContent = `${root.id()} to ${node.id()}: ${miles} miles, ${path.edges().length} legs.`;
}
root = null;
});Youngstown, OH to Savannah, GA prints "829 miles, 4 legs". Seattle, WA to Tampa, FL has no route.
Reading the result
graphtyDijkstra runs once from root and answers for every goal. distanceTo(goal) is the summed weight of the shortest route, or Infinity when there is none. pathTo(goal) is a collection of node, edge, node in route order, empty when there is no route.
weight names the edge data field that holds the cost; without it every edge weighs 1 and you get the fewest hops. root takes a node, a collection or a selector. Edges are undirected unless you pass directed: true.
One route at a time with A*
For a single goal, graphtyAStar({ root, goal, weight, heuristic }) returns { found, distance, path }. heuristic(node) estimates the distance left to goal and must never overestimate it; straight-line miles from each city's latitude and longitude qualify. Without a heuristic, A* searches like Dijkstra. See graphtyAStar in the algorithm reference.
Try it
The Dijkstra gallery story: shortest distances from one node on a weighted graph, and on generated graphs from its controls; the paths and trees gallery shows the other path and tree algorithms.