Compare commits
166 Commits
f0cd97fbc7
...
master
Author | SHA1 | Date | |
---|---|---|---|
56ac9dc948 | |||
9ef63c9886 | |||
2b5dd91335 | |||
9f0d47ed59 | |||
371989b34d | |||
e53d1086cc | |||
dc98fb51b1 | |||
3077b4d8b8 | |||
7c5d87ca76 | |||
a5f272dfb9 | |||
f84aa82186 | |||
6d59253a0b | |||
af821a761f | |||
9b88996439 | |||
5733b0edb3 | |||
923cbee280 | |||
f19aa0007e | |||
f525b88a3a | |||
d497196f9f | |||
c705fdb63a | |||
aa05aad5b3 | |||
7d33d11a03 | |||
edd931bca5 | |||
6b5dddb1e3 | |||
fea0ecf17b | |||
30b29aa25c | |||
73e7daffd7 | |||
7b88616373 | |||
2799db162d | |||
9301e948b0 | |||
ec6725a5c5 | |||
886ccaa8dc | |||
af1d9baf4f | |||
e2332847cd | |||
dca4d56866 | |||
5f6cccd17d | |||
97a8c2ea6f | |||
b89a3715a1 | |||
6bc1d3c7ce | |||
5b8a1d1e10 | |||
7856f1c66c | |||
97a057a3d4 | |||
bc39785f6f | |||
18822e2152 | |||
68cb0ee3fd | |||
465d40a475 | |||
7fd9047ac4 | |||
601200a8d6 | |||
a758c8c63e | |||
ed46a419e3 | |||
a1d9ccad46 | |||
914731c8a3 | |||
d8ce6e4ce5 | |||
976108569b | |||
6373874495 | |||
c43c6dc985 | |||
33232a7eb7 | |||
cf5b1e9945 | |||
aa8b1e4451 | |||
95c0088b73 | |||
cd3905915b | |||
dd37430761 | |||
42e915ee05 | |||
7d769a064f | |||
93a448e189 | |||
750ba5c624 | |||
1facca84ba | |||
8b7cfcbd77 | |||
28ab2b2bb8 | |||
7201b9c993 | |||
d1f311a76b | |||
2b252e2b06 | |||
d456275fc1 | |||
2bd6c5d9c4 | |||
90a09e84c5 | |||
b87d8a0300 | |||
23429c8a00 | |||
946fa0206b | |||
5a1dce9883 | |||
619cad61ee | |||
bfb117164e | |||
208c000577 | |||
5212e43897 | |||
6fb88b5c9c | |||
9e0c4f65db | |||
055a751c9d | |||
08ebc9a26b | |||
d35aab9c39 | |||
08e3da6fe3 | |||
367e9cfde0 | |||
a70284aa87 | |||
13915c9773 | |||
c373451007 | |||
308579279b | |||
2131ac4afe | |||
428fcb9bf8 | |||
6b496957d7 | |||
874d60992e | |||
c1557b7678 | |||
14533c150f | |||
5289020d44 | |||
bb789e731d | |||
9e72d50448 | |||
c238a9eed3 | |||
932465a564 | |||
a0d2284e45 | |||
6938c86ce2 | |||
6e836db79b | |||
9448187452 | |||
2ca4207fd7 | |||
0f53ae579c | |||
d8f8a41dcc | |||
6eab23ff16 | |||
206f9c5811 | |||
e0bb3ce3de | |||
2904be84f0 | |||
13beaeaf73 | |||
ea7ce1f630 | |||
9c7fec1c37 | |||
5efec08bbc | |||
05ae0bff6e | |||
8bd0c5a4d4 | |||
feb9b70e50 | |||
9ef0e421bc | |||
a54b189b08 | |||
585a9213ce | |||
bf08f38a1e | |||
fc5d388ecd | |||
58d1031524 | |||
fe0ccd0fca | |||
dfc9ffeb2c | |||
9c53c67763 | |||
f266c6c7e6 | |||
8813023cd6 | |||
72b5511c26 | |||
f42e458048 | |||
3e23635cd1 | |||
ed8558049c | |||
20d4da9e6f | |||
e9f1ba2e73 | |||
5fe0acf0b4 | |||
29189c5d8d | |||
7f06a6f880 | |||
8dd63411ea | |||
8f9f6f630e | |||
2c5ab070a2 | |||
4600105b0b | |||
0f0f4182ac | |||
5ebe843048 | |||
03322ea143 | |||
349ed9da94 | |||
87e260562f | |||
82d2de1537 | |||
01deb02666 | |||
2826ff2502 | |||
8cf13efae3 | |||
b3da6936da | |||
312536f0c0 | |||
556d4b1ffb | |||
2d5ffabb5d | |||
0bfc120ede | |||
806dcf98c9 | |||
c2cadd678c | |||
15628d3544 | |||
452c5d3177 | |||
9dc282253d |
4
.gitignore
vendored
4
.gitignore
vendored
@ -2,4 +2,6 @@ bin/
|
|||||||
obj/
|
obj/
|
||||||
/packages/
|
/packages/
|
||||||
riderModule.iml
|
riderModule.iml
|
||||||
/_ReSharper.Caches/
|
/_ReSharper.Caches/
|
||||||
|
.idea/
|
||||||
|
*.DotSettings*
|
||||||
|
13
.idea/.idea.OSMServer/.idea/.gitignore
generated
vendored
13
.idea/.idea.OSMServer/.idea/.gitignore
generated
vendored
@ -1,13 +0,0 @@
|
|||||||
# Default ignored files
|
|
||||||
/shelf/
|
|
||||||
/workspace.xml
|
|
||||||
# Rider ignored files
|
|
||||||
/projectSettingsUpdater.xml
|
|
||||||
/contentModel.xml
|
|
||||||
/modules.xml
|
|
||||||
/.idea.OSMServer.iml
|
|
||||||
# Editor-based HTTP Client requests
|
|
||||||
/httpRequests/
|
|
||||||
# Datasource local storage ignored files
|
|
||||||
/dataSources/
|
|
||||||
/dataSources.local.xml
|
|
8
.idea/.idea.OSMServer/.idea/indexLayout.xml
generated
8
.idea/.idea.OSMServer/.idea/indexLayout.xml
generated
@ -1,8 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="UserContentModel">
|
|
||||||
<attachedFolders />
|
|
||||||
<explicitIncludes />
|
|
||||||
<explicitExcludes />
|
|
||||||
</component>
|
|
||||||
</project>
|
|
6
.idea/.idea.OSMServer/.idea/vcs.xml
generated
6
.idea/.idea.OSMServer/.idea/vcs.xml
generated
@ -1,6 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="VcsDirectoryMappings">
|
|
||||||
<mapping directory="" vcs="Git" />
|
|
||||||
</component>
|
|
||||||
</project>
|
|
23
API/API.csproj
Normal file
23
API/API.csproj
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net7.0</TargetFramework>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="7.0.4" />
|
||||||
|
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Folder Include="Controllers" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\Pathfinding\Pathfinding.csproj" />
|
||||||
|
<ProjectReference Include="..\RenderPath\RenderPath.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
52
API/Program.cs
Normal file
52
API/Program.cs
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
using OSMDatastructure;
|
||||||
|
using OSMDatastructure.Graph;
|
||||||
|
using Pathfinding;
|
||||||
|
|
||||||
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
|
|
||||||
|
// Add services to the container.
|
||||||
|
|
||||||
|
builder.Services.AddControllers();
|
||||||
|
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
|
||||||
|
builder.Services.AddEndpointsApiExplorer();
|
||||||
|
builder.Services.AddSwaggerGen();
|
||||||
|
|
||||||
|
var app = builder.Build();
|
||||||
|
|
||||||
|
app.MapGet("/getRoute", (float latStart, float lonStart, float latEnd, float lonEnd, Tag.SpeedType vehicle, double useHigherLevelRoadsPriority, double maxTurnAngle) =>
|
||||||
|
{
|
||||||
|
Pathfinder result = new Pathfinder("D:/stuttgart-regbez-latest", useHigherLevelRoadsPriority, maxTurnAngle).AStar(new Coordinates(latStart, lonStart),
|
||||||
|
new Coordinates(latEnd, lonEnd), vehicle, 3);
|
||||||
|
return result.pathResult;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
app.MapGet("/getShortestRoute", (float latStart, float lonStart, float latEnd, float lonEnd) =>
|
||||||
|
{
|
||||||
|
Pathfinder result = new Pathfinder("D:/stuttgart-regbez-latest", 0, 30).AStar(new Coordinates(latStart, lonStart),
|
||||||
|
new Coordinates(latEnd, lonEnd), Tag.SpeedType.any, 3);
|
||||||
|
return result.pathResult;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
app.MapGet("/getClosestNode", (float lat, float lon) =>
|
||||||
|
{
|
||||||
|
RegionManager regionManager = new RegionManager("D:/stuttgart-regbez-latest");
|
||||||
|
return regionManager.ClosestNodeToCoordinates(new Coordinates(lat, lon), Tag.SpeedType.any);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Configure the HTTP request pipeline.
|
||||||
|
if (app.Environment.IsDevelopment())
|
||||||
|
{
|
||||||
|
app.UseSwagger();
|
||||||
|
app.UseSwaggerUI();
|
||||||
|
}
|
||||||
|
|
||||||
|
app.UseHttpsRedirection();
|
||||||
|
|
||||||
|
app.UseAuthorization();
|
||||||
|
|
||||||
|
app.MapControllers();
|
||||||
|
|
||||||
|
app.Run();
|
||||||
|
|
41
API/Properties/launchSettings.json
Normal file
41
API/Properties/launchSettings.json
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://json.schemastore.org/launchsettings.json",
|
||||||
|
"iisSettings": {
|
||||||
|
"windowsAuthentication": false,
|
||||||
|
"anonymousAuthentication": true,
|
||||||
|
"iisExpress": {
|
||||||
|
"applicationUrl": "http://localhost:39952",
|
||||||
|
"sslPort": 44326
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"profiles": {
|
||||||
|
"http": {
|
||||||
|
"commandName": "Project",
|
||||||
|
"dotnetRunMessages": true,
|
||||||
|
"launchBrowser": true,
|
||||||
|
"launchUrl": "swagger",
|
||||||
|
"applicationUrl": "http://localhost:5057",
|
||||||
|
"environmentVariables": {
|
||||||
|
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"https": {
|
||||||
|
"commandName": "Project",
|
||||||
|
"dotnetRunMessages": true,
|
||||||
|
"launchBrowser": true,
|
||||||
|
"launchUrl": "swagger",
|
||||||
|
"applicationUrl": "https://localhost:7095;http://localhost:5057",
|
||||||
|
"environmentVariables": {
|
||||||
|
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"IIS Express": {
|
||||||
|
"commandName": "IISExpress",
|
||||||
|
"launchBrowser": true,
|
||||||
|
"launchUrl": "swagger",
|
||||||
|
"environmentVariables": {
|
||||||
|
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
8
API/appsettings.Development.json
Normal file
8
API/appsettings.Development.json
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"Logging": {
|
||||||
|
"LogLevel": {
|
||||||
|
"Default": "Information",
|
||||||
|
"Microsoft.AspNetCore": "Warning"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
9
API/appsettings.json
Normal file
9
API/appsettings.json
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"Logging": {
|
||||||
|
"LogLevel": {
|
||||||
|
"Default": "Information",
|
||||||
|
"Microsoft.AspNetCore": "Warning"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"AllowedHosts": "*"
|
||||||
|
}
|
@ -1,4 +1,5 @@
|
|||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
namespace OSMDatastructure.Graph;
|
namespace OSMDatastructure.Graph;
|
||||||
|
|
||||||
@ -8,6 +9,7 @@ public class Coordinates
|
|||||||
public float latitude { get; }
|
public float latitude { get; }
|
||||||
public float longitude { get; }
|
public float longitude { get; }
|
||||||
|
|
||||||
|
[JsonConstructor]
|
||||||
public Coordinates(float latitude, float longitude)
|
public Coordinates(float latitude, float longitude)
|
||||||
{
|
{
|
||||||
this.latitude = latitude;
|
this.latitude = latitude;
|
||||||
@ -19,8 +21,7 @@ public class Coordinates
|
|||||||
if (obj == null || obj.GetType() != this.GetType())
|
if (obj == null || obj.GetType() != this.GetType())
|
||||||
return false;
|
return false;
|
||||||
Coordinates convObj = (Coordinates)obj;
|
Coordinates convObj = (Coordinates)obj;
|
||||||
// ReSharper disable twice CompareOfFloatsByEqualityOperator static values
|
return convObj.latitude.Equals(this.latitude) && convObj.longitude.Equals(longitude);
|
||||||
return convObj.latitude == this.latitude && convObj.longitude == this.longitude;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ulong GetRegionHashCode(float latitude, float longitude)
|
public static ulong GetRegionHashCode(float latitude, float longitude)
|
||||||
@ -47,6 +48,6 @@ public class Coordinates
|
|||||||
public override string ToString()
|
public override string ToString()
|
||||||
{
|
{
|
||||||
return
|
return
|
||||||
$"COORDINATES Lat: {this.latitude.ToString(NumberFormatInfo.InvariantInfo)} Lon: {this.longitude.ToString(CultureInfo.InvariantCulture)}";
|
$"Coordinates lat:{latitude.ToString(NumberFormatInfo.InvariantInfo)} lon:{longitude.ToString(CultureInfo.InvariantCulture)}";
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,7 +1,9 @@
|
|||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
namespace OSMDatastructure.Graph;
|
namespace OSMDatastructure.Graph;
|
||||||
|
|
||||||
[Serializable]
|
[Serializable]
|
||||||
public class OsmWay
|
public class OsmEdge
|
||||||
{
|
{
|
||||||
public ulong wayId { get; }
|
public ulong wayId { get; }
|
||||||
public ulong startId { get; }
|
public ulong startId { get; }
|
||||||
@ -9,7 +11,8 @@ public class OsmWay
|
|||||||
public ulong neighborRegion { get; }
|
public ulong neighborRegion { get; }
|
||||||
|
|
||||||
|
|
||||||
public OsmWay(ulong wayId, ulong startId, ulong neighborId, ulong neighborRegion)
|
[JsonConstructor]
|
||||||
|
public OsmEdge(ulong wayId, ulong startId, ulong neighborId, ulong neighborRegion)
|
||||||
{
|
{
|
||||||
this.wayId = wayId;
|
this.wayId = wayId;
|
||||||
this.startId = startId;
|
this.startId = startId;
|
||||||
@ -19,6 +22,6 @@ public class OsmWay
|
|||||||
|
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
{
|
{
|
||||||
return $"EDGE WayID: {wayId} StartID: {startId} NeighborID: {neighborId} in {neighborRegion}";
|
return $"Edge wayId:{wayId} n1:{startId} n2:{neighborId} in regionId:{neighborRegion}";
|
||||||
}
|
}
|
||||||
}
|
}
|
45
OSMDatastructure/Graph/OsmNode.cs
Normal file
45
OSMDatastructure/Graph/OsmNode.cs
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace OSMDatastructure.Graph;
|
||||||
|
|
||||||
|
[Serializable]
|
||||||
|
public class OsmNode
|
||||||
|
{
|
||||||
|
public ulong nodeId { get; }
|
||||||
|
public HashSet<OsmEdge> edges { get; set; }
|
||||||
|
public Coordinates coordinates { get; }
|
||||||
|
|
||||||
|
public OsmNode(ulong nodeId, float lat, float lon)
|
||||||
|
{
|
||||||
|
this.nodeId = nodeId;
|
||||||
|
edges = new();
|
||||||
|
coordinates = new Coordinates(lat, lon);
|
||||||
|
}
|
||||||
|
|
||||||
|
[JsonConstructor]
|
||||||
|
public OsmNode(ulong nodeId, Coordinates coordinates)
|
||||||
|
{
|
||||||
|
this.nodeId = nodeId;
|
||||||
|
edges = new();
|
||||||
|
this.coordinates = coordinates;
|
||||||
|
}
|
||||||
|
|
||||||
|
public OsmEdge? GetEdgeToNode(OsmNode n)
|
||||||
|
{
|
||||||
|
HashSet<OsmEdge> e = edges.Where(edge => edge.neighborId == n.nodeId).ToHashSet();
|
||||||
|
if (e.Count > 0)
|
||||||
|
return e.First();
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool Equals(object? obj)
|
||||||
|
{
|
||||||
|
return obj != null && obj.GetType() == this.GetType() && ((OsmNode)obj).nodeId == this.nodeId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return $"Node id:{nodeId} coordinates:{coordinates} edges-count:{edges.Count}";
|
||||||
|
}
|
||||||
|
}
|
@ -6,4 +6,8 @@
|
|||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="System.Runtime.Serialization.Json" Version="4.3.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -1,60 +0,0 @@
|
|||||||
namespace OSMDatastructure.Graph;
|
|
||||||
|
|
||||||
[Serializable]
|
|
||||||
public class OsmNode
|
|
||||||
{
|
|
||||||
public ulong nodeId { get; }
|
|
||||||
public HashSet<OsmWay> edges { get; }
|
|
||||||
public Coordinates coordinates { get; }
|
|
||||||
|
|
||||||
[NonSerialized]public OsmNode? previousPathNode = null;
|
|
||||||
[NonSerialized]public double currentPathWeight = double.MaxValue;
|
|
||||||
[NonSerialized]public double currentPathLength = double.MaxValue;
|
|
||||||
[NonSerialized]public double directDistanceToGoal = double.MaxValue;
|
|
||||||
|
|
||||||
public OsmNode(ulong nodeId, float lat, float lon)
|
|
||||||
{
|
|
||||||
this.nodeId = nodeId;
|
|
||||||
this.edges = new();
|
|
||||||
this.coordinates = new Coordinates(lat, lon);
|
|
||||||
}
|
|
||||||
|
|
||||||
public OsmNode(ulong nodeId, Coordinates coordinates)
|
|
||||||
{
|
|
||||||
this.nodeId = nodeId;
|
|
||||||
this.edges = new();
|
|
||||||
this.coordinates = coordinates;
|
|
||||||
}
|
|
||||||
|
|
||||||
public OsmWay? GetEdgeToNode(OsmNode n)
|
|
||||||
{
|
|
||||||
foreach (OsmWay e in this.edges)
|
|
||||||
if (e.neighborId == n.nodeId)
|
|
||||||
return e;
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override bool Equals(object? obj)
|
|
||||||
{
|
|
||||||
return obj != null && obj.GetType() == this.GetType() && ((OsmNode)obj).nodeId == this.nodeId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override int GetHashCode()
|
|
||||||
{
|
|
||||||
return HashCode.Combine(coordinates);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override string ToString()
|
|
||||||
{
|
|
||||||
if(this.previousPathNode != null)
|
|
||||||
return string.Format(
|
|
||||||
"NODE {0} Edges-Count: {1} previousPathNode: {2} currentPathWeight: {3} currentPathLength: {4} directDistanceToGoal: {5}",
|
|
||||||
this.coordinates.ToString(), this.edges.Count, this.previousPathNode.coordinates.ToString(),
|
|
||||||
this.currentPathWeight, this.currentPathLength, this.directDistanceToGoal);
|
|
||||||
else
|
|
||||||
return string.Format(
|
|
||||||
"NODE {0} Edges-Count: {1} previousPathNode: NO PREVIOUS NODE currentPathWeight: {2} currentPathLength: {3} directDistanceToGoal: {4}",
|
|
||||||
this.coordinates.ToString(), this.edges.Count,
|
|
||||||
this.currentPathWeight, this.currentPathLength, this.directDistanceToGoal);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,3 +1,5 @@
|
|||||||
|
using System.Text.Json;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
using OSMDatastructure.Graph;
|
using OSMDatastructure.Graph;
|
||||||
|
|
||||||
namespace OSMDatastructure;
|
namespace OSMDatastructure;
|
||||||
@ -5,29 +7,52 @@ namespace OSMDatastructure;
|
|||||||
[Serializable]
|
[Serializable]
|
||||||
public class Region
|
public class Region
|
||||||
{
|
{
|
||||||
[NonSerialized]public const float RegionSize = 0.1f;
|
[JsonIgnore]public const float RegionSize = 0.025f;
|
||||||
public readonly HashSet<OsmNode> nodes = new();
|
[JsonIgnore]public static readonly JsonSerializerOptions serializerOptions = new()
|
||||||
public readonly HashSet<OsmWay> ways = new();
|
{
|
||||||
public ulong regionHash { get; }
|
IncludeFields = true,
|
||||||
public TagManager tagManager { get; }
|
MaxDepth = 32,
|
||||||
|
IgnoreReadOnlyFields = false//,
|
||||||
|
//WriteIndented = true
|
||||||
|
};
|
||||||
|
|
||||||
|
[JsonRequired]public HashSet<OsmNode> nodes { get; set; }
|
||||||
|
public ulong regionHash { get; }
|
||||||
|
[JsonRequired]public TagManager tagManager { get; set; }
|
||||||
|
|
||||||
|
[JsonConstructor]
|
||||||
public Region(ulong regionHash)
|
public Region(ulong regionHash)
|
||||||
{
|
{
|
||||||
this.regionHash = regionHash;
|
this.regionHash = regionHash;
|
||||||
tagManager = new TagManager();
|
tagManager = new TagManager();
|
||||||
|
nodes = new HashSet<OsmNode>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Region(Coordinates coordinates)
|
public Region(Coordinates coordinates)
|
||||||
{
|
{
|
||||||
regionHash = Coordinates.GetRegionHashCode(coordinates);
|
regionHash = Coordinates.GetRegionHashCode(coordinates);
|
||||||
tagManager = new TagManager();
|
tagManager = new TagManager();
|
||||||
|
nodes = new HashSet<OsmNode>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public OsmNode? GetNodeWithCoordinates(Coordinates coordinates)
|
public bool ContainsNode(ulong id)
|
||||||
{
|
{
|
||||||
foreach(OsmNode node in this.nodes)
|
return nodes.Any(node => node.nodeId == id);
|
||||||
if (node.coordinates.Equals(coordinates))
|
|
||||||
return node;
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool ContainsNode(Coordinates coordinates)
|
||||||
|
{
|
||||||
|
return nodes.Any(node => node.coordinates.Equals(coordinates));
|
||||||
|
}
|
||||||
|
|
||||||
|
public OsmNode? GetNode(ulong id)
|
||||||
|
{
|
||||||
|
return ContainsNode(id) ? nodes.First(node => node.nodeId == id) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public OsmNode? GetNode(Coordinates coordinates)
|
||||||
|
{
|
||||||
|
return ContainsNode(coordinates) ? nodes.First(node => node.coordinates.Equals(coordinates)) : null;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -1,4 +1,7 @@
|
|||||||
namespace OSMDatastructure.Graph;
|
using System.Text.Json;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace OSMDatastructure;
|
||||||
|
|
||||||
[Serializable]
|
[Serializable]
|
||||||
public class Tag
|
public class Tag
|
||||||
@ -6,95 +9,122 @@ public class Tag
|
|||||||
public TagType key { get; }
|
public TagType key { get; }
|
||||||
public dynamic value { get; }
|
public dynamic value { get; }
|
||||||
|
|
||||||
|
[JsonConstructor]
|
||||||
public Tag(TagType key, dynamic value)
|
public Tag(TagType key, dynamic value)
|
||||||
{
|
{
|
||||||
this.key = key;
|
this.key = key;
|
||||||
this.value = value;
|
if (value is JsonElement)
|
||||||
}
|
|
||||||
|
|
||||||
public static Tag FromBytes(byte[] bytes)
|
|
||||||
{
|
|
||||||
TagType type = (TagType)bytes[0];
|
|
||||||
dynamic value = false;
|
|
||||||
switch (type)
|
|
||||||
{
|
{
|
||||||
case TagType.highway:
|
switch (key)
|
||||||
case TagType.oneway:
|
{
|
||||||
case TagType.forward:
|
case TagType.highway:
|
||||||
value = BitConverter.ToBoolean(bytes, 1);
|
this.value = value.GetByte();
|
||||||
break;
|
break;
|
||||||
case TagType.maxspeed:
|
case TagType.maxspeed:
|
||||||
value = bytes[1];
|
this.value = value.GetByte();
|
||||||
break;
|
break;
|
||||||
|
case TagType.forward:
|
||||||
|
case TagType.oneway:
|
||||||
|
this.value = value.GetBoolean();
|
||||||
|
break;
|
||||||
|
case TagType.id:
|
||||||
|
this.value = value.GetUInt64();
|
||||||
|
break;
|
||||||
|
case TagType.name:
|
||||||
|
case TagType.tagref:
|
||||||
|
this.value = value.GetString();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
this.value = value;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this.value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Tag(type, value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Tag ConvertToTag(string key, string value)
|
public static HashSet<Tag> ConvertToTags(string key, string value)
|
||||||
{
|
{
|
||||||
|
HashSet<Tag> ret = new HashSet<Tag>();
|
||||||
switch (key)
|
switch (key)
|
||||||
{
|
{
|
||||||
case "highway":
|
case "highway":
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
return new Tag(Tag.TagType.highway, (Tag.WayType)Enum.Parse(typeof(Tag.WayType), value, true));
|
ret.Add(new Tag(TagType.highway, (WayType)Enum.Parse(typeof(WayType), value, true)));
|
||||||
}
|
}
|
||||||
catch (ArgumentException)
|
catch (ArgumentException)
|
||||||
{
|
{
|
||||||
return new Tag(Tag.TagType.highway, Tag.WayType.unclassified);
|
ret.Add(new Tag(TagType.highway, WayType.unclassified));
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
case "maxspeed":
|
case "maxspeed":
|
||||||
|
case "maxspeed:max":
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
byte speed = Convert.ToByte(value);
|
byte speed = Convert.ToByte(value);
|
||||||
if (speed == 255)
|
if (speed != 255)
|
||||||
return new Tag(Tag.TagType.highway, false);
|
ret.Add(new Tag(TagType.maxspeed, speed));
|
||||||
else
|
|
||||||
return new Tag(Tag.TagType.maxspeed, speed);
|
|
||||||
}
|
}
|
||||||
catch (Exception)
|
catch (Exception)
|
||||||
{
|
{
|
||||||
//Console.WriteLine(e);
|
ret.Add(new Tag(TagType.maxspeed, byte.MinValue));
|
||||||
//Console.WriteLine("Continuing...");
|
|
||||||
return new Tag(Tag.TagType.maxspeed, byte.MaxValue);
|
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
case "oneway":
|
case "oneway":
|
||||||
switch (value)
|
switch (value)
|
||||||
{
|
{
|
||||||
case "yes":
|
case "yes":
|
||||||
return new Tag(Tag.TagType.oneway, true);
|
ret.Add(new Tag(TagType.oneway, true));
|
||||||
|
break;
|
||||||
case "-1":
|
case "-1":
|
||||||
return new Tag(Tag.TagType.forward, false);
|
ret.Add(new Tag(TagType.forward, false));
|
||||||
|
ret.Add(new Tag(TagType.oneway, true));
|
||||||
|
break;
|
||||||
case "no":
|
case "no":
|
||||||
return new Tag(Tag.TagType.oneway, false);
|
ret.Add(new Tag(TagType.oneway, false));
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
case "name":
|
||||||
|
ret.Add(new Tag(TagType.name, value));
|
||||||
|
break;
|
||||||
|
case "ref":
|
||||||
|
ret.Add(new Tag(TagType.tagref, value));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return new Tag(Tag.TagType.EMPTY, false);
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return $"TAG {key.ToString()} {value.ToString()}";
|
||||||
|
}
|
||||||
|
|
||||||
public enum TagType : byte
|
public enum TagType : byte
|
||||||
{
|
{
|
||||||
highway, oneway, footway, sidewalk, cycleway, busway, forward, maxspeed, name, surface, lanes, access, tracktype, id, EMPTY
|
highway, oneway, footway, sidewalk, cycleway, busway, forward, maxspeed, name, surface, lanes, access, tracktype, id, tagref
|
||||||
}
|
}
|
||||||
|
|
||||||
public static readonly Dictionary<WayType, byte> defaultSpeedCar = new() {
|
public static readonly Dictionary<WayType, byte> defaultSpeedCar = new() {
|
||||||
{ WayType.NONE, 0 },
|
{ WayType.NONE, 0 },
|
||||||
{ WayType.motorway, 110 },
|
{ WayType.motorway, 130 },
|
||||||
{ WayType.trunk, 100 },
|
{ WayType.motorroad, 100 },
|
||||||
{ WayType.primary, 80 },
|
{ WayType.trunk, 85 },
|
||||||
{ WayType.secondary, 80 },
|
{ WayType.primary, 65 },
|
||||||
{ WayType.tertiary, 70 },
|
{ WayType.secondary, 60 },
|
||||||
{ WayType.unclassified, 20 },
|
{ WayType.tertiary, 50 },
|
||||||
{ WayType.residential, 10 },
|
{ WayType.unclassified, 30 },
|
||||||
{ WayType.motorway_link, 50 },
|
{ WayType.residential, 20 },
|
||||||
|
{ WayType.motorway_link, 60 },
|
||||||
{ WayType.trunk_link, 50 },
|
{ WayType.trunk_link, 50 },
|
||||||
{ WayType.primary_link, 30 },
|
{ WayType.primary_link, 50 },
|
||||||
{ WayType.secondary_link, 25 },
|
{ WayType.secondary_link, 50 },
|
||||||
{ WayType.tertiary_link, 25 },
|
{ WayType.tertiary_link, 30 },
|
||||||
{ WayType.living_street, 10 },
|
{ WayType.living_street, 10 },
|
||||||
{ WayType.service, 0 },
|
{ WayType.service, 0 },
|
||||||
{ WayType.pedestrian, 0 },
|
{ WayType.pedestrian, 0 },
|
||||||
@ -102,7 +132,7 @@ public class Tag
|
|||||||
{ WayType.bus_guideway, 0 },
|
{ WayType.bus_guideway, 0 },
|
||||||
{ WayType.escape, 0 },
|
{ WayType.escape, 0 },
|
||||||
{ WayType.raceway, 0 },
|
{ WayType.raceway, 0 },
|
||||||
{ WayType.road, 25 },
|
{ WayType.road, 20 },
|
||||||
{ WayType.busway, 0 },
|
{ WayType.busway, 0 },
|
||||||
{ WayType.footway, 0 },
|
{ WayType.footway, 0 },
|
||||||
{ WayType.bridleway, 0 },
|
{ WayType.bridleway, 0 },
|
||||||
@ -119,9 +149,9 @@ public class Tag
|
|||||||
{ WayType.trunk, 0 },
|
{ WayType.trunk, 0 },
|
||||||
{ WayType.primary, 0 },
|
{ WayType.primary, 0 },
|
||||||
{ WayType.secondary, 0 },
|
{ WayType.secondary, 0 },
|
||||||
{ WayType.tertiary, 0 },
|
{ WayType.tertiary, 2 },
|
||||||
{ WayType.unclassified, 1 },
|
{ WayType.unclassified, 1 },
|
||||||
{ WayType.residential, 3 },
|
{ WayType.residential, 4 },
|
||||||
{ WayType.motorway_link, 0 },
|
{ WayType.motorway_link, 0 },
|
||||||
{ WayType.trunk_link, 0 },
|
{ WayType.trunk_link, 0 },
|
||||||
{ WayType.primary_link, 0 },
|
{ WayType.primary_link, 0 },
|
||||||
@ -130,21 +160,23 @@ public class Tag
|
|||||||
{ WayType.living_street, 5 },
|
{ WayType.living_street, 5 },
|
||||||
{ WayType.service, 2 },
|
{ WayType.service, 2 },
|
||||||
{ WayType.pedestrian, 5 },
|
{ WayType.pedestrian, 5 },
|
||||||
{ WayType.track, 0 },
|
{ WayType.track, 1 },
|
||||||
{ WayType.bus_guideway, 0 },
|
{ WayType.bus_guideway, 0 },
|
||||||
{ WayType.escape, 0 },
|
{ WayType.escape, 1 },
|
||||||
{ WayType.raceway, 0 },
|
{ WayType.raceway, 0 },
|
||||||
{ WayType.road, 3 },
|
{ WayType.road, 2 },
|
||||||
{ WayType.busway, 0 },
|
{ WayType.busway, 0 },
|
||||||
{ WayType.footway, 4 },
|
{ WayType.footway, 4 },
|
||||||
{ WayType.bridleway, 1 },
|
{ WayType.bridleway, 1 },
|
||||||
{ WayType.steps, 2 },
|
{ WayType.steps, 2 },
|
||||||
{ WayType.corridor, 3 },
|
{ WayType.corridor, 3 },
|
||||||
{ WayType.path, 4 },
|
{ WayType.path, 4 },
|
||||||
{ WayType.cycleway, 2 },
|
{ WayType.cycleway, 1 },
|
||||||
{ WayType.construction, 0 }
|
{ WayType.construction, 0 }
|
||||||
};
|
};
|
||||||
public enum WayType : byte { 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 }
|
// ReSharper disable InconsistentNaming
|
||||||
|
public enum WayType : byte { NONE, motorway, motorroad, 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 }
|
||||||
|
// ReSharper restore InconsistentNaming
|
||||||
|
|
||||||
public enum SpeedType { pedestrian, car, road }
|
public enum SpeedType { pedestrian, car, any }
|
||||||
}
|
}
|
@ -1,52 +1,58 @@
|
|||||||
using System.Runtime.CompilerServices;
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
namespace OSMDatastructure.Graph;
|
namespace OSMDatastructure;
|
||||||
|
|
||||||
[Serializable]
|
[Serializable]
|
||||||
public class TagManager
|
public class TagManager
|
||||||
{
|
{
|
||||||
public readonly Dictionary<ulong, HashSet<Tag>> wayTags = new();
|
[JsonRequired]public Dictionary<ulong, HashSet<Tag>> wayTagSets { get; set; }
|
||||||
|
|
||||||
|
public TagManager()
|
||||||
|
{
|
||||||
|
wayTagSets = new();
|
||||||
|
}
|
||||||
|
|
||||||
public bool ContainsKey(ulong wayId, Tag.TagType key)
|
public bool ContainsKey(ulong wayId, Tag.TagType key)
|
||||||
{
|
{
|
||||||
return wayTags.ContainsKey(wayId) && wayTags[wayId].Any(tag => tag.key == key);
|
return wayTagSets.ContainsKey(wayId) && wayTagSets[wayId].Any(tag => tag.key == key);
|
||||||
}
|
}
|
||||||
|
|
||||||
public object? GetTag(ulong wayId, Tag.TagType key)
|
public object? GetTag(ulong wayId, Tag.TagType key)
|
||||||
{
|
{
|
||||||
return ContainsKey(wayId, key) ? wayTags[wayId].First(tag => tag.key == key) : null;
|
return ContainsKey(wayId, key) ? wayTagSets[wayId].First(tag => tag.key == key).value : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddTag(ulong wayId, string key, string value)
|
public void AddTag(ulong wayId, string key, string value)
|
||||||
{
|
{
|
||||||
Tag tag = Tag.ConvertToTag(key, value);
|
HashSet<Tag> pTags = Tag.ConvertToTags(key, value);
|
||||||
AddTag(wayId, tag);
|
if(pTags.Count > 0)
|
||||||
|
foreach (Tag pTag in pTags)
|
||||||
|
AddTag(wayId, pTag);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddTag(ulong wayId, Tag tag)
|
public void AddTag(ulong wayId, Tag tag)
|
||||||
{
|
{
|
||||||
if (tag.key != Tag.TagType.EMPTY)
|
if(!wayTagSets.ContainsKey(wayId))
|
||||||
|
wayTagSets.Add(wayId, new HashSet<Tag>());
|
||||||
|
HashSet<Tag> wayTags = wayTagSets[wayId];
|
||||||
|
if (!wayTags.Any(wayTag => wayTag.key == tag.key))
|
||||||
{
|
{
|
||||||
if(!wayTags.ContainsKey(wayId))
|
wayTags.Add(tag);
|
||||||
wayTags.Add(wayId, new HashSet<Tag>());
|
|
||||||
wayTags[wayId].Add(tag);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddTag(ulong wayId, KeyValuePair<Tag.TagType, dynamic> keyValuePair)
|
public void AddTag(ulong wayId, KeyValuePair<Tag.TagType, dynamic> tag)
|
||||||
{
|
{
|
||||||
if(!wayTags.ContainsKey(wayId))
|
AddTag(wayId, new Tag(tag.Key, tag.Value));
|
||||||
wayTags.Add(wayId, new HashSet<Tag>());
|
|
||||||
wayTags[wayId].Add(new Tag(keyValuePair.Key, keyValuePair.Value));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public HashSet<Tag>? GetTagsForWayId(ulong wayId)
|
public HashSet<Tag>? GetTagsForWayId(ulong wayId)
|
||||||
{
|
{
|
||||||
return wayTags.TryGetValue(wayId, out HashSet<Tag>? value) ? value : null;
|
return wayTagSets.TryGetValue(wayId, out HashSet<Tag>? value) ? value : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool ContainsWay(ulong wayId)
|
public bool ContainsWay(ulong wayId)
|
||||||
{
|
{
|
||||||
return wayTags.ContainsKey(wayId);
|
return wayTagSets.ContainsKey(wayId);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -65,12 +65,12 @@ namespace OSMDatastructure
|
|||||||
return d;
|
return d;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static double DegreesToRadians(double deg)
|
public static double DegreesToRadians(double deg)
|
||||||
{
|
{
|
||||||
return deg * Math.PI / 180.0;
|
return deg * Math.PI / 180.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static double RadiansToDegrees(double rad)
|
public static double RadiansToDegrees(double rad)
|
||||||
{
|
{
|
||||||
return rad * 180.0 / Math.PI;
|
return rad * 180.0 / Math.PI;
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OSMDatastructure", "OSMData
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Pathfinding", "Pathfinding\Pathfinding.csproj", "{D4AA1C47-98D4-415C-B026-3BC25B6B6F9D}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Pathfinding", "Pathfinding\Pathfinding.csproj", "{D4AA1C47-98D4-415C-B026-3BC25B6B6F9D}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "API", "API\API.csproj", "{1D364F40-1681-4D36-A625-83B324F6AC89}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RenderPath", "RenderPath\RenderPath.csproj", "{54CAC127-4EB6-4E06-A5C8-35343C5FF76A}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
@ -24,5 +28,13 @@ Global
|
|||||||
{D4AA1C47-98D4-415C-B026-3BC25B6B6F9D}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{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.ActiveCfg = Release|Any CPU
|
||||||
{D4AA1C47-98D4-415C-B026-3BC25B6B6F9D}.Release|Any CPU.Build.0 = Release|Any CPU
|
{D4AA1C47-98D4-415C-B026-3BC25B6B6F9D}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{1D364F40-1681-4D36-A625-83B324F6AC89}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{1D364F40-1681-4D36-A625-83B324F6AC89}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{1D364F40-1681-4D36-A625-83B324F6AC89}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{1D364F40-1681-4D36-A625-83B324F6AC89}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{54CAC127-4EB6-4E06-A5C8-35343C5FF76A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{54CAC127-4EB6-4E06-A5C8-35343C5FF76A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{54CAC127-4EB6-4E06-A5C8-35343C5FF76A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{54CAC127-4EB6-4E06-A5C8-35343C5FF76A}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
EndGlobal
|
EndGlobal
|
||||||
|
@ -1,4 +0,0 @@
|
|||||||
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=busway/@EntryIndexedValue">True</s:Boolean>
|
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=maxspeed/@EntryIndexedValue">True</s:Boolean>
|
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=oberbayern/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
|
|
@ -1,5 +0,0 @@
|
|||||||
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
|
||||||
<s:Boolean x:Key="/Default/AddReferences/RecentPaths/=_002Fhome_002Fglax_002FRiderProjects_002FGeo_002DGraph_002FGeo_002DGraph_002Fobj_002FDebug_002Fnet6_002E0_002FGeo_002DGraph_002Edll/@EntryIndexedValue">True</s:Boolean>
|
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=ferrata/@EntryIndexedValue">True</s:Boolean>
|
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=glax/@EntryIndexedValue">True</s:Boolean>
|
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=guideway/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
|
|
@ -1,25 +0,0 @@
|
|||||||
using OSMDatastructure;
|
|
||||||
|
|
||||||
namespace OSMImporter;
|
|
||||||
|
|
||||||
internal class RegionCollection
|
|
||||||
{
|
|
||||||
private readonly Dictionary<ulong, Region> _regions = new();
|
|
||||||
|
|
||||||
public Region GetRegion(Coordinates coordinates)
|
|
||||||
{
|
|
||||||
if(this._regions.ContainsKey(coordinates.GetRegionHash()))
|
|
||||||
return this._regions[coordinates.GetRegionHash()];
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Region newRegion = new Region(coordinates);
|
|
||||||
this._regions.Add(newRegion.regionHash, value: newRegion);
|
|
||||||
return newRegion;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Region[] GetAllRegions()
|
|
||||||
{
|
|
||||||
return this._regions.Values.ToArray();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,183 +0,0 @@
|
|||||||
using System.Globalization;
|
|
||||||
using System.Text;
|
|
||||||
using System.Xml;
|
|
||||||
using OSMDatastructure;
|
|
||||||
|
|
||||||
namespace OSMImporter
|
|
||||||
{
|
|
||||||
public static class XmlImporter
|
|
||||||
{
|
|
||||||
|
|
||||||
private static readonly XmlReaderSettings readerSettings = new()
|
|
||||||
{
|
|
||||||
IgnoreWhitespace = true,
|
|
||||||
IgnoreComments = true
|
|
||||||
};
|
|
||||||
|
|
||||||
private static readonly NumberFormatInfo coordinateFormat = new NumberFormatInfo()
|
|
||||||
{
|
|
||||||
NumberDecimalSeparator = "."
|
|
||||||
};
|
|
||||||
|
|
||||||
public static void Split(string xmlFilePath, string outputFolderPath)
|
|
||||||
{
|
|
||||||
if (!File.Exists(xmlFilePath))
|
|
||||||
throw new FileNotFoundException();
|
|
||||||
FileStream xmlFileStream = File.OpenRead(xmlFilePath);
|
|
||||||
|
|
||||||
Console.WriteLine("Reading ways once...");
|
|
||||||
Dictionary<ulong, Node?> nodes = ReturnNodeIdDictionary(XmlReader.Create(xmlFileStream, readerSettings));
|
|
||||||
|
|
||||||
xmlFileStream.Position = 0;
|
|
||||||
RegionCollection regionCollection = new RegionCollection();
|
|
||||||
|
|
||||||
Console.WriteLine("Reading nodes...");
|
|
||||||
LoadNodesIntoDictionary(ref nodes, XmlReader.Create(xmlFileStream, readerSettings), ref regionCollection);
|
|
||||||
|
|
||||||
xmlFileStream.Position = 0;
|
|
||||||
|
|
||||||
Console.WriteLine("Reading ways twice...");
|
|
||||||
CreateConnections(XmlReader.Create(xmlFileStream, readerSettings), ref nodes);
|
|
||||||
|
|
||||||
Console.WriteLine("Writing...");
|
|
||||||
|
|
||||||
foreach(Region region in regionCollection.GetAllRegions())
|
|
||||||
WriteRegion(region, outputFolderPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Dictionary<ulong, Node?> ReturnNodeIdDictionary(XmlReader xmlReader)
|
|
||||||
{
|
|
||||||
Dictionary<ulong, Node?> retSet = new Dictionary<ulong, Node?>();
|
|
||||||
while (xmlReader.ReadToFollowing("way"))
|
|
||||||
{
|
|
||||||
XmlReader wayReader = xmlReader.ReadSubtree();
|
|
||||||
bool addNodes = false;
|
|
||||||
HashSet<string> nodeIds = new();
|
|
||||||
while (wayReader.Read())
|
|
||||||
{
|
|
||||||
if (xmlReader.IsStartElement() && xmlReader.Name.Equals("nd"))
|
|
||||||
{
|
|
||||||
nodeIds.Add(wayReader.GetAttribute("ref")!);
|
|
||||||
}
|
|
||||||
else if (xmlReader.IsStartElement() && !addNodes && xmlReader.Name.Equals("tag") && xmlReader.GetAttribute("k")!.Equals("highway"))
|
|
||||||
{
|
|
||||||
addNodes = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
wayReader.Close();
|
|
||||||
|
|
||||||
if (addNodes)
|
|
||||||
{
|
|
||||||
foreach (string nodeId in nodeIds)
|
|
||||||
{
|
|
||||||
retSet.TryAdd(Convert.ToUInt64(nodeId), null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
xmlReader.Close();
|
|
||||||
return retSet;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void LoadNodesIntoDictionary(ref Dictionary<ulong, Node?> nodes, XmlReader xmlReader, ref RegionCollection regionCollection)
|
|
||||||
{
|
|
||||||
while (xmlReader.ReadToFollowing("node"))
|
|
||||||
{
|
|
||||||
ulong id = Convert.ToUInt64(xmlReader.GetAttribute("id"));
|
|
||||||
if (nodes.ContainsKey(id))
|
|
||||||
{
|
|
||||||
float lat = Convert.ToSingle(xmlReader.GetAttribute("lat")!, coordinateFormat);
|
|
||||||
float lon = Convert.ToSingle(xmlReader.GetAttribute("lon")!, coordinateFormat);
|
|
||||||
Node newNode = new Node(lat, lon);
|
|
||||||
nodes[id] = newNode;
|
|
||||||
regionCollection.GetRegion(newNode).AddNode(id, newNode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
xmlReader.Close();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void CreateConnections(XmlReader xmlReader, ref Dictionary<ulong, Node?> nodes)
|
|
||||||
{
|
|
||||||
while (xmlReader.ReadToFollowing("way"))
|
|
||||||
{
|
|
||||||
XmlReader wayReader = xmlReader.ReadSubtree();
|
|
||||||
HashSet<ulong> nodesInWay = new ();
|
|
||||||
Dictionary<string, string> tags = new ();
|
|
||||||
bool addNodes = false;
|
|
||||||
while (wayReader.Read())
|
|
||||||
{
|
|
||||||
if (wayReader.IsStartElement() && wayReader.Name.Equals("nd"))
|
|
||||||
{
|
|
||||||
nodesInWay.Add(Convert.ToUInt64(wayReader.GetAttribute("ref")));
|
|
||||||
}
|
|
||||||
else if (wayReader.IsStartElement() && wayReader.Name.Equals("tag"))
|
|
||||||
{
|
|
||||||
if (wayReader.GetAttribute("k")!.Equals("highway"))
|
|
||||||
{
|
|
||||||
addNodes = true;
|
|
||||||
}
|
|
||||||
tags.Add(wayReader.GetAttribute("k")!, value: wayReader.GetAttribute("v")!);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
wayReader.Close();
|
|
||||||
|
|
||||||
if (addNodes)
|
|
||||||
{
|
|
||||||
ConnectNodesOfWay(nodes, nodesInWay.ToArray(), tags);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
xmlReader.Close();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void ConnectNodesOfWay(Dictionary<ulong, Node?> nodes, ulong[] nodeIds,
|
|
||||||
Dictionary<string, string> tags)
|
|
||||||
{
|
|
||||||
string oneWayString = tags.ContainsKey("oneway") ? tags["oneway"] : "no";
|
|
||||||
bool oneWay = false;
|
|
||||||
bool forward = true;
|
|
||||||
switch (oneWayString)
|
|
||||||
{
|
|
||||||
case "yes":
|
|
||||||
oneWay = true;
|
|
||||||
break;
|
|
||||||
case "-1":
|
|
||||||
forward = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < nodeIds.Length - 1; i++)
|
|
||||||
{
|
|
||||||
if (oneWay)
|
|
||||||
{
|
|
||||||
if(nodes.ContainsKey(nodeIds[i]))
|
|
||||||
nodes[nodeIds[i]]!.AddConnection(new Connection(nodeIds[i + 1], nodes[nodeIds[i + 1]]!, tags));
|
|
||||||
if(nodes.ContainsKey(nodeIds[i + 1]))
|
|
||||||
nodes[nodeIds[i + 1]]!.AddConnection(new Connection(nodeIds[i], nodes[nodeIds[i]]!, tags));
|
|
||||||
}
|
|
||||||
else if (forward)
|
|
||||||
{
|
|
||||||
if(nodes.ContainsKey(nodeIds[i]))
|
|
||||||
nodes[nodeIds[i]]!.AddConnection(new Connection(nodeIds[i + 1], nodes[nodeIds[i + 1]]!, tags));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if(nodes.ContainsKey(nodeIds[i + 1]))
|
|
||||||
nodes[nodeIds[i + 1]]!.AddConnection(new Connection(nodeIds[i], nodes[nodeIds[i]]!, tags));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void WriteRegion(Region region, string outputFolderPath)
|
|
||||||
{
|
|
||||||
if (!Directory.Exists(outputFolderPath))
|
|
||||||
Directory.CreateDirectory(outputFolderPath);
|
|
||||||
string fileName = region.regionHash.ToString();
|
|
||||||
string fullPath = Path.Combine(outputFolderPath, fileName);
|
|
||||||
if (!File.Exists(fullPath))
|
|
||||||
File.Create(fullPath).Close();
|
|
||||||
FileStream fileStream = new FileStream(fullPath, FileMode.Append);
|
|
||||||
fileStream.Write(ByteConverter.GetBytes(region));
|
|
||||||
fileStream.Close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
38
Pathfinding/PathNode.cs
Normal file
38
Pathfinding/PathNode.cs
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
using System.Text.Json.Serialization;
|
||||||
|
using OSMDatastructure;
|
||||||
|
using OSMDatastructure.Graph;
|
||||||
|
|
||||||
|
namespace Pathfinding;
|
||||||
|
|
||||||
|
public class PathNode : OsmNode
|
||||||
|
{
|
||||||
|
[JsonInclude]public Dictionary<string, string> tags = new();
|
||||||
|
|
||||||
|
[JsonConstructor]
|
||||||
|
public PathNode(ulong nodeId, Coordinates coordinates, Dictionary<string, string> tags) : base(nodeId, coordinates)
|
||||||
|
{
|
||||||
|
this.tags = tags;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PathNode(ulong nodeId, float lat, float lon) : base(nodeId, lat, lon)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public PathNode(ulong nodeId, Coordinates coordinates) : base(nodeId, coordinates)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PathNode? FromOsmNode(OsmNode? node, HashSet<Tag>? tags)
|
||||||
|
{
|
||||||
|
if (node is null)
|
||||||
|
return null;
|
||||||
|
PathNode retNode = new(node.nodeId, node.coordinates);
|
||||||
|
if (tags != null)
|
||||||
|
foreach (Tag tag in tags)
|
||||||
|
{
|
||||||
|
retNode.tags.Add(tag.key.ToString(), tag.value.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
return retNode;
|
||||||
|
}
|
||||||
|
}
|
27
Pathfinding/PathResult.cs
Normal file
27
Pathfinding/PathResult.cs
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
using System.Text.Json;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Pathfinding;
|
||||||
|
|
||||||
|
public class PathResult
|
||||||
|
{
|
||||||
|
[JsonInclude]public double distance;
|
||||||
|
[JsonInclude]public double weight;
|
||||||
|
[JsonInclude]public TimeSpan calcTime;
|
||||||
|
[JsonInclude]public List<PathNode> pathNodes;
|
||||||
|
|
||||||
|
[JsonConstructor]
|
||||||
|
public PathResult(TimeSpan calcTime, List<PathNode> pathNodes, double distance, double weight)
|
||||||
|
{
|
||||||
|
this.calcTime = calcTime;
|
||||||
|
this.pathNodes = pathNodes;
|
||||||
|
this.distance = distance;
|
||||||
|
this.weight = weight;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PathResult PathresultFromFile(string filePath)
|
||||||
|
{
|
||||||
|
return JsonSerializer.Deserialize<PathResult>(new FileStream(filePath, FileMode.Open, FileAccess.Read,
|
||||||
|
FileShare.Read))!;
|
||||||
|
}
|
||||||
|
}
|
@ -1,104 +1,228 @@
|
|||||||
using OSMDatastructure;
|
using System.Text.Json;
|
||||||
|
using OSMDatastructure;
|
||||||
using OSMDatastructure.Graph;
|
using OSMDatastructure.Graph;
|
||||||
using OSMImporter;
|
using static OSMDatastructure.Tag;
|
||||||
|
|
||||||
namespace Pathfinding;
|
namespace Pathfinding;
|
||||||
|
|
||||||
public class Pathfinder
|
public class Pathfinder
|
||||||
{
|
{
|
||||||
/*
|
|
||||||
public static List<OsmNode> CustomAStar(string workingDir, Coordinates start, Coordinates goal, OsmWay.speedType vehicle)
|
public RegionManager regionManager;
|
||||||
|
public PathResult? pathResult;
|
||||||
|
public Dictionary<OsmNode, double>? gScore;
|
||||||
|
private Dictionary<OsmNode, OsmNode>? _cameFromDict;
|
||||||
|
private SpeedType _speedType;
|
||||||
|
private double roadPriorityFactor, turnAngle;
|
||||||
|
|
||||||
|
public Pathfinder(string workingDirectory, double roadPriorityFactor, double turnAngle)
|
||||||
{
|
{
|
||||||
RegionManager regionManager = new RegionManager(workingDir);
|
if (!Path.Exists(workingDirectory))
|
||||||
Region startRegion, goalRegion;
|
throw new DirectoryNotFoundException(workingDirectory);
|
||||||
try
|
regionManager = new(workingDirectory);
|
||||||
|
this.roadPriorityFactor = roadPriorityFactor;
|
||||||
|
this.turnAngle = turnAngle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Pathfinder(RegionManager regionManager, double roadPriorityFactor, double turnAngle)
|
||||||
|
{
|
||||||
|
this.regionManager = regionManager;
|
||||||
|
this.roadPriorityFactor = roadPriorityFactor;
|
||||||
|
this.turnAngle = turnAngle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Pathfinder AStar(Coordinates startCoordinates, Coordinates goalCoordinates, SpeedType vehicle, double extraTime)
|
||||||
|
{
|
||||||
|
DateTime startCalc = DateTime.Now;
|
||||||
|
_speedType = vehicle;
|
||||||
|
OsmNode? startNode = regionManager.ClosestNodeToCoordinates(startCoordinates, _speedType);
|
||||||
|
OsmNode? goalNode = regionManager.ClosestNodeToCoordinates(goalCoordinates, _speedType);
|
||||||
|
if (startNode is null || goalNode is null)
|
||||||
{
|
{
|
||||||
startRegion = regionManager.GetRegion(start);
|
pathResult = new(DateTime.Now - startCalc, new List<PathNode>(),0 ,0);
|
||||||
goalRegion = regionManager.GetRegion(goal);
|
return this;
|
||||||
}
|
|
||||||
catch (FileNotFoundException e)
|
|
||||||
{
|
|
||||||
throw new Exception(string.Format("No region at coordinates {0}", e.FileName), e);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
OsmNode? startNode = ClosestNodeToCoordinates(start, startRegion);
|
RPriorityQueue<OsmNode, double> openSetfScore = new();
|
||||||
OsmNode? goalNode = ClosestNodeToCoordinates(goal, goalRegion);
|
openSetfScore.Enqueue(startNode, 0);
|
||||||
if (startNode == null || goalNode == null)
|
gScore = new() { { startNode, 0 } };
|
||||||
return new List<OsmNode>();
|
_cameFromDict = new();
|
||||||
|
|
||||||
List<OsmNode> toVisit = new() { startNode };
|
bool found = false;
|
||||||
OsmNode closestNodeToGoal = toVisit.First();
|
|
||||||
closestNodeToGoal.currentPathWeight = 0;
|
|
||||||
closestNodeToGoal.currentPathLength = 0;
|
|
||||||
bool stop = false;
|
bool stop = false;
|
||||||
|
TimeSpan firstFound = TimeSpan.MaxValue;
|
||||||
|
double maxGscore = double.MaxValue;
|
||||||
|
|
||||||
while (toVisit.Count > 0 && !stop)
|
while (openSetfScore.Count > 0 && !stop)
|
||||||
{
|
{
|
||||||
//Console.WriteLine("toVisit-length: {0}", toVisit.Count);
|
OsmNode currentNode = openSetfScore.Dequeue()!;
|
||||||
closestNodeToGoal = toVisit.First();
|
if (currentNode.Equals(goalNode))
|
||||||
foreach (OsmNode node in toVisit)
|
|
||||||
{
|
{
|
||||||
if (node.directDistanceToGoal.Equals(double.MaxValue))
|
if (!found)
|
||||||
{
|
{
|
||||||
node.directDistanceToGoal = Utils.DistanceBetween(node, goalNode);
|
firstFound = DateTime.Now - startCalc;
|
||||||
}
|
found = true;
|
||||||
if (node.directDistanceToGoal < closestNodeToGoal.directDistanceToGoal)
|
Console.WriteLine($"First: {firstFound} Multiplied by {extraTime}: {firstFound.Multiply(extraTime)}");
|
||||||
{
|
|
||||||
closestNodeToGoal = node;
|
|
||||||
}
|
}
|
||||||
|
maxGscore = gScore[goalNode];
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (OsmWay edge in closestNodeToGoal.edges)
|
if (found && DateTime.Now - startCalc > firstFound.Multiply(extraTime))
|
||||||
|
stop = true;
|
||||||
|
|
||||||
|
foreach (OsmEdge edge in currentNode.edges)
|
||||||
{
|
{
|
||||||
OsmNode? neighbor = regionManager.GetNode(edge.neighborCoordinates);
|
OsmNode? neighbor = regionManager.GetNode(edge.neighborId, edge.neighborRegion);
|
||||||
if (neighbor != null)
|
if (neighbor is not null)
|
||||||
{
|
{
|
||||||
double newPotentialWeight =
|
double tentativeGScore = gScore[currentNode] + Weight(currentNode, neighbor, edge);
|
||||||
closestNodeToGoal.currentPathWeight + edge.GetWeight(closestNodeToGoal, vehicle);
|
gScore.TryAdd(neighbor, double.MaxValue);
|
||||||
if (neighbor.currentPathWeight > newPotentialWeight)
|
if ((!found || (found && tentativeGScore < maxGscore)) && tentativeGScore < gScore[neighbor])
|
||||||
{
|
{
|
||||||
neighbor.previousPathNode = closestNodeToGoal;
|
if(!_cameFromDict.TryAdd(neighbor, currentNode))
|
||||||
neighbor.currentPathWeight = newPotentialWeight;
|
_cameFromDict[neighbor] = currentNode;
|
||||||
neighbor.currentPathLength = closestNodeToGoal.currentPathLength + Utils.DistanceBetween(closestNodeToGoal, neighbor);
|
gScore[neighbor] = tentativeGScore;
|
||||||
|
double h = Heuristic(currentNode, neighbor, goalNode, edge);
|
||||||
if (neighbor.Equals(goalNode))
|
openSetfScore.Enqueue(neighbor, tentativeGScore + h);
|
||||||
stop = true;
|
|
||||||
else
|
|
||||||
toVisit.Add(neighbor);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
toVisit.Remove(closestNodeToGoal);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
List<OsmNode> path = new();
|
|
||||||
OsmNode? currentNode = goalNode;
|
|
||||||
while (currentNode != null && !currentNode.Equals(startNode))
|
|
||||||
{
|
|
||||||
path.Add(currentNode);
|
|
||||||
currentNode = currentNode.previousPathNode;
|
|
||||||
}
|
|
||||||
path.Add(startNode);
|
|
||||||
path.Reverse();
|
|
||||||
|
|
||||||
return path;
|
TimeSpan calcTime = DateTime.Now - startCalc;
|
||||||
|
if (!found)
|
||||||
|
{
|
||||||
|
pathResult = new(DateTime.Now - startCalc, new List<PathNode>(),0 ,0);
|
||||||
|
Console.Write("No path found.");
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pathResult = GetPath(goalNode, calcTime);
|
||||||
|
}
|
||||||
|
Console.WriteLine($"Path found. {calcTime} PathLength {pathResult.pathNodes.Count} VisitedNodes {gScore.Count} Distance {pathResult.distance} Duration {pathResult.weight}");
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static OsmNode? ClosestNodeToCoordinates(Coordinates coordinates, Region region)
|
private double Weight(OsmNode currentNode, OsmNode neighborNode, OsmEdge edge)
|
||||||
{
|
{
|
||||||
OsmNode? closest = null;
|
double distance = Utils.DistanceBetween(currentNode, neighborNode);
|
||||||
double distance = double.MaxValue;
|
double speed = regionManager.GetSpeedForEdge(currentNode, edge.wayId, _speedType);
|
||||||
foreach (OsmNode node in region.nodes)
|
|
||||||
|
double angle = 1;
|
||||||
|
if (_cameFromDict!.ContainsKey(currentNode))
|
||||||
{
|
{
|
||||||
double nodeDistance = Utils.DistanceBetween(node, coordinates);
|
OsmNode previousNode = _cameFromDict[currentNode];
|
||||||
if (nodeDistance < distance)
|
Vector v1 = new(currentNode, previousNode);
|
||||||
{
|
Vector v2 = new(currentNode, neighborNode);
|
||||||
closest = node;
|
double nodeAngle = v1.Angle(v2);
|
||||||
distance = nodeDistance;
|
if (nodeAngle < turnAngle)
|
||||||
}
|
angle = 0;
|
||||||
|
else
|
||||||
|
angle = nodeAngle / 180;
|
||||||
|
}
|
||||||
|
double prio = regionManager.GetPriorityForVehicle(_speedType,edge, currentNode) * roadPriorityFactor;
|
||||||
|
|
||||||
|
return distance / (speed * angle + prio + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private double Heuristic(OsmNode currentNode, OsmNode neighborNode, OsmNode goalNode, OsmEdge edge)
|
||||||
|
{
|
||||||
|
if (neighborNode.Equals(goalNode)) return 0;
|
||||||
|
double priority = regionManager.GetPriorityForVehicle(_speedType, edge, currentNode);
|
||||||
|
if (priority == 0)
|
||||||
|
return double.MaxValue;
|
||||||
|
|
||||||
|
double distance = Utils.DistanceBetween(neighborNode, goalNode);
|
||||||
|
|
||||||
|
double speed = regionManager.GetSpeedForEdge(currentNode, edge.wayId, _speedType);
|
||||||
|
|
||||||
|
double roadPriority = priority * roadPriorityFactor;
|
||||||
|
|
||||||
|
double angle = 0;
|
||||||
|
if (_cameFromDict!.ContainsKey(currentNode))
|
||||||
|
{
|
||||||
|
OsmNode previousNode = _cameFromDict[currentNode];
|
||||||
|
Vector v1 = new(currentNode, previousNode);
|
||||||
|
Vector v2 = new(currentNode, neighborNode);
|
||||||
|
double nodeAngle = v1.Angle(v2);
|
||||||
|
if (nodeAngle < turnAngle)
|
||||||
|
angle = 0;
|
||||||
|
else
|
||||||
|
angle = nodeAngle / 180;
|
||||||
}
|
}
|
||||||
|
|
||||||
return closest;
|
return distance / (speed * angle + roadPriority + 1);
|
||||||
}*/
|
}
|
||||||
|
|
||||||
|
public void SaveResult(string path)
|
||||||
|
{
|
||||||
|
if(File.Exists(path))
|
||||||
|
File.Delete(path);
|
||||||
|
FileStream fs = new (path, FileMode.CreateNew);
|
||||||
|
JsonSerializer.Serialize(fs, pathResult, JsonSerializerOptions.Default);
|
||||||
|
fs.Dispose();
|
||||||
|
Console.WriteLine($"Saved result to {path}");
|
||||||
|
}
|
||||||
|
|
||||||
|
private PathResult GetPath(OsmNode goalNode, TimeSpan calcFinished)
|
||||||
|
{
|
||||||
|
List<PathNode> path = new();
|
||||||
|
OsmNode currentNode = goalNode;
|
||||||
|
double retDistance = 0;
|
||||||
|
double weight = 0;
|
||||||
|
while (_cameFromDict!.ContainsKey(_cameFromDict[currentNode]))
|
||||||
|
{
|
||||||
|
OsmEdge? currentEdge = _cameFromDict[currentNode].edges.FirstOrDefault(edge => edge.neighborId == currentNode.nodeId);
|
||||||
|
HashSet<Tag>? tags =
|
||||||
|
regionManager.GetRegion(currentNode.coordinates)!.tagManager.GetTagsForWayId(currentEdge!.wayId);
|
||||||
|
PathNode? newNode = PathNode.FromOsmNode(currentNode, tags);
|
||||||
|
if(newNode is not null)
|
||||||
|
path.Add(newNode);
|
||||||
|
|
||||||
|
double distance = Utils.DistanceBetween(currentNode, _cameFromDict[currentNode]);
|
||||||
|
retDistance += distance;
|
||||||
|
weight += regionManager.GetSpeedForEdge(_cameFromDict[currentNode], currentEdge.wayId, _speedType);
|
||||||
|
|
||||||
|
currentNode = _cameFromDict[currentNode];
|
||||||
|
}
|
||||||
|
|
||||||
|
path.Reverse();
|
||||||
|
|
||||||
|
return new PathResult(calcFinished, path, retDistance, retDistance / (weight / path.Count));
|
||||||
|
}
|
||||||
|
|
||||||
|
private class Vector
|
||||||
|
{
|
||||||
|
public readonly float x, y;
|
||||||
|
|
||||||
|
public Vector(float x, float y)
|
||||||
|
{
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vector(OsmNode n1, OsmNode n2)
|
||||||
|
{
|
||||||
|
this.x = n1.coordinates.longitude - n2.coordinates.longitude;
|
||||||
|
this.y = n1.coordinates.latitude - n2.coordinates.latitude;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double Angle(Vector v2)
|
||||||
|
{
|
||||||
|
return Angle(this, v2);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static double Angle(Vector v1, Vector v2)
|
||||||
|
{
|
||||||
|
double dotProd = v1.x * v2.x + v1.y * v2.y;
|
||||||
|
double v1L = Math.Sqrt(v1.x * v1.x + v1.y * v1.y);
|
||||||
|
double v2L = Math.Sqrt(v2.x * v2.x + v2.y * v2.y);
|
||||||
|
double ang = Math.Acos(dotProd / (v1L * v2L));
|
||||||
|
if (ang.Equals(double.NaN))
|
||||||
|
return 0;
|
||||||
|
double angle = Utils.RadiansToDegrees(ang);
|
||||||
|
return angle;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
@ -10,4 +10,8 @@
|
|||||||
<ProjectReference Include="..\OSMDatastructure\OSMDatastructure.csproj" />
|
<ProjectReference Include="..\OSMDatastructure\OSMDatastructure.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="System.Runtime.Serialization.Json" Version="4.3.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
47
Pathfinding/RPriorityQueue.cs
Normal file
47
Pathfinding/RPriorityQueue.cs
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
namespace Pathfinding;
|
||||||
|
|
||||||
|
public class RPriorityQueue<TKey, TPriority> where TKey : notnull
|
||||||
|
{
|
||||||
|
public Dictionary<TKey, TPriority> queue;
|
||||||
|
public int Count => queue.Count;
|
||||||
|
|
||||||
|
public RPriorityQueue()
|
||||||
|
{
|
||||||
|
queue = new();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Enqueue(TKey key, TPriority priority)
|
||||||
|
{
|
||||||
|
if (!queue.TryAdd(key, priority))
|
||||||
|
queue[key] = priority;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TKey Dequeue()
|
||||||
|
{
|
||||||
|
TKey retKey = queue.MinBy(item => item.Value).Key;
|
||||||
|
queue.Remove(retKey);
|
||||||
|
return retKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Remove(IEnumerable<TKey> elements)
|
||||||
|
{
|
||||||
|
int before = Count;
|
||||||
|
queue = queue.Where(queueitem => !elements.Contains(queueitem.Key))
|
||||||
|
.ToDictionary(item => item.Key, item => item.Value);
|
||||||
|
return before - Count;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int RemoveExcept(IEnumerable<TKey> exceptKeys)
|
||||||
|
{
|
||||||
|
int before = Count;
|
||||||
|
queue = queue.IntersectBy(exceptKeys, item => item.Key).ToDictionary(item => item.Key, item => item.Value);
|
||||||
|
return before - Count;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Clear()
|
||||||
|
{
|
||||||
|
int before = Count;
|
||||||
|
queue.Clear();
|
||||||
|
return before;
|
||||||
|
}
|
||||||
|
}
|
@ -1,7 +1,10 @@
|
|||||||
|
using System.Text.Json;
|
||||||
using OSMDatastructure;
|
using OSMDatastructure;
|
||||||
using OSMDatastructure.Graph;
|
using OSMDatastructure.Graph;
|
||||||
|
using SpeedType = OSMDatastructure.Tag.SpeedType;
|
||||||
|
using WayType = OSMDatastructure.Tag.WayType;
|
||||||
|
|
||||||
namespace OSMImporter
|
namespace Pathfinding
|
||||||
{
|
{
|
||||||
public class RegionManager
|
public class RegionManager
|
||||||
{
|
{
|
||||||
@ -12,45 +15,186 @@ namespace OSMImporter
|
|||||||
{
|
{
|
||||||
this.workingDirectory = workingDirectory;
|
this.workingDirectory = workingDirectory;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
public Region? GetRegion(Coordinates coordinates)
|
||||||
/// Checks wether the Region is already loaded and returns the Region, or tries to load the Region from file in workingDirectory
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="coordinates">Coordinates of the Region (or within the Region) to load</param>
|
|
||||||
/// <returns>The Region at the specified Coordinates containing Nodes and Connections</returns>
|
|
||||||
/// <exception cref="FileNotFoundException">If the Regionfile can not be found.</exception>
|
|
||||||
public Region GetRegion(Coordinates coordinates)
|
|
||||||
{
|
{
|
||||||
if(_regions.ContainsKey(Coordinates.GetRegionHashCode(coordinates)))
|
return GetRegion(Coordinates.GetRegionHashCode(coordinates));
|
||||||
return _regions[Coordinates.GetRegionHashCode(coordinates)];
|
}
|
||||||
else
|
|
||||||
|
public Region? GetRegion(ulong id)
|
||||||
|
{
|
||||||
|
if (!_regions.ContainsKey(id))
|
||||||
{
|
{
|
||||||
Region loadedRegion = LoadRegion(coordinates);
|
Region? loadedRegion = RegionFromId(id);
|
||||||
_regions.Add(loadedRegion.regionHash, value: loadedRegion);
|
if (loadedRegion is not null)
|
||||||
return loadedRegion;
|
_regions.TryAdd(loadedRegion.regionHash, loadedRegion);
|
||||||
|
return _regions[id]; //return from _regions instead of loadedRegion for multithreading/pointers
|
||||||
}
|
}
|
||||||
|
return _regions[id];
|
||||||
}
|
}
|
||||||
|
|
||||||
public Region[] GetAllRegions()
|
public Region[] GetAllRegions()
|
||||||
{
|
{
|
||||||
return this._regions.Values.ToArray();
|
return _regions.Values.ToArray();
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="coordinates">Coordinates of the Region (or within the Region) to load</param>
|
|
||||||
/// <returns>The Region at the specified Coordinates containing Nodes and Connections</returns>
|
|
||||||
/// <exception cref="FileNotFoundException">If the Regionfile can not be found.</exception>
|
|
||||||
private Region LoadRegion(Coordinates coordinates)
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public OsmNode? GetNode(Coordinates coordinates)
|
private static Region? RegionFromFile(string filePath)
|
||||||
{
|
{
|
||||||
Region regionWithNode = GetRegion(coordinates);
|
if (!File.Exists(filePath))
|
||||||
return regionWithNode.GetNodeWithCoordinates(coordinates);
|
{
|
||||||
|
//throw new FileNotFoundException(filePath);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
FileStream regionFile = new (filePath, FileMode.Open, FileAccess.Read, FileShare.Read, (int)new FileInfo(filePath).Length, FileOptions.SequentialScan);
|
||||||
|
Region retRegion = JsonSerializer.Deserialize<Region>(regionFile, Region.serializerOptions)!;
|
||||||
|
regionFile.Dispose();
|
||||||
|
return retRegion;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Region? RegionFromId(ulong regionId)
|
||||||
|
{
|
||||||
|
string filePath = Path.Join(workingDirectory, $"{regionId}.region");
|
||||||
|
return RegionFromFile(filePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
public OsmNode? GetNode(ulong nodeId, ulong regionId)
|
||||||
|
{
|
||||||
|
Region? r = GetRegion(regionId);
|
||||||
|
return r?.GetNode(nodeId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TestValidConnectionForType(OsmNode node1, OsmNode node2, SpeedType type)
|
||||||
|
{
|
||||||
|
foreach (OsmEdge edge in node1.edges.Where(edge => edge.neighborId.Equals(node2.nodeId)))
|
||||||
|
{
|
||||||
|
return TestValidConnectionForType(node1, edge, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TestValidConnectionForType(OsmNode node1, OsmEdge edge, SpeedType type)
|
||||||
|
{
|
||||||
|
if (type == SpeedType.any)
|
||||||
|
return true;
|
||||||
|
byte speed = GetSpeedForEdge(node1, edge.wayId, type);
|
||||||
|
return (speed is not 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public OsmNode? ClosestNodeToCoordinates(Coordinates coordinates, SpeedType vehicle)
|
||||||
|
{
|
||||||
|
OsmNode? closest = null;
|
||||||
|
double distance = double.MaxValue;
|
||||||
|
Region? region = GetRegion(coordinates);
|
||||||
|
if (region is null)
|
||||||
|
return null;
|
||||||
|
foreach (OsmNode node in region.nodes)
|
||||||
|
{
|
||||||
|
bool hasConnectionUsingVehicle = true;
|
||||||
|
if (vehicle is not SpeedType.any)
|
||||||
|
{
|
||||||
|
hasConnectionUsingVehicle = false;
|
||||||
|
foreach (OsmEdge edge in node.edges)
|
||||||
|
{
|
||||||
|
if (TestValidConnectionForType(node, edge, vehicle))
|
||||||
|
hasConnectionUsingVehicle = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
double nodeDistance = Utils.DistanceBetween(node, coordinates);
|
||||||
|
if (hasConnectionUsingVehicle && nodeDistance < distance)
|
||||||
|
{
|
||||||
|
closest = node;
|
||||||
|
distance = nodeDistance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return closest;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte GetSpeedForEdge(OsmNode node1, ulong wayId, SpeedType vehicle)
|
||||||
|
{
|
||||||
|
TagManager tags = GetRegion(node1.coordinates)!.tagManager;
|
||||||
|
WayType wayType = (WayType)tags.GetTag(wayId, Tag.TagType.highway)!;
|
||||||
|
byte speed = 0;
|
||||||
|
switch (vehicle)
|
||||||
|
{
|
||||||
|
case SpeedType.pedestrian:
|
||||||
|
speed = Tag.defaultSpeedPedestrian[wayType];
|
||||||
|
return speed;
|
||||||
|
case SpeedType.car:
|
||||||
|
byte? maxSpeed = (byte?)tags.GetTag(wayId, Tag.TagType.maxspeed);
|
||||||
|
speed = Tag.defaultSpeedCar[wayType];
|
||||||
|
return maxSpeed < speed ? (byte)maxSpeed : speed;
|
||||||
|
case SpeedType.any:
|
||||||
|
return 1;
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public double GetPriorityForVehicle(SpeedType speedType, OsmEdge edge, OsmNode node)
|
||||||
|
{
|
||||||
|
if (speedType == SpeedType.any)
|
||||||
|
return 1;
|
||||||
|
Region region = GetRegion(node.coordinates)!;
|
||||||
|
WayType? wayType = (WayType?)region.tagManager.GetTag(edge.wayId, Tag.TagType.highway);
|
||||||
|
if(wayType is null)
|
||||||
|
return 0;
|
||||||
|
if (speedType == SpeedType.car)
|
||||||
|
{
|
||||||
|
switch (wayType)
|
||||||
|
{
|
||||||
|
case WayType.motorway:
|
||||||
|
case WayType.motorway_link:
|
||||||
|
case WayType.motorroad:
|
||||||
|
return 20;
|
||||||
|
case WayType.trunk:
|
||||||
|
case WayType.trunk_link:
|
||||||
|
case WayType.primary:
|
||||||
|
case WayType.primary_link:
|
||||||
|
return 10;
|
||||||
|
case WayType.secondary:
|
||||||
|
case WayType.secondary_link:
|
||||||
|
case WayType.tertiary:
|
||||||
|
case WayType.tertiary_link:
|
||||||
|
return 6;
|
||||||
|
case WayType.unclassified:
|
||||||
|
case WayType.residential:
|
||||||
|
case WayType.road:
|
||||||
|
case WayType.living_street:
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (speedType == SpeedType.pedestrian)
|
||||||
|
{
|
||||||
|
switch (wayType)
|
||||||
|
{
|
||||||
|
case WayType.pedestrian:
|
||||||
|
case WayType.corridor:
|
||||||
|
case WayType.footway:
|
||||||
|
case WayType.path:
|
||||||
|
case WayType.steps:
|
||||||
|
case WayType.residential:
|
||||||
|
case WayType.living_street:
|
||||||
|
return 10;
|
||||||
|
case WayType.service:
|
||||||
|
case WayType.cycleway:
|
||||||
|
case WayType.bridleway:
|
||||||
|
case WayType.road:
|
||||||
|
case WayType.track:
|
||||||
|
case WayType.unclassified:
|
||||||
|
return 5;
|
||||||
|
case WayType.tertiary:
|
||||||
|
case WayType.tertiary_link:
|
||||||
|
case WayType.escape:
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
33
RenderPath/Bounds.cs
Normal file
33
RenderPath/Bounds.cs
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
using System.Text.Json.Serialization;
|
||||||
|
using OSMDatastructure.Graph;
|
||||||
|
|
||||||
|
namespace RenderPath;
|
||||||
|
|
||||||
|
public class Bounds
|
||||||
|
{
|
||||||
|
[JsonInclude]public float minLat, maxLat, minLon, maxLon;
|
||||||
|
|
||||||
|
[JsonConstructor]
|
||||||
|
public Bounds(float minLat, float minLon, float maxLat, float maxLon)
|
||||||
|
{
|
||||||
|
this.minLon = minLon;
|
||||||
|
this.maxLat = maxLat;
|
||||||
|
this.maxLon = maxLon;
|
||||||
|
this.minLat = minLat;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Bounds FromCoords(float lat1, float lon1, float lat2, float lon2)
|
||||||
|
{
|
||||||
|
float minLat = lat1 < lat2 ? lat1 : lat2;
|
||||||
|
float minLon = lon1 < lon2 ? lon1 : lon2;
|
||||||
|
float maxLat = lat1 > lat2 ? lat1 : lat2;
|
||||||
|
float maxLon = lon1 > lon2 ? lon1 : lon2;
|
||||||
|
|
||||||
|
return new Bounds(minLat, minLon, maxLat, maxLon);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Bounds FromCoords(Coordinates c1, Coordinates c2)
|
||||||
|
{
|
||||||
|
return FromCoords(c1.latitude, c1.longitude, c2.latitude, c2.longitude);
|
||||||
|
}
|
||||||
|
}
|
44
RenderPath/PNGRenderer.cs
Normal file
44
RenderPath/PNGRenderer.cs
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
using System.Drawing;
|
||||||
|
using System.Drawing.Imaging;
|
||||||
|
|
||||||
|
namespace RenderPath;
|
||||||
|
|
||||||
|
#pragma warning disable CA1416
|
||||||
|
public class PNGRenderer : Renderer
|
||||||
|
{
|
||||||
|
private readonly Image _image;
|
||||||
|
private readonly Graphics _graphics;
|
||||||
|
|
||||||
|
public PNGRenderer(int width, int height)
|
||||||
|
{
|
||||||
|
_image = new Bitmap(width, height, PixelFormat.Format32bppPArgb);
|
||||||
|
_graphics = Graphics.FromImage(_image);
|
||||||
|
_graphics.Clear(Color.White);
|
||||||
|
}
|
||||||
|
|
||||||
|
public PNGRenderer(Image renderOver)
|
||||||
|
{
|
||||||
|
_image = renderOver;
|
||||||
|
_graphics = Graphics.FromImage(renderOver);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void DrawLine(float x1, float y1, float x2, float y2, int width, Color color)
|
||||||
|
{
|
||||||
|
Pen p = new Pen(color, width);
|
||||||
|
_graphics.DrawLine(p, x1, y1, x2, y2);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void DrawDot(float x, float y, int radius, Color color)
|
||||||
|
{
|
||||||
|
Brush b = new SolidBrush(color);
|
||||||
|
x -= radius / 2f;
|
||||||
|
y -= radius / 2f;
|
||||||
|
_graphics.FillEllipse(b, x, y, radius, radius);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Save(string path)
|
||||||
|
{
|
||||||
|
_image.Save($"{path}.png", ImageFormat.Png);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#pragma warning restore CA1416
|
@ -4,11 +4,16 @@
|
|||||||
<TargetFramework>net7.0</TargetFramework>
|
<TargetFramework>net7.0</TargetFramework>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<RootNamespace>OSMServer</RootNamespace>
|
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="System.Drawing.Common" Version="7.0.0" />
|
||||||
|
<PackageReference Include="System.Runtime.Serialization.Json" Version="4.3.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\OSMDatastructure\OSMDatastructure.csproj" />
|
<ProjectReference Include="..\OSMDatastructure\OSMDatastructure.csproj" />
|
||||||
|
<ProjectReference Include="..\Pathfinding\Pathfinding.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
194
RenderPath/Renderer.cs
Normal file
194
RenderPath/Renderer.cs
Normal file
@ -0,0 +1,194 @@
|
|||||||
|
using System.Drawing;
|
||||||
|
using OSMDatastructure;
|
||||||
|
using OSMDatastructure.Graph;
|
||||||
|
using Pathfinding;
|
||||||
|
|
||||||
|
namespace RenderPath;
|
||||||
|
|
||||||
|
public abstract class Renderer
|
||||||
|
{
|
||||||
|
private const int ImageMaxSize = 20000;
|
||||||
|
private const int PenThickness = 2;
|
||||||
|
private static readonly Color RouteColor = Color.Red;
|
||||||
|
private static readonly Color WeightStartColor = Color.FromArgb(127, 0, 100, 255);
|
||||||
|
private static readonly Color WeightEndColor = Color.FromArgb(255, 0, 255, 0);
|
||||||
|
private static readonly Color RoadPrioStart = Color.FromArgb(200, 100, 100, 100);
|
||||||
|
private static readonly Color RoadPrioEnd = Color.FromArgb(255, 255, 180, 0);
|
||||||
|
|
||||||
|
public Bounds? bounds;
|
||||||
|
|
||||||
|
public enum RenderType { png, svg}
|
||||||
|
|
||||||
|
public abstract void DrawLine(float x1, float y1, float x2, float y2, int width, Color color);
|
||||||
|
public abstract void DrawDot(float x, float y, int r, Color color);
|
||||||
|
public abstract void Save(string path);
|
||||||
|
|
||||||
|
public static Renderer DrawArea(RegionManager rm, RenderType renderType)
|
||||||
|
{
|
||||||
|
HashSet<OsmNode> nodes = new();
|
||||||
|
foreach (OSMDatastructure.Region r in rm.GetAllRegions())
|
||||||
|
nodes = nodes.Concat(r.nodes).ToHashSet();
|
||||||
|
|
||||||
|
float minLat = nodes.Min(node => node.coordinates.latitude);
|
||||||
|
float minLon = nodes.Min(node => node.coordinates.longitude);
|
||||||
|
float maxLat = nodes.Max(node => node.coordinates.latitude);
|
||||||
|
float maxLon = nodes.Max(node => node.coordinates.longitude);
|
||||||
|
|
||||||
|
float latDiff = maxLat - minLat;
|
||||||
|
float lonDiff = maxLon - minLon;
|
||||||
|
|
||||||
|
float scaleFactor = latDiff > lonDiff ? ImageMaxSize / latDiff : ImageMaxSize / lonDiff;
|
||||||
|
|
||||||
|
int pixelsX = (int)(lonDiff * scaleFactor);
|
||||||
|
int pixelsY = (int)(latDiff * scaleFactor);
|
||||||
|
|
||||||
|
Renderer renderer;
|
||||||
|
switch (renderType)
|
||||||
|
{
|
||||||
|
case RenderType.svg:
|
||||||
|
renderer = new SVGRenderer(pixelsX, pixelsY);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
renderer = new PNGRenderer(pixelsX, pixelsY);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (OsmNode node in nodes)
|
||||||
|
{
|
||||||
|
foreach (OsmEdge edge in node.edges)
|
||||||
|
{
|
||||||
|
double priority = rm.GetPriorityForVehicle(Tag.SpeedType.car, edge, node) / 20;
|
||||||
|
Coordinates c1 = node.coordinates;
|
||||||
|
OsmNode nNode = rm.GetNode(edge.neighborId, edge.neighborRegion)!;
|
||||||
|
Coordinates c2 = nNode.coordinates;
|
||||||
|
|
||||||
|
float x1 = (c1.longitude - minLon) * scaleFactor;
|
||||||
|
float y1 = (maxLat - c1.latitude) * scaleFactor;
|
||||||
|
float x2 = (c2.longitude - minLon) * scaleFactor;
|
||||||
|
float y2 = (maxLat - c2.latitude) * scaleFactor;
|
||||||
|
|
||||||
|
renderer.DrawLine(x1, y1, x2, y2, PenThickness, ColorInterp(RoadPrioStart, RoadPrioEnd, priority));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
renderer.bounds = new Bounds(minLat,minLon,maxLat,maxLon);
|
||||||
|
return renderer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Renderer DrawPath(PathResult pathResult, RenderType renderType, Renderer? drawOver)
|
||||||
|
{
|
||||||
|
List<Coordinates> coordinates = new();
|
||||||
|
foreach(PathNode node in pathResult.pathNodes)
|
||||||
|
coordinates.Add(node.coordinates);
|
||||||
|
|
||||||
|
float minLat = drawOver?.bounds!.minLat ?? coordinates.Min(coords => coords.latitude);
|
||||||
|
float minLon = drawOver?.bounds!.minLon ?? coordinates.Min(coords => coords.longitude);
|
||||||
|
float maxLat = drawOver?.bounds!.maxLat ?? coordinates.Max(coords => coords.latitude);
|
||||||
|
float maxLon = drawOver?.bounds!.maxLon ?? coordinates.Max(coords => coords.longitude);
|
||||||
|
|
||||||
|
float latDiff = maxLat - minLat;
|
||||||
|
float lonDiff = maxLon - minLon;
|
||||||
|
|
||||||
|
float scaleFactor = latDiff > lonDiff ? ImageMaxSize / latDiff : ImageMaxSize / lonDiff;
|
||||||
|
|
||||||
|
int pixelsX = (int)(lonDiff * scaleFactor);
|
||||||
|
int pixelsY = (int)(latDiff * scaleFactor);
|
||||||
|
|
||||||
|
Renderer renderer;
|
||||||
|
if(drawOver is null)
|
||||||
|
switch (renderType)
|
||||||
|
{
|
||||||
|
case RenderType.svg:
|
||||||
|
renderer = new SVGRenderer(pixelsX, pixelsY);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
renderer = new PNGRenderer(pixelsX, pixelsY);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
renderer = drawOver;
|
||||||
|
|
||||||
|
for (int i = 0; i < coordinates.Count - 1; i++)
|
||||||
|
{
|
||||||
|
Coordinates c1 = coordinates[i];
|
||||||
|
Coordinates c2 = coordinates[i + 1];
|
||||||
|
float x1 = (c1.longitude - minLon) * scaleFactor;
|
||||||
|
float y1 = (maxLat - c1.latitude) * scaleFactor;
|
||||||
|
float x2 = (c2.longitude - minLon) * scaleFactor;
|
||||||
|
float y2 = (maxLat - c2.latitude) * scaleFactor;
|
||||||
|
|
||||||
|
renderer.DrawLine(x1, y1, x2, y2, PenThickness, RouteColor);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderer.bounds = new Bounds(minLat, minLon, maxLat, maxLon);
|
||||||
|
return renderer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Renderer DrawGScores(Dictionary<OsmNode, double> gScore, RenderType renderType, Renderer? drawOver)
|
||||||
|
{
|
||||||
|
|
||||||
|
float minLat = drawOver?.bounds!.minLat ?? gScore.Min(kv => kv.Key.coordinates.latitude);
|
||||||
|
float minLon = drawOver?.bounds!.minLon ?? gScore.Min(kv => kv.Key.coordinates.longitude);
|
||||||
|
float maxLat = drawOver?.bounds!.maxLat ?? gScore.Max(kv => kv.Key.coordinates.latitude);
|
||||||
|
float maxLon = drawOver?.bounds!.maxLon ?? gScore.Max(kv => kv.Key.coordinates.longitude);
|
||||||
|
|
||||||
|
double minWeight = gScore.Min(kv => kv.Value);
|
||||||
|
double maxWeight = gScore.Max(kv => kv.Value);
|
||||||
|
|
||||||
|
float latDiff = maxLat - minLat;
|
||||||
|
float lonDiff = maxLon - minLon;
|
||||||
|
|
||||||
|
float scaleFactor = latDiff > lonDiff ? ImageMaxSize / latDiff : ImageMaxSize / lonDiff;
|
||||||
|
|
||||||
|
int pixelsX = (int)(lonDiff * scaleFactor);
|
||||||
|
int pixelsY = (int)(latDiff * scaleFactor);
|
||||||
|
|
||||||
|
Renderer renderer;
|
||||||
|
if(drawOver is null)
|
||||||
|
switch (renderType)
|
||||||
|
{
|
||||||
|
case RenderType.svg:
|
||||||
|
renderer = new SVGRenderer(pixelsX, pixelsY);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
renderer = new PNGRenderer(pixelsX, pixelsY);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
renderer = drawOver;
|
||||||
|
|
||||||
|
foreach (KeyValuePair<OsmNode, double> kv in gScore)
|
||||||
|
{
|
||||||
|
double percentage = (kv.Value - minWeight) / (maxWeight - minWeight);
|
||||||
|
float x = (kv.Key.coordinates.longitude - minLon) * scaleFactor;
|
||||||
|
float y = (maxLat - kv.Key.coordinates.latitude) * scaleFactor;
|
||||||
|
|
||||||
|
renderer.DrawDot(x, y, PenThickness, ColorInterp(WeightStartColor, WeightEndColor, percentage));
|
||||||
|
}
|
||||||
|
|
||||||
|
renderer.bounds = new Bounds(minLat,minLon,maxLat,maxLon);
|
||||||
|
return renderer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Renderer DrawPathfinder(Pathfinder pathfinder, RenderType renderType)
|
||||||
|
{
|
||||||
|
Console.WriteLine("Rendering loaded Regions");
|
||||||
|
Renderer areaRender = DrawArea(pathfinder.regionManager, renderType);
|
||||||
|
Console.WriteLine("Rendering gScores (Weights)");
|
||||||
|
Renderer areaGScoreRender = DrawGScores(pathfinder.gScore!, renderType, areaRender);
|
||||||
|
Console.WriteLine("Rendering path");
|
||||||
|
Renderer areaGScorePathRender = DrawPath(pathfinder.pathResult!, renderType, areaGScoreRender);
|
||||||
|
|
||||||
|
return areaGScorePathRender;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* https://stackoverflow.com/questions/55601338/get-a-color-value-within-a-gradient-based-on-a-value
|
||||||
|
*/
|
||||||
|
private static int LinearInterp(int start, int end, double percentage) => start + (int)Math.Round(percentage * (end - start));
|
||||||
|
private static Color ColorInterp(Color start, Color end, double percentage) =>
|
||||||
|
Color.FromArgb(LinearInterp(start.A, end.A, percentage),
|
||||||
|
LinearInterp(start.R, end.R, percentage),
|
||||||
|
LinearInterp(start.G, end.G, percentage),
|
||||||
|
LinearInterp(start.B, end.B, percentage));
|
||||||
|
}
|
92
RenderPath/SVGRenderer.cs
Normal file
92
RenderPath/SVGRenderer.cs
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
using System.Drawing;
|
||||||
|
|
||||||
|
namespace RenderPath;
|
||||||
|
using System.Xml;
|
||||||
|
|
||||||
|
public class SVGRenderer : Renderer
|
||||||
|
{
|
||||||
|
private readonly XmlDocument _image;
|
||||||
|
private XmlElement _document;
|
||||||
|
|
||||||
|
public SVGRenderer(int width, int height)
|
||||||
|
{
|
||||||
|
_image = new XmlDocument();
|
||||||
|
CreateTree(width, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SVGRenderer(XmlDocument renderOver)
|
||||||
|
{
|
||||||
|
_image = renderOver;
|
||||||
|
_document = _image.GetElementById("svg")!;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CreateTree(int width, int height)
|
||||||
|
{
|
||||||
|
XmlDeclaration xmlDeclaration = _image.CreateXmlDeclaration( "1.0", "UTF-8", null );
|
||||||
|
_image.InsertBefore(xmlDeclaration, _image.DocumentElement);
|
||||||
|
XmlElement pElement = _image.CreateElement("svg");
|
||||||
|
XmlAttribute xmlns = _image.CreateAttribute("xmlns");
|
||||||
|
xmlns.Value = "http://www.w3.org/2000/svg";
|
||||||
|
pElement.Attributes.Append(xmlns);
|
||||||
|
XmlAttribute aWidth = _image.CreateAttribute("width");
|
||||||
|
aWidth.Value = width.ToString();
|
||||||
|
pElement.Attributes.Append(aWidth);
|
||||||
|
XmlAttribute aHeight = _image.CreateAttribute("height");
|
||||||
|
aHeight.Value = height.ToString();
|
||||||
|
pElement.Attributes.Append(aHeight);
|
||||||
|
_image.AppendChild(pElement);
|
||||||
|
_document = pElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void DrawLine(float x1, float y1, float x2, float y2, int width, Color color)
|
||||||
|
{
|
||||||
|
XmlElement newLine = _image.CreateElement("line");
|
||||||
|
XmlAttribute aX1 = _image.CreateAttribute("x1");
|
||||||
|
aX1.Value = Math.Floor(x1).ToString("0");
|
||||||
|
newLine.Attributes.Append(aX1);
|
||||||
|
XmlAttribute aY1 = _image.CreateAttribute("y1");
|
||||||
|
aY1.Value = Math.Floor(y1).ToString("0");
|
||||||
|
newLine.Attributes.Append(aY1);
|
||||||
|
XmlAttribute aX2 = _image.CreateAttribute("x2");
|
||||||
|
aX2.Value = Math.Floor(x2).ToString("0");
|
||||||
|
newLine.Attributes.Append(aX2);
|
||||||
|
XmlAttribute aY2 = _image.CreateAttribute("y2");
|
||||||
|
aY2.Value = Math.Floor(y2).ToString("0");
|
||||||
|
newLine.Attributes.Append(aY2);
|
||||||
|
XmlAttribute stroke = _image.CreateAttribute("stroke-width");
|
||||||
|
stroke.Value = width.ToString();
|
||||||
|
newLine.Attributes.Append(stroke);
|
||||||
|
XmlAttribute aColor = _image.CreateAttribute("stroke");
|
||||||
|
aColor.Value = HexFromColor(color);
|
||||||
|
newLine.Attributes.Append(aColor);
|
||||||
|
_document.AppendChild(newLine);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void DrawDot(float x, float y, int radius, Color color)
|
||||||
|
{
|
||||||
|
XmlElement newCircle = _image.CreateElement("circle");
|
||||||
|
XmlAttribute aX = _image.CreateAttribute("cx");
|
||||||
|
aX.Value = Math.Floor(x).ToString("0");
|
||||||
|
newCircle.Attributes.Append(aX);
|
||||||
|
XmlAttribute aY = _image.CreateAttribute("cy");
|
||||||
|
aY.Value = Math.Floor(y).ToString("0");
|
||||||
|
newCircle.Attributes.Append(aY);
|
||||||
|
XmlAttribute aR = _image.CreateAttribute("r");
|
||||||
|
aR.Value = radius.ToString();
|
||||||
|
newCircle.Attributes.Append(aR);
|
||||||
|
XmlAttribute fill = _image.CreateAttribute("fill");
|
||||||
|
fill.Value = HexFromColor(color);
|
||||||
|
newCircle.Attributes.Append(fill);
|
||||||
|
_document.AppendChild(newCircle);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Save(string path)
|
||||||
|
{
|
||||||
|
_image.Save($"{path}.svg");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string HexFromColor(Color color)
|
||||||
|
{
|
||||||
|
return $"#{color.R:X2}{color.G:X2}{color.B:X2}";
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,3 @@
|
|||||||
using System.Globalization;
|
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
namespace Server;
|
namespace Server;
|
||||||
@ -28,7 +27,7 @@ public class ConsoleWriter : TextWriter
|
|||||||
public override void WriteLine(string? text)
|
public override void WriteLine(string? text)
|
||||||
{
|
{
|
||||||
DateTime now = DateTime.Now;
|
DateTime now = DateTime.Now;
|
||||||
string dateTimeString = $"[{now.ToUniversalTime():u} ({Math.Floor((now - execStart).TotalMilliseconds):####00000})]";
|
string dateTimeString = $"[{now.ToUniversalTime():u} ({Math.Floor((now - execStart).TotalMilliseconds):###,###,###})]";
|
||||||
if(text is not null)
|
if(text is not null)
|
||||||
OnWriteLine?.Invoke(this, new ConsoleWriterEventArgs($"{dateTimeString} {text}"));
|
OnWriteLine?.Invoke(this, new ConsoleWriterEventArgs($"{dateTimeString} {text}"));
|
||||||
stdOut.WriteLine($"{dateTimeString} {text}");
|
stdOut.WriteLine($"{dateTimeString} {text}");
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.Runtime.Serialization.Formatters.Binary;
|
using System.Runtime.Serialization.Formatters.Binary;
|
||||||
|
using System.Text.Json;
|
||||||
using System.Xml;
|
using System.Xml;
|
||||||
|
using OSMDatastructure;
|
||||||
using OSMDatastructure.Graph;
|
using OSMDatastructure.Graph;
|
||||||
|
|
||||||
namespace Server;
|
namespace Server;
|
||||||
@ -19,7 +21,8 @@ public class RegionConverter
|
|||||||
|
|
||||||
public const string NodesFileName = "region.nodes";
|
public const string NodesFileName = "region.nodes";
|
||||||
public const string WaysFileName = "region.ways";
|
public const string WaysFileName = "region.ways";
|
||||||
public const string tagsFileName = "region.tags";
|
public const string TagsFileName = "region.tags";
|
||||||
|
public const string RegionsFileName = ".region";
|
||||||
|
|
||||||
public static void ConvertXMLToRegions(string filePath, string outputPath)
|
public static void ConvertXMLToRegions(string filePath, string outputPath)
|
||||||
{
|
{
|
||||||
@ -35,11 +38,15 @@ public class RegionConverter
|
|||||||
|
|
||||||
Console.WriteLine("Converting Ways...");
|
Console.WriteLine("Converting Ways...");
|
||||||
ImportWays(XmlReader.Create(xmlFileStream, ReaderSettings), nodeIdRegionDict, outputPath);
|
ImportWays(XmlReader.Create(xmlFileStream, ReaderSettings), nodeIdRegionDict, outputPath);
|
||||||
|
xmlFileStream.Dispose();
|
||||||
|
|
||||||
|
Console.WriteLine("Combining tmpFiles into region");
|
||||||
|
CombineTmpFiles(outputPath, nodeIdRegionDict.Values.ToHashSet());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Dictionary<ulong, ulong> GetNodesAndRegions(XmlReader xmlReader, string outputPath)
|
private static Dictionary<ulong, ulong> GetNodesAndRegions(XmlReader xmlReader, string outputPath)
|
||||||
{
|
{
|
||||||
BinaryFormatter bFormatter = new BinaryFormatter();
|
|
||||||
Dictionary<ulong, ulong> nodeRegions = new();
|
Dictionary<ulong, ulong> nodeRegions = new();
|
||||||
Dictionary<ulong, FileStream> regionFileStreams = new();
|
Dictionary<ulong, FileStream> regionFileStreams = new();
|
||||||
Dictionary<ulong, OsmNode> tmpAllNodes = new();
|
Dictionary<ulong, OsmNode> tmpAllNodes = new();
|
||||||
@ -75,6 +82,7 @@ public class RegionConverter
|
|||||||
|
|
||||||
foreach (ulong nodeId in currentIds.Where(key => tmpAllNodes.ContainsKey(key)))
|
foreach (ulong nodeId in currentIds.Where(key => tmpAllNodes.ContainsKey(key)))
|
||||||
{
|
{
|
||||||
|
BinaryFormatter bFormatter = new BinaryFormatter();
|
||||||
if (isHighway)
|
if (isHighway)
|
||||||
{
|
{
|
||||||
ulong regionHash = Coordinates.GetRegionHashCode(tmpAllNodes[nodeId].coordinates);
|
ulong regionHash = Coordinates.GetRegionHashCode(tmpAllNodes[nodeId].coordinates);
|
||||||
@ -90,7 +98,7 @@ public class RegionConverter
|
|||||||
regionFileStreams.Add(regionHash, nodesRegionStream);
|
regionFileStreams.Add(regionHash, nodesRegionStream);
|
||||||
}
|
}
|
||||||
nodeRegions.Add(nodeId, regionHash);
|
nodeRegions.Add(nodeId, regionHash);
|
||||||
|
|
||||||
#pragma warning disable SYSLIB0011 //eheheh
|
#pragma warning disable SYSLIB0011 //eheheh
|
||||||
bFormatter.Serialize(nodesRegionStream, tmpAllNodes[nodeId]);
|
bFormatter.Serialize(nodesRegionStream, tmpAllNodes[nodeId]);
|
||||||
#pragma warning restore SYSLIB0011
|
#pragma warning restore SYSLIB0011
|
||||||
@ -110,6 +118,7 @@ public class RegionConverter
|
|||||||
Dictionary<Tag.TagType, dynamic> currentTags = new();
|
Dictionary<Tag.TagType, dynamic> currentTags = new();
|
||||||
Dictionary<ulong, FileStream> regionWaysFileStreams = new();
|
Dictionary<ulong, FileStream> regionWaysFileStreams = new();
|
||||||
Dictionary<ulong, FileStream> regionTagsFileStreams = new();
|
Dictionary<ulong, FileStream> regionTagsFileStreams = new();
|
||||||
|
Dictionary<ulong, HashSet<ulong>> writtenWayTagsInRegion = new();
|
||||||
while (xmlReader.ReadToFollowing("way"))
|
while (xmlReader.ReadToFollowing("way"))
|
||||||
{
|
{
|
||||||
currentNodeIds.Clear();
|
currentNodeIds.Clear();
|
||||||
@ -120,8 +129,10 @@ public class RegionConverter
|
|||||||
currentTags.TryAdd(Tag.TagType.id, Convert.ToUInt64(wayReader.GetAttribute("id")!));
|
currentTags.TryAdd(Tag.TagType.id, Convert.ToUInt64(wayReader.GetAttribute("id")!));
|
||||||
if (wayReader.Name == "tag")
|
if (wayReader.Name == "tag")
|
||||||
{
|
{
|
||||||
Tag wayTag = Tag.ConvertToTag(wayReader.GetAttribute("k")!, wayReader.GetAttribute("v")!);
|
HashSet<Tag> pTags = Tag.ConvertToTags(wayReader.GetAttribute("k")!, wayReader.GetAttribute("v")!);
|
||||||
currentTags.TryAdd(wayTag.key, wayTag.value);
|
if(pTags.Count > 0)
|
||||||
|
foreach (Tag pTag in pTags)
|
||||||
|
currentTags.TryAdd(pTag.key, pTag.value);
|
||||||
}
|
}
|
||||||
else if (wayReader.Name == "nd")
|
else if (wayReader.Name == "nd")
|
||||||
{
|
{
|
||||||
@ -136,31 +147,42 @@ public class RegionConverter
|
|||||||
{
|
{
|
||||||
ulong node1Id = currentNodeIds[i];
|
ulong node1Id = currentNodeIds[i];
|
||||||
ulong node2Id = currentNodeIds[i+1];
|
ulong node2Id = currentNodeIds[i+1];
|
||||||
if (currentTags.ContainsKey(Tag.TagType.oneway) && (bool)currentTags[Tag.TagType.oneway] && nodeRegions.ContainsKey(node1Id) && nodeRegions.ContainsKey(node2Id))
|
if (nodeRegions.ContainsKey(node1Id) && nodeRegions.ContainsKey(node2Id))
|
||||||
{
|
{
|
||||||
if (currentTags.ContainsKey(Tag.TagType.forward) && !(bool)currentTags[Tag.TagType.forward])
|
if (currentTags.ContainsKey(Tag.TagType.oneway) && (bool)currentTags[Tag.TagType.oneway])
|
||||||
{
|
{
|
||||||
OsmWay n21e = new OsmWay(currentTags[Tag.TagType.id], node2Id, node1Id, nodeRegions[node2Id]);
|
if (currentTags.ContainsKey(Tag.TagType.forward) && !(bool)currentTags[Tag.TagType.forward])
|
||||||
WriteWay(ref regionWaysFileStreams, nodeRegions[node2Id], n21e, outputPath);
|
{
|
||||||
WriteTag(ref regionTagsFileStreams, nodeRegions[node2Id], currentTags, outputPath);
|
OsmEdge n21e = new OsmEdge(currentTags[Tag.TagType.id], node2Id, node1Id,
|
||||||
|
nodeRegions[node1Id]);
|
||||||
|
WriteWay(ref regionWaysFileStreams, nodeRegions[node2Id], n21e, outputPath);
|
||||||
|
WriteTags(ref regionTagsFileStreams, ref writtenWayTagsInRegion, nodeRegions[node2Id],
|
||||||
|
currentTags, outputPath);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
OsmEdge n12e = new OsmEdge(currentTags[Tag.TagType.id], node1Id, node2Id,
|
||||||
|
nodeRegions[node2Id]);
|
||||||
|
WriteWay(ref regionWaysFileStreams, nodeRegions[node1Id], n12e, outputPath);
|
||||||
|
WriteTags(ref regionTagsFileStreams, ref writtenWayTagsInRegion, nodeRegions[node1Id],
|
||||||
|
currentTags, outputPath);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
OsmWay n12e = new OsmWay(currentTags[Tag.TagType.id], node1Id, node2Id, nodeRegions[node2Id]);
|
OsmEdge n12e = new OsmEdge(currentTags[Tag.TagType.id], node1Id, node2Id,
|
||||||
|
nodeRegions[node2Id]);
|
||||||
WriteWay(ref regionWaysFileStreams, nodeRegions[node1Id], n12e, outputPath);
|
WriteWay(ref regionWaysFileStreams, nodeRegions[node1Id], n12e, outputPath);
|
||||||
WriteTag(ref regionTagsFileStreams, nodeRegions[node1Id], currentTags, outputPath);
|
WriteTags(ref regionTagsFileStreams, ref writtenWayTagsInRegion, nodeRegions[node1Id],
|
||||||
|
currentTags, outputPath);
|
||||||
|
|
||||||
|
OsmEdge n21e = new OsmEdge(currentTags[Tag.TagType.id], node2Id, node1Id,
|
||||||
|
nodeRegions[node1Id]);
|
||||||
|
WriteWay(ref regionWaysFileStreams, nodeRegions[node2Id], n21e, outputPath);
|
||||||
|
WriteTags(ref regionTagsFileStreams, ref writtenWayTagsInRegion, nodeRegions[node2Id],
|
||||||
|
currentTags, outputPath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if(nodeRegions.ContainsKey(node1Id) && nodeRegions.ContainsKey(node2Id))
|
|
||||||
{
|
|
||||||
OsmWay n12e = new OsmWay(currentTags[Tag.TagType.id], node1Id, node2Id, nodeRegions[node2Id]);
|
|
||||||
WriteWay(ref regionWaysFileStreams, nodeRegions[node1Id], n12e, outputPath);
|
|
||||||
WriteTag(ref regionTagsFileStreams, nodeRegions[node1Id], currentTags, outputPath);
|
|
||||||
|
|
||||||
OsmWay n21e = new OsmWay(currentTags[Tag.TagType.id], node2Id, node1Id, nodeRegions[node2Id]);
|
|
||||||
WriteWay(ref regionWaysFileStreams, nodeRegions[node2Id], n21e, outputPath);
|
|
||||||
WriteTag(ref regionTagsFileStreams, nodeRegions[node2Id], currentTags, outputPath);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -171,7 +193,7 @@ public class RegionConverter
|
|||||||
f.Dispose();
|
f.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void WriteWay(ref Dictionary<ulong, FileStream> regionWaysFileStreams, ulong regionHash, OsmWay way, string outputPath)
|
private static void WriteWay(ref Dictionary<ulong, FileStream> regionWaysFileStreams, ulong regionHash, OsmEdge edge, string outputPath)
|
||||||
{
|
{
|
||||||
BinaryFormatter bFormatter = new BinaryFormatter();
|
BinaryFormatter bFormatter = new BinaryFormatter();
|
||||||
if (!regionWaysFileStreams.ContainsKey(regionHash))
|
if (!regionWaysFileStreams.ContainsKey(regionHash))
|
||||||
@ -180,21 +202,101 @@ public class RegionConverter
|
|||||||
regionWaysFileStreams.Add(regionHash, new FileStream(waysRegionPath, FileMode.OpenOrCreate));
|
regionWaysFileStreams.Add(regionHash, new FileStream(waysRegionPath, FileMode.OpenOrCreate));
|
||||||
}
|
}
|
||||||
#pragma warning disable SYSLIB0011
|
#pragma warning disable SYSLIB0011
|
||||||
bFormatter.Serialize(regionWaysFileStreams[regionHash], way);
|
bFormatter.Serialize(regionWaysFileStreams[regionHash], edge);
|
||||||
#pragma warning restore SYSLIB0011
|
#pragma warning restore SYSLIB0011
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void WriteTag(ref Dictionary<ulong, FileStream> regionTagsFileStreams, ulong regionHash, Dictionary<Tag.TagType, dynamic> currentTags, string outputPath)
|
private static void WriteTags(ref Dictionary<ulong, FileStream> regionTagsFileStreams, ref Dictionary<ulong, HashSet<ulong>> writtenWayTagsInRegion, ulong regionHash, Dictionary<Tag.TagType, dynamic> currentTags, string outputPath)
|
||||||
{
|
{
|
||||||
|
if (writtenWayTagsInRegion.ContainsKey(regionHash) &&
|
||||||
|
writtenWayTagsInRegion[regionHash].Contains(currentTags[Tag.TagType.id]))
|
||||||
|
return;
|
||||||
|
else if(!writtenWayTagsInRegion.ContainsKey(regionHash))
|
||||||
|
writtenWayTagsInRegion.Add(regionHash, new HashSet<ulong>());
|
||||||
|
|
||||||
BinaryFormatter bFormatter = new BinaryFormatter();
|
BinaryFormatter bFormatter = new BinaryFormatter();
|
||||||
if (!regionTagsFileStreams.ContainsKey(regionHash))
|
if (!regionTagsFileStreams.ContainsKey(regionHash))
|
||||||
{
|
{
|
||||||
string tagsRegionPath = Path.Combine(outputPath, regionHash.ToString(), tagsFileName);
|
string tagsRegionPath = Path.Combine(outputPath, regionHash.ToString(), TagsFileName);
|
||||||
regionTagsFileStreams.Add(regionHash, new FileStream(tagsRegionPath, FileMode.OpenOrCreate));
|
regionTagsFileStreams.Add(regionHash, new FileStream(tagsRegionPath, FileMode.OpenOrCreate));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ulong wayId = currentTags[Tag.TagType.id];
|
||||||
|
writtenWayTagsInRegion[regionHash].Add(wayId);
|
||||||
|
TagManager tm = new TagManager();
|
||||||
|
foreach(KeyValuePair<Tag.TagType, dynamic> kv in currentTags)
|
||||||
|
tm.AddTag(wayId, kv);
|
||||||
#pragma warning disable SYSLIB0011
|
#pragma warning disable SYSLIB0011
|
||||||
bFormatter.Serialize(regionTagsFileStreams[regionHash], currentTags);
|
bFormatter.Serialize(regionTagsFileStreams[regionHash], tm);
|
||||||
#pragma warning restore SYSLIB0011
|
#pragma warning restore SYSLIB0011
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void CombineTmpFiles(string folderPath, HashSet<ulong> regionHashes)
|
||||||
|
{
|
||||||
|
foreach (ulong regionId in regionHashes)
|
||||||
|
{
|
||||||
|
ValueTuple<Region, HashSet<OsmEdge>> tmpRegion = LoadRegion(folderPath, regionId);
|
||||||
|
foreach (OsmEdge edge in tmpRegion.Item2)
|
||||||
|
{
|
||||||
|
OsmNode? startNode = tmpRegion.Item1.GetNode(edge.startId);
|
||||||
|
if (startNode is not null)
|
||||||
|
{
|
||||||
|
startNode.edges.Add(edge);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
using (FileStream tmpRegionFileStream = new FileStream(Path.Join(folderPath, $"{regionId}{RegionsFileName}"), FileMode.Create))
|
||||||
|
{
|
||||||
|
JsonSerializer.Serialize(tmpRegionFileStream, tmpRegion.Item1, typeof(Region), Region.serializerOptions);
|
||||||
|
}
|
||||||
|
Directory.Delete(Path.Join(folderPath, regionId.ToString()), true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ValueTuple<Region, HashSet<OsmEdge>> LoadRegion(string folderPath, ulong regionHash)
|
||||||
|
{
|
||||||
|
Region newRegion = new Region(regionHash);
|
||||||
|
HashSet<OsmEdge> ways = new();
|
||||||
|
BinaryFormatter bFormatter = new BinaryFormatter();
|
||||||
|
if (!Directory.Exists(Path.Join(folderPath, regionHash.ToString())))
|
||||||
|
throw new FileNotFoundException("Region does not exist");
|
||||||
|
|
||||||
|
#pragma warning disable SYSLIB0011
|
||||||
|
string waysPath = Path.Join(folderPath, regionHash.ToString(), RegionConverter.WaysFileName);
|
||||||
|
if(File.Exists(waysPath))
|
||||||
|
using (FileStream wayFileStream = new FileStream(waysPath, FileMode.Open))
|
||||||
|
{
|
||||||
|
while (wayFileStream.Position < wayFileStream.Length)
|
||||||
|
{
|
||||||
|
OsmEdge deserializedEdge = (OsmEdge)bFormatter.Deserialize(wayFileStream);
|
||||||
|
ways.Add(deserializedEdge);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
using (FileStream nodeFileStream = new FileStream(Path.Join(folderPath, regionHash.ToString(), RegionConverter.NodesFileName), FileMode.Open))
|
||||||
|
{
|
||||||
|
while (nodeFileStream.Position < nodeFileStream.Length)
|
||||||
|
{
|
||||||
|
OsmNode deserializedNode = (OsmNode)bFormatter.Deserialize(nodeFileStream);
|
||||||
|
newRegion.nodes.Add(deserializedNode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
string tagsPath = Path.Join(folderPath, regionHash.ToString(), RegionConverter.TagsFileName);
|
||||||
|
if(File.Exists(tagsPath))
|
||||||
|
using (FileStream tagsFileStream = new FileStream(tagsPath, FileMode.Open))
|
||||||
|
{
|
||||||
|
while (tagsFileStream.Position < tagsFileStream.Length)
|
||||||
|
{
|
||||||
|
TagManager tm = (TagManager)bFormatter.Deserialize(tagsFileStream);
|
||||||
|
ulong id = (ulong)tm.wayTagSets.First()!.Value.First(tag => tag.key == Tag.TagType.id)!.value;
|
||||||
|
foreach(Tag tag in tm.wayTagSets.First()!.Value)
|
||||||
|
newRegion.tagManager.AddTag(id, tag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#pragma warning restore SYSLIB0011
|
||||||
|
|
||||||
|
return new ValueTuple<Region, HashSet<OsmEdge>>(newRegion, ways);
|
||||||
|
}
|
||||||
}
|
}
|
@ -1,68 +0,0 @@
|
|||||||
using System.Runtime.Serialization.Formatters.Binary;
|
|
||||||
using OSMDatastructure;
|
|
||||||
using OSMDatastructure.Graph;
|
|
||||||
|
|
||||||
namespace Server;
|
|
||||||
|
|
||||||
public class RegionLoader
|
|
||||||
{
|
|
||||||
private string path { get; }
|
|
||||||
private Dictionary<ulong, Region> regionIdsDict;
|
|
||||||
public RegionLoader(string path)
|
|
||||||
{
|
|
||||||
this.path = path;
|
|
||||||
this.regionIdsDict = new();
|
|
||||||
}
|
|
||||||
|
|
||||||
public HashSet<OsmNode> GetNodes(ulong regionHash)
|
|
||||||
{
|
|
||||||
Region r = GetRegion(regionHash);
|
|
||||||
return r.nodes;
|
|
||||||
}
|
|
||||||
|
|
||||||
public HashSet<OsmWay> GetWays(ulong regionHash)
|
|
||||||
{
|
|
||||||
Region r = GetRegion(regionHash);
|
|
||||||
return r.ways;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Region GetRegion(ulong regionHash)
|
|
||||||
{
|
|
||||||
if (regionIdsDict.ContainsKey(regionHash))
|
|
||||||
return regionIdsDict[regionHash];
|
|
||||||
else return LoadRegion(regionHash);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Region LoadRegion(ulong regionHash)
|
|
||||||
{
|
|
||||||
Region newRegion = new Region(regionHash);
|
|
||||||
BinaryFormatter bFormatter = new BinaryFormatter();
|
|
||||||
if (regionIdsDict.ContainsKey(regionHash))
|
|
||||||
throw new Exception("Region already loaded");
|
|
||||||
if (!Directory.Exists(Path.Join(path, regionHash.ToString())))
|
|
||||||
throw new FileNotFoundException("Region does not exist");
|
|
||||||
|
|
||||||
#pragma warning disable SYSLIB0011
|
|
||||||
using (FileStream wayFileStream = new FileStream(Path.Join(path, regionHash.ToString(), RegionConverter.WaysFileName), FileMode.Open))
|
|
||||||
{
|
|
||||||
while (wayFileStream.Position < wayFileStream.Length)
|
|
||||||
{
|
|
||||||
OsmWay deserializedWay = (OsmWay)bFormatter.Deserialize(wayFileStream);
|
|
||||||
newRegion.ways.Add(deserializedWay);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
using (FileStream nodeFileStream = new FileStream(Path.Join(path, regionHash.ToString(), RegionConverter.NodesFileName), FileMode.Open))
|
|
||||||
{
|
|
||||||
while (nodeFileStream.Position < nodeFileStream.Length)
|
|
||||||
{
|
|
||||||
OsmNode deserializedNode = (OsmNode)bFormatter.Deserialize(nodeFileStream);
|
|
||||||
newRegion.nodes.Add(deserializedNode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#pragma warning restore SYSLIB0011
|
|
||||||
|
|
||||||
regionIdsDict.Add(regionHash, newRegion);
|
|
||||||
return newRegion;
|
|
||||||
}
|
|
||||||
}
|
|
184
Server/Server.cs
184
Server/Server.cs
@ -1,39 +1,183 @@
|
|||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using System.Drawing;
|
||||||
|
using System.Drawing.Imaging;
|
||||||
|
using System.Text.Json;
|
||||||
using OSMDatastructure;
|
using OSMDatastructure;
|
||||||
using OSMDatastructure.Graph;
|
using OSMDatastructure.Graph;
|
||||||
|
using Pathfinding;
|
||||||
|
using RenderPath;
|
||||||
|
using Region = OSMDatastructure.Region;
|
||||||
|
|
||||||
namespace Server;
|
namespace Server;
|
||||||
|
|
||||||
public class Server
|
public class Server
|
||||||
{
|
{
|
||||||
|
|
||||||
|
[SuppressMessage("Interoperability", "CA1416:Validate platform compatibility")]
|
||||||
public static void Main(string[] args)
|
public static void Main(string[] args)
|
||||||
{
|
{
|
||||||
ConsoleWriter newConsole = new ConsoleWriter();
|
ConsoleWriter newConsole = new();
|
||||||
Console.SetOut(newConsole);
|
Console.SetOut(newConsole);
|
||||||
Console.SetError(newConsole);
|
Console.SetError(newConsole);
|
||||||
|
|
||||||
|
string workingDir = "D:/stuttgart-regbez-latest";
|
||||||
|
|
||||||
//RegionConverter.ConvertXMLToRegions("D:/stuttgart-regbez-latest.osm", "D:/stuttgart-regbez-latest");
|
//RegionConverter.ConvertXMLToRegions("D:/stuttgart-regbez-latest.osm", "D:/stuttgart-regbez-latest");
|
||||||
RegionConverter.ConvertXMLToRegions("D:/map.osm", "D:/map");
|
//RegionConverter.ConvertXMLToRegions("D:/map.osm", "D:/map");
|
||||||
Console.WriteLine("Loaded");
|
//RegionConverter.ConvertXMLToRegions("D:/germany-latest.osm", "D:/germany-latest");
|
||||||
RegionLoader rl = new RegionLoader("D:/map");
|
|
||||||
Region r = rl.GetRegion(13870001898000);
|
Coordinates start = new (48.7933798f, 9.8275859f);
|
||||||
|
Coordinates finish = new (48.795918f, 9.021618f);
|
||||||
|
|
||||||
|
Pathfinder result = new Pathfinder(workingDir, 2, 30).AStar(start, finish, Tag.SpeedType.car, 1);
|
||||||
|
Renderer image = Renderer.DrawPathfinder(result, Renderer.RenderType.png);
|
||||||
|
image.Save("D:/stuttgart-regbez-latest");
|
||||||
|
|
||||||
|
/*
|
||||||
|
if(File.Exists(@"D:\bounds"))
|
||||||
|
File.Delete(@"D:\bounds");
|
||||||
|
RegionManager rm = LoadRegions(workingDir, start, finish);
|
||||||
|
Renderer areaRender = Renderer.DrawArea(rm, Renderer.RenderType.PNG);
|
||||||
|
FileStream s = new(@"D:\bounds", FileMode.OpenOrCreate);
|
||||||
|
JsonSerializer.Serialize(s, areaRender.bounds, JsonSerializerOptions.Default);
|
||||||
|
areaRender.Save(@"D:\Base");
|
||||||
|
s.Dispose();
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
Coordinates start = new Coordinates(48.243351f, 11.640417f);
|
//TestVariables(workingDir, start, finish, 12);
|
||||||
Coordinates finish = new Coordinates(48.25239f, 11.53272f);
|
//GetShortestRoute("D:");
|
||||||
OsmNode[] path = Pathfinder.CustomAStar("/home/glax/Downloads/oberbayern-latest", start, finish, OsmEdge.speedType.car).ToArray();
|
|
||||||
Console.WriteLine("{0}\n", path[0].ToString());
|
/*
|
||||||
for (int i = 0; i < path.Length - 1; i++)
|
string parentFolder = new DirectoryInfo(workingDir).Parent!.FullName;
|
||||||
|
Renderer.Bounds bounds = JsonSerializer.Deserialize<Renderer.Bounds>(new FileStream(@"D:\bounds", FileMode.Open));
|
||||||
|
Image baseImage = Image.FromFile(@"D:\Base.png");
|
||||||
|
|
||||||
|
Pathfinder result = new Pathfinder(workingDir, 2, 30).AStar(start,
|
||||||
|
finish, Tag.SpeedType.car, 4);
|
||||||
|
|
||||||
|
Console.WriteLine($"Calc-time {result.pathResult!.calcTime} Path-length: {result.pathResult.pathNodes.Count} Visited-nodes: {result.gScore!.Count}");
|
||||||
|
|
||||||
|
string fileName = DateTime.Now.ToFileTime().ToString();
|
||||||
|
|
||||||
|
string resultFileName = $"{new DirectoryInfo(workingDir).Name}-{fileName}.result";
|
||||||
|
result.SaveResult(Path.Join(parentFolder, resultFileName));
|
||||||
|
|
||||||
|
string renderFileName = $"{new DirectoryInfo(workingDir).Name}-{fileName}.render.png";
|
||||||
|
|
||||||
|
Image renderWeights = Renderer.DrawGScores(result.gScore, baseImage, bounds).Item1;
|
||||||
|
Image render = Renderer.DrawPath(result.pathResult, renderWeights, bounds).Item1;
|
||||||
|
render.Save(Path.Join(parentFolder, renderFileName), ImageFormat.Png);
|
||||||
|
*/
|
||||||
|
Console.Beep(400, 50);
|
||||||
|
Console.Beep(600, 50);
|
||||||
|
Console.Beep(400, 50);
|
||||||
|
Console.Beep(600, 50);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void GetShortestRoute(string directory)
|
||||||
|
{
|
||||||
|
DateTime start = DateTime.Now;
|
||||||
|
HashSet<string> allFiles = Directory.GetFiles(directory).Where(file => file.EndsWith(".result")).ToHashSet();
|
||||||
|
Dictionary<PathResult, string> results = new();
|
||||||
|
int loaded = 0;
|
||||||
|
foreach (string filePath in allFiles)
|
||||||
{
|
{
|
||||||
OsmNode n1 = path[i];
|
PathResult result = PathResult.PathresultFromFile(filePath);
|
||||||
OsmNode n2 = path[i + 1];
|
results.Add(result, filePath);
|
||||||
OsmEdge? e = n1.GetEdgeToNode(n2);
|
Console.WriteLine($"{loaded++}/{allFiles.Count()} {filePath} " +
|
||||||
if(e != null)
|
$"Time elapsed: {DateTime.Now - start} " +
|
||||||
Console.WriteLine("{0}\n{1}", e.ToString(), n2.ToString());
|
$"Remaining {((DateTime.Now - start)/loaded)*(allFiles.Count-loaded)}");
|
||||||
else
|
}
|
||||||
Console.WriteLine("NO EDGE\n{0}", n2.ToString());
|
|
||||||
|
KeyValuePair<PathResult, string> shortest = results.MinBy(result => result.Key.distance);
|
||||||
|
KeyValuePair<PathResult, string> fastest = results.MinBy(result => result.Key.weight);
|
||||||
|
KeyValuePair<PathResult, string> calcTime = results.MinBy(result => result.Key.calcTime);
|
||||||
|
Console.WriteLine($"\nShortest:\t{shortest.Key.distance:0.0} {shortest.Key.weight:0.00} {shortest.Key.calcTime} {shortest.Value}\n" +
|
||||||
|
$"Fastest:\t{fastest.Key.distance:0.0} {fastest.Key.weight:0.00} {fastest.Key.calcTime} {fastest.Value}\n" +
|
||||||
|
$"CalcTime:\t{calcTime.Key.distance:0.0} {calcTime.Key.weight:0.00} {calcTime.Key.calcTime} {calcTime.Value}");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static RegionManager LoadRegions(string workingDir, Coordinates c1, Coordinates c2)
|
||||||
|
{
|
||||||
|
float minLat = c1.latitude < c2.latitude ? c1.latitude : c2.latitude;
|
||||||
|
float minLon = c1.longitude < c2.longitude ? c1.longitude : c2.longitude;
|
||||||
|
float maxLat = c1.latitude > c2.latitude ? c1.latitude : c2.latitude;
|
||||||
|
float maxLon = c1.longitude > c2.longitude ? c1.longitude : c2.longitude;
|
||||||
|
|
||||||
|
RegionManager allRegions = new(workingDir);
|
||||||
|
for (float lat = minLat - Region.RegionSize * 3; lat < maxLat + Region.RegionSize * 3; lat += Region.RegionSize / 2)
|
||||||
|
{
|
||||||
|
for (float lon = minLon - Region.RegionSize; lon < maxLon + Region.RegionSize; lon += Region.RegionSize / 2)
|
||||||
|
{
|
||||||
|
allRegions.GetRegion(new Coordinates(lat, lon));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Console.WriteLine("Loaded needed Regions");
|
||||||
|
return allRegions;
|
||||||
|
}
|
||||||
|
|
||||||
|
[SuppressMessage("Interoperability", "CA1416:Validate platform compatibility")]
|
||||||
|
private static void TestVariables(string workingDir, Coordinates start, Coordinates finish, int threads)
|
||||||
|
{
|
||||||
|
string parentFolder = new DirectoryInfo(workingDir).Parent!.FullName;
|
||||||
|
|
||||||
|
RegionManager rm = LoadRegions(workingDir, start, finish);
|
||||||
|
|
||||||
|
Queue<Thread> calcThreads = new();
|
||||||
|
|
||||||
|
Bounds bounds = JsonSerializer.Deserialize<Bounds>(new FileStream(@"D:\bounds", FileMode.Open))!;
|
||||||
|
|
||||||
|
|
||||||
|
for (double extraTime = 1.5; extraTime >= 1; extraTime -= 0.25)
|
||||||
|
{
|
||||||
|
for (double roadFactor = 0.05; roadFactor < 5; roadFactor += 0.05)
|
||||||
|
{
|
||||||
|
double road = roadFactor;
|
||||||
|
double time = extraTime;
|
||||||
|
calcThreads.Enqueue(new Thread(() =>
|
||||||
|
{
|
||||||
|
Pathfinder testresult = new Pathfinder(workingDir, road, 30).AStar(start,
|
||||||
|
finish, Tag.SpeedType.car, time);
|
||||||
|
Image baseImage = Image.FromStream(new FileStream(@"D:\Base.png", FileMode.Open, FileAccess.Read, FileShare.Read,
|
||||||
|
(int)new FileInfo(@"D:\Base.png").Length, FileOptions.Asynchronous));
|
||||||
|
Renderer renderer = new PNGRenderer(baseImage);
|
||||||
|
renderer.bounds = bounds;
|
||||||
|
Renderer renderWeights = Renderer.DrawGScores(testresult.gScore!, Renderer.RenderType.png, renderer);
|
||||||
|
Renderer render = Renderer.DrawPath(testresult.pathResult!, Renderer.RenderType.png, renderWeights);
|
||||||
|
string fileName = $"road{road:0.00}_time{time:0.00}";
|
||||||
|
string resultFileName = Path.Combine("D:", $"{fileName}.result");
|
||||||
|
testresult.SaveResult(resultFileName);
|
||||||
|
string imageFileName = Path.Combine("D:", fileName);
|
||||||
|
render.Save(imageFileName);
|
||||||
|
Console.WriteLine($"Saved {fileName}");
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int totalTasks = calcThreads.Count;
|
||||||
|
int completedTasks = 0;
|
||||||
|
DateTime startTime = DateTime.Now;
|
||||||
|
|
||||||
|
HashSet<Thread> runningThreads = new();
|
||||||
|
Console.WriteLine($"Running {threads} Threads on {totalTasks} Tasks.");
|
||||||
|
while (calcThreads.Count > 0 || runningThreads.Count > 0)
|
||||||
|
{
|
||||||
|
while (runningThreads.Count < threads && calcThreads.Count > 0)
|
||||||
|
{
|
||||||
|
Thread t = calcThreads.Dequeue();
|
||||||
|
runningThreads.Add(t);
|
||||||
|
t.Start();
|
||||||
|
}
|
||||||
|
|
||||||
|
int newCompletedTasks = runningThreads.RemoveWhere(thread => !thread.IsAlive);
|
||||||
|
completedTasks += newCompletedTasks;
|
||||||
|
if (newCompletedTasks > 0)
|
||||||
|
{
|
||||||
|
TimeSpan elapsedTime = DateTime.Now - startTime;
|
||||||
|
Console.WriteLine($"To calculate: {calcThreads.Count}(+{runningThreads.Count} running)/{totalTasks} Time Average: {(elapsedTime/completedTasks)} Elapsed: {elapsedTime} Remaining: {(elapsedTime/completedTasks*calcThreads.Count)}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Console.WriteLine();*/
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -9,6 +9,12 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Pathfinding\Pathfinding.csproj" />
|
<ProjectReference Include="..\Pathfinding\Pathfinding.csproj" />
|
||||||
|
<ProjectReference Include="..\RenderPath\RenderPath.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="System.Drawing.Common" Version="7.0.0" />
|
||||||
|
<PackageReference Include="System.Runtime.Serialization.Json" Version="4.3.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
Reference in New Issue
Block a user