diff --git a/OSMDatastructure/ByteConverter.cs b/OSMDatastructure/ByteConverter.cs index 2f81c9c..0babb61 100644 --- a/OSMDatastructure/ByteConverter.cs +++ b/OSMDatastructure/ByteConverter.cs @@ -1,198 +1,35 @@ +using System.Text; +using GeoGraph; + namespace OSMDatastructure; public static class ByteConverter { - #region Node - /* - * Node Byteform: - * +-------------------------------+ - * | Latitude | Longitude | ConnectionSize| connection | - * |---------------+---------------+---------------+---------------+ - * | 4 bytes | 4 bytes | 4 bytes | ConnectionSize| - * | float | float | int | Connection | - * +---------------+---------------+ - */ - - public static byte[] GetBytes(Node node) - { - byte[] latitudeBytes = BitConverter.GetBytes(node.lat); - byte[] longitudeBytes = BitConverter.GetBytes(node.lon); - - Connection[] connections = node.GetConnections(); - Dictionary connectionsBytes = new (); - int byteArraySize = latitudeBytes.Length + longitudeBytes.Length; - - foreach (Connection connection in connections) - { - byte[] connectionBytes = ByteConverter.GetBytes(connection); - byte[] connectionSizeBytes = BitConverter.GetBytes(connectionBytes.Length); - connectionsBytes.Add(connectionSizeBytes, connectionBytes); - byteArraySize += connectionSizeBytes.Length + connectionBytes.Length; - } - - byte[] retByteArray = new byte[byteArraySize]; - int offset = 0; - - Array.Copy(latitudeBytes, 0, retByteArray, offset, latitudeBytes.Length); - offset += latitudeBytes.Length; - - Array.Copy(longitudeBytes, 0, retByteArray, offset, longitudeBytes.Length); - offset += longitudeBytes.Length; - - foreach (KeyValuePair connectionBytes in connectionsBytes) - { - Array.Copy(connectionBytes.Key, 0, retByteArray, offset, connectionBytes.Key.Length); - offset += connectionBytes.Key.Length; - - Array.Copy(connectionBytes.Value, 0, retByteArray, offset, connectionBytes.Value.Length); - offset += connectionBytes.Value.Length; - } - - return retByteArray; - } - - public static Node ToNode(byte[] bytes) - { - int offset = 0; - float lat = BitConverter.ToSingle(bytes, offset); - offset += sizeof(float); - - float lon = BitConverter.ToSingle(bytes, offset); - offset += sizeof(float); - - Node retNode = new Node(lat, lon); - - while (offset < bytes.Length) - { - int size = BitConverter.ToInt32(bytes, offset); - offset += sizeof(int); - - byte[] connectionBytes = new byte[size]; - Array.Copy(bytes, offset, connectionBytes, 0, size); - retNode.AddConnection(ByteConverter.ToConnection(connectionBytes)); - offset += connectionBytes.Length; - } - - return retNode; - } - #endregion - - #region Connection - /* - * Connection Byteform: - * +---------------+---------------+ - * | endId | endLatitude | endLongitude | tagBytes | tag | value | - * |---------------+---------------+---------------+---------------+---------------+---------------+ - * | 8 bytes | 4 bytes | 4 bytes | 4 bytes | 1 byte | 1 byte | - * | ulong | float | float | int | | | - * +---------------+---------------+ - */ - - public static byte[] GetBytes(Connection connection) - { - byte[] endIdBytes = BitConverter.GetBytes(connection.endNodeId); - byte[] endLatBytes = BitConverter.GetBytes(connection.endNodeCoordinates.lat); - byte[] endLonBytes = BitConverter.GetBytes(connection.endNodeCoordinates.lon); - - Dictionary connectionTags = connection.GetTags(); - byte[] tagBytes = BitConverter.GetBytes(connectionTags.Count * 2); - - int byteArraySize = endIdBytes.Length + endLatBytes.Length + endLonBytes.Length + tagBytes.Length + connectionTags.Count * 2; - byte[] retBytes = new byte[byteArraySize]; - int offset = 0; - - Array.Copy(endIdBytes, 0, retBytes, offset, endIdBytes.Length); - offset += endIdBytes.Length; - - Array.Copy(endLatBytes, 0, retBytes, offset, endLatBytes.Length); - offset += endLatBytes.Length; - - Array.Copy(endLonBytes, 0 , retBytes, offset, endLonBytes.Length); - offset += endLonBytes.Length; - - Array.Copy(tagBytes, 0, retBytes, offset, tagBytes.Length); - offset += tagBytes.Length; - - foreach (KeyValuePair tagKv in connectionTags) - { - retBytes[offset] = (byte)tagKv.Key; - offset++; - retBytes[offset] = (byte)tagKv.Value; - offset++; - } - - return retBytes; - } - - public static Connection ToConnection(byte[] bytes) - { - int offset = 0; - ulong endId = BitConverter.ToUInt64(bytes, offset); - offset += sizeof(ulong); - - float endLat = BitConverter.ToSingle(bytes, offset); - offset += sizeof(float); - - float endLon = BitConverter.ToSingle(bytes, offset); - offset += sizeof(float); - - Connection retConnection = new Connection(endId, new Coordinates(endLat, endLon)); - - int tagBytes = BitConverter.ToInt32(bytes, offset); - offset += sizeof(int); - while (offset < bytes.Length) - { - byte key = bytes[offset]; - byte value = bytes[offset + 1]; - retConnection.AddTag(key, value); - offset += 2; - } - - return retConnection; - } - #endregion - #region Region /* - * Region Byteform: - * +---------------+---------------+---------------+ - * | regionHash | nodeBytes | nodeId | node | - * |---------------+---------------+---------------+---------------+ - * | 8 bytes | 4 bytes | 8 bytes | nodeBytes | - * | ulong | int | ulong | Node | - * +---------------+---------------+---------------+ + * | regionHash | Nodes | + * |---------------+---------------+ + * | 8 bytes | + * | ulong | */ - public static byte[] GetBytes(Region region) { - byte[] regionHashBytes = BitConverter.GetBytes(region.regionHash); - int byteArraySize = regionHashBytes.Length; - - Dictionary nodesBytes = new(); - foreach(KeyValuePair nodeKv in region.GetNodes()) + int totalNodeBytes = 0; + HashSet nodes = new(); + foreach (OsmNode node in region.nodes) { - byte[] nodeIdBytes = BitConverter.GetBytes(nodeKv.Key); - byte[] nodeBytes = GetBytes(nodeKv.Value); - nodesBytes.Add(nodeIdBytes, nodeBytes); - byteArraySize += sizeof(int) + nodeIdBytes.Length + nodeBytes.Length; + byte[] nodeBytes = GetBytes(node); + totalNodeBytes += nodeBytes.Length; + nodes.Add(nodeBytes); } - - byte[] retBytes = new byte[byteArraySize]; - int offset = 0; - - Array.Copy(regionHashBytes, 0, retBytes, offset, regionHashBytes.Length); - offset += regionHashBytes.Length; - foreach (KeyValuePair nodeByteKv in nodesBytes) + byte[] retBytes = new byte[sizeof(ulong) + totalNodeBytes]; + Array.Copy(BitConverter.GetBytes(region.regionHash), 0, retBytes, 0, sizeof(ulong)); + int offset = sizeof(ulong); + foreach (byte[] node in nodes) { - Array.Copy(BitConverter.GetBytes(nodeByteKv.Value.Length), 0, retBytes, offset, sizeof(int)); - offset += sizeof(int); - - Array.Copy(nodeByteKv.Key, 0, retBytes, offset, nodeByteKv.Key.Length); - offset += nodeByteKv.Key.Length; - - Array.Copy(nodeByteKv.Value, 0, retBytes, offset, nodeByteKv.Value.Length); - offset += nodeByteKv.Value.Length; + Array.Copy(node, 0, retBytes, offset, node.Length); + offset += node.Length; } return retBytes; @@ -200,27 +37,228 @@ public static class ByteConverter public static Region ToRegion(byte[] bytes) { - int offset = 0; - ulong regionHash = BitConverter.ToUInt64(bytes, offset); - offset += sizeof(ulong); - - Region retRegion = new Region(regionHash); - + Region retRegion = new Region(BitConverter.ToUInt64(bytes, 0)); + int offset = sizeof(ulong); while (offset < bytes.Length) { - int nodeBytesAmount = BitConverter.ToInt32(bytes, offset); - offset += sizeof(int); - - ulong nodeId = BitConverter.ToUInt64(bytes, offset); - offset += sizeof(ulong); - - byte[] nodeBytes = new byte[nodeBytesAmount]; - Array.Copy(bytes, offset, nodeBytes, 0, nodeBytesAmount); - retRegion.AddNode(nodeId, ByteConverter.ToNode(nodeBytes)); - offset += nodeBytes.Length; + int size = BitConverter.ToInt32(bytes, offset); + byte[] nodeBytes = new byte[size]; + Array.Copy(bytes, offset, nodeBytes, 0, size); + offset += size; + retRegion.nodes.Add(ToNode(nodeBytes)); } return retRegion; } + #endregion -} \ No newline at end of file + + #region Coordinates + + /* + * | Latitude | Longitude | + * |---------------+---------------+ + * | 4 bytes | 4 bytes | + * | float | float | + */ + + public static byte[] GetBytes(Coordinates coordinates) + { + byte[] retBytes = new byte[8]; + Array.Copy(BitConverter.GetBytes(coordinates.latitude), 0, retBytes, 0, sizeof(float)); + Array.Copy(BitConverter.GetBytes(coordinates.longitude), 0, retBytes, sizeof(float), sizeof(float)); + return retBytes; + } + + public static Coordinates ToCoordinates(byte[] bytes) + { + if (bytes.Length != 8) + throw new ArgumentException("Needs exactly 8 bytes", nameof(bytes)); + float latitude = BitConverter.ToSingle(bytes, 0); + float longitude = BitConverter.ToSingle(bytes, sizeof(float)); + return new Coordinates(latitude, longitude); + } + + #endregion + + #region OsmNode + + /* + * | Size | Coordinates | Edges + * |---------------+---------------+---------------+ + * | 4 bytes | 8 bytes | + * | int | byte[] | + */ + + public static byte[] GetBytes(OsmNode node) + { + int totalBytes = 4 + 8; //size + Coordinates + HashSet edges = new(); + foreach (OsmEdge edge in node.edges) + { + edges.Add(GetBytes(edge)); + } + + byte[] retBytes = new byte[totalBytes]; + Array.Copy(BitConverter.GetBytes(totalBytes), retBytes, 4); + Array.Copy(GetBytes(node.coordinates), 0, retBytes, 4, 8); + int offset = 4 + 8; + foreach (byte[] edgeBytes in edges) + { + Array.Copy(edgeBytes, 0, retBytes, offset, edgeBytes.Length); + offset += edgeBytes.Length; + } + + return retBytes; + } + + public static OsmNode ToNode(byte[] bytes) + { + byte[] coordinateBytes = new byte[8]; + Array.Copy(bytes, 4, coordinateBytes, 0, 8); + Coordinates coordinates = ToCoordinates(coordinateBytes); + + OsmNode retNode = new OsmNode(coordinates); + + int offset = sizeof(int) + 8; + while (offset < bytes.Length) + { + int tagsSize = BitConverter.ToInt32(bytes, offset); + byte[] edgeBytes = new byte[sizeof(int) + 8 + tagsSize]; + Array.Copy(bytes, offset, edgeBytes, 0, edgeBytes.Length); + offset += edgeBytes.Length; + retNode.edges.Add(ToEdge(edgeBytes)); + } + + return retNode; + } + + #endregion + + #region OsmEdge + + /* Edge: + * | tagsSize | neighborCoord | tags + * |---------------+---------------+-------------- + * | 4 bytes | 8 bytes | + * | int | byte[] | + * + */ + + public static byte[] GetBytes(OsmEdge edge) + { + int totalBytes = sizeof(int) + 8; //size + Coordinates + int offset = totalBytes; + + int tagsSize = 0; + HashSet tags = new(); + foreach (KeyValuePair kv in edge.tags) + { + byte[] tagBytes = GetBytes(kv); + tags.Add(tagBytes); + tagsSize += tagBytes.Length; + } + + byte[] retBytes = new byte[totalBytes]; + Array.Copy(BitConverter.GetBytes(tagsSize), retBytes, 4); + Array.Copy(GetBytes(edge.neighborCoordinates), 0, retBytes, sizeof(int), 8); + + foreach (byte[] tag in tags) + { + Array.Copy(tag, 0, retBytes, offset, tag.Length); + offset += tag.Length; + } + + return retBytes; + } + + public static OsmEdge ToEdge(byte[] bytes) + { + byte[] coordinateBytes = new byte[8]; + Array.Copy(bytes, 4, coordinateBytes, 0, 8); + Coordinates coordinates = ToCoordinates(coordinateBytes); + + OsmEdge retEdge = new OsmEdge(coordinates); + + int offset = sizeof(int) + 8; + while (offset < bytes.Length) + { + int tagContentSize = BitConverter.ToInt32(bytes, offset); + byte[] tagBytes = new byte[sizeof(int) + 1 + tagContentSize]; + Array.Copy(bytes, offset, tagBytes, 0, tagBytes.Length); + offset += tagBytes.Length; + KeyValuePair tag = ToTag(tagBytes); + retEdge.tags.Add(tag.Key, tag.Value); + } + + return retEdge; + } + + #endregion + + #region OsmEdge.Tag + /* + * Tag: + * | contentSize | tagType | tagContent + * |---------------+---------------+--------------- + * | 4 bytes | 1 byte | + * | int | byte | + */ + + public static byte[] GetBytes(KeyValuePair tag) + { + byte[] objectBytes = Array.Empty(); + if (tag.Value.GetType() == Type.GetType("string")) + { + objectBytes = Encoding.ASCII.GetBytes((string)tag.Value); + } + else if (tag.Value.GetType() == Type.GetType("bool")) + { + objectBytes = BitConverter.GetBytes((bool)tag.Value); + } + else if (tag.Value.GetType() == Type.GetType("int")) + { + objectBytes = BitConverter.GetBytes((int)tag.Value); + } + else if (tag.Value.GetType() == Type.GetType("ulong")) + { + objectBytes = BitConverter.GetBytes((ulong)tag.Value); + } + else if (tag.Value.GetType() == Type.GetType("byte")) + { + objectBytes = new[] { (byte)tag.Value }; + } + + byte[] retBytes = new byte[sizeof(int) + 1 + objectBytes.Length]; + Array.Copy(BitConverter.GetBytes(objectBytes.Length), 0, retBytes, 0, sizeof(int)); + retBytes[sizeof(int)] = (byte)tag.Key; + Array.Copy(objectBytes, 0, retBytes, sizeof(int) + 1, retBytes.Length); + return retBytes; + } + + public static KeyValuePair ToTag(byte[] bytes) + { + OsmEdge.tagType tagType = (OsmEdge.tagType)bytes[sizeof(int)]; + int contentSize = BitConverter.ToInt32(bytes, 0); + byte[] content = new byte[contentSize]; + Array.Copy(bytes, sizeof(int) + 1, content, 0, contentSize); + + switch (tagType) + { + case OsmEdge.tagType.highway: + return new KeyValuePair(tagType, (Way.wayType)content[0]); + case OsmEdge.tagType.maxspeed: + return new KeyValuePair(tagType, content[0]); + case OsmEdge.tagType.oneway: + return new KeyValuePair(tagType, BitConverter.ToBoolean(content)); + case OsmEdge.tagType.forward: + return new KeyValuePair(tagType, BitConverter.ToBoolean(content)); + case OsmEdge.tagType.id: + return new KeyValuePair(tagType, BitConverter.ToUInt64(content)); + default: + return new KeyValuePair(tagType, content); + } + } + #endregion +} + diff --git a/OSMDatastructure/Connection.cs b/OSMDatastructure/Connection.cs deleted file mode 100644 index d497e05..0000000 --- a/OSMDatastructure/Connection.cs +++ /dev/null @@ -1,166 +0,0 @@ -namespace OSMDatastructure -{ - public class Connection - { - public ulong endNodeId { get; } - - public Coordinates endNodeCoordinates { get; } - - private readonly Dictionary _tags = new(); - - public enum tagType : byte - { - highway, oneway, footway, sidewalk, cycleway, busway, forward, maxspeed, name, surface, lanes, access, tracktype - } - - public Connection(ulong endNodeId, Coordinates endNodeCoordinates) - { - this.endNodeId = endNodeId; - this.endNodeCoordinates = endNodeCoordinates; - } - - public Connection(ulong endNodeId, Coordinates endNodeCoordinates, Dictionary tags) - { - this.endNodeId = endNodeId; - this.endNodeCoordinates = endNodeCoordinates; - ImportTags(tags); - } - - public void AddTag(string key, string value) - { - switch (key) - { - case "highway": - if(Enum.TryParse(value, out highwayType hwType)) - this._tags.TryAdd(key: tagType.highway, hwType); - break; - case "footway": - if(Enum.TryParse(value, out footwayType fwType)) - this._tags.TryAdd(tagType.footway, fwType); - break; - case "oneway": - if (value.Equals("yes", StringComparison.CurrentCultureIgnoreCase)) - { - this._tags.TryAdd(tagType.oneway, (byte)1); - } - else if (value.Equals("no", StringComparison.CurrentCultureIgnoreCase)) - { - this._tags.TryAdd(tagType.oneway, (byte)0); - } - else if (value.Equals("reversible", StringComparison.CurrentCultureIgnoreCase)) - { - this._tags.TryAdd(tagType.oneway, (byte)1); - this._tags.TryAdd(tagType.forward, (byte)0); - } - break; - case "sidewalk": - if(Enum.TryParse(value, out sidewalkSide swType)) - this._tags.TryAdd(tagType.footway, swType); - break; - case "cycleway": - if(Enum.TryParse(value, out cyclewayType cwType)) - this._tags.TryAdd(tagType.footway, cwType); - break; - case "busway": - //TODO - break; - case "maxspeed": - try - { - this._tags.TryAdd(tagType.maxspeed, Convert.ToByte(value)); - } - catch (FormatException e) - { - Console.WriteLine("maxspeed '{0}' is not a number", value); - } - break; - case "name": - //TODO - break; - case "surface": - //TODO - break; - case "lanes": - //TODO - break; - case "access": - //TODO - break; - case "tracktype": - //TODO - break; - default: - Console.WriteLine("Unknown Tag: {0} Value: {1}", key, value); - break; - } - } - - public void AddTag(byte tag, byte value) - { - switch ((tagType)tag) - { - case tagType.highway: - this._tags.TryAdd(tagType.highway, (highwayType)value); - break; - case tagType.footway: - this._tags.TryAdd(tagType.footway, (footwayType)value); - break; - case tagType.oneway: - this._tags.TryAdd(tagType.oneway, value); - break; - case tagType.sidewalk: - this._tags.TryAdd(tagType.sidewalk, (sidewalkSide)value); - break; - case tagType.cycleway: - this._tags.TryAdd(tagType.cycleway, (cyclewayType)value); - break; - case tagType.busway: - //TODO - break; - case tagType.maxspeed: - this._tags.TryAdd(tagType.maxspeed, value); - break; - case tagType.name: - //TODO - break; - case tagType.surface: - //TODO - break; - case tagType.lanes: - //TODO - break; - case tagType.access: - //TODO - break; - case tagType.tracktype: - //TODO - break; - } - } - - private void ImportTags(Dictionary tags) - { - foreach (KeyValuePair tag in tags) - { - this.AddTag(tag.Key, tag.Value); - } - } - - public object? GetTag(string key) - { - if (Enum.TryParse(key, out tagType tag)) - { - return this._tags.ContainsKey(tag) ? this._tags[tag] : null; - } - else - { - return null; - } - } - - public Dictionary GetTags() - { - return this._tags; - } - } -} \ No newline at end of file diff --git a/OSMDatastructure/Coordinates.cs b/OSMDatastructure/Coordinates.cs index 9bb6ff5..cbcfcbc 100644 --- a/OSMDatastructure/Coordinates.cs +++ b/OSMDatastructure/Coordinates.cs @@ -1,62 +1,42 @@ -using System.Globalization; +namespace OSMDatastructure; -namespace OSMDatastructure +public class Coordinates { - public class Coordinates + public float latitude { get; } + public float longitude { get; } + + public Coordinates(float latitude, float longitude) { - public float lat { get; } - public float lon { get; } - - public Coordinates(float lat, float lon) - { - this.lat = lat; - this.lon = lon; - } - - private const float decimalCoordsSave = 10000; - private const ulong offset = 10000000; - //Latitude maxChars = 7 - //Longitude maxChars = 8 - public ulong GetHash() - { - ulong latHash = Convert.ToUInt64((this.lat + 90) * decimalCoordsSave); - ulong lonHash = Convert.ToUInt64((this.lon + 180) * decimalCoordsSave); - return latHash * offset + lonHash; - } - - public ulong GetRegionHash() - { - float latRegion = this.lat - this.lat % Region.regionSize; - float lonRegion = this.lon - this.lon % Region.regionSize; - ulong latHash = Convert.ToUInt64((latRegion + 90) * decimalCoordsSave); - ulong lonHash = Convert.ToUInt64((lonRegion + 180) * decimalCoordsSave); - return latHash * offset + lonHash; - } - - public static Coordinates GetFromHash(ulong hash) - { - ulong latHash = (hash / offset) - 90; - ulong lonHash = (hash % offset) - 180; - float lat = Convert.ToSingle(latHash) / decimalCoordsSave; - float lon = Convert.ToSingle(lonHash) / decimalCoordsSave; - return new Coordinates(lat, lon); - } - - public override string ToString() - { - return string.Format("{0} {1}", this.lat.ToString(CultureInfo.InvariantCulture), this.lon.ToString(CultureInfo.InvariantCulture)); - } - - public override bool Equals(object? obj) - { - if (obj != null && obj.GetType() == this.GetType()) - { - Coordinates objconv = (Coordinates)obj; - if (objconv.lat.Equals(this.lat) && objconv.lon.Equals(this.lon)) - return true; - } + this.latitude = latitude; + this.longitude = longitude; + } + public override bool Equals(object? obj) + { + if (obj == null || obj.GetType() != this.GetType()) return false; - } + Coordinates convObj = (Coordinates)obj; + // ReSharper disable twice CompareOfFloatsByEqualityOperator static values + return convObj.latitude == this.latitude && convObj.longitude == this.longitude; + } + + private const float decimalCoordsSave = 10000; + private const ulong offset = 10000000; + //Latitude maxChars = 7 + //Longitude maxChars = 8 + public ulong GetHash() + { + ulong latHash = Convert.ToUInt64((this.latitude + 90) * decimalCoordsSave); + ulong lonHash = Convert.ToUInt64((this.longitude + 180) * decimalCoordsSave); + return latHash * offset + lonHash; + } + + public ulong GetRegionHash() + { + float latRegion = this.latitude - this.latitude % Region.regionSize; + float lonRegion = this.longitude - this.longitude % Region.regionSize; + ulong latHash = Convert.ToUInt64((latRegion + 90) * decimalCoordsSave); + ulong lonHash = Convert.ToUInt64((lonRegion + 180) * decimalCoordsSave); + return latHash * offset + lonHash; } } \ No newline at end of file diff --git a/OSMDatastructure/Node.cs b/OSMDatastructure/Node.cs deleted file mode 100644 index b3c1c55..0000000 --- a/OSMDatastructure/Node.cs +++ /dev/null @@ -1,22 +0,0 @@ -namespace OSMDatastructure -{ - public class Node : Coordinates - { - private readonly HashSet _connections = new HashSet(); - - public Node(float lat, float lon) : base(lat, lon) - { - - } - - public void AddConnection(Connection connection) - { - this._connections.Add(connection); - } - - public Connection[] GetConnections() - { - return this._connections.ToArray(); - } - } -} \ No newline at end of file diff --git a/OSMDatastructure/OSMDatastructure.csproj b/OSMDatastructure/OSMDatastructure.csproj index 6836c68..f35be79 100644 --- a/OSMDatastructure/OSMDatastructure.csproj +++ b/OSMDatastructure/OSMDatastructure.csproj @@ -6,4 +6,8 @@ enable + + + + diff --git a/OSMDatastructure/OSMEdge.cs b/OSMDatastructure/OSMEdge.cs new file mode 100644 index 0000000..8d048b5 --- /dev/null +++ b/OSMDatastructure/OSMEdge.cs @@ -0,0 +1,172 @@ +namespace OSMDatastructure; + +public class OsmEdge +{ + public Coordinates neighborCoordinates { get; } + public Dictionary tags { get; } + + public OsmEdge(float lat, float lon) + { + this.neighborCoordinates = new Coordinates(lat, lon); + this.tags = new(); + //TODO tags + } + + public OsmEdge(Coordinates neighborCoordinates) + { + this.neighborCoordinates = neighborCoordinates; + this.tags = new(); + //TODO tags + } + + public enum tagType : byte + { + highway, oneway, footway, sidewalk, cycleway, busway, forward, maxspeed, name, surface, lanes, access, tracktype, id + } + + public static Dictionary speedcar = new() { + { wayType.NONE, 0 }, + { wayType.motorway, 110 }, + { wayType.trunk, 100 }, + { wayType.primary, 80 }, + { wayType.secondary, 80 }, + { wayType.tertiary, 70 }, + { wayType.unclassified, 20 }, + { wayType.residential, 10 }, + { wayType.motorway_link, 50 }, + {wayType.trunk_link, 50 }, + {wayType.primary_link, 30 }, + { wayType.secondary_link, 25 }, + { wayType.tertiary_link, 25 }, + { wayType.living_street, 10 }, + { wayType.service, 0 }, + { wayType.pedestrian, 0 }, + { wayType.track, 0 }, + { wayType.bus_guideway, 0 }, + { wayType.escape, 0 }, + { wayType.raceway, 0 }, + { wayType.road, 25 }, + { wayType.busway, 0 }, + { wayType.footway, 0 }, + { wayType.bridleway, 0 }, + { wayType.steps, 0 }, + { wayType.corridor, 0 }, + { wayType.path, 0 }, + { wayType.cycleway, 0 }, + { wayType.construction, 0 } + }; + + public static Dictionary speedped = new() { + { wayType.NONE, 0 }, + { wayType.motorway, 0 }, + { wayType.trunk, 0 }, + { wayType.primary, 0 }, + { wayType.secondary, 0 }, + { wayType.tertiary, 0 }, + { wayType.unclassified, 1 }, + { wayType.residential, 3 }, + { wayType.motorway_link, 0 }, + { wayType.trunk_link, 0 }, + { wayType.primary_link, 0 }, + { wayType.secondary_link, 0 }, + { wayType.tertiary_link, 0 }, + { wayType.living_street, 5 }, + { wayType.service, 2 }, + { wayType.pedestrian, 5 }, + { wayType.track, 0 }, + { wayType.bus_guideway, 0 }, + { wayType.escape, 0 }, + { wayType.raceway, 0 }, + { wayType.road, 3 }, + { wayType.busway, 0 }, + { wayType.footway, 4 }, + { wayType.bridleway, 1 }, + { wayType.steps, 2 }, + { wayType.corridor, 3 }, + { wayType.path, 4 }, + { wayType.cycleway, 2 }, + { wayType.construction, 0 } + }; + public enum wayType { NONE, motorway, trunk, primary, secondary, tertiary, unclassified, residential, motorway_link, trunk_link, primary_link, secondary_link, tertiary_link, living_street, service, pedestrian, track, bus_guideway, escape, raceway, road, busway, footway, bridleway, steps, corridor, path, cycleway, construction } + + public enum speedType { pedestrian, car, road } + + public void AddTag(string key, string value) + { + switch (key) + { + case "highway": + try + { + this.tags.Add(tagType.highway, (wayType)Enum.Parse(typeof(wayType), value, true)); + } + catch (ArgumentException) + { + //TODO + } + break; + case "maxspeed": + try + { + this.tags.Add(tagType.maxspeed, Convert.ToByte(value)); + } + catch (Exception) + { + //TODO + } + break; + case "oneway": + switch (value) + { + case "yes": + this.tags.Add(tagType.oneway, true); + break; + case "-1": + this.tags.Add(tagType.forward, false); + break; + case "no": + this.tags.Add(tagType.oneway, false); + break; + } + break; + case "id": + this.tags.Add(tagType.id, Convert.ToUInt64(value)); + break; + } + } + + public ulong GetId() + { + return this.tags.ContainsKey(tagType.id) ? (ulong)this.tags[tagType.id] : 0; + } + + public wayType GetHighwayType() + { + return this.tags.ContainsKey(tagType.highway) ? (wayType)this.tags[tagType.highway] : wayType.NONE; + } + + public bool IsOneWay() + { + return this.tags.ContainsKey(tagType.oneway) && (bool)this.tags[tagType.oneway]; + } + + public byte? GetMaxSpeed(speedType type) + { + if(type == speedType.road) + { + return this.tags.ContainsKey(tagType.maxspeed) ? (byte)this.tags[tagType.maxspeed] : null; + } + if(type == speedType.car) + { + return this.tags.ContainsKey(tagType.maxspeed) + ? (byte)this.tags[tagType.maxspeed] + : speedcar[this.GetHighwayType()]; + } + return speedped[this.GetHighwayType()]; + } + + public bool IsForward() + { + return this.tags.ContainsKey(tagType.forward) && (bool)this.tags[tagType.forward]; + } +} \ No newline at end of file diff --git a/OSMDatastructure/OsmNode.cs b/OSMDatastructure/OsmNode.cs new file mode 100644 index 0000000..859b168 --- /dev/null +++ b/OSMDatastructure/OsmNode.cs @@ -0,0 +1,33 @@ +using GeoGraph; + +namespace OSMDatastructure; + +public class OsmNode : Node +{ + public new HashSet edges { get; } + public Coordinates coordinates { get; } + + public OsmNode previousPathNode = null; + public double currentPathWeight = double.MaxValue; + public double DirectDistanceToGoal = double.MaxValue; + + public OsmNode(float lat, float lon) : base(lat, lon) + { + this.edges = new(); + this.coordinates = new Coordinates(lat, lon); + } + + public OsmNode(Coordinates coordinates) : base(coordinates.latitude, coordinates.longitude) + { + this.edges = new(); + this.coordinates = new Coordinates(lat, lon); + } + + public OsmEdge? GetEdgeToNode(OsmNode n) + { + foreach (OsmEdge e in this.edges) + if (e.neighborCoordinates.Equals(n.coordinates)) + return e; + return null; + } +} \ No newline at end of file diff --git a/OSMDatastructure/Region.cs b/OSMDatastructure/Region.cs index 5cab5d6..2e4b57a 100644 --- a/OSMDatastructure/Region.cs +++ b/OSMDatastructure/Region.cs @@ -1,51 +1,26 @@ -namespace OSMDatastructure +namespace OSMDatastructure; + +public class Region { - public class Region + public const float regionSize = 0.01f; + public readonly HashSet nodes = new(); + public ulong regionHash { get; } + + public Region(ulong regionHash) { - public const float regionSize = 0.01f; //For Splitting - private readonly Dictionary _nodesInRegion = new(); - public ulong regionHash { get; } - - public Region(Coordinates regionCoordinates) - { - this.regionHash = regionCoordinates.GetRegionHash(); - } + this.regionHash = regionHash; + } - public Region(ulong nodeId, Node firstNode) - { - this.regionHash = firstNode.GetRegionHash(); - this._nodesInRegion.Add(nodeId, value: firstNode); - } + public Region(Coordinates regionCoordinates) + { + this.regionHash = regionCoordinates.GetRegionHash(); + } - public Region(ulong regionHash) - { - this.regionHash = regionHash; - } - - public void AddNode(ulong nodeId, Node node) - { - this._nodesInRegion.Add(nodeId, value: node); - } - - public Dictionary GetNodes() - { - return this._nodesInRegion; - } - - public Node? GetNode(ulong id) - { - return this._nodesInRegion.ContainsKey(id) ? this._nodesInRegion[id] : null; - } - - public Node? GetNode(Coordinates coordinates) - { - foreach (Node node in this._nodesInRegion.Values) - { - if (node.Equals(coordinates)) - return node; - } - - return null; - } + public OsmNode? GetNode(Coordinates coordinates) + { + foreach(OsmNode node in this.nodes) + if (node.coordinates.Equals(coordinates)) + return node; + return null; } } \ No newline at end of file diff --git a/OSMDatastructure/cyclewayType.cs b/OSMDatastructure/cyclewayType.cs deleted file mode 100644 index afff7b9..0000000 --- a/OSMDatastructure/cyclewayType.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace OSMDatastructure; - -public enum cyclewayType : byte -{ - lane, - opposite, - opposite_lane, - track, - opposite_track, - share_busway, - opposite_share_busway, - shared_lane -} \ No newline at end of file diff --git a/OSMDatastructure/footwayType.cs b/OSMDatastructure/footwayType.cs deleted file mode 100644 index b4fef67..0000000 --- a/OSMDatastructure/footwayType.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace OSMDatastructure; - -public enum footwayType : byte -{ - sidewalk, - crossing -} \ No newline at end of file diff --git a/OSMDatastructure/highwayType.cs b/OSMDatastructure/highwayType.cs deleted file mode 100644 index 89f22f1..0000000 --- a/OSMDatastructure/highwayType.cs +++ /dev/null @@ -1,36 +0,0 @@ -namespace OSMDatastructure -{ - public enum highwayType : byte - { - motorway, - trunk, - primary, - secondary, - tertiary, - unclassified, - residential, - motorway_link, - trunk_link, - primary_link, - secondary_link, - tertiary_link, - living_street, - service, - pedestrian, - track, - bus_guideway, - escape, - raceway, - road, - busway, - footway, - bridleway, - steps, - corridor, - path, - via_ferratsa, - cycleway, - proposed, - construction - } -} \ No newline at end of file diff --git a/OSMDatastructure/sidewalkSide.cs b/OSMDatastructure/sidewalkSide.cs deleted file mode 100644 index 1efd141..0000000 --- a/OSMDatastructure/sidewalkSide.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace OSMDatastructure; - -public enum sidewalkSide : byte -{ - both, - left, - right, - no -} \ No newline at end of file diff --git a/OSMServer.sln b/OSMServer.sln index dd57ab1..6b9dd4f 100644 --- a/OSMServer.sln +++ b/OSMServer.sln @@ -1,23 +1,19 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OSMSplitter", "OSMSplitter\OSMSplitter.csproj", "{BE5C111D-AD61-433A-AE7E-F02B78551347}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Server", "Server\Server.csproj", "{7E025EFC-B889-4B57-A03F-EF294CFFBCD6}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OSMDatastructure", "OSMDatastructure\OSMDatastructure.csproj", "{A2489EF1-64E9-41C9-A295-B3D551D810DA}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Pathfinding", "Pathfinding\Pathfinding.csproj", "{D4AA1C47-98D4-415C-B026-3BC25B6B6F9D}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Geo-Graph", "..\Geo-Graph\Geo-Graph\Geo-Graph.csproj", "{2F3E2E4A-6C1E-46AF-AC2C-62E5D4E317A0}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {BE5C111D-AD61-433A-AE7E-F02B78551347}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {BE5C111D-AD61-433A-AE7E-F02B78551347}.Debug|Any CPU.Build.0 = Debug|Any CPU - {BE5C111D-AD61-433A-AE7E-F02B78551347}.Release|Any CPU.ActiveCfg = Release|Any CPU - {BE5C111D-AD61-433A-AE7E-F02B78551347}.Release|Any CPU.Build.0 = Release|Any CPU {7E025EFC-B889-4B57-A03F-EF294CFFBCD6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {7E025EFC-B889-4B57-A03F-EF294CFFBCD6}.Debug|Any CPU.Build.0 = Debug|Any CPU {7E025EFC-B889-4B57-A03F-EF294CFFBCD6}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -30,5 +26,9 @@ Global {D4AA1C47-98D4-415C-B026-3BC25B6B6F9D}.Debug|Any CPU.Build.0 = Debug|Any CPU {D4AA1C47-98D4-415C-B026-3BC25B6B6F9D}.Release|Any CPU.ActiveCfg = Release|Any CPU {D4AA1C47-98D4-415C-B026-3BC25B6B6F9D}.Release|Any CPU.Build.0 = Release|Any CPU + {2F3E2E4A-6C1E-46AF-AC2C-62E5D4E317A0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2F3E2E4A-6C1E-46AF-AC2C-62E5D4E317A0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2F3E2E4A-6C1E-46AF-AC2C-62E5D4E317A0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2F3E2E4A-6C1E-46AF-AC2C-62E5D4E317A0}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection EndGlobal diff --git a/Pathfinding/PathNode.cs b/Pathfinding/PathNode.cs deleted file mode 100644 index 8e84617..0000000 --- a/Pathfinding/PathNode.cs +++ /dev/null @@ -1,22 +0,0 @@ -using OSMDatastructure; - -namespace Pathfinding; - -public class PathNode : Node -{ - public PathNode? previousPathNode = null; - public double currentPathWeight = double.MaxValue; - public double DirectDistanceToGoal = double.MaxValue; - - - public PathNode(float lat, float lon) : base(lat, lon) - { - - } - - public static PathNode FromNode(Node node) - { - return new PathNode(node.lat, node.lon); - } - -} \ No newline at end of file diff --git a/Pathfinding/Pathfinder.cs b/Pathfinding/Pathfinder.cs index 99c71cf..ca8dc09 100644 --- a/Pathfinding/Pathfinder.cs +++ b/Pathfinding/Pathfinder.cs @@ -6,48 +6,50 @@ namespace Pathfinding; public class Pathfinder { - public static List CustomAStar(string workingDir, Coordinates start, Coordinates goal) + public static List CustomAStar(string workingDir, Coordinates start, Coordinates goal) { RegionManager regionManager = new RegionManager(workingDir); Region startRegion, goalRegion; - PathNode startNode, goalNode; + OsmNode? startNode, goalNode; try { startRegion = regionManager.GetRegion(start); - startNode = PathNode.FromNode(ClosestNodeToCoordinates(start, startRegion)!); //TODO null handling - } - catch (FileNotFoundException e) - { - throw new Exception(string.Format("No region at coordinates {0}", start), e); - } - try - { goalRegion = regionManager.GetRegion(goal); - goalNode = PathNode.FromNode(ClosestNodeToCoordinates(goal, goalRegion)!); //TODO null handling } catch (FileNotFoundException e) { - throw new Exception(string.Format("No region at coordinates {0}", start), e); + throw new Exception(string.Format("No region at coordinates {0}", e.FileName), e); } + + startNode = ClosestNodeToCoordinates(start, startRegion); + goalNode = ClosestNodeToCoordinates(goal, goalRegion); + if (startNode == null || goalNode == null) + return new List(); - List toVisit = new() { startNode }; - startNode.DirectDistanceToGoal = Utils.DistanceBetween(startNode, goalNode); + List toVisit = new() { startNode }; + OsmNode closestNodeToGoal; bool stop = false; while (toVisit.Count > 0 && !stop) { - PathNode closestNodeToGoal = toVisit.First(); - foreach (PathNode node in toVisit) + Console.WriteLine("toVisit length: {0}", toVisit.Count.ToString()); + closestNodeToGoal = toVisit.First(); + foreach (OsmNode node in toVisit) { + if (node.DirectDistanceToGoal.Equals(double.MaxValue)) + { + node.DirectDistanceToGoal = Utils.DistanceBetween(node, goalNode); + } if (node.DirectDistanceToGoal < closestNodeToGoal.DirectDistanceToGoal) { closestNodeToGoal = node; } } - foreach (Connection connection in closestNodeToGoal.GetConnections()) + foreach (OsmEdge connection in closestNodeToGoal.edges) { - PathNode? neighbor = (PathNode)regionManager.GetNode(connection.endNodeCoordinates); + OsmNode? neighbor = regionManager.GetNode(connection.neighborCoordinates); + Console.WriteLine(neighbor); if (neighbor != null && neighbor.currentPathWeight < closestNodeToGoal.currentPathWeight + Utils.DistanceBetween(closestNodeToGoal, neighbor)) { neighbor.previousPathNode = closestNodeToGoal; @@ -60,40 +62,36 @@ public class Pathfinder toVisit.Add(neighbor); } } + + toVisit.Remove(closestNodeToGoal); } - List path = new(); - PathNode currentNode = goalNode; - while (!currentNode.Equals(startNode)) + List path = new(); + OsmNode? currentNode = goalNode; + while (currentNode != null && !currentNode.Equals(startNode)) { path.Add(currentNode); - currentNode = currentNode.previousPathNode!; + currentNode = currentNode.previousPathNode; } path.Add(startNode); return path; } - private static Node? ClosestNodeToCoordinates(Coordinates coordinates, Region region) + private static OsmNode? ClosestNodeToCoordinates(Coordinates coordinates, Region region) { - ulong? closestId = ClosestNodeIdToCoordinates(coordinates, region); - return closestId != null ? region.GetNode((ulong)closestId) : null; - } - - private static ulong? ClosestNodeIdToCoordinates(Coordinates coordinates, Region region) - { - ulong? closestId = null; - double closestDistance = double.MaxValue, distance; - - foreach (KeyValuePair kv in region.GetNodes()) + OsmNode? closest = null; + double distance = double.MaxValue; + foreach (OsmNode node in region.nodes) { - distance = Utils.DistanceBetween(kv.Value, coordinates); - if (distance < closestDistance) + double nodeDistance = Utils.DistanceBetween(node, coordinates); + if (nodeDistance < distance) { - closestDistance = distance; - closestId = kv.Key; + closest = node; + distance = nodeDistance; } } - return closestId; + + return closest; } } \ No newline at end of file diff --git a/Pathfinding/RegionManager.cs b/Pathfinding/RegionManager.cs index 4c48d0b..a7fbe29 100644 --- a/Pathfinding/RegionManager.cs +++ b/Pathfinding/RegionManager.cs @@ -1,4 +1,5 @@ using OSMDatastructure; +using Pathfinding; namespace OSMImporter { @@ -59,23 +60,9 @@ namespace OSMImporter return ByteConverter.ToRegion(regionBytes); } - public Node? GetNode(ulong id) + public OsmNode? GetNode(Coordinates coordinates) { - foreach (Region region in this._regions.Values) - { - Node? node = region.GetNode(id); - if (node != null) - return node; - } - - return null; - } - - public Node? GetNode(Coordinates coordinates) - { - Region? regionWithNode = GetRegion(coordinates); - if (regionWithNode == null) - return null;//TODO null handling + Region regionWithNode = GetRegion(coordinates); return regionWithNode.GetNode(coordinates); } } diff --git a/Pathfinding/Utils.cs b/Pathfinding/Utils.cs index 226e871..0ba8f3e 100644 --- a/Pathfinding/Utils.cs +++ b/Pathfinding/Utils.cs @@ -6,12 +6,12 @@ namespace Pathfinding { public static double DistanceBetween(Coordinates c1, Coordinates n2) { - return DistanceBetween(c1.lat, c1.lon, n2.lat, n2.lon); + return DistanceBetween(c1.latitude, c1.longitude, n2.latitude, n2.longitude); } public static double DistanceBetween(Coordinates c1, float lat2, float lon2) { - return DistanceBetween(c1.lat, c1.lon, lat2, lon2); + return DistanceBetween(c1.latitude, c1.longitude, lat2, lon2); } public static double DistanceBetween(float lat1, float lon1, Coordinates c2) @@ -19,6 +19,21 @@ namespace Pathfinding return DistanceBetween(c2, lat1, lon1); } + public static double DistanceBetween(OsmNode node1, OsmNode node2) + { + return DistanceBetween(node1.coordinates, node2.coordinates); + } + + public static double DistanceBetween(OsmNode node1, Coordinates c2) + { + return DistanceBetween(node1.coordinates, c2); + } + + public static double DistanceBetween(Coordinates c1, OsmNode n2) + { + return DistanceBetween(c1, n2.coordinates); + } + public static double DistanceBetween(float lat1, float lon1, float lat2, float lon2) //Law of Cosines { /* diff --git a/Server/Server.cs b/Server/Server.cs index a101440..ef05344 100644 --- a/Server/Server.cs +++ b/Server/Server.cs @@ -1,31 +1,14 @@ -using OSMImporter; using OSMDatastructure; +using Pathfinding; namespace Server; public class Server { - - public static Region LoadRegion(string folderPath, Coordinates coordinates) - { - string fullPath = Path.Combine(folderPath, coordinates.GetRegionHash().ToString()); - Console.WriteLine(fullPath); - Region retRegion = new Region(coordinates); - if (!File.Exists(fullPath)) - return retRegion; - - FileStream fileStream = new FileStream(fullPath, FileMode.Open); - - byte[] regionBytes = new byte[fileStream.Length]; - fileStream.Read(regionBytes, 0, regionBytes.Length); - fileStream.Close(); - - return ByteConverter.ToRegion(regionBytes); - } - public static void Main(string[] args) { - Importer.Split("/home/glax/Downloads/bayern-latest.osm", "/home/glax/Downloads/bayern-latest"); - Region r = LoadRegion("/home/glax/Downloads/bayern-latest", new Coordinates(47.890f,12.56f)); + //XmlImporter.Split("/home/glax/Downloads/oberbayern-latest.osm", "/home/glax/Downloads/oberbayern-latest"); + //Region r = LoadRegion("/home/glax/Downloads/bayern-latest", new Coordinates(47.890f,12.56f)); + Pathfinder.CustomAStar("/home/glax/Downloads/oberbayern-latest", new Coordinates(48.243351f, 11.640417f), new Coordinates(48.25239f, 11.53272f)); } } \ No newline at end of file diff --git a/Server/Server.csproj b/Server/Server.csproj index 5843f3a..0469d09 100644 --- a/Server/Server.csproj +++ b/Server/Server.csproj @@ -8,7 +8,6 @@ -