Datastructure for ASTAR and OSM_Regions

This commit is contained in:
glax 2024-07-22 04:57:55 +02:00
commit 12398ca09e
15 changed files with 285 additions and 0 deletions

5
.gitignore vendored Normal file
View File

@ -0,0 +1,5 @@
bin/
obj/
/packages/
riderModule.iml
/_ReSharper.Caches/

13
.idea/.idea.OSM_Graph/.idea/.gitignore vendored Normal file
View File

@ -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

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding" addBOMForNewFiles="with BOM under Windows, with no BOM otherwise" />
</project>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="UserContentModel">
<attachedFolders />
<explicitIncludes />
<explicitExcludes />
</component>
</project>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

45
Graph/Graph.cs Normal file
View File

@ -0,0 +1,45 @@
namespace Graph;
public class Graph
{
public readonly Dictionary<ulong, Node> Nodes = new();
public readonly Dictionary<ulong, Way> 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<ulong, Node> 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.";
}
}

9
Graph/Graph.csproj Normal file
View File

@ -0,0 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>

16
Graph/Node.cs Normal file
View File

@ -0,0 +1,16 @@
namespace Graph;
public class Node(float lat, float lon, Dictionary<ulong, ulong>? neighbors = null)
{
public readonly Dictionary<ulong, ulong> 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}";
}
}

49
Graph/Utils/NodeUtils.cs Normal file
View File

@ -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;
}
}

12
Graph/Way.cs Normal file
View File

@ -0,0 +1,12 @@
namespace Graph;
public class Way(Dictionary<string, string> tags)
{
public readonly Dictionary<string, string> Tags = tags;
public override string ToString()
{
return $"Way\n" +
$"Tags:\t{string.Join("\n\t", Tags.Select(t => $"{t.Key,10}={t.Value,30}"))}";
}
}

22
OSM_Graph.sln Normal file
View File

@ -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

View File

@ -0,0 +1,7 @@
namespace OSM_Graph;
public interface IOSMSerializable<out T>
{
public abstract string Serialize();
public static abstract T Deserialize(string serialized);
}

37
OSM_Graph/Node.cs Normal file
View File

@ -0,0 +1,37 @@
using System.Globalization;
using System.Runtime.Serialization;
namespace OSM_Graph;
public class Node(ulong id, float lat, float lon, Dictionary<ulong, ulong>? neighbors = null) : Graph.Node(lat, lon, neighbors), IOSMSerializable<Node>
{
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<ulong, ulong> 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);
}
}
}

View File

@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Graph\Graph.csproj" />
</ItemGroup>
</Project>

39
OSM_Graph/Way.cs Normal file
View File

@ -0,0 +1,39 @@
using System.Runtime.Serialization;
using System.Text.RegularExpressions;
namespace OSM_Graph;
public class Way(ulong id, Dictionary<string, string> tags, List<ulong> nodeIds) : Graph.Way(tags), IOSMSerializable<Way>
{
public readonly ulong ID = id;
public readonly List<ulong> 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<ulong> nodes = parts[1].Split(',').Select(ulong.Parse).ToList();
Dictionary<string, string> 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);
}
}
}