using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using System.IO;
/* Fetch .osm
wget http://download.geofabrik.de/osm/europe/czech_republic.osm.bz2
bzcat czech*.osm.bz | osmosis \
--read-xml enableDateParsing=no file=/dev/stdin \
--way-key-value keyValueList="highway.motorway,highway.motorway_link,highway.trunk,highway.trunk_link,highway.primary,highway.primary_link,highway.secondary,highway.secondary_link,highway.tertiary,highway.unclassified,highway.road,highway.residential" \
--used-node \
--write-xml file=cz_silnice.osm
*/
//Openstreetmap datafile example
//
//
//
//
//
//
//
//
namespace OsmDijkstraRouter
{
class Program
{
private static Dictionary nodesById;
static void Main(string[] args)
{
System.Threading.Thread.CurrentThread.CurrentCulture = System.Globalization.CultureInfo.InvariantCulture;
Console.WriteLine("Semester work in csharp at Charles University Prague");
Console.WriteLine(" FINDING THE SHORTEST ROUTE USING DIJKSTRA ALGORITHM");
Console.WriteLine();
Console.WriteLine("code (c) Pavel Zbytovsky 2010, GPLv3");
Console.WriteLine("data (c) openstreetmap.org and contributors, CC-BY-SA ");
Console.WriteLine();
Console.WriteLine();
//args = new String[3];
//args[0] = "../highways-hostivice.osm";
//args[1] = "105018767";
//args[2] = "21312439";
//check args and set strStartTarget + xmlreader
#region args handling
if (args.Length != 1 && args.Length != 3)
CwExit("Usage: [-]");
//todo: start and target as coordinates (getting the closest) ... easy
string strStartTarget = "";
if (args.Length == 3)
strStartTarget = args[1]+" "+args[2];
XmlTextReader reader = null;
if(File.Exists(args[0]))
reader = new XmlTextReader(args[0]); //read xmldokumentu (http://support.microsoft.com/kb/307548)
else
CwExit("The osm datafile does not exist.");
#endregion
//nodes - creating new es and adding them to notVisited and nodesById; set start/target
#region loading all s
nodesById = new Dictionary();
bool skip = false;
while (reader.Read())
{
skip = false;
if (reader.NodeType != XmlNodeType.Element) continue;
if (reader.Name == "way") break;
if (reader.Name != "node") continue;
Vertex tmpVertex = new Vertex(); //the only factory for Vertexes
tmpVertex.distance = Double.MaxValue;
while (reader.MoveToNextAttribute())
{ // reading attributes
switch (reader.Name)
{
case "id":
tmpVertex.id = Convert.ToInt64(reader.Value);
break;
case "lat":
tmpVertex.lat = double.Parse(reader.Value);
break;
case "lon":
tmpVertex.lon = double.Parse(reader.Value);
break;
case "action":
if (reader.Value == "delete")
skip = true; //NOTE: csharp cant continue outer loop?
break;
}
}
if (skip) continue;
//adds to hastable
nodesById.Add(tmpVertex.id, tmpVertex);
//fancy output
if (nodesById.Count % 10000 == 0)
Console.Write("\rLoading s... {0} ", nodesById.Count);
}
Console.WriteLine("\rLoading s... {0} ", nodesById.Count);
#endregion
//way - just adding neighbors to Vertexes
#region loading all s
if (reader.Name != "way")
CwExit("No found in openstreetmap data file.");
LinkedList way = new LinkedList(); //temporary way holding all nodes
short oneway = 0;
int waysCount = 0;
do
{
switch (reader.NodeType)
{
case XmlNodeType.Element:
//if (reader.Name == "way") nothing;
if (reader.Name == "nd") // add node to temporary way if found
{
reader.MoveToNextAttribute();
long nodeId = long.Parse(reader.Value);
try { way.AddLast(nodesById[nodeId]); }
catch (KeyNotFoundException) { }
}
else if (reader.Name == "tag") // set oneway=-1/0/1
{
reader.MoveToNextAttribute();
if (reader.Value == "oneway")
{
reader.MoveToNextAttribute();
if (reader.Value == "yes" || reader.Value == "1")
oneway = 1;
else if (reader.Value == "-1")
oneway = -1;
}
}
break;
case XmlNodeType.EndElement: //handle .. add the neighbors
if (reader.Name == "way") //we dont like
{
Vertex u, w;
LinkedListNode item = way.First;
if(item != null)
while (item.Next != null) //iterating through temporary way
{
u = item.Value;
w = item.Next.Value;
if (oneway != -1)
u.neighbors.AddLast(w); //add the forward neighbor
if (oneway != 1)
w.neighbors.AddLast(u); //add the backward neighbor
item = item.Next;
}
way.Clear();
oneway = 0;
//fancy output
if (++waysCount % 1000 == 0)
Console.Write("\rLoading s... {0} ", waysCount);
}
break;
}
} while (reader.Read());
Console.WriteLine("\rLoading s... {0} ", waysCount);
Console.WriteLine();
#endregion
//invoking the RoutingAlgorithm
#region console usage & interactive mode
if (strStartTarget.Length > 1)
{
RoutingAlgorithm(strStartTarget); //console usage
}
else //interactive usage
{
while (true)
{
Console.WriteLine("Please specify start and target ids on one line: (for exit hit Enter)");
strStartTarget = Console.ReadLine().Trim();
if (strStartTarget.Length == 0) break;
RoutingAlgorithm(strStartTarget);
Console.WriteLine();
foreach (KeyValuePair item in nodesById)
{
Vertex v = item.Value;
v.distance = double.MaxValue;
v.previous = null;
}
}
}
#endregion
Console.ReadKey();
}
public static void RoutingAlgorithm(string strStartTarget)
{
//parse start & target + initialize notVisited
#region start/target handling
long startId=0, targetId=0;
Vertex start = null, target = null;
try
{
startId = Convert.ToInt64(strStartTarget.Split(' ')[0]);
targetId = Convert.ToInt64(strStartTarget.Split(' ')[1]);
}
catch (Exception)
{
CwExit("start/target ids are not numbers.");
}
//start and target
target = nodesById[targetId];
start = nodesById[startId];
start.distance = 0;
//do we have destinations?
if (start == null || target == null)
CwExit("Start or target not found in openstreetmap data file.");
GPWiki.BinaryHeap notVisited = new GPWiki.BinaryHeap();
notVisited.Add(start);
#endregion
// ---- dijkstra algorithm ----
//each Vertex in nodesById has previous=null and distance=MaxValue (start is in notVisited, distance=0)
#region dijkstra alogirthm
while (notVisited.Count > 0)
{
Vertex u = notVisited.Remove();
if (u.distance == double.MaxValue) // all remaining vertices are inaccessible from source
break;
if (u == target) // target reached with shortest way
break;
foreach (Vertex v in u.neighbors)
{
double distThroughU = u.distance + Distance(u, v);
if (v.distance == double.MaxValue) //never been reached
{
v.distance = distThroughU;
v.previous = u;
notVisited.Add(v);
}
else if (distThroughU < v.distance) //found shorter way, relaxing the edge
{
v.distance = distThroughU;
v.previous = u;
notVisited.CustomUpHeap(v);
}
}
//fancy output
if (notVisited.Count % 50 == 0)
Console.Write("\rNot visited count: {0} ", notVisited.Count);
}
Console.WriteLine("\rNot visited count: {0} ", notVisited.Count);
#endregion
//outputs KML, GPX and console info
#region final output
StreamWriter swKml = new StreamWriter("output"+strStartTarget+".kml", false);
StreamWriter swGpx = new StreamWriter("output"+strStartTarget+".gpx", false);
swKml.WriteLine("1");
swGpx.WriteLine("");
Vertex x = target;
int i = 0;
while (x.previous != null) //writing backwards, but doesnt matter
{
//Console.WriteLine("<{0}> {1} {2}", x.id, x.lat, x.lon);
swKml.Write("{1},{0},0 ", x.lat, x.lon);
swGpx.Write("", x.lat, x.lon);
x = x.previous;
i++;
}
Console.WriteLine("Done - {0} nodes, {1} km", i, target.distance == double.MaxValue ? 0 : target.distance);
swKml.WriteLine("{1},{0},0", x.lat, x.lon);
swKml.WriteLine("Length: {0} km", target.distance);
swKml.Close();
swGpx.Write("", x.lat, x.lon);
swGpx.WriteLine("");
swGpx.Close();
#endregion
}
#region other methods
//http://www.movable-type.co.uk/scripts/latlong.html
public static double Distance(Vertex c1, Vertex c2)
{
double R = 6371; // km
double dLat = ToRad(c2.lat - c1.lat);
double dLon = ToRad(c2.lon - c1.lon); //c2.lon - c2.lon -> acts funny - taking short detours
double a = Math.Sin(dLat / 2) * Math.Sin(dLat / 2) +
Math.Cos(ToRad(c1.lat)) * Math.Cos(ToRad(c2.lat)) *
Math.Sin(dLon / 2) * Math.Sin(dLon / 2);
double c = 2 * Math.Atan2(Math.Sqrt(a), Math.Sqrt(1 - a));
return R * c;
}
public static double ToRad(double deg)
{
return deg * (Math.PI / 180);
}
static public void CwExit(string s)
{
Console.WriteLine();
Console.WriteLine(s);
Console.ReadKey();
Environment.Exit(-1);
}
#endregion
}
class Vertex : IComparable, GPWiki.IBinaryHeapIndexed
{
public double lat;
public double lon;
public long id;
public LinkedList neighbors;
public double distance;// = double.MaxValue;
public Vertex previous;
public Vertex()
{
this.neighbors = new LinkedList();
}
#region IComparable Members
public int CompareTo(Vertex obj)
{
return distance.CompareTo(obj.distance);
}
#endregion
#region IBinaryHeapIndexed Members
private int binaryHeapIndex;
public int BinaryHeapIndex
{
get { return binaryHeapIndex; }
set { binaryHeapIndex = value; }
}
#endregion
}
}
namespace GPWiki
{
//source http://gpwiki.org/index.php/C_sharp:BinaryHeapOfT
#region IBinaryHeapIndexed interface
///
/// For instant access of CustomUpHeap of any item [by Pavel Zbytovský]
///
public interface IBinaryHeapIndexed
{
int BinaryHeapIndex { get; set; }
}
#endregion
///
/// A binary heap, useful for sorting data and priority queues.
///
/// type of item in the heap]]>.
public class BinaryHeap : ICollection
where T : IBinaryHeapIndexed, IComparable
{
// Constants
private const int DEFAULT_SIZE = 4;
// Fields
private T[] _data = new T[DEFAULT_SIZE];
private int _count = 0;
private int _capacity = DEFAULT_SIZE;
private bool _sorted;
// Properties
///
/// Gets the number of values in the heap.
///
public int Count
{
get { return _count; }
}
///
/// Gets or sets the capacity of the heap.
///
public int Capacity
{
get { return _capacity; }
set
{
int previousCapacity = _capacity;
_capacity = Math.Max(value, _count);
if (_capacity != previousCapacity)
{
T[] temp = new T[_capacity];
Array.Copy(_data, temp, _count);
_data = temp;
}
}
}
// Methods
///
/// Creates a new binary heap.
///
public BinaryHeap()
{
}
private BinaryHeap(T[] data, int count)
{
Capacity = count;
_count = count;
Array.Copy(data, _data, count);
}
///
/// Gets the first value in the heap without removing it.
///
/// The lowest value of type TValue.
public T Peek()
{
return _data[0];
}
///
/// Removes all items from the heap.
///
public void Clear()
{
this._count = 0;
_data = new T[_capacity];
}
private void UpdateItemIndex(int index)
//Updates BinaryHeapIndex in item T via IBinaryHeapIndexed interface; needed for CustomUpHeap()
{
_data[index].BinaryHeapIndex = index;
}
///
/// Adds a key and value to the heap.
///
/// The item to add to the heap.
public void Add(T item)
{
if (_count == _capacity)
{
Capacity *= 2;
}
_data[_count] = item;
UpdateItemIndex(_count);
UpHeap();
_count++;
}
///
/// Removes and returns the first item in the heap.
///
/// The next value in the heap.
public T Remove()
{
if (this._count == 0)
{
throw new InvalidOperationException("Cannot remove item, heap is empty.");
}
T v = _data[0];
_count--;
_data[0] = _data[_count];
_data[_count] = default(T); //Clears the Last Node
if(_count > 0)
DownHeap();
return v;
}
///
/// Performs up-heap bubbling from supplied item. [by Pavel Zbytovský]
///
public void CustomUpHeap(T item)
{
//int p = _count;
//T item = _data[p];
int p = item.BinaryHeapIndex; // n-times slower: Array.IndexOf(_data, item);
int par = Parent(p);
while (par > -1 && item.CompareTo(_data[par]) < 0) //item < parent
{
_data[p] = _data[par]; //Swap nodes
UpdateItemIndex(p);
p = par;
par = Parent(p);
}
_data[p] = item;
UpdateItemIndex(p);
_sorted = false;
}
private void UpHeap()
//helper function that performs up-heap bubbling
{
_sorted = false;
int p = _count;
T item = _data[p];
int par = Parent(p);
while (par > -1 && item.CompareTo(_data[par]) < 0)
{
_data[p] = _data[par]; //Swap nodes
UpdateItemIndex(p);
p = par;
par = Parent(p);
}
_data[p] = item;
UpdateItemIndex(p);
}
private void DownHeap()
//helper function that performs down-heap bubbling
{
_sorted = false;
int n;
int p = 0;
T item = _data[p];
while (true)
{
int ch1 = Child1(p);
if (ch1 >= _count) break;
int ch2 = Child2(p);
if (ch2 >= _count)
{
n = ch1;
}
else
{
n = _data[ch1].CompareTo(_data[ch2]) < 0 ? ch1 : ch2;
}
if (item.CompareTo(_data[n]) > 0)
{
_data[p] = _data[n]; //Swap nodes
UpdateItemIndex(p);
p = n;
}
else
{
break;
}
}
_data[p] = item;
UpdateItemIndex(p);
}
public void EnsureSort()
{
throw new NotImplementedException("in EnsureSort");
//if (_sorted) return;
//Array.Sort(_data, 0, _count);
//!!! neeeds sth like this: foreach p: UpdateItemIndex(p);
//_sorted = true;
}
private static int Parent(int index)
//helper function that calculates the parent of a node
{
return (index - 1) >> 1;
}
private static int Child1(int index)
//helper function that calculates the first child of a node
{
return (index << 1) + 1;
}
private static int Child2(int index)
//helper function that calculates the second child of a node
{
return (index << 1) + 2;
}
///
/// Creates a new instance of an identical binary heap.
///
/// A BinaryHeap.
public BinaryHeap Copy()
{
return new BinaryHeap(_data, _count);
}
///
/// Gets an enumerator for the binary heap.
///
/// An IEnumerator of type T.
public IEnumerator GetEnumerator()
{
EnsureSort();
for (int i = 0; i < _count; i++)
{
yield return _data[i];
}
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
///
/// Checks to see if the binary heap contains the specified item.
///
/// The item to search the binary heap for.
/// A boolean, true if binary heap contains item.
public bool Contains(T item)
{
EnsureSort();
return Array.BinarySearch(_data, 0, _count, item) >= 0;
}
///
/// Copies the binary heap to an array at the specified index.
///
/// One dimensional array that is the destination of the copied elements.
/// The zero-based index at which copying begins.
public void CopyTo(T[] array, int arrayIndex)
{
EnsureSort();
Array.Copy(_data, array, _count);
}
///
/// Gets whether or not the binary heap is readonly.
///
public bool IsReadOnly
{
get { return false; }
}
///
/// [SLOW] Removes an item from the binary heap. This utilizes the type T's Comparer and will not remove duplicates.
///
/// The item to be removed.
/// Boolean true if the item was removed.
public bool Remove(T item)
{
EnsureSort();
int i = Array.BinarySearch(_data, 0, _count, item);
if (i < 0) return false;
Array.Copy(_data, i + 1, _data, i, _count - i);
_data[_count] = default(T);
_count--;
return true;
}
}
}