From 12398ca09ee9c84c16633143ee9f09514182e9b4 Mon Sep 17 00:00:00 2001 From: glax Date: Mon, 22 Jul 2024 04:57:55 +0200 Subject: [PATCH] Datastructure for ASTAR and OSM_Regions --- .gitignore | 5 +++ .idea/.idea.OSM_Graph/.idea/.gitignore | 13 ++++++ .idea/.idea.OSM_Graph/.idea/encodings.xml | 4 ++ .idea/.idea.OSM_Graph/.idea/indexLayout.xml | 8 ++++ .idea/.idea.OSM_Graph/.idea/vcs.xml | 6 +++ Graph/Graph.cs | 45 +++++++++++++++++++ Graph/Graph.csproj | 9 ++++ Graph/Node.cs | 16 +++++++ Graph/Utils/NodeUtils.cs | 49 +++++++++++++++++++++ Graph/Way.cs | 12 +++++ OSM_Graph.sln | 22 +++++++++ OSM_Graph/IOSMSerializable.cs | 7 +++ OSM_Graph/Node.cs | 37 ++++++++++++++++ OSM_Graph/OSM_Graph.csproj | 13 ++++++ OSM_Graph/Way.cs | 39 ++++++++++++++++ 15 files changed, 285 insertions(+) create mode 100644 .gitignore create mode 100644 .idea/.idea.OSM_Graph/.idea/.gitignore create mode 100644 .idea/.idea.OSM_Graph/.idea/encodings.xml create mode 100644 .idea/.idea.OSM_Graph/.idea/indexLayout.xml create mode 100644 .idea/.idea.OSM_Graph/.idea/vcs.xml create mode 100644 Graph/Graph.cs create mode 100644 Graph/Graph.csproj create mode 100644 Graph/Node.cs create mode 100644 Graph/Utils/NodeUtils.cs create mode 100644 Graph/Way.cs create mode 100644 OSM_Graph.sln create mode 100644 OSM_Graph/IOSMSerializable.cs create mode 100644 OSM_Graph/Node.cs create mode 100644 OSM_Graph/OSM_Graph.csproj create mode 100644 OSM_Graph/Way.cs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..add57be --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +bin/ +obj/ +/packages/ +riderModule.iml +/_ReSharper.Caches/ \ No newline at end of file diff --git a/.idea/.idea.OSM_Graph/.idea/.gitignore b/.idea/.idea.OSM_Graph/.idea/.gitignore new file mode 100644 index 0000000..ff0a05f --- /dev/null +++ b/.idea/.idea.OSM_Graph/.idea/.gitignore @@ -0,0 +1,13 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Rider ignored files +/projectSettingsUpdater.xml +/.idea.OSM_Graph.iml +/modules.xml +/contentModel.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/.idea.OSM_Graph/.idea/encodings.xml b/.idea/.idea.OSM_Graph/.idea/encodings.xml new file mode 100644 index 0000000..df87cf9 --- /dev/null +++ b/.idea/.idea.OSM_Graph/.idea/encodings.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/.idea/.idea.OSM_Graph/.idea/indexLayout.xml b/.idea/.idea.OSM_Graph/.idea/indexLayout.xml new file mode 100644 index 0000000..7b08163 --- /dev/null +++ b/.idea/.idea.OSM_Graph/.idea/indexLayout.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/.idea.OSM_Graph/.idea/vcs.xml b/.idea/.idea.OSM_Graph/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/.idea.OSM_Graph/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/Graph/Graph.cs b/Graph/Graph.cs new file mode 100644 index 0000000..5f44aae --- /dev/null +++ b/Graph/Graph.cs @@ -0,0 +1,45 @@ +namespace Graph; + +public class Graph +{ + public readonly Dictionary Nodes = new(); + public readonly Dictionary Ways = new (); + + public void ConcatGraph(Graph graph) + { + foreach ((ulong id, Node n) in graph.Nodes) + this.Nodes.TryAdd(id, n); + foreach ((ulong id, Way w) in graph.Ways) + this.Ways.TryAdd(id, w); + } + + public bool ContainsNode(Node node) + { + return Nodes.ContainsValue(node); + } + + public bool ContainsNode(ulong nodeId) + { + return Nodes.ContainsKey(nodeId); + } + + public bool ContainsWay(Way way) + { + return Ways.ContainsValue(way); + } + + public bool ContainsWay(ulong wayId) + { + return Ways.ContainsKey(wayId); + } + + public KeyValuePair ClosestNodeToCoordinates(float lat, float lon) + { + return Nodes.MinBy(n => n.Value.DistanceTo(lat, lon)); + } + + public override string ToString() + { + return $"Graph {Nodes.Count} Nodes {Ways.Count} Ways."; + } +} \ No newline at end of file diff --git a/Graph/Graph.csproj b/Graph/Graph.csproj new file mode 100644 index 0000000..3a63532 --- /dev/null +++ b/Graph/Graph.csproj @@ -0,0 +1,9 @@ + + + + net8.0 + enable + enable + + + diff --git a/Graph/Node.cs b/Graph/Node.cs new file mode 100644 index 0000000..dd8084f --- /dev/null +++ b/Graph/Node.cs @@ -0,0 +1,16 @@ +namespace Graph; + +public class Node(float lat, float lon, Dictionary? neighbors = null) +{ + public readonly Dictionary Neighbors = neighbors??new(); //nodeId, wayId + public readonly float Lat = lat, Lon = lon; + + public double DistanceTo(Node n2) => Utils.NodeUtils.DistanceBetween(this, n2); + public double DistanceTo(float latitude, float longitude) => Utils.NodeUtils.DistanceBetween(this, latitude, longitude); + public bool HasEdgeTo(ulong neighbor) => this.Neighbors.ContainsKey(neighbor); + + public override string ToString() + { + return $"Node {Lat:00.000000} {Lon:000.000000}"; + } +} \ No newline at end of file diff --git a/Graph/Utils/NodeUtils.cs b/Graph/Utils/NodeUtils.cs new file mode 100644 index 0000000..93ece5b --- /dev/null +++ b/Graph/Utils/NodeUtils.cs @@ -0,0 +1,49 @@ +namespace Graph.Utils; + +public static class NodeUtils +{ + public const int EarthRadius = 6371000; + + /* + * Close enough + */ + public static double DistanceBetween(float lat1, float lon1, float lat2, float lon2) + { + double radiansLat1 = DegreesToRadians(lat1); + double radiansLat2 = DegreesToRadians(lat2); + double deltaRadiansLon = DegreesToRadians(lon2 - lon1); + + double distance = Math.Acos(Math.Sin(radiansLat1) * Math.Sin(radiansLat2) + Math.Cos(radiansLat1) * Math.Cos(radiansLat2) * Math.Cos(deltaRadiansLon)) * EarthRadius; + + return distance; + } + + public static double DistanceBetween(Node n1, Node n2) => DistanceBetween(n1.Lat, n1.Lon, n2.Lat, n2.Lon); + public static double DistanceBetween(Node n1, float lat2, float lon2) => DistanceBetween(n1.Lat, n1.Lon, lat2, lon2); + public static double DistanceBetween(float lat1, float lon1, Node n2) => DistanceBetween(n2, lat1, lon1); + + /* + * From https://www.movable-type.co.uk/scripts/latlong.html + */ + public static double DistanceBetweenHaversine(float lat1, float lon1, float lat2, float lon2) + { + double radiansLat1 = DegreesToRadians(lat1); + double radiansLat2 = DegreesToRadians(lat2); + double deltaRadiansLat = DegreesToRadians(lat2 - lat1); + double deltaRadiansLon = DegreesToRadians(lon2 - lon1); + double a = Math.Sin(deltaRadiansLat / 2) * Math.Sin(deltaRadiansLat / 2) + Math.Cos(radiansLat1) * Math.Cos(radiansLat2) * Math.Sin(deltaRadiansLon / 2) * Math.Sin(deltaRadiansLon / 2); + double c = 2 * Math.Atan2(Math.Sqrt(a), Math.Sqrt(1 - a)); + double d = EarthRadius * c; + return d; + } + + public static double DegreesToRadians(float deg) + { + return deg * Math.PI /180; + } + + public static double RadiansToDegrees(float rad) + { + return rad * 180 / Math.PI; + } +} \ No newline at end of file diff --git a/Graph/Way.cs b/Graph/Way.cs new file mode 100644 index 0000000..a47ff85 --- /dev/null +++ b/Graph/Way.cs @@ -0,0 +1,12 @@ +namespace Graph; + +public class Way(Dictionary tags) +{ + public readonly Dictionary Tags = tags; + + public override string ToString() + { + return $"Way\n" + + $"Tags:\t{string.Join("\n\t", Tags.Select(t => $"{t.Key,10}={t.Value,30}"))}"; + } +} \ No newline at end of file diff --git a/OSM_Graph.sln b/OSM_Graph.sln new file mode 100644 index 0000000..15ac884 --- /dev/null +++ b/OSM_Graph.sln @@ -0,0 +1,22 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OSM_Graph", "OSM_Graph\OSM_Graph.csproj", "{82A1033D-89AD-4C05-9771-4CE48ABD006A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Graph", "Graph\Graph.csproj", "{6B699841-C159-4A29-9574-3FAD6F364BCF}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {82A1033D-89AD-4C05-9771-4CE48ABD006A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {82A1033D-89AD-4C05-9771-4CE48ABD006A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {82A1033D-89AD-4C05-9771-4CE48ABD006A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {82A1033D-89AD-4C05-9771-4CE48ABD006A}.Release|Any CPU.Build.0 = Release|Any CPU + {6B699841-C159-4A29-9574-3FAD6F364BCF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6B699841-C159-4A29-9574-3FAD6F364BCF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6B699841-C159-4A29-9574-3FAD6F364BCF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6B699841-C159-4A29-9574-3FAD6F364BCF}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal diff --git a/OSM_Graph/IOSMSerializable.cs b/OSM_Graph/IOSMSerializable.cs new file mode 100644 index 0000000..a3344ed --- /dev/null +++ b/OSM_Graph/IOSMSerializable.cs @@ -0,0 +1,7 @@ +namespace OSM_Graph; + +public interface IOSMSerializable +{ + public abstract string Serialize(); + public static abstract T Deserialize(string serialized); +} \ No newline at end of file diff --git a/OSM_Graph/Node.cs b/OSM_Graph/Node.cs new file mode 100644 index 0000000..8a7da77 --- /dev/null +++ b/OSM_Graph/Node.cs @@ -0,0 +1,37 @@ +using System.Globalization; +using System.Runtime.Serialization; + +namespace OSM_Graph; + +public class Node(ulong id, float lat, float lon, Dictionary? neighbors = null) : Graph.Node(lat, lon, neighbors), IOSMSerializable +{ + public readonly ulong ID = id; + + public string Serialize() + { + return $"{this.ID}#{this.Lat.ToString(CultureInfo.InvariantCulture)}#{this.Lon.ToString(CultureInfo.InvariantCulture)}#{string.Join(',', this.Neighbors.Select(n => $"{n.Key}-{n.Value}"))}"; + } + + public static Node Deserialize(string serialized) + { + string[] parts = serialized.Split('#'); + if (parts.Length != 4) + throw new SerializationException($"Can not deserialize String ({serialized}) to object of type "); + try + { + ulong id = ulong.Parse(parts[0]); + float lat = float.Parse(parts[1], NumberStyles.Float, CultureInfo.InvariantCulture); + float lon = float.Parse(parts[2], NumberStyles.Float, CultureInfo.InvariantCulture); + Dictionary neighbors = parts[3].Length > 3 + ? parts[3].Split(',').ToDictionary(str => ulong.Parse(str.Split('-')[0]), str => ulong.Parse(str.Split('-')[1])) + : new(); + + return new Node(id, lat, lon, neighbors); + } + catch (Exception e) + { + throw new SerializationException($"Exception during Deserialization of OSM_Graph.Node ({serialized}):", e); + } + + } +} \ No newline at end of file diff --git a/OSM_Graph/OSM_Graph.csproj b/OSM_Graph/OSM_Graph.csproj new file mode 100644 index 0000000..7c572a0 --- /dev/null +++ b/OSM_Graph/OSM_Graph.csproj @@ -0,0 +1,13 @@ + + + + net8.0 + enable + enable + + + + + + + diff --git a/OSM_Graph/Way.cs b/OSM_Graph/Way.cs new file mode 100644 index 0000000..6db8325 --- /dev/null +++ b/OSM_Graph/Way.cs @@ -0,0 +1,39 @@ +using System.Runtime.Serialization; +using System.Text.RegularExpressions; + +namespace OSM_Graph; + +public class Way(ulong id, Dictionary tags, List nodeIds) : Graph.Way(tags), IOSMSerializable +{ + public readonly ulong ID = id; + public readonly List NodeIds = nodeIds; + + public string Serialize() + { + string tags = string.Join(',', Tags.Select(t => + $"{t.Key.Replace("$", "Dollar")}${t.Value.Replace("$", "Dollar")}" + .Replace(',', '.') + .Replace("#", "No."))); + return $"{ID}#{string.Join(',', NodeIds)}#{Regex.Replace(tags, @"[^a-zA-Z0-9!%-\/:-@\[-_\{-\~$]", "")}"; + } + + public static Way Deserialize(string serialized) + { + string[] parts = serialized.Split('#'); + if (parts.Length != 3) + throw new SerializationException($"Can not deserialize String ({serialized}) to OSM_Graph.Way"); + try + { + ulong id = ulong.Parse(parts[0]); + List nodes = parts[1].Split(',').Select(ulong.Parse).ToList(); + Dictionary t = parts[2].Length > 0 + ? parts[2].Split(',') .ToDictionary(str => str.Split('$')[0].Replace("Dollar", "$"), str => str.Split('$')[1].Replace("Dollar", "$")) + : new(); + return new Way(id, t, nodes); + } + catch (Exception e) + { + throw new SerializationException($"Exception during Deserialization of OSM_Graph.Way ({serialized}):", e); + } + } +} \ No newline at end of file