/* * * Dijkstra Short Path Calculator and Graph Plotter * Uses D3 JS (V3) * */ var ShortestPathCalculator = function(nodes, paths) { this.nodes = nodes; // nodes => [ { index: 0, value: 'a', r: 20 }, ... ] this.paths = paths; // paths => [ { source: 0, target: 1, distance: 150 }, ... ] this.distances = []; // [ [ x, 100, 150 ], [ 100, x, 10] ] this.graph = {}; /*console.log("sp paths"); console.log(this.paths); console.log("sp nodes"); console.log(this.nodes);*/ var maxNodes = 1000; //updated from 100 var minNodes = 3; if(!d3) throw new ShortestPathCalculator.SpcError(10, 'D3 library not found'); if(!nodes.length || nodes.length > maxNodes || nodes.length < minNodes) throw new ShortestPathCalculator.SpcError(11, 'Nodes format invalid => ' + JSON.stringify(nodes) ); } ShortestPathCalculator.isInteger = function(i) { return /^\d+$/.test(i); } ShortestPathCalculator.SpcError = function(code, message) { console.log(message); //alert(message); return { code: code, message: message }; } ShortestPathCalculator.prototype.findRoute = function(source, target) { if(!ShortestPathCalculator.isInteger(source) || !ShortestPathCalculator.isInteger(target)) throw new ShortestPathCalculator.SpcError(20, "Source and target must be ints"); if(source > this.nodes.length - 1|| target > this.nodes.length - 1) throw new ShortestPathCalculator.SpcError(21, "Source or target put of range"); this.makeDistanceArrayFromNodes(); this.populateDistances(); this.result = this.dijkstra(source, target); return this.result; } ShortestPathCalculator.prototype.makeDistanceArrayFromNodes = function() { this.distances = []; for(var i=0; i 0; --i) force.tick(); force.stop(); this.graph.svg.selectAll("line") .data(this.paths) .enter() .append("line") .attr("class", function(d) { if(that.result.path !== null) { for (var i = 0; i < that.result.path.length; i++) { if ((that.result.path[i].source === d.source.index && that.result.path[i].target === d.target.index) || (that.result.path[i].source === d.target.index && that.result.path[i].target === d.source.index)) return 'link bold'; } } return 'link'; }) .attr("x1", function(d) { return d.source.x; }) .attr("y1", function(d) { return d.source.y; }) .attr("x2", function(d) { return d.target.x; }) .attr("y2", function(d) { return d.target.y; }); this.graph.svg.append("svg:g") .selectAll("circle") .data(this.nodes) .enter() .append("svg:circle") .attr("class", "node") .attr("cx", function(d) { return d.x; }) .attr("cy", function(d) { return d.y; }) .attr("r", function(d) { return 15; }); this.graph.svg.append("svg:g") .selectAll("text") .data(this.nodes) .enter() .append("svg:text") .attr("class", "label") .attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; }) .attr("text-anchor", "middle") .attr("y", ".3em") .text(function(d) { return d.value; }); } ShortestPathCalculator.prototype.formatResult = function() { // result => {mesg:"OK", path:[0, 1, 4], distance:250} var res = ""; res += "

Result : " + this.result.mesg + "

"; if(this.result.path === null) return "

No path found from " + this.result.source + " to " + this.result.target + "

"; if(this.result.path.length === 0) return "

Path is from " + SpUtils.nodeNames[this.result.source] + " to " + SpUtils.nodeNames[this.result.target] + ". Expect a journey time of approximately zero.

" res += "

Path : "; for(var i=0; i ' + targetNode.value; } res += "

"; res += "

Distance : " + this.result.distance + "

"; return res; } /* * * Calculate shortest path between two nodes in a graph * * @param {Integer} start index of node to start from * @param {Integer} end index of node to end at * */ ShortestPathCalculator.prototype.dijkstra = function(start, end) { var nodeCount = this.distances.length, infinity = 99999, // larger than largest distance in distances array shortestPath = new Array(nodeCount), nodeChecked = new Array(nodeCount), pred = new Array(nodeCount); // initialise data placeholders for(var i=0; i=0) { v = pred[v]; //console.log('v'); //console.log(v); if (v!==null && v>=0) { step.source = v; newPath.unshift(step); step = {target: v}; } } totalDistance = shortestPath[end]; return {mesg:'OK', path: newPath, source: start, target: end, distance:totalDistance}; } else { return {mesg:'No path found', path: null, source: start, target: end, distance: 0 }; } function distanceBetween(fromNode, toNode, distances) { dist = distances[fromNode][toNode]; if(dist==='x') dist = infinity; return dist; } }