Compare commits

...

152 Commits

Author SHA1 Message Date
56ac9dc948 unnecessary type-conversion. 2023-05-17 19:19:40 +02:00
9ef63c9886 Make it more clear what is being returned as string. 2023-05-17 19:14:36 +02:00
2b5dd91335 floating point comparison 2023-05-17 19:13:31 +02:00
9f0d47ed59 Combine statements 2023-05-17 19:08:10 +02:00
371989b34d Make GetRegion more readable / Less convoluted and complicated due to multithreading. 2023-05-17 19:06:48 +02:00
e53d1086cc TKey can not be null. 2023-05-17 19:01:00 +02:00
dc98fb51b1 Make RPriorityQueue.Count point directly to the queue.Count instead of manually updating every operation. 2023-05-17 19:00:47 +02:00
3077b4d8b8 Added abstract Renderer class that SVGRenderer and PNGRenderer inherit from.
This way standardized rendering methods can be implemented.
2023-05-16 20:00:18 +02:00
7c5d87ca76 Moved Bounds to seperate Class 2023-05-15 14:07:04 +02:00
a5f272dfb9 Removed commented Code
Added Logging to see progress
2023-05-15 13:16:41 +02:00
f84aa82186 Rename Method "FromRandomCoords" -> FromCoords 2023-05-15 13:16:11 +02:00
6d59253a0b Bounds serialization 2023-04-24 20:48:23 +02:00
af821a761f added extraTime Variable to api 2023-04-24 19:47:54 +02:00
9b88996439 added extratime variable 2023-04-24 19:39:19 +02:00
5733b0edb3 Adjusted priority render 2023-04-24 19:39:04 +02:00
923cbee280 Removed result from getshortestroute 2023-04-24 19:38:51 +02:00
f19aa0007e Testsetup for time 2023-04-24 19:38:23 +02:00
f525b88a3a Best factors for current heurisitc: roadPriority 0.0215, extraTime: 3 2023-04-24 19:38:09 +02:00
d497196f9f Added methods RemoveExcept(Ienumberable) and Clear() 2023-04-24 19:37:25 +02:00
c705fdb63a Throw exception instead of returning null when file not found. 2023-04-24 18:53:20 +02:00
aa05aad5b3 Added missing maxspeed:max 2023-04-24 18:53:02 +02:00
7d33d11a03 shortest path 2023-04-23 16:23:49 +02:00
edd931bca5 logging format 2023-04-23 16:23:06 +02:00
6b5dddb1e3 Prevent method from returning before all threads are finished.
Include calculating threads in output.
2023-04-23 16:03:39 +02:00
fea0ecf17b remove exisiting results. 2023-04-23 16:02:55 +02:00
30b29aa25c whitespace and readability 2023-04-23 15:27:23 +02:00
73e7daffd7 Formatting of finding best variables 2023-04-23 15:27:06 +02:00
7b88616373 Preload Regions 2023-04-23 15:26:29 +02:00
2799db162d readability 2023-04-23 15:09:22 +02:00
9301e948b0 Filename-formatting 2023-04-23 15:09:04 +02:00
ec6725a5c5 Removed unnecessary factors 2023-04-23 15:07:26 +02:00
886ccaa8dc removed nodeAngleFactor 2023-04-23 15:00:22 +02:00
af1d9baf4f Testsetup for cariable testing 2023-04-23 14:51:13 +02:00
e2332847cd Adjust priority in weight calculation with factor. 2023-04-23 14:50:45 +02:00
dca4d56866 Keep searching for the same amount of time already search to get shorter path. 2023-04-23 14:50:16 +02:00
5f6cccd17d Added priority queue with changable priority. 2023-04-23 14:49:09 +02:00
97a8c2ea6f Maximum turn angle variable 2023-04-23 14:48:00 +02:00
b89a3715a1 Created variable for maximum turn angle. 2023-04-23 14:47:26 +02:00
6bc1d3c7ce Adjusted API to new Pathfinder calls. 2023-04-23 14:06:19 +02:00
5b8a1d1e10 cleanup 2023-04-23 13:43:01 +02:00
7856f1c66c Changed factors to globalfields to reduce parametersin methodcall 2023-04-23 13:42:46 +02:00
97a057a3d4 Fixed gScore with MaxValue due to Vector Angle returning NaN 2023-04-23 13:41:22 +02:00
bc39785f6f Start of complete rethinking of Heuristic function and approach to get shortest path. 2023-04-23 13:11:52 +02:00
18822e2152 Changed ColorInterp 2023-04-23 13:06:47 +02:00
68cb0ee3fd Filter MaxValueWeights 2023-04-23 13:06:29 +02:00
465d40a475 Cleanup variable rename 2023-04-23 12:58:32 +02:00
7fd9047ac4 Changed Colors for weights and created variable for pointsize 2023-04-22 18:34:12 +02:00
601200a8d6 Changed imageformat 2023-04-22 18:33:34 +02:00
a758c8c63e Changed Color and thickness values 2023-04-22 17:49:59 +02:00
ed46a419e3 Moved fields for serialization. 2023-04-22 17:49:27 +02:00
a1d9ccad46 Added deserialization of PathResult 2023-04-21 18:35:02 +02:00
914731c8a3 Prevent other thread from changing collection /multithreading) 2023-04-21 18:34:49 +02:00
d8ce6e4ce5 Mutlithreaded RegionManager 2023-04-21 15:14:02 +02:00
976108569b Threadsafe for masstesting variables. 2023-04-21 15:13:42 +02:00
6373874495 Added function to render a base-area-image. 2023-04-21 14:44:50 +02:00
c43c6dc985 Cleanup
Added "sequential read" to fileoptions on deserialization.
2023-04-21 14:44:18 +02:00
33232a7eb7 Roads are now painted according to priority. 2023-04-21 14:37:38 +02:00
cf5b1e9945 Masstesting new var: Threadcount
corrected capitalization
2023-04-21 14:29:52 +02:00
aa8b1e4451 Inverse Angle 2023-04-21 14:28:02 +02:00
95c0088b73 Added function to mass-test the heuristic variables. 2023-04-21 13:49:48 +02:00
cd3905915b Cleanup 2023-04-21 13:43:50 +02:00
dd37430761 Moved RoadPriority Function to RegionManager 2023-04-21 13:41:01 +02:00
42e915ee05 Added distance and duration to PathResult. 2023-04-21 11:40:26 +02:00
7d769a064f Parallel reading of regionfiles. 2023-04-21 11:40:15 +02:00
93a448e189 Made Conversion-methods public 2023-04-21 11:01:50 +02:00
750ba5c624 Fixed angle calculation 2023-04-21 11:01:31 +02:00
1facca84ba Prevent multiple processes from reading same file. 2023-04-21 11:01:05 +02:00
8b7cfcbd77 Method for mass-testing all factors. 2023-04-21 00:43:58 +02:00
28ab2b2bb8 Added angleFactor variable 2023-04-21 00:43:38 +02:00
7201b9c993 Add turn-angle to Weight calculation.
For that added Vector-class.
Highways now have much higher priority.
2023-04-21 00:42:21 +02:00
d1f311a76b Cleanup 2023-04-21 00:41:18 +02:00
2b252e2b06 Cleanup of unnecessary code 2023-04-20 23:02:38 +02:00
d456275fc1 private field naming _cameFromDict 2023-04-20 22:58:50 +02:00
2bd6c5d9c4 Replaced local vars with fields. 2023-04-20 22:58:27 +02:00
90a09e84c5 Removed/Replaced unnecessary checks in AStar. 2023-04-20 22:53:23 +02:00
b87d8a0300 Changed calls to new backend.
Pathfinder now finds the result and returns itself.
Pathfinder includes PathResult for later usage.
2023-04-20 19:41:40 +02:00
23429c8a00 Renderer now renders 3 different layers over each other: Area (Regions), gScore-Weights (as blobs) and PathResult path.
Returns the Image instead of saving to disk.
2023-04-20 19:40:50 +02:00
946fa0206b Made Pathfinder non-static to enable field-usage in other methods/classes.
Added fields RegionManager, workingDirectory, PathResult (result of AStar), gScore (result of AStar). Fields later used in Renderer.

SaveGraph is now called SaveResult and serializes PathResult as JSON.
2023-04-20 19:39:18 +02:00
5a1dce9883 PathResult now only includes Path and calcTime.
Other fields have been moved to Pathfinder
2023-04-20 19:37:34 +02:00
619cad61ee Adjusted to new Functions and return values.
Now renders complete result with path and all loaded regions.
2023-04-19 22:55:49 +02:00
bfb117164e Added Area-render (Renders all loaded nodes and edges)
Route can now be drawn over other pictures (e.g. the area-render)
Increased resolution to 20.000
Added Bounds-Class for information exchange with image.
2023-04-19 22:53:49 +02:00
208c000577 Changed values for heuristic and weight functions, as well as increased priority for type1-roads (motorways). 2023-04-19 22:51:47 +02:00
5212e43897 Add regionManager to PathResult for internal use 2023-04-19 22:15:57 +02:00
6fb88b5c9c Include RegionManager in result for internal use 2023-04-19 22:15:43 +02:00
9e0c4f65db appropriate names in PathResult for nodes gSCoreNodes 2023-04-19 22:02:15 +02:00
055a751c9d Save Pathfinding result to file.
Load result for render.
2023-04-13 19:18:25 +02:00
08ebc9a26b Changed some display values 2023-04-13 01:12:29 +02:00
d35aab9c39 More logging 2023-04-13 01:12:16 +02:00
08e3da6fe3 Render result to png after every routing 2023-04-13 01:00:56 +02:00
367e9cfde0 Removed API 2023-04-13 01:00:38 +02:00
a70284aa87 Added visualization to confirm paths 2023-04-13 00:24:33 +02:00
13915c9773 No more use of "track" or "service" for cars. 2023-04-13 00:24:03 +02:00
c373451007 fixed missing oneway streets without "forward" tag 2023-04-13 00:18:30 +02:00
308579279b Working. Weight calculation is still wonky, as well as heuristic needing tuning. 2023-04-11 01:04:19 +02:00
2131ac4afe Still requires tuning 2023-04-10 01:33:18 +02:00
428fcb9bf8 fixed return type for tagtypes name, tagref 2023-04-10 01:33:07 +02:00
6b496957d7 Fixes for oneway-change 2023-04-09 23:55:24 +02:00
874d60992e Try improving priority 2023-04-09 22:10:58 +02:00
c1557b7678 cleanup 2023-04-09 22:10:41 +02:00
14533c150f Fixing one-way issue 2023-04-09 22:10:23 +02:00
5289020d44 correct node for closest. 2023-04-09 21:02:32 +02:00
bb789e731d Correct node for path length 2023-04-09 21:02:19 +02:00
9e72d50448 Fixed valid start and end nodes (for type) 2023-04-09 21:02:01 +02:00
c238a9eed3 Implemented "smart" routing through priority:
Factors:
speed,
roadtype,
junctions / waychanges.
2023-04-09 20:41:33 +02:00
932465a564 changed some speeds 2023-04-09 20:40:53 +02:00
a0d2284e45 whitespace 2023-04-09 20:40:41 +02:00
6938c86ce2 moved to correct namespacefolder 2023-04-09 20:39:59 +02:00
6e836db79b changed speeds and return type to byte 2023-04-09 19:22:34 +02:00
9448187452 Added checks if item already in queue, only update direct-distance if it not already calculated. 2023-04-09 19:22:21 +02:00
2ca4207fd7 renamed currentNode 2023-04-09 18:37:45 +02:00
0f53ae579c Made speedtype any generic.
Will use any connection (highway), and return the same speed for all highways.
2023-04-09 18:27:53 +02:00
d8f8a41dcc Renamed PathNode distance and weight to include "delta".
Added directDistanceDelta
2023-04-09 17:52:37 +02:00
6eab23ff16 bug: forgot to exchange from distance to weight during copy & paste 2023-04-09 17:47:45 +02:00
206f9c5811 Added weight and distance delta to pathnode 2023-04-09 17:46:35 +02:00
e0bb3ce3de Added Method for path-return (returns the path from current graph).
Added "tags" to return value for path.
2023-04-09 17:38:57 +02:00
2904be84f0 changed NONE speeds 2023-04-09 17:37:52 +02:00
13beaeaf73 removed old method, changed some speeds 2023-04-09 17:14:14 +02:00
ea7ce1f630 Changed/Fixed Namespaces 2023-04-09 17:06:45 +02:00
9c7fec1c37 Fixed infinity error JSON 2023-04-09 17:02:56 +02:00
5efec08bbc EdgeWeight rewrite 2023-04-09 17:00:28 +02:00
05ae0bff6e Fixed invalid json-type 2023-04-09 16:49:22 +02:00
8bd0c5a4d4 Added Pathfinding Time
Renamed GetRoute -> GetRouteTime and GetRouteDistance
2023-04-09 16:47:33 +02:00
feb9b70e50 Added Pathfinding Time 2023-04-09 16:47:30 +02:00
9ef0e421bc Moved Pathfinding ClosestNode and SpeedCalc to RegionManager (more appropriate).
Added validation if edge is valid connection for vehicle.
2023-04-09 16:41:42 +02:00
a54b189b08 Sorted Methods 2023-04-09 16:32:02 +02:00
585a9213ce Fixed result to correct time and no path error handling 2023-04-09 16:29:09 +02:00
bf08f38a1e fixed pathfinding namespace 2023-04-09 16:24:43 +02:00
fc5d388ecd Dispose of Region-filestream 2023-04-09 16:22:22 +02:00
58d1031524 Splitting Pathfinding into separate files for each type of routing.
Removing timetracking again from routing-algos (not in scope)
2023-04-09 16:17:15 +02:00
fe0ccd0fca working 2023-04-06 14:46:19 +02:00
dfc9ffeb2c CustomAStar now returns ValueTuple with calctime and path as result 2023-04-06 14:46:08 +02:00
9c53c67763 Added /getClosestNode lat lon API 2023-04-06 14:33:00 +02:00
f266c6c7e6 ClosestNode no longer requires Region parameter, because regionmanager is passed. 2023-04-06 14:31:38 +02:00
8813023cd6 Created seperate PathNode class for Json Serialization 2023-04-06 14:27:25 +02:00
72b5511c26 better readability 2023-04-06 02:32:04 +02:00
f42e458048 Created GetSpeed method for uniform implementation
Changed Pathfinding class to static
Changed AStar to continue after path found.
2023-04-06 02:23:12 +02:00
3e23635cd1 Serialize Path for API 2023-04-06 02:22:03 +02:00
ed8558049c Added first API. 2023-04-06 01:29:45 +02:00
20d4da9e6f Changed to JsonSerialization to permanently store regions. 2023-04-06 01:29:30 +02:00
e9f1ba2e73 removed Dotsettings 2023-04-01 22:03:06 +02:00
5fe0acf0b4 Removed .idea 2023-04-01 22:02:14 +02:00
29189c5d8d Removed dead files 2023-04-01 22:01:13 +02:00
7f06a6f880 working! 2023-04-01 18:28:26 +02:00
8dd63411ea Changed some speedvalues for cars and pedestrians (to allow use of mis-qualified roads) 2023-04-01 18:28:21 +02:00
8f9f6f630e stop within 10 meters of goal 2023-04-01 18:27:52 +02:00
2c5ab070a2 working 2023-04-01 18:10:26 +02:00
4600105b0b Changed ClosestNodeToCoordinates to include only nodes that have connections for appropriate SpeedType (e.g. roads for cars, footways for pedestrians)
Changed toVisit to be a priorityqueue.

Search is aborted, if within 250m of goal.
2023-04-01 18:10:21 +02:00
0f0f4182ac Region Size 0.025f 2023-04-01 18:08:59 +02:00
5ebe843048 Added Logging for plausability 2023-04-01 18:08:48 +02:00
03322ea143 Console time formatting 2023-04-01 18:08:31 +02:00
349ed9da94 Check if files for ways and tags exist upon import. 2023-04-01 15:54:35 +02:00
87e260562f Changed Regionsize to 0.01f 2023-04-01 15:53:51 +02:00
38 changed files with 1383 additions and 565 deletions

4
.gitignore vendored
View File

@ -2,4 +2,6 @@ bin/
obj/
/packages/
riderModule.iml
/_ReSharper.Caches/
/_ReSharper.Caches/
.idea/
*.DotSettings*

View File

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

View File

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

View File

@ -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
View 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
View 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();

View 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"
}
}
}
}

View File

@ -0,0 +1,8 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}

9
API/appsettings.json Normal file
View File

@ -0,0 +1,9 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
}

View File

@ -1,4 +1,5 @@
using System.Globalization;
using System.Text.Json.Serialization;
namespace OSMDatastructure.Graph;
@ -8,6 +9,7 @@ public class Coordinates
public float latitude { get; }
public float longitude { get; }
[JsonConstructor]
public Coordinates(float latitude, float longitude)
{
this.latitude = latitude;
@ -19,8 +21,7 @@ public class Coordinates
if (obj == null || obj.GetType() != this.GetType())
return false;
Coordinates convObj = (Coordinates)obj;
// ReSharper disable twice CompareOfFloatsByEqualityOperator static values
return convObj.latitude == this.latitude && convObj.longitude == this.longitude;
return convObj.latitude.Equals(this.latitude) && convObj.longitude.Equals(longitude);
}
public static ulong GetRegionHashCode(float latitude, float longitude)
@ -47,6 +48,6 @@ public class Coordinates
public override string ToString()
{
return
$"lat:{latitude.ToString(NumberFormatInfo.InvariantInfo)} lon:{longitude.ToString(CultureInfo.InvariantCulture)}";
$"Coordinates lat:{latitude.ToString(NumberFormatInfo.InvariantInfo)} lon:{longitude.ToString(CultureInfo.InvariantCulture)}";
}
}

View File

@ -1,3 +1,5 @@
using System.Text.Json.Serialization;
namespace OSMDatastructure.Graph;
[Serializable]
@ -9,6 +11,7 @@ public class OsmEdge
public ulong neighborRegion { get; }
[JsonConstructor]
public OsmEdge(ulong wayId, ulong startId, ulong neighborId, ulong neighborRegion)
{
this.wayId = wayId;
@ -19,6 +22,6 @@ public class OsmEdge
public override string ToString()
{
return $"w:{wayId} n1:{startId} n2:{neighborId} in r:{neighborRegion}";
return $"Edge wayId:{wayId} n1:{startId} n2:{neighborId} in regionId:{neighborRegion}";
}
}

View 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}";
}
}

View File

@ -6,4 +6,8 @@
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="System.Runtime.Serialization.Json" Version="4.3.0" />
</ItemGroup>
</Project>

View File

@ -1,60 +0,0 @@
using System.ComponentModel;
using System.Runtime.Serialization;
namespace OSMDatastructure.Graph;
[Serializable]
public class OsmNode
{
public ulong nodeId { get; }
public HashSet<OsmEdge> 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;
[OnDeserialized]
internal void SetDefaultValues(StreamingContext context)
{
currentPathWeight = double.MaxValue;
currentPathLength = double.MaxValue;
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 OsmEdge? GetEdgeToNode(OsmNode n)
{
HashSet<OsmEdge> e = edges.Where(edge => edge.neighborId == n.nodeId).ToHashSet();
if (e.Count > 0)
return e.First();
else return null;
}
public override bool Equals(object? obj)
{
return obj != null && obj.GetType() == this.GetType() && ((OsmNode)obj).nodeId == this.nodeId;
}
public override string ToString()
{
if(previousPathNode is not null)
return $"{nodeId} {coordinates} ec:{edges.Count} d:{directDistanceToGoal} w:{currentPathWeight} l:{currentPathLength} p:{previousPathNode.nodeId}";
return
$"{nodeId} {coordinates} ec:{edges.Count} d:{directDistanceToGoal} w:{currentPathWeight} l:{currentPathLength} null";
}
}

View File

@ -1,4 +1,5 @@
using System.Runtime.Serialization.Formatters.Binary;
using System.Text.Json;
using System.Text.Json.Serialization;
using OSMDatastructure.Graph;
namespace OSMDatastructure;
@ -6,21 +7,32 @@ namespace OSMDatastructure;
[Serializable]
public class Region
{
[NonSerialized]public const float RegionSize = 0.1f;
public readonly HashSet<OsmNode> nodes = new();
public ulong regionHash { get; }
public TagManager tagManager { get; }
[JsonIgnore]public const float RegionSize = 0.025f;
[JsonIgnore]public static readonly JsonSerializerOptions serializerOptions = new()
{
IncludeFields = true,
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)
{
this.regionHash = regionHash;
tagManager = new TagManager();
nodes = new HashSet<OsmNode>();
}
public Region(Coordinates coordinates)
{
regionHash = Coordinates.GetRegionHashCode(coordinates);
tagManager = new TagManager();
nodes = new HashSet<OsmNode>();
}
public bool ContainsNode(ulong id)
@ -35,31 +47,12 @@ public class Region
public OsmNode? GetNode(ulong id)
{
if (ContainsNode(id))
return nodes.First(node => node.nodeId == id);
else return null;
return ContainsNode(id) ? nodes.First(node => node.nodeId == id) : null;
}
public OsmNode? GetNode(Coordinates coordinates)
{
if (ContainsNode(coordinates))
return nodes.First(node => node.coordinates.Equals(coordinates));
else return null;
return ContainsNode(coordinates) ? nodes.First(node => node.coordinates.Equals(coordinates)) : null;
}
public static Region? FromFile(string filePath)
{
BinaryFormatter bFormatter = new BinaryFormatter();
#pragma warning disable SYSLIB0011
if (File.Exists(filePath))
return (Region)bFormatter.Deserialize(new FileStream(filePath, FileMode.Open));
#pragma warning restore SYSLIB0011
else return null;
}
public static Region? FromId(string path, ulong regionId)
{
string filePath = Path.Join(path, $"{regionId}.region");
return FromFile(filePath);
}
}

View File

@ -1,4 +1,7 @@
namespace OSMDatastructure.Graph;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace OSMDatastructure;
[Serializable]
public class Tag
@ -6,75 +9,95 @@ public class Tag
public TagType key { get; }
public dynamic value { get; }
[JsonConstructor]
public Tag(TagType key, dynamic value)
{
this.key = key;
this.value = value;
}
public static Tag FromBytes(byte[] bytes)
{
TagType type = (TagType)bytes[0];
dynamic value = false;
switch (type)
if (value is JsonElement)
{
case TagType.highway:
case TagType.oneway:
case TagType.forward:
value = BitConverter.ToBoolean(bytes, 1);
break;
case TagType.maxspeed:
value = bytes[1];
break;
switch (key)
{
case TagType.highway:
this.value = value.GetByte();
break;
case TagType.maxspeed:
this.value = value.GetByte();
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)
{
case "highway":
try
{
return new Tag(TagType.highway, (WayType)Enum.Parse(typeof(WayType), value, true));
ret.Add(new Tag(TagType.highway, (WayType)Enum.Parse(typeof(WayType), value, true)));
}
catch (ArgumentException)
{
return new Tag(TagType.highway, WayType.unclassified);
ret.Add(new Tag(TagType.highway, WayType.unclassified));
}
break;
case "maxspeed":
case "maxspeed:max":
try
{
byte speed = Convert.ToByte(value);
if (speed == 255)
return new Tag(TagType.highway, false);
else
return new Tag(TagType.maxspeed, speed);
if (speed != 255)
ret.Add(new Tag(TagType.maxspeed, speed));
}
catch (Exception)
{
//Console.WriteLine(e);
//Console.WriteLine("Continuing...");
return new Tag(TagType.maxspeed, byte.MaxValue);
ret.Add(new Tag(TagType.maxspeed, byte.MinValue));
}
break;
case "oneway":
switch (value)
{
case "yes":
return new Tag(TagType.oneway, true);
ret.Add(new Tag(TagType.oneway, true));
break;
case "-1":
return new Tag(TagType.forward, false);
ret.Add(new Tag(TagType.forward, false));
ret.Add(new Tag(TagType.oneway, true));
break;
case "no":
return new 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;
}
return null;
return ret;
}
public override string ToString()
@ -84,23 +107,24 @@ public class Tag
public enum TagType : byte
{
highway, oneway, footway, sidewalk, cycleway, busway, forward, maxspeed, name, surface, lanes, access, tracktype, id
highway, oneway, footway, sidewalk, cycleway, busway, forward, maxspeed, name, surface, lanes, access, tracktype, id, tagref
}
public static readonly Dictionary<WayType, byte> defaultSpeedCar = new() {
{ WayType.NONE, 0 },
{ WayType.motorway, 110 },
{ WayType.trunk, 100 },
{ WayType.primary, 80 },
{ WayType.secondary, 80 },
{ WayType.tertiary, 70 },
{ WayType.unclassified, 20 },
{ WayType.residential, 10 },
{ WayType.motorway_link, 50 },
{ WayType.motorway, 130 },
{ WayType.motorroad, 100 },
{ WayType.trunk, 85 },
{ WayType.primary, 65 },
{ WayType.secondary, 60 },
{ WayType.tertiary, 50 },
{ WayType.unclassified, 30 },
{ WayType.residential, 20 },
{ WayType.motorway_link, 60 },
{ WayType.trunk_link, 50 },
{ WayType.primary_link, 30 },
{ WayType.secondary_link, 25 },
{ WayType.tertiary_link, 25 },
{ WayType.primary_link, 50 },
{ WayType.secondary_link, 50 },
{ WayType.tertiary_link, 30 },
{ WayType.living_street, 10 },
{ WayType.service, 0 },
{ WayType.pedestrian, 0 },
@ -108,7 +132,7 @@ public class Tag
{ WayType.bus_guideway, 0 },
{ WayType.escape, 0 },
{ WayType.raceway, 0 },
{ WayType.road, 25 },
{ WayType.road, 20 },
{ WayType.busway, 0 },
{ WayType.footway, 0 },
{ WayType.bridleway, 0 },
@ -125,9 +149,9 @@ public class Tag
{ WayType.trunk, 0 },
{ WayType.primary, 0 },
{ WayType.secondary, 0 },
{ WayType.tertiary, 0 },
{ WayType.tertiary, 2 },
{ WayType.unclassified, 1 },
{ WayType.residential, 3 },
{ WayType.residential, 4 },
{ WayType.motorway_link, 0 },
{ WayType.trunk_link, 0 },
{ WayType.primary_link, 0 },
@ -136,21 +160,23 @@ public class Tag
{ WayType.living_street, 5 },
{ WayType.service, 2 },
{ WayType.pedestrian, 5 },
{ WayType.track, 0 },
{ WayType.track, 1 },
{ WayType.bus_guideway, 0 },
{ WayType.escape, 0 },
{ WayType.escape, 1 },
{ WayType.raceway, 0 },
{ WayType.road, 3 },
{ WayType.road, 2 },
{ WayType.busway, 0 },
{ WayType.footway, 4 },
{ WayType.bridleway, 1 },
{ WayType.steps, 2 },
{ WayType.corridor, 3 },
{ WayType.path, 4 },
{ WayType.cycleway, 2 },
{ WayType.cycleway, 1 },
{ 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 }
}

View File

@ -1,11 +1,16 @@
using System.Runtime.CompilerServices;
using System.Text.Json.Serialization;
namespace OSMDatastructure.Graph;
namespace OSMDatastructure;
[Serializable]
public class TagManager
{
public readonly Dictionary<ulong, HashSet<Tag>> wayTagSets = new();
[JsonRequired]public Dictionary<ulong, HashSet<Tag>> wayTagSets { get; set; }
public TagManager()
{
wayTagSets = new();
}
public bool ContainsKey(ulong wayId, Tag.TagType key)
{
@ -19,9 +24,10 @@ public class TagManager
public void AddTag(ulong wayId, string key, string value)
{
Tag? tag = Tag.ConvertToTag(key, value);
if(tag is not null)
AddTag(wayId, tag);
HashSet<Tag> pTags = Tag.ConvertToTags(key, value);
if(pTags.Count > 0)
foreach (Tag pTag in pTags)
AddTag(wayId, pTag);
}
public void AddTag(ulong wayId, Tag tag)

View File

@ -65,12 +65,12 @@ namespace OSMDatastructure
return d;
}
private static double DegreesToRadians(double deg)
public static double DegreesToRadians(double deg)
{
return deg * Math.PI / 180.0;
}
private static double RadiansToDegrees(double rad)
public static double RadiansToDegrees(double rad)
{
return rad * 180.0 / Math.PI;
}

View File

@ -6,6 +6,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OSMDatastructure", "OSMData
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Pathfinding", "Pathfinding\Pathfinding.csproj", "{D4AA1C47-98D4-415C-B026-3BC25B6B6F9D}"
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
GlobalSection(SolutionConfigurationPlatforms) = preSolution
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}.Release|Any CPU.ActiveCfg = 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
EndGlobal

View File

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

View File

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

View File

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

View File

@ -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
View 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
View 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))!;
}
}

View File

@ -1,119 +1,228 @@
using OSMDatastructure;
using System.Text.Json;
using OSMDatastructure;
using OSMDatastructure.Graph;
using OSMImporter;
using static OSMDatastructure.Tag;
namespace Pathfinding;
public class Pathfinder
{
public static List<OsmNode> CustomAStar(string workingDir, Coordinates start, Coordinates goal, Tag.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);
Region? startRegion = regionManager.GetRegion(start);
Region? goalRegion = regionManager.GetRegion(goal);
if (startRegion is null || goalRegion is null)
return new List<OsmNode>();
if (!Path.Exists(workingDirectory))
throw new DirectoryNotFoundException(workingDirectory);
regionManager = new(workingDirectory);
this.roadPriorityFactor = roadPriorityFactor;
this.turnAngle = turnAngle;
}
OsmNode? startNode = ClosestNodeToCoordinates(start, startRegion);
OsmNode? goalNode = ClosestNodeToCoordinates(goal, goalRegion);
if (startNode == null || goalNode == null)
return new List<OsmNode>();
List<OsmNode> toVisit = new() { startNode };
OsmNode closestNodeToGoal = toVisit.First();
closestNodeToGoal.currentPathWeight = 0;
closestNodeToGoal.currentPathLength = 0;
bool stop = false;
public Pathfinder(RegionManager regionManager, double roadPriorityFactor, double turnAngle)
{
this.regionManager = regionManager;
this.roadPriorityFactor = roadPriorityFactor;
this.turnAngle = turnAngle;
}
while (toVisit.Count > 0 && !stop)
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)
{
//Console.WriteLine("toVisit-length: {0}", toVisit.Count);
closestNodeToGoal = toVisit.First();
foreach (OsmNode node in toVisit.Where(node => node.directDistanceToGoal.Equals(Double.MaxValue)))
pathResult = new(DateTime.Now - startCalc, new List<PathNode>(),0 ,0);
return this;
}
RPriorityQueue<OsmNode, double> openSetfScore = new();
openSetfScore.Enqueue(startNode, 0);
gScore = new() { { startNode, 0 } };
_cameFromDict = new();
bool found = false;
bool stop = false;
TimeSpan firstFound = TimeSpan.MaxValue;
double maxGscore = double.MaxValue;
while (openSetfScore.Count > 0 && !stop)
{
OsmNode currentNode = openSetfScore.Dequeue()!;
if (currentNode.Equals(goalNode))
{
node.directDistanceToGoal = Utils.DistanceBetween(node, goalNode);
if (!found)
{
firstFound = DateTime.Now - startCalc;
found = true;
Console.WriteLine($"First: {firstFound} Multiplied by {extraTime}: {firstFound.Multiply(extraTime)}");
}
maxGscore = gScore[goalNode];
}
closestNodeToGoal = toVisit.OrderBy(node => node.directDistanceToGoal).First();
if (found && DateTime.Now - startCalc > firstFound.Multiply(extraTime))
stop = true;
foreach (OsmEdge edge in closestNodeToGoal.edges)
foreach (OsmEdge edge in currentNode.edges)
{
OsmNode? neighbor = regionManager.GetNode(edge.neighborId, edge.neighborRegion);
if (neighbor is not null)
{
double newPotentialWeight =
closestNodeToGoal.currentPathWeight + EdgeWeight(closestNodeToGoal, neighbor, edge.wayId, vehicle, ref regionManager);
if (neighbor.currentPathWeight > newPotentialWeight)
double tentativeGScore = gScore[currentNode] + Weight(currentNode, neighbor, edge);
gScore.TryAdd(neighbor, double.MaxValue);
if ((!found || (found && tentativeGScore < maxGscore)) && tentativeGScore < gScore[neighbor])
{
neighbor.previousPathNode = closestNodeToGoal;
neighbor.currentPathWeight = newPotentialWeight;
neighbor.currentPathLength = closestNodeToGoal.currentPathLength + Utils.DistanceBetween(closestNodeToGoal, neighbor);
if (neighbor.Equals(goalNode))
stop = true;
else
toVisit.Add(neighbor);
if(!_cameFromDict.TryAdd(neighbor, currentNode))
_cameFromDict[neighbor] = currentNode;
gScore[neighbor] = tentativeGScore;
double h = Heuristic(currentNode, neighbor, goalNode, edge);
openSetfScore.Enqueue(neighbor, tentativeGScore + h);
}
}
}
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 = double.MaxValue;
foreach (OsmNode node in region.nodes)
{
double nodeDistance = Utils.DistanceBetween(node, coordinates);
if (nodeDistance < distance)
{
closest = node;
distance = nodeDistance;
}
}
double distance = Utils.DistanceBetween(currentNode, neighborNode);
double speed = regionManager.GetSpeedForEdge(currentNode, edge.wayId, _speedType);
return closest;
double angle = 1;
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;
}
double prio = regionManager.GetPriorityForVehicle(_speedType,edge, currentNode) * roadPriorityFactor;
return distance / (speed * angle + prio + 1);
}
private static double EdgeWeight(OsmNode node1, OsmNode node2, ulong wayId, Tag.SpeedType vehicle, ref RegionManager regionManager)
private double Heuristic(OsmNode currentNode, OsmNode neighborNode, OsmNode goalNode, OsmEdge edge)
{
TagManager tags = regionManager.GetRegion(node1.coordinates)!.tagManager;
double distance = Utils.DistanceBetween(node1, node2);
Tag.WayType wayType = (Tag.WayType)tags.GetTag(wayId, Tag.TagType.highway)!;
switch (vehicle)
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))
{
case Tag.SpeedType.pedestrian:
byte speed = Tag.defaultSpeedPedestrian[wayType];
if(speed is not 0)
return distance / speed;
else return Double.PositiveInfinity;
case Tag.SpeedType.car:
case Tag.SpeedType.road:
byte? maxspeed = (byte?)tags.GetTag(wayId, Tag.TagType.maxspeed);
if (maxspeed is not null)
return distance / Convert.ToDouble(maxspeed);
else
maxspeed = Tag.defaultSpeedCar[wayType];
if(maxspeed is not 0)
return distance / Tag.defaultSpeedCar[wayType];
else return Double.PositiveInfinity;;
default:
return double.PositiveInfinity;
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 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;
}
}
}

View File

@ -10,4 +10,8 @@
<ProjectReference Include="..\OSMDatastructure\OSMDatastructure.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="System.Runtime.Serialization.Json" Version="4.3.0" />
</ItemGroup>
</Project>

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

View File

@ -1,7 +1,10 @@
using System.Text.Json;
using OSMDatastructure;
using OSMDatastructure.Graph;
using SpeedType = OSMDatastructure.Tag.SpeedType;
using WayType = OSMDatastructure.Tag.WayType;
namespace OSMImporter
namespace Pathfinding
{
public class RegionManager
{
@ -20,38 +23,39 @@ namespace OSMImporter
public Region? GetRegion(ulong id)
{
if(_regions.TryGetValue(id, out Region? value))
return value;
else
if (!_regions.ContainsKey(id))
{
Region? loadedRegion = LoadRegion(id);
if(loadedRegion is not null)
_regions.Add(loadedRegion!.regionHash, value: loadedRegion);
return loadedRegion;
Region? loadedRegion = RegionFromId(id);
if (loadedRegion is not null)
_regions.TryAdd(loadedRegion.regionHash, loadedRegion);
return _regions[id]; //return from _regions instead of loadedRegion for multithreading/pointers
}
return _regions[id];
}
public Region[] GetAllRegions()
{
return this._regions.Values.ToArray();
return _regions.Values.ToArray();
}
private Region? LoadRegion(Coordinates coordinates)
private static Region? RegionFromFile(string filePath)
{
return LoadRegion(Coordinates.GetRegionHashCode(coordinates));
if (!File.Exists(filePath))
{
//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? LoadRegion(ulong id)
private Region? RegionFromId(ulong regionId)
{
return Region.FromId(workingDirectory, id);
}
public OsmNode? GetNode(Coordinates coordinates)
{
Region? regionWithNode = GetRegion(coordinates);
if (regionWithNode is not null)
return regionWithNode.GetNode(coordinates);
else return null;
string filePath = Path.Join(workingDirectory, $"{regionId}.region");
return RegionFromFile(filePath);
}
public OsmNode? GetNode(ulong nodeId, ulong regionId)
@ -59,5 +63,138 @@ namespace OSMImporter
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
View 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
View 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

View File

@ -4,11 +4,16 @@
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<RootNamespace>OSMServer</RootNamespace>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="System.Drawing.Common" Version="7.0.0" />
<PackageReference Include="System.Runtime.Serialization.Json" Version="4.3.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\OSMDatastructure\OSMDatastructure.csproj" />
<ProjectReference Include="..\Pathfinding\Pathfinding.csproj" />
</ItemGroup>
</Project>

194
RenderPath/Renderer.cs Normal file
View 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
View 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}";
}
}

View File

@ -1,4 +1,3 @@
using System.Globalization;
using System.Text;
namespace Server;
@ -28,7 +27,7 @@ public class ConsoleWriter : TextWriter
public override void WriteLine(string? text)
{
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)
OnWriteLine?.Invoke(this, new ConsoleWriterEventArgs($"{dateTimeString} {text}"));
stdOut.WriteLine($"{dateTimeString} {text}");

View File

@ -1,5 +1,6 @@
using System.Globalization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text.Json;
using System.Xml;
using OSMDatastructure;
using OSMDatastructure.Graph;
@ -46,7 +47,6 @@ public class RegionConverter
private static Dictionary<ulong, ulong> GetNodesAndRegions(XmlReader xmlReader, string outputPath)
{
BinaryFormatter bFormatter = new BinaryFormatter();
Dictionary<ulong, ulong> nodeRegions = new();
Dictionary<ulong, FileStream> regionFileStreams = new();
Dictionary<ulong, OsmNode> tmpAllNodes = new();
@ -82,6 +82,7 @@ public class RegionConverter
foreach (ulong nodeId in currentIds.Where(key => tmpAllNodes.ContainsKey(key)))
{
BinaryFormatter bFormatter = new BinaryFormatter();
if (isHighway)
{
ulong regionHash = Coordinates.GetRegionHashCode(tmpAllNodes[nodeId].coordinates);
@ -97,7 +98,7 @@ public class RegionConverter
regionFileStreams.Add(regionHash, nodesRegionStream);
}
nodeRegions.Add(nodeId, regionHash);
#pragma warning disable SYSLIB0011 //eheheh
bFormatter.Serialize(nodesRegionStream, tmpAllNodes[nodeId]);
#pragma warning restore SYSLIB0011
@ -128,9 +129,10 @@ public class RegionConverter
currentTags.TryAdd(Tag.TagType.id, Convert.ToUInt64(wayReader.GetAttribute("id")!));
if (wayReader.Name == "tag")
{
Tag? wayTag = Tag.ConvertToTag(wayReader.GetAttribute("k")!, wayReader.GetAttribute("v")!);
if(wayTag is not null)
currentTags.TryAdd(wayTag.key, wayTag.value);
HashSet<Tag> pTags = Tag.ConvertToTags(wayReader.GetAttribute("k")!, wayReader.GetAttribute("v")!);
if(pTags.Count > 0)
foreach (Tag pTag in pTags)
currentTags.TryAdd(pTag.key, pTag.value);
}
else if (wayReader.Name == "nd")
{
@ -145,31 +147,42 @@ public class RegionConverter
{
ulong node1Id = currentNodeIds[i];
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])
{
OsmEdge n21e = new OsmEdge(currentTags[Tag.TagType.id], node2Id, node1Id, nodeRegions[node2Id]);
WriteWay(ref regionWaysFileStreams, nodeRegions[node2Id], n21e, outputPath);
WriteTags(ref regionTagsFileStreams, ref writtenWayTagsInRegion, nodeRegions[node2Id], currentTags, outputPath);
if (currentTags.ContainsKey(Tag.TagType.forward) && !(bool)currentTags[Tag.TagType.forward])
{
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
{
OsmEdge n12e = new OsmEdge(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);
WriteTags(ref regionTagsFileStreams, ref writtenWayTagsInRegion, 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))
{
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);
OsmEdge n21e = new OsmEdge(currentTags[Tag.TagType.id], node2Id, node1Id, nodeRegions[node2Id]);
WriteWay(ref regionWaysFileStreams, nodeRegions[node2Id], n21e, outputPath);
WriteTags(ref regionTagsFileStreams, ref writtenWayTagsInRegion, nodeRegions[node2Id], currentTags, outputPath);
}
}
}
}
@ -221,7 +234,6 @@ public class RegionConverter
private static void CombineTmpFiles(string folderPath, HashSet<ulong> regionHashes)
{
BinaryFormatter bFormatter = new BinaryFormatter();
foreach (ulong regionId in regionHashes)
{
ValueTuple<Region, HashSet<OsmEdge>> tmpRegion = LoadRegion(folderPath, regionId);
@ -233,11 +245,11 @@ public class RegionConverter
startNode.edges.Add(edge);
}
}
FileStream tmpRegionFileStream = new FileStream(Path.Join(folderPath, $"{regionId}{RegionsFileName}"), FileMode.Create);
#pragma warning disable SYSLIB0011
bFormatter.Serialize(tmpRegionFileStream, tmpRegion.Item1);
#pragma warning restore SYSLIB0011
tmpRegionFileStream.Dispose();
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);
}
}
@ -251,14 +263,16 @@ public class RegionConverter
throw new FileNotFoundException("Region does not exist");
#pragma warning disable SYSLIB0011
using (FileStream wayFileStream = new FileStream(Path.Join(folderPath, regionHash.ToString(), RegionConverter.WaysFileName), FileMode.Open))
{
while (wayFileStream.Position < wayFileStream.Length)
string waysPath = Path.Join(folderPath, regionHash.ToString(), RegionConverter.WaysFileName);
if(File.Exists(waysPath))
using (FileStream wayFileStream = new FileStream(waysPath, FileMode.Open))
{
OsmEdge deserializedEdge = (OsmEdge)bFormatter.Deserialize(wayFileStream);
ways.Add(deserializedEdge);
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))
{
@ -268,17 +282,19 @@ public class RegionConverter
newRegion.nodes.Add(deserializedNode);
}
}
using (FileStream tagsFileStream = new FileStream(Path.Join(folderPath, regionHash.ToString(), RegionConverter.TagsFileName), FileMode.Open))
{
while (tagsFileStream.Position < tagsFileStream.Length)
string tagsPath = Path.Join(folderPath, regionHash.ToString(), RegionConverter.TagsFileName);
if(File.Exists(tagsPath))
using (FileStream tagsFileStream = new FileStream(tagsPath, FileMode.Open))
{
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);
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);

View File

@ -1,37 +1,183 @@
using System.Diagnostics.CodeAnalysis;
using System.Drawing;
using System.Drawing.Imaging;
using System.Text.Json;
using OSMDatastructure;
using OSMDatastructure.Graph;
using Pathfinding;
using RenderPath;
using Region = OSMDatastructure.Region;
namespace Server;
public class Server
{
[SuppressMessage("Interoperability", "CA1416:Validate platform compatibility")]
public static void Main(string[] args)
{
ConsoleWriter newConsole = new ConsoleWriter();
ConsoleWriter newConsole = new();
Console.SetOut(newConsole);
Console.SetError(newConsole);
string workingDir = "D:/stuttgart-regbez-latest";
//RegionConverter.ConvertXMLToRegions("D:/stuttgart-regbez-latest.osm", "D:/stuttgart-regbez-latest");
//RegionConverter.ConvertXMLToRegions("D:/map.osm", "D:/map");
Console.WriteLine("Loaded");
//RegionConverter.ConvertXMLToRegions("D:/germany-latest.osm", "D:/germany-latest");
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();
*/
//TestVariables(workingDir, start, finish, 12);
//GetShortestRoute("D:");
/*
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)
{
PathResult result = PathResult.PathresultFromFile(filePath);
results.Add(result, filePath);
Console.WriteLine($"{loaded++}/{allFiles.Count()} {filePath} " +
$"Time elapsed: {DateTime.Now - start} " +
$"Remaining {((DateTime.Now - start)/loaded)*(allFiles.Count-loaded)}");
}
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))!;
Coordinates start = new Coordinates(48.7933989f, 9.8301467f);
Coordinates finish = new Coordinates(48.7906258f, 9.8355983f);
OsmNode[] path = Pathfinder.CustomAStar("D:/map", start, finish, Tag.SpeedType.pedestrian).ToArray();
Console.WriteLine("{0}\n", path[0].ToString());
for (int i = 0; i < path.Length - 1; i++)
for (double extraTime = 1.5; extraTime >= 1; extraTime -= 0.25)
{
OsmNode n1 = path[i];
OsmNode n2 = path[i + 1];
OsmEdge? e = n1.GetEdgeToNode(n2);
if(e != null)
Console.WriteLine("{0}\n{1}", e.ToString(), n2.ToString());
else
Console.WriteLine("NO EDGE\n{0}", n2.ToString());
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();
}
}

View File

@ -9,6 +9,12 @@
<ItemGroup>
<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>
</Project>