├── .gitignore ├── Resources └── Icon128.png ├── Source ├── SumoUE4 │ ├── Public │ │ ├── SumoUE4Module.h │ │ ├── SumoBaseActor.h │ │ ├── SumoStructs │ │ │ ├── SumoStopOffset.h │ │ │ ├── SumoRequest.h │ │ │ ├── SumoLane.h │ │ │ ├── SumoVehicleType.h │ │ │ ├── SumoTrafficLightProgram.h │ │ │ ├── SumoJunction.h │ │ │ ├── SumoRoadType.h │ │ │ ├── SumoEdge.h │ │ │ ├── SumoConnection.h │ │ │ └── SumoTrafficLightPhase.h │ │ ├── SumoNetworkAsset.h │ │ ├── SumoJunctionActor.h │ │ ├── SumoEdgeActor.h │ │ └── earcut │ │ │ └── earcut.hpp │ ├── Private │ │ ├── SumoUE4Module.cpp │ │ ├── SumoBaseActor.cpp │ │ ├── SumoEdgeActor.cpp │ │ └── SumoJunctionActor.cpp │ └── SumoUE4.Build.cs └── SumoUE4Editor │ ├── Private │ ├── Factories │ │ ├── SumoNetworkAssetFactory.h │ │ └── SumoNetworkAssetFactory.cpp │ ├── Helpers │ │ ├── SumoNetworkParser.h │ │ └── SumoNetworkParser.cpp │ └── SumoUE4EditorModule.cpp │ └── SumoUE4Editor.Build.cs ├── .gitattributes ├── LICENSE ├── SumoUE4.uplugin └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | Binaries/* 2 | Intermediate/* 3 | dist/* 4 | *.gch 5 | -------------------------------------------------------------------------------- /Resources/Icon128.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:f7239efaeefbd82de33ebe18518e50de075ea4188a468a9e4991396433d2275f 3 | size 12699 4 | -------------------------------------------------------------------------------- /Source/SumoUE4/Public/SumoUE4Module.h: -------------------------------------------------------------------------------- 1 | // Copyright Epic Games, Inc. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "Modules/ModuleManager.h" 7 | 8 | class FSumoUE4Module : public IModuleInterface 9 | { 10 | public: 11 | 12 | /** IModuleInterface implementation */ 13 | virtual void StartupModule() override; 14 | virtual void ShutdownModule() override; 15 | }; 16 | -------------------------------------------------------------------------------- /Source/SumoUE4/Private/SumoUE4Module.cpp: -------------------------------------------------------------------------------- 1 | // Copyright Epic Games, Inc. All Rights Reserved. 2 | 3 | #include "SumoUE4Module.h" 4 | 5 | #define LOCTEXT_NAMESPACE "FSumoUE4Module" 6 | 7 | void FSumoUE4Module::StartupModule() 8 | { 9 | // This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module 10 | } 11 | 12 | void FSumoUE4Module::ShutdownModule() 13 | { 14 | // This function may be called during shutdown to clean up your module. For modules that support dynamic reloading, 15 | // we call this function before unloading the module. 16 | } 17 | 18 | #undef LOCTEXT_NAMESPACE 19 | 20 | IMPLEMENT_MODULE(FSumoUE4Module, SumoUE4) 21 | -------------------------------------------------------------------------------- /Source/SumoUE4Editor/Private/Factories/SumoNetworkAssetFactory.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Iwer Petersen. All rights reserved. 2 | #pragma once 3 | 4 | #include "Factories/Factory.h" 5 | #include "UObject/ObjectMacros.h" 6 | 7 | #include "SumoNetworkAssetFactory.generated.h" 8 | 9 | UCLASS(BlueprintType, hidecategories=Object) 10 | class USumoNetworkAssetFactory 11 | : public UFactory 12 | { 13 | GENERATED_BODY() 14 | public: 15 | USumoNetworkAssetFactory(const FObjectInitializer& ObjectInitializer); 16 | virtual UObject* FactoryCreateFile(UClass* InClass, UObject* InParent, FName InName, EObjectFlags Flags, const FString& Filename, const TCHAR* Parms, FFeedbackContext* Warn, bool& bOutOperationCanceled) override; 17 | virtual bool FactoryCanImport(const FString & Filename) override; 18 | }; 19 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.fbx filter=lfs diff=lfs merge=lfs -text 2 | *.blend filter=lfs diff=lfs merge=lfs -text 3 | *.obj filter=lfs diff=lfs merge=lfs -text 4 | *.png filter=lfs diff=lfs merge=lfs -text 5 | *.jpg filter=lfs diff=lfs merge=lfs -text 6 | *.jpeg filter=lfs diff=lfs merge=lfs -text 7 | *.hdr filter=lfs diff=lfs merge=lfs -text 8 | *.exr filter=lfs diff=lfs merge=lfs -text 9 | *.wav filter=lfs diff=lfs merge=lfs -text 10 | *.mp3 filter=lfs diff=lfs merge=lfs -text 11 | *.mp4 filter=lfs diff=lfs merge=lfs -text 12 | *.mov filter=lfs diff=lfs merge=lfs -text 13 | *.psd filter=lfs diff=lfs merge=lfs -text 14 | *.tga filter=lfs diff=lfs merge=lfs -text 15 | *.tif filter=lfs diff=lfs merge=lfs -text 16 | *.uasset filter=lfs diff=lfs merge=lfs -text 17 | *.umap filter=lfs diff=lfs merge=lfs -text 18 | *.upk filter=lfs diff=lfs merge=lfs -text 19 | *.udk filter=lfs diff=lfs merge=lfs -text 20 | -------------------------------------------------------------------------------- /Source/SumoUE4/Public/SumoBaseActor.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Iwer Petersen. All rights reserved. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "GameFramework/Actor.h" 7 | #include "Components/SplineComponent.h" 8 | #include "GeoLocatedActor.h" 9 | #include "SumoNetworkAsset.h" 10 | 11 | #include "SumoBaseActor.generated.h" 12 | 13 | 14 | UCLASS(Abstract) 15 | class SUMOUE4_API ASumoBaseActor : public AGeoLocatedActor 16 | { 17 | GENERATED_BODY() 18 | public: 19 | UPROPERTY(BlueprintReadWrite, EditAnywhere) 20 | USumoNetworkAsset * SumoNetwork; 21 | 22 | ASumoBaseActor(); 23 | 24 | protected: 25 | // Called when the game starts or when spawned 26 | virtual void BeginPlay() override; 27 | 28 | public: 29 | // Called every frame 30 | virtual void Tick(float DeltaTime) override; 31 | virtual void OnConstruction(const FTransform &Transform) override; 32 | 33 | USplineComponent * ConstructSpline(TArray &shape, bool bPutToGround, float groundOffset = 0); 34 | 35 | }; 36 | -------------------------------------------------------------------------------- /Source/SumoUE4/Public/SumoStructs/SumoStopOffset.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Iwer Petersen. All rights reserved. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "SumoVehicleType.h" 7 | 8 | #include "SumoStopOffset.generated.h" 9 | 10 | USTRUCT(Blueprintable, BlueprintType) 11 | struct FSumoStopOffset { 12 | GENERATED_BODY() 13 | 14 | /** 15 | * The stop offset as positive value in meters. 16 | */ 17 | UPROPERTY(BlueprintReadWrite, EditAnywhere) 18 | float Value; 19 | 20 | /** 21 | * Specifies, for which vehicle classes the stopOffset applies. 22 | */ 23 | UPROPERTY(BlueprintReadWrite, EditAnywhere) 24 | TArray> VehicleClasses; 25 | 26 | /** 27 | * Specifies, for which vehicle classes the stopOffset does not apply. 28 | */ 29 | UPROPERTY(BlueprintReadWrite, EditAnywhere) 30 | TArray> Exceptions; 31 | 32 | FSumoStopOffset() { 33 | Value = 0.0; 34 | VehicleClasses.Empty(); 35 | Exceptions.Empty(); 36 | } 37 | }; 38 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Petersen, Iwer 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /SumoUE4.uplugin: -------------------------------------------------------------------------------- 1 | { 2 | "FileVersion": 3, 3 | "Version": 1, 4 | "VersionName": "1.0", 5 | "FriendlyName": "SumoUE4", 6 | "Description": "Importer for SUMO .net.xml files", 7 | "Category": "Other", 8 | "CreatedBy": "Iwer Petersen", 9 | "CreatedByURL": "", 10 | "DocsURL": "", 11 | "MarketplaceURL": "", 12 | "SupportURL": "", 13 | "CanContainContent": true, 14 | "IsBetaVersion": true, 15 | "IsExperimentalVersion": false, 16 | "Installed": false, 17 | "Modules": [ 18 | { 19 | "Name": "SumoUE4", 20 | "Type": "Runtime", 21 | "LoadingPhase": "Default", 22 | "AdditionalDependencies": [ 23 | "GeoReference", 24 | "SpatialGeometryTools" 25 | ] 26 | }, 27 | { 28 | "Name": "SumoUE4Editor", 29 | "Type": "Editor", 30 | "LoadingPhase": "PostEngineInit", 31 | "AdditionalDependencies": [ 32 | "GeoReference" 33 | ] 34 | } 35 | ], 36 | "Plugins": [ 37 | { 38 | "Enabled": true, 39 | "Name": "ProceduralMeshComponent" 40 | }, 41 | { 42 | "Enabled": true, 43 | "Name": "GeoReference" 44 | }, 45 | { 46 | "Enabled": true, 47 | "Name": "SpatialGeometryTools" 48 | } 49 | ] 50 | } 51 | -------------------------------------------------------------------------------- /Source/SumoUE4/Public/SumoNetworkAsset.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Iwer Petersen. All rights reserved. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "UObject/Object.h" 7 | #include "UObject/ObjectMacros.h" 8 | #include "Engine/DataAsset.h" 9 | #include "SumoStructs/SumoJunction.h" 10 | #include "SumoStructs/SumoEdge.h" 11 | #include "SumoStructs/SumoRoadType.h" 12 | #include "SumoStructs/SumoTrafficLightProgram.h" 13 | #include "SumoStructs/SumoConnection.h" 14 | 15 | #include "SumoNetworkAsset.generated.h" 16 | /** 17 | * 18 | */ 19 | 20 | 21 | UCLASS(BlueprintType, hidecategories=(Object)) 22 | class SUMOUE4_API USumoNetworkAsset : public UDataAsset 23 | { 24 | GENERATED_BODY() 25 | 26 | public: 27 | UPROPERTY(BlueprintReadOnly,EditAnywhere) 28 | TMap Edges; 29 | 30 | UPROPERTY(BlueprintReadOnly,EditAnywhere) 31 | TMap Junctions; 32 | 33 | UPROPERTY(BlueprintReadOnly,EditAnywhere) 34 | TMap RoadTypes; 35 | 36 | UPROPERTY(BlueprintReadOnly,EditAnywhere) 37 | TMap TrafficLightPrograms; 38 | 39 | UPROPERTY(BlueprintReadOnly, EditAnywhere) 40 | TArray Connections; 41 | }; 42 | -------------------------------------------------------------------------------- /Source/SumoUE4/Public/SumoStructs/SumoRequest.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Iwer Petersen. All rights reserved. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | 7 | 8 | #include "SumoRequest.generated.h" 9 | 10 | USTRUCT(Blueprintable, BlueprintType) 11 | struct FSumoRequest { 12 | GENERATED_BODY() 13 | 14 | /** 15 | * The index of the described connection in the right-of-way matrix 16 | */ 17 | UPROPERTY(BlueprintReadWrite, EditAnywhere) 18 | int Index; 19 | 20 | /** 21 | * A bitstring that describes for each connection whether it prohibits 22 | * un-decelerated passing of the intersection for vehicles at this 23 | * connection. The rightmost bit corresponds to index 0. 24 | */ 25 | UPROPERTY(BlueprintReadWrite, EditAnywhere) 26 | FString Response; 27 | 28 | /** 29 | * 30 | */ 31 | UPROPERTY(BlueprintReadWrite, EditAnywhere) 32 | FString Foes; 33 | 34 | /** 35 | * A bitstring that describes for each connection whether it conflicts 36 | * with this connection. The rightmost bit corresponds to index 0. 37 | */ 38 | UPROPERTY(BlueprintReadWrite, EditAnywhere) 39 | bool bContinue; 40 | 41 | FSumoRequest() { 42 | Index = 0; 43 | Response = TEXT(""); 44 | Foes = TEXT(""); 45 | bContinue = false; 46 | } 47 | }; 48 | -------------------------------------------------------------------------------- /Source/SumoUE4/SumoUE4.Build.cs: -------------------------------------------------------------------------------- 1 | // Copyright Epic Games, Inc. All Rights Reserved. 2 | 3 | using UnrealBuildTool; 4 | 5 | public class SumoUE4 : ModuleRules 6 | { 7 | public SumoUE4(ReadOnlyTargetRules Target) : base(Target) 8 | { 9 | PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs; 10 | 11 | PublicIncludePaths.AddRange( 12 | new string[] { 13 | // ... add public include paths required here ... 14 | } 15 | ); 16 | 17 | 18 | PrivateIncludePaths.AddRange( 19 | new string[] { 20 | // ... add other private include paths required here ... 21 | } 22 | ); 23 | 24 | 25 | PublicDependencyModuleNames.AddRange( 26 | new string[] 27 | { 28 | "Core", 29 | // ... add other public dependencies that you statically link with here ... 30 | } 31 | ); 32 | 33 | 34 | PrivateDependencyModuleNames.AddRange( 35 | new string[] 36 | { 37 | "CoreUObject", 38 | "Engine", 39 | "Slate", 40 | "SlateCore", 41 | "ProceduralMeshComponent", 42 | "GeoReference", 43 | "SpatialGeometryTools" 44 | // ... add private dependencies that you statically link with here ... 45 | } 46 | ); 47 | 48 | 49 | DynamicallyLoadedModuleNames.AddRange( 50 | new string[] 51 | { 52 | // ... add any modules that your module loads dynamically here ... 53 | } 54 | ); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /Source/SumoUE4/Public/SumoStructs/SumoLane.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Iwer Petersen. All rights reserved. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "SumoStopOffset.h" 7 | #include "SumoLane.generated.h" 8 | 9 | USTRUCT(Blueprintable, BlueprintType) 10 | struct FSumoLane { 11 | GENERATED_BODY() 12 | 13 | /** 14 | * The id of the lane 15 | */ 16 | UPROPERTY(BlueprintReadWrite, EditAnywhere) 17 | FString ID; 18 | 19 | /** 20 | * A running number, starting with zero at the right-most lane 21 | */ 22 | UPROPERTY(BlueprintReadWrite, EditAnywhere) 23 | int Index; 24 | 25 | /** 26 | * The maximum speed allowed on this lane [m/s] 27 | */ 28 | UPROPERTY(BlueprintReadWrite, EditAnywhere) 29 | float Speed; 30 | 31 | /** 32 | * The length of this lane [m] 33 | */ 34 | UPROPERTY(BlueprintReadWrite, EditAnywhere) 35 | float Length; 36 | 37 | /** 38 | * 39 | */ 40 | UPROPERTY(BlueprintReadWrite, EditAnywhere) 41 | FSumoStopOffset StopOffset; 42 | 43 | /** 44 | * The geometry of the lane, given by a polyline that describes the lane's 45 | * center line; must not be empty or have less than two positions 46 | */ 47 | UPROPERTY(BlueprintReadWrite, EditAnywhere) 48 | TArray Shape; 49 | 50 | FSumoLane() { 51 | ID = TEXT(""); 52 | Index = 0; 53 | Speed = 0.0; 54 | Length = 0.1; 55 | Shape.Empty(); 56 | } 57 | }; 58 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SumoUE4 2 | 3 | Unreal Engine Plugin to import [SUMO](https://www.eclipse.org/sumo/) *.net.xml files into Unreal Editor and generate roads. It is inspired by and builds upon the work of [CruzWay](https://github.com/AugmentedDesignLab/CruzWay) but tries to be more flexible by separating data and representation. 4 | 5 | This should be considered WIP. 6 | 7 | ## Compatibility 8 | 9 | * Unreal Engine 4 is supported at least with 4.27 (branch ue4, tag ue4.27) 10 | * Unreal Engine 5 is supported up to 5.1 (on master branch) 11 | 12 | ## Dependencies 13 | 14 | Depends on [GeoReference](https://github.com/iwer/GeoReference) and [SpatialGeometryTools](https://github.com/iwer/SpatialGeometryTools) Plugin. 15 | 16 | ## Features 17 | 18 | #### Data Asset Import 19 | Import of *.net.xml Files as structured data assets as per (SUMO-Docs)(https://sumo.dlr.de/docs/Networks/SUMO_Road_Networks.html), including: 20 | * Edges and Lanes 21 | * Traffic Light Programs (Without additionals) 22 | * Junction including Right-of-Way 23 | * Connections 24 | 25 | This data can then be used to create data-driven Assets. Be aware that this data assets can get very large and should not be opened in the Unreal Editor. Instead, in Blueprints use a reference to the data asset and get individual junctions, edges, lanes etc. to construct visual representation or routing information actors. 26 | 27 | #### Data-driven actors 28 | 29 | * SumoJunctionActor: Geo located ProceduralMeshComponent with Lane Splines 30 | * Sumo EdgeActor: Geo located lane Splines with Splinemeshcomponents 31 | -------------------------------------------------------------------------------- /Source/SumoUE4Editor/Private/Factories/SumoNetworkAssetFactory.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Iwer Petersen. All rights reserved. 2 | 3 | #include "SumoNetworkAssetFactory.h" 4 | #include "SumoNetworkAsset.h" 5 | #include "SumoNetworkParser.h" 6 | 7 | USumoNetworkAssetFactory::USumoNetworkAssetFactory( const FObjectInitializer& ObjectInitializer ) 8 | : Super(ObjectInitializer) 9 | { 10 | SupportedClass = USumoNetworkAsset::StaticClass(); 11 | bCreateNew = false; 12 | bEditorImport = true; 13 | Formats.Add(TEXT("xml;Sumo network definition file")); 14 | } 15 | 16 | UObject * USumoNetworkAssetFactory::FactoryCreateFile(UClass* InClass, 17 | UObject* InParent, 18 | FName InName, 19 | EObjectFlags Flags, 20 | const FString& Filename, 21 | const TCHAR* Parms, 22 | FFeedbackContext* Warn, 23 | bool& bOutOperationCanceled) 24 | { 25 | USumoNetworkAsset* Asset = NewObject(InParent, InClass, InName, Flags); 26 | 27 | // populate Asset 28 | USumoNetworkParser parser; 29 | parser.ParseFile(Filename, Asset); 30 | 31 | return Asset; 32 | } 33 | 34 | bool USumoNetworkAssetFactory::FactoryCanImport(const FString & Filename) 35 | { 36 | if(!Filename.EndsWith(TEXT(".net.xml"))){ 37 | return false; 38 | } 39 | return true; 40 | } 41 | -------------------------------------------------------------------------------- /Source/SumoUE4Editor/SumoUE4Editor.Build.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Iwer Petersen. All rights reserved. 2 | 3 | using UnrealBuildTool; 4 | 5 | public class SumoUE4Editor : ModuleRules 6 | { 7 | public SumoUE4Editor(ReadOnlyTargetRules Target) : base(Target) 8 | { 9 | PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs; 10 | 11 | PublicIncludePaths.AddRange( 12 | new string[] { 13 | // ... add public include paths required here ... 14 | }); 15 | 16 | 17 | PrivateIncludePaths.AddRange( 18 | new string[] { 19 | // ... add other private include paths required here ... 20 | "SumoUE4Editor/Private", 21 | "SumoUE4Editor/Private/Factories", 22 | "SumoUE4Editor/Private/Helpers" 23 | }); 24 | 25 | 26 | PrivateDependencyModuleNames.AddRange( 27 | new string[] 28 | { 29 | // ... add private dependencies that you statically link with here ... 30 | "ContentBrowser", 31 | "Core", 32 | "CoreUObject", 33 | "DesktopWidgets", 34 | "EditorStyle", 35 | "Engine", 36 | "InputCore", 37 | "Projects", 38 | "RenderCore", 39 | "Slate", 40 | "SlateCore", 41 | "UnrealEd", 42 | "XmlParser", 43 | "GDAL", 44 | "UnrealGDAL", 45 | "GeoReference", 46 | "SumoUE4" 47 | }); 48 | 49 | 50 | DynamicallyLoadedModuleNames.AddRange( 51 | new string[] 52 | { 53 | // ... add any modules that your module loads dynamically here ... 54 | "AssetTools", 55 | "MainFrame" 56 | }); 57 | 58 | PrivateIncludePathModuleNames.AddRange( 59 | new string[] { 60 | "AssetTools", 61 | "UnrealEd" 62 | }); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /Source/SumoUE4/Public/SumoJunctionActor.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Iwer Petersen. All rights reserved. 2 | 3 | #pragma once 4 | 5 | #include "SumoBaseActor.h" 6 | #include "ProceduralMeshComponent.h" 7 | 8 | 9 | #include "SumoJunctionActor.generated.h" 10 | 11 | UCLASS() 12 | class SUMOUE4_API ASumoJunctionActor : public ASumoBaseActor 13 | { 14 | GENERATED_BODY() 15 | 16 | public: 17 | UPROPERTY(BlueprintReadWrite, EditAnywhere) 18 | FSumoJunction JunctionData; 19 | 20 | UPROPERTY(BlueprintReadWrite, EditAnywhere) 21 | UProceduralMeshComponent *ProcMesh; 22 | 23 | UPROPERTY(BlueprintReadWrite, EditAnywhere) 24 | float PavementHeight; 25 | 26 | UPROPERTY(BlueprintReadWrite, EditAnywhere) 27 | TArray LaneSplines; 28 | 29 | // Sets default values for this actor's properties 30 | ASumoJunctionActor(); 31 | 32 | 33 | protected: 34 | // Called when the game starts or when spawned 35 | virtual void BeginPlay() override; 36 | 37 | public: 38 | // Called every frame 39 | virtual void Tick(float DeltaTime) override; 40 | virtual void OnConstruction(const FTransform &Transform) override; 41 | private: 42 | struct MeshData { 43 | int32 OrigVertexNum; 44 | TArray Vertices; 45 | TArray TriIndices; 46 | TArray Normals; 47 | TArray UV0; 48 | TArray VertColors; 49 | TArray Tangents; 50 | }; 51 | void SetLocation(FVector2D & geocoordinates); 52 | void ConstructMesh(TArray &vertices); 53 | void AddQuadMeshSection(MeshData &meshdata, const FVector &p1, const FVector &p2, const FVector &p1a, const FVector &p2a); 54 | 55 | void ConstructInternalLanes(); 56 | 57 | }; 58 | -------------------------------------------------------------------------------- /Source/SumoUE4/Public/SumoEdgeActor.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Iwer Petersen. All rights reserved. 2 | 3 | #pragma once 4 | 5 | #include "SumoBaseActor.h" 6 | #include "Components/SplineMeshComponent.h" 7 | #include "SumoStructs/SumoEdge.h" 8 | #include "SumoJunctionActor.h" 9 | 10 | #include "SumoEdgeActor.generated.h" 11 | 12 | UCLASS() 13 | class SUMOUE4_API ASumoEdgeActor : public ASumoBaseActor 14 | { 15 | GENERATED_BODY() 16 | public: 17 | UPROPERTY(BlueprintReadWrite, EditAnywhere) 18 | FSumoEdge EdgeData; 19 | 20 | UPROPERTY(BlueprintReadWrite, EditAnywhere) 21 | TArray Lanes; 22 | 23 | UPROPERTY(BlueprintReadWrite, EditAnywhere) 24 | TArray LaneMeshes; 25 | 26 | UPROPERTY(BlueprintReadWrite, EditAnywhere) 27 | UStaticMesh * RoadProfileMesh; 28 | 29 | UPROPERTY(BlueprintReadWrite, EditAnywhere) 30 | bool bReparamSpline; 31 | 32 | UPROPERTY(BlueprintReadWrite, EditAnywhere) 33 | float SplineSectionLength; 34 | 35 | UPROPERTY(BlueprintReadWrite, EditAnywhere) 36 | TMap FromJunctions; 37 | 38 | UPROPERTY(BlueprintReadWrite, EditAnywhere) 39 | TMap ToJunctions; 40 | 41 | ASumoEdgeActor(); 42 | 43 | protected: 44 | // Called when the game starts or when spawned 45 | virtual void BeginPlay() override; 46 | 47 | public: 48 | // Called every frame 49 | virtual void Tick(float DeltaTime) override; 50 | virtual void OnConstruction(const FTransform &Transform) override; 51 | 52 | private: 53 | void ConstructLanes(); 54 | USplineComponent * ConstructBaseSpline(FSumoLane &lane); 55 | void ConstructLaneMesh(USplineComponent * basespline, float laneWidth); 56 | FVector DetermineEdgeGeoCenter(); 57 | 58 | }; 59 | -------------------------------------------------------------------------------- /Source/SumoUE4/Private/SumoBaseActor.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Iwer Petersen. All rights reserved. 2 | 3 | #include "SumoBaseActor.h" 4 | 5 | ASumoBaseActor::ASumoBaseActor() 6 | { 7 | PrimaryActorTick.bCanEverTick = false; 8 | } 9 | 10 | // Called when the game starts or when spawned 11 | void ASumoBaseActor::BeginPlay() 12 | { 13 | Super::BeginPlay(); 14 | 15 | } 16 | 17 | // Called every frame 18 | void ASumoBaseActor::Tick(float DeltaTime) 19 | { 20 | Super::Tick(DeltaTime); 21 | 22 | } 23 | 24 | void ASumoBaseActor::OnConstruction(const FTransform &Transform) 25 | { 26 | // Construct parent after Lat/Lon is set 27 | Super::OnConstruction(Transform); 28 | } 29 | 30 | USplineComponent * ASumoBaseActor::ConstructSpline(TArray &shape, bool bPutToGround, float groundOffset) 31 | { 32 | if(shape.Num() >= 2) { 33 | // create spline component for lane 34 | USplineComponent * currspline = NewObject(this, USplineComponent::StaticClass()); 35 | currspline->ClearSplinePoints(true); 36 | 37 | for(auto &v : shape) { 38 | FVector gv = GeoLocation->GeoRef->ToGameCoordinate(FVector(v.X, v.Y, GetActorLocation().Z)); 39 | 40 | // snap to ground 41 | if(bPutToGround) 42 | gv = GeoLocation->SnapToGround(gv, 1000); 43 | 44 | // make relative to origin 45 | gv -= GetActorLocation(); 46 | 47 | // offset 48 | gv += FVector(0,0,groundOffset); 49 | 50 | // add spline point 51 | currspline->AddSplineLocalPoint(gv); 52 | } 53 | 54 | currspline->UpdateSpline(); 55 | currspline->AttachToComponent(GetRootComponent(), FAttachmentTransformRules::KeepRelativeTransform); 56 | currspline->RegisterComponent(); 57 | return currspline; 58 | } 59 | return nullptr; 60 | } 61 | -------------------------------------------------------------------------------- /Source/SumoUE4Editor/Private/Helpers/SumoNetworkParser.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Iwer Petersen. All rights reserved. 2 | #pragma once 3 | 4 | 5 | #include "Runtime/XmlParser/Public/FastXml.h" 6 | #include "Runtime/XmlParser/Public/XmlParser.h" 7 | #include "SumoNetworkAsset.h" 8 | 9 | class USumoNetworkParser : public IFastXmlCallback 10 | { 11 | public: 12 | USumoNetworkParser(); 13 | virtual ~USumoNetworkParser(); 14 | 15 | bool ParseFile(const FString &filename, USumoNetworkAsset * asset); 16 | 17 | protected: 18 | //IFastXMLCallback 19 | bool ProcessXmlDeclaration(const TCHAR* ElementData, int32 XmlFileLineNumber); 20 | bool ProcessElement(const TCHAR* ElementName, const TCHAR* ElementData, int32 XmlFileLineNumber); 21 | bool ProcessAttribute(const TCHAR* AttributeName, const TCHAR* AttributeValue); 22 | bool ProcessClose(const TCHAR* Element); 23 | bool ProcessComment(const TCHAR* Comment); 24 | 25 | private: 26 | enum ParsingState { 27 | Root, 28 | Location, 29 | Type, 30 | Junction, 31 | Request, 32 | Edge, 33 | Lane, 34 | EdgeStopOffset, 35 | LaneStopOffset, 36 | TLLogic, 37 | TLPhase, 38 | Connection 39 | }; 40 | 41 | USumoNetworkAsset * Target; 42 | ParsingState State; 43 | FSumoJunction * CurrentJunction; 44 | FSumoRequest * CurrentRequest; 45 | FSumoEdge * CurrentEdge; 46 | FSumoLane * CurrentLane; 47 | FSumoRoadType * CurrentRoadType; 48 | FSumoStopOffset * CurrentStopOffset; 49 | FSumoTrafficLightProgram * CurrentTLProgram; 50 | FSumoTrafficLightPhase * CurrentTLPhase; 51 | FSumoConnection * CurrentConnection; 52 | 53 | int UTMZone; 54 | int bNorthernHemisphere; 55 | 56 | double UTMOffsetX; 57 | double UTMOffsetY; 58 | 59 | double UTMJunctionLocationX; 60 | double UTMJunctionLocationY; 61 | 62 | TEnumAsByte ToVehicleTypeEnum(FString &typestring); 63 | TArray ProcessShape(FString & ShapeCoordinates); 64 | }; 65 | -------------------------------------------------------------------------------- /Source/SumoUE4/Public/SumoStructs/SumoVehicleType.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Iwer Petersen. All rights reserved. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | 7 | UENUM(BlueprintType) 8 | enum ESumoVehicleType { 9 | SVT_Passenger UMETA(ToolTip = "Default vehicle class"), 10 | SVT_Private UMETA(ToolTip = "A passenger car assigned for private use"), 11 | SVT_Taxi UMETA(ToolTip = "Vehicle for hire with a driver"), 12 | SVT_Bus UMETA(ToolTip = "Urban line traffic"), 13 | SVT_Coach UMETA(ToolTip = "Overland transport"), 14 | SVT_Delivery UMETA(ToolTip = "Vehicle specialized to deliver goods"), 15 | SVT_Truck UMETA(ToolTip = "Vehicle designed to transport cargo"), 16 | SVT_Trailer UMETA(ToolTip = "Truck with trailer"), 17 | SVT_Emergency UMETA(ToolTip = "Vehicle designed to respond to an emergency"), 18 | SVT_Motorcycle UMETA(ToolTip = "Two- or three-wheeled motor vehicle"), 19 | SVT_Moped UMETA(ToolTip = "Motorcycle not allowed in motorways"), 20 | SVT_Bicycle UMETA(ToolTip = "Human-powered, pedal-driven vehicle"), 21 | SVT_Pedestrian UMETA(ToolTip = "Person traveling on foot"), 22 | SVT_Tram UMETA(ToolTip = "Rail vehicle which runs on tracks"), 23 | SVT_RailElectric UMETA(ToolTip = "Rail electric vehicle"), 24 | SVT_RailFast UMETA(ToolTip = "High-speed rail vehicle"), 25 | SVT_RailUrban UMETA(ToolTip = "Heavier than tram"), 26 | SVT_Rail UMETA(ToolTip = "Heavy rail vehicle"), 27 | SVT_EVehicle UMETA(ToolTip = "Future electric mobility vehicles"), 28 | SVT_Army UMETA(ToolTip = "Vehicle designed for military forces"), 29 | SVT_Ship UMETA(ToolTip = "Basic class for navigating waterways"), 30 | SVT_Authority UMETA(ToolTip = "Vehicle of a governmental security agency"), 31 | SVT_Vip UMETA(ToolTip = "A civilian security armoured car used by VIPs"), 32 | SVT_Hov UMETA(ToolTip = "High-occupancy vehicle (two or more passengers)"), 33 | SVT_Custom1 UMETA(ToolTip = "Reserved for user-defined semantics"), 34 | SVT_Custom2 UMETA(ToolTip = "Reserved for user-defined semantics") 35 | }; 36 | -------------------------------------------------------------------------------- /Source/SumoUE4/Public/SumoStructs/SumoTrafficLightProgram.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Iwer Petersen. All rights reserved. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "SumoVehicleType.h" 7 | #include "SumoTrafficLightPhase.h" 8 | 9 | #include "SumoTrafficLightProgram.generated.h" 10 | 11 | UENUM(BlueprintType) 12 | enum ESumoTrafficLightProgramType { 13 | STLPT_Static, 14 | STLPT_Actuated, 15 | STLPT_DelayBased 16 | }; 17 | 18 | USTRUCT(Blueprintable, BlueprintType) 19 | struct FSumoTrafficLightProgram { 20 | GENERATED_BODY() 21 | 22 | /** 23 | * The id of the traffic light. This must be an existing traffic light id 24 | * in the .net.xml file. Typically the id for a traffic light is identical 25 | * with the junction id. The name may be obtained by right-clicking the 26 | * red/green bars in front of a controlled intersection. 27 | */ 28 | UPROPERTY(BlueprintReadWrite, EditAnywhere) 29 | FString ID; 30 | 31 | /** 32 | * The type of the traffic light (fixed phase durations, phase prolongation 33 | * based on time gaps between vehicles (actuated), or on accumulated time 34 | * loss of queued vehicles (delay_based) ) 35 | */ 36 | UPROPERTY(BlueprintReadWrite, EditAnywhere) 37 | TEnumAsByte Type; 38 | 39 | /** 40 | * The id of the traffic light program; This must be a new program name for 41 | * the traffic light id. Please note that "off" is reserved 42 | */ 43 | UPROPERTY(BlueprintReadWrite, EditAnywhere) 44 | FString ProgramID; 45 | 46 | /** 47 | * The initial time offset of the program 48 | */ 49 | UPROPERTY(BlueprintReadWrite, EditAnywhere) 50 | int TimeOffset; 51 | 52 | /** 53 | * The phases of this program 54 | */ 55 | UPROPERTY(BlueprintReadWrite, EditAnywhere) 56 | TArray Phases; 57 | 58 | FSumoTrafficLightProgram() { 59 | ID = TEXT(""); 60 | Type = ESumoTrafficLightProgramType::STLPT_Static; 61 | ProgramID = TEXT(""); 62 | TimeOffset = 0; 63 | } 64 | }; 65 | -------------------------------------------------------------------------------- /Source/SumoUE4/Public/SumoStructs/SumoJunction.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Iwer Petersen. All rights reserved. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "SumoRequest.h" 7 | 8 | #include "SumoJunction.generated.h" 9 | 10 | UENUM(BlueprintType) 11 | enum ESumoJunctionType { 12 | SJT_Unknown, 13 | SJT_TrafficLight, 14 | SJT_TrafficLightUnregulated, 15 | SJT_TrafficLightRightOnRed, 16 | SJT_RailSignal, 17 | SJT_RailCrossing, 18 | SJT_Priority, 19 | SJT_PriorityStop, 20 | SJT_RightBeforeLeft, 21 | SJT_AllwayStop, 22 | SJT_Zipper, 23 | SJT_District, 24 | SJT_Unregulated 25 | }; 26 | 27 | 28 | USTRUCT(Blueprintable, BlueprintType) 29 | struct FSumoJunction { 30 | GENERATED_BODY() 31 | 32 | /** 33 | * The id of the junction; please note, that a traffic light definition 34 | * must use the same ID when controlling this intersection. 35 | */ 36 | UPROPERTY(BlueprintReadWrite, EditAnywhere) 37 | FString ID;// 38 | 39 | /** 40 | * Location of this junction in WGS84 Long,Lat 41 | */ 42 | UPROPERTY(BlueprintReadWrite, EditAnywhere) 43 | FVector2D Location; 44 | 45 | /** 46 | * Junction type 47 | */ 48 | UPROPERTY(BlueprintReadWrite, EditAnywhere) 49 | TEnumAsByte Type; 50 | 51 | /** 52 | * A polygon describing the road boundaries of the intersection in WGS84 Long,Lat 53 | */ 54 | UPROPERTY(BlueprintReadWrite, EditAnywhere) 55 | TArray Shape; 56 | 57 | /** 58 | * The ids of the lanes that end at the intersection; sorted by direction, clockwise, with direction up = 0 59 | */ 60 | UPROPERTY(BlueprintReadWrite, EditAnywhere) 61 | TArray IncomingLaneIDs; 62 | 63 | /** 64 | * The IDs of the lanes within the intersection 65 | */ 66 | UPROPERTY(BlueprintReadWrite, EditAnywhere) 67 | TArray InternalLaneIDs; 68 | 69 | /** 70 | * Requests 71 | */ 72 | UPROPERTY(BlueprintReadWrite, EditAnywhere) 73 | TArray Requests; 74 | 75 | FSumoJunction() 76 | { 77 | ID = TEXT(""); 78 | Location = FVector2D::ZeroVector; 79 | Type = ESumoJunctionType::SJT_Unknown; 80 | Shape.Empty(); 81 | IncomingLaneIDs.Empty(); 82 | InternalLaneIDs.Empty(); 83 | Requests.Empty(); 84 | }; 85 | }; 86 | -------------------------------------------------------------------------------- /Source/SumoUE4/Public/SumoStructs/SumoRoadType.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Iwer Petersen. All rights reserved. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "SumoVehicleType.h" 7 | 8 | #include "SumoRoadType.generated.h" 9 | 10 | USTRUCT(Blueprintable, BlueprintType) 11 | struct FSumoRoadType { 12 | GENERATED_BODY() 13 | 14 | /** 15 | * The name of the road type. This is the only mandatory attribute. For 16 | * OpenStreetMap data, the name could, for example, be highway.trunk or 17 | * highway.residential. For ArcView data, the name of the road type is a 18 | * number. 19 | */ 20 | UPROPERTY(BlueprintReadWrite, EditAnywhere) 21 | FString ID; 22 | 23 | /** 24 | * A number, which determines the priority between different road types. 25 | * netconvert derives the right-of-way rules at junctions from the priority. 26 | * The number starts with one; higher numbers represent more important roads. 27 | */ 28 | UPROPERTY(BlueprintReadWrite, EditAnywhere) 29 | int Priority; 30 | 31 | /** 32 | * A number, which determines the priority between different road types. 33 | * netconvert derives the right-of-way rules at junctions from the priority. 34 | * The number starts with one; higher numbers represent more important roads. 35 | */ 36 | UPROPERTY(BlueprintReadWrite, EditAnywhere) 37 | int NumLanes; 38 | 39 | /** 40 | * The default (implicit) speed limit in m/s. 41 | */ 42 | UPROPERTY(BlueprintReadWrite, EditAnywhere) 43 | float Speed; 44 | 45 | /** 46 | * List of allowed vehicle classes. 47 | */ 48 | UPROPERTY(BlueprintReadWrite, EditAnywhere) 49 | TArray> AllowedVehicleTypes; 50 | 51 | /** 52 | * List of not allowed vehicle classes. 53 | */ 54 | UPROPERTY(BlueprintReadWrite, EditAnywhere) 55 | TArray> DisallowedVehicleTypes; 56 | 57 | /** 58 | * True if this road type is only passable in one direction 59 | */ 60 | UPROPERTY(BlueprintReadWrite, EditAnywhere) 61 | bool bOneWay; 62 | 63 | /** 64 | * The default width for added sidewalks (not so sure because actually named 65 | * in the docs but netconvert(v1_7_0+1227-e79d6b3592) spits 66 | * it out like this: ) 67 | */ 68 | UPROPERTY(BlueprintReadWrite, EditAnywhere) 69 | float Width; 70 | 71 | FSumoRoadType() { 72 | ID = TEXT(""); 73 | Priority = 0; 74 | NumLanes = 1; 75 | Speed = 0.0; 76 | AllowedVehicleTypes.Empty(); 77 | DisallowedVehicleTypes.Empty(); 78 | bOneWay = false; 79 | Width = 0.0; 80 | } 81 | }; 82 | -------------------------------------------------------------------------------- /Source/SumoUE4Editor/Private/SumoUE4EditorModule.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Iwer Petersen. All rights reserved. 2 | #include "Modules/ModuleManager.h" 3 | #include "Toolkits/AssetEditorToolkit.h" 4 | 5 | 6 | #define LOCTEXT_NAMESPACE "FSumoUE4EditorModule" 7 | 8 | class FSumoUE4EditorModule : public IModuleInterface 9 | { 10 | public: 11 | 12 | /** IModuleInterface implementation */ 13 | virtual void StartupModule() override 14 | { 15 | // Style = MakeShareable(new FGeotiffHeightmapAssetEditorStyle()); 16 | 17 | RegisterAssetTools(); 18 | } 19 | 20 | virtual void ShutdownModule() override 21 | { 22 | UnregisterAssetTools(); 23 | //UnregisterLandscapeFileFormats(); 24 | } 25 | 26 | virtual bool SupportsDynamicReloading() override 27 | { 28 | return true; 29 | } 30 | protected: 31 | /** Registers asset tool actions. */ 32 | void RegisterAssetTools() 33 | { 34 | // IAssetTools& AssetTools = FModuleManager::LoadModuleChecked("AssetTools").Get(); 35 | // TSharedPtr heightActionType = MakeShareable(new FGeotiffHeightmapAssetActions(Style.ToSharedRef())); 36 | // RegisterAssetTypeAction(AssetTools, heightActionType.ToSharedRef()); 37 | // TSharedPtr weightActionType = MakeShareable(new FS2GLCWeightmapAssetActions(Style.ToSharedRef())); 38 | // RegisterAssetTypeAction(AssetTools, weightActionType.ToSharedRef()); 39 | 40 | } 41 | 42 | /** 43 | * Registers a single asset type action. 44 | * 45 | * @param AssetTools The asset tools object to register with. 46 | * @param Action The asset type action to register. 47 | */ 48 | // void RegisterAssetTypeAction(IAssetTools& AssetTools, TSharedRef Action) 49 | // { 50 | // // AssetTools.RegisterAssetTypeActions(Action); 51 | // // RegisteredAssetTypeActions.Add(Action); 52 | // } 53 | 54 | /** Unregisters asset tool actions. */ 55 | void UnregisterAssetTools() 56 | { 57 | // FAssetToolsModule* AssetToolsModule = FModuleManager::GetModulePtr("AssetTools"); 58 | // 59 | // if (AssetToolsModule) 60 | // { 61 | // IAssetTools& AssetTools = AssetToolsModule->Get(); 62 | // 63 | // for (auto Action : RegisteredAssetTypeActions) 64 | // { 65 | // AssetTools.UnregisterAssetTypeActions(Action); 66 | // } 67 | // } 68 | } 69 | 70 | 71 | private: 72 | /** The collection of registered asset type actions. */ 73 | // TArray> RegisteredAssetTypeActions; 74 | /** Holds the plug-ins style set. */ 75 | // TSharedPtr Style; 76 | }; 77 | 78 | IMPLEMENT_MODULE(FSumoUE4EditorModule, SumoUE4Editor); 79 | #undef LOCTEXT_NAMESPACE 80 | -------------------------------------------------------------------------------- /Source/SumoUE4/Public/SumoStructs/SumoEdge.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Iwer Petersen. All rights reserved. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "SumoVehicleType.h" 7 | #include "SumoLane.h" 8 | #include "SumoRoadType.h" 9 | #include "SumoConnection.h" 10 | 11 | #include "SumoEdge.generated.h" 12 | 13 | UENUM(BlueprintType) 14 | enum ESumoEdgeFunction { 15 | SEF_Normal UMETA(ToolTip = "The edge is a plain part of a road network, like a highway or a normal street which connects two roads"), 16 | SEF_Internal UMETA(ToolTip = "The edge is a macroscopic connector - not a part of the real world road network"), 17 | SEF_Connector UMETA(ToolTip = "The edge is a part of an intersection (is located within the intersection)"), 18 | SEF_Crossing, 19 | SEF_WalkingArea 20 | }; 21 | 22 | UENUM(BlueprintType) 23 | enum ESumoEdgeSpreadType { 24 | SEST_Right, 25 | SEST_RoadCenter, 26 | SEST_Center 27 | }; 28 | 29 | USTRUCT(Blueprintable, BlueprintType) 30 | struct FSumoEdge { 31 | GENERATED_BODY() 32 | 33 | /** 34 | * The id of the edge 35 | */ 36 | UPROPERTY(BlueprintReadWrite, EditAnywhere) 37 | FString ID; 38 | 39 | /** 40 | * road name (optional) 41 | */ 42 | UPROPERTY(BlueprintReadWrite, EditAnywhere) 43 | FString Name; 44 | 45 | /** 46 | * type of this road 47 | */ 48 | UPROPERTY(BlueprintReadWrite, EditAnywhere) 49 | FSumoRoadType Type; 50 | 51 | /** 52 | * The id of the junction it starts at 53 | */ 54 | UPROPERTY(BlueprintReadWrite, EditAnywhere) 55 | FString FromJunction; 56 | 57 | /** 58 | * The id of the junction it ends at 59 | */ 60 | UPROPERTY(BlueprintReadWrite, EditAnywhere) 61 | FString ToJunction; 62 | 63 | /** 64 | * Indicates how important the road is (optional) 65 | */ 66 | UPROPERTY(BlueprintReadWrite, EditAnywhere) 67 | int Priority; 68 | 69 | /** 70 | * An abstract edge purpose (optional with default "normal") 71 | */ 72 | UPROPERTY(BlueprintReadWrite, EditAnywhere) 73 | TEnumAsByte Function; 74 | 75 | /** 76 | * Lanes this edge consists of 77 | */ 78 | UPROPERTY(BlueprintReadWrite, EditAnywhere) 79 | TArray Lanes; 80 | 81 | /** 82 | * Stop offset 83 | */ 84 | UPROPERTY(BlueprintReadWrite, EditAnywhere) 85 | FSumoStopOffset StopOffset; 86 | 87 | FSumoEdge() { 88 | ID = TEXT(""); 89 | Name = TEXT(""); 90 | //Type = TEXT(""); 91 | FromJunction = TEXT(""); 92 | ToJunction = TEXT(""); 93 | Priority = 0; 94 | Function = ESumoEdgeFunction::SEF_Normal; 95 | Lanes.Empty(); 96 | } 97 | }; 98 | -------------------------------------------------------------------------------- /Source/SumoUE4/Public/SumoStructs/SumoConnection.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Iwer Petersen. All rights reserved. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | 7 | #include "SumoConnection.generated.h" 8 | 9 | UENUM(BlueprintType) 10 | enum ESumoConnectionDirection { 11 | SCD_Straight, 12 | SCD_Turn, 13 | SCD_Left, 14 | SCD_Right, 15 | SCD_PartiallyLeft, 16 | SCD_PartiallyRight, 17 | SCD_Invalid 18 | }; 19 | 20 | UENUM(BlueprintType) 21 | enum ESumoConnectionState { 22 | SCS_DeadEnd, 23 | SCS_Equal, 24 | SCS_MinorLink, 25 | SCS_MajorLink, 26 | SCS_ControllerOff, 27 | SCS_YellowFlashing, 28 | SCS_YellowMinorLink, 29 | SCS_YellowMajorLink, 30 | SCS_Red, 31 | SCS_GreenMinor, 32 | SCS_GreenMajor 33 | }; 34 | 35 | 36 | USTRUCT(Blueprintable, BlueprintType) 37 | struct FSumoConnection { 38 | GENERATED_BODY() 39 | 40 | /** 41 | * The ID of the incoming edge at which the connection begins 42 | */ 43 | UPROPERTY(BlueprintReadWrite, EditAnywhere) 44 | FString FromEdgeID; 45 | 46 | /** 47 | * The ID of the outgoing edge at which the connection ends 48 | */ 49 | UPROPERTY(BlueprintReadWrite, EditAnywhere) 50 | FString ToEdgeID; 51 | 52 | /** 53 | * The lane of the incoming edge at which the connection begins 54 | */ 55 | UPROPERTY(BlueprintReadWrite, EditAnywhere) 56 | int FromLaneIndex; 57 | 58 | /** 59 | * The lane of the outgoing edge at which the connection ends 60 | */ 61 | UPROPERTY(BlueprintReadWrite, EditAnywhere) 62 | int ToLaneIndex; 63 | 64 | /** 65 | * The id of the lane to use to pass this connection across the junction 66 | */ 67 | UPROPERTY(BlueprintReadWrite, EditAnywhere) 68 | FString ViaLaneID; 69 | 70 | /** 71 | * The id of the traffic light that controls this connection; 72 | * the attribute is missing if the connection is not controlled by a traffic light 73 | */ 74 | UPROPERTY(BlueprintReadWrite, EditAnywhere) 75 | FString TrafficLightID; 76 | 77 | /** 78 | * The index of the signal responsible for the connection within the traffic light; 79 | * the attribute is missing if the connection is not controlled by a traffic light 80 | */ 81 | UPROPERTY(BlueprintReadWrite, EditAnywhere) 82 | int LinkIndex; 83 | 84 | /** 85 | * The direction of the connection 86 | */ 87 | UPROPERTY(BlueprintReadWrite, EditAnywhere) 88 | TEnumAsByte Direction; 89 | 90 | /** 91 | * The state of the connection 92 | */ 93 | UPROPERTY(BlueprintReadWrite, EditAnywhere) 94 | TEnumAsByte State; 95 | 96 | FSumoConnection() { 97 | FromEdgeID = TEXT(""); 98 | ToEdgeID = TEXT(""); 99 | FromLaneIndex = 0; 100 | ToLaneIndex = 0; 101 | ViaLaneID = TEXT(""); 102 | TrafficLightID = TEXT(""); 103 | LinkIndex = 0; 104 | Direction = ESumoConnectionDirection::SCD_Invalid; 105 | State = ESumoConnectionState::SCS_Equal; 106 | } 107 | }; 108 | -------------------------------------------------------------------------------- /Source/SumoUE4/Public/SumoStructs/SumoTrafficLightPhase.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Iwer Petersen. All rights reserved. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | 7 | #include "SumoTrafficLightPhase.generated.h" 8 | 9 | UENUM(BlueprintType) 10 | enum ESumoTrafficLightSignalState { 11 | STLSS_Red UMETA(ToolTip = "red light for a signal - vehicles must stop"), 12 | STLSS_Yellow UMETA(ToolTip = "amber (yellow) light for a signal - vehicles will start to decelerate if far away from the junction, otherwise they pass"), 13 | STLSS_Green UMETA(ToolTip = "green light for a signal, no priority - vehicles may pass the junction if no vehicle uses a higher priorised foe stream, otherwise they decelerate for letting it pass."), 14 | STLSS_GreenPriority UMETA(ToolTip = "green light for a signal, priority - vehicles may pass the junction"), 15 | STLSS_RightGreen UMETA(ToolTip = "green right-turn arrow requires stopping - vehicles may pass the junction if no vehicle uses a higher priorised foe stream. They always stop before passing."), 16 | STLSS_RedYellow UMETA(ToolTip = "red+yellow light for a signal, may be used to indicate upcoming green phase but vehicles may not drive yet"), 17 | STLSS_OffBlink UMETA(ToolTip = "off - blinking signal is switched off, blinking light indicates vehicles have to yield"), 18 | STLSS_Off UMETA(ToolTip = "off - no signal signal is switched off, vehicles have the right of way") 19 | }; 20 | 21 | USTRUCT(Blueprintable, BlueprintType) 22 | struct FSumoTrafficLightPhase { 23 | GENERATED_BODY() 24 | 25 | /** 26 | * The duration of the phase 27 | */ 28 | UPROPERTY(BlueprintReadWrite, EditAnywhere) 29 | int Duration; 30 | 31 | /** 32 | * The traffic light states for this phase 33 | */ 34 | UPROPERTY(BlueprintReadWrite, EditAnywhere) 35 | TArray> States; 36 | 37 | /** 38 | * The minimum duration of the phase when using type actuated. Optional, defaults to duration. 39 | */ 40 | UPROPERTY(BlueprintReadWrite, EditAnywhere) 41 | int MinDuration; 42 | 43 | /** 44 | * The maximum duration of the phase when using type actuated. Optional, defaults to duration. 45 | */ 46 | UPROPERTY(BlueprintReadWrite, EditAnywhere) 47 | int MaxDuration; 48 | 49 | /** 50 | * An optional description for the phase. This can be used to establish the 51 | * correspondence between SUMO-phase-indexing and traffic engineering phase names. 52 | */ 53 | UPROPERTY(BlueprintReadWrite, EditAnywhere) 54 | FString Name; 55 | 56 | /** 57 | * The next phase in the cycle after the current. This is useful when adding 58 | * extra transition phases to a traffic light plan which are not part of 59 | * every cycle. Traffic lights of type 'actuated' can make use of a list of 60 | * indices for selecting among alternative successor phases. 61 | */ 62 | UPROPERTY(BlueprintReadWrite, EditAnywhere) 63 | TArray Next; 64 | 65 | FSumoTrafficLightPhase() { 66 | Duration = 0; 67 | States.Empty(); 68 | MinDuration = 0; 69 | MaxDuration = 0; 70 | Name = TEXT(""); 71 | Next.Empty(); 72 | } 73 | }; 74 | -------------------------------------------------------------------------------- /Source/SumoUE4/Private/SumoEdgeActor.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Iwer Petersen. All rights reserved. 2 | 3 | #include "SumoEdgeActor.h" 4 | 5 | ASumoEdgeActor::ASumoEdgeActor() 6 | { 7 | PrimaryActorTick.bCanEverTick = false; 8 | bReparamSpline = false; 9 | SplineSectionLength = 1000; // 10m 10 | } 11 | 12 | // Called when the game starts or when spawned 13 | void ASumoEdgeActor::BeginPlay() 14 | { 15 | Super::BeginPlay(); 16 | 17 | } 18 | 19 | // Called every frame 20 | void ASumoEdgeActor::Tick(float DeltaTime) 21 | { 22 | Super::Tick(DeltaTime); 23 | 24 | } 25 | void ASumoEdgeActor::OnConstruction(const FTransform &Transform) 26 | { 27 | FVector center = DetermineEdgeGeoCenter(); 28 | GeoLocation->GeoRef->Longitude = center.X; 29 | GeoLocation->GeoRef->Latitude = center.Y; 30 | 31 | // Construct parent after Lat/Lon is set 32 | Super::OnConstruction(Transform); 33 | 34 | // cleanup 35 | for(auto &lane : Lanes) { 36 | if(lane) 37 | lane->DestroyComponent(); 38 | } 39 | Lanes.Empty(); 40 | // clear current meshes 41 | for(auto &lanemesh : LaneMeshes) { 42 | if(lanemesh) { 43 | lanemesh->DestroyComponent(true); 44 | } 45 | } 46 | LaneMeshes.Empty(); 47 | 48 | if(EdgeData.Function == ESumoEdgeFunction::SEF_Normal) { 49 | if(EdgeData.Lanes.Num() > 0) { 50 | ConstructLanes(); 51 | } else { 52 | UE_LOG(LogTemp, Warning, TEXT("ASumoEdgeActor: EdgeData has no lanes!")) 53 | } 54 | } else { 55 | UE_LOG(LogTemp, Warning, TEXT("ASumoEdgeActor: EdgeData not SEF_Normal!")) 56 | } 57 | 58 | } 59 | 60 | void ASumoEdgeActor::ConstructLanes() 61 | { 62 | // do nothing when no georef is present 63 | if(!GeoLocation->GeoRef) 64 | return; 65 | 66 | // each lane 67 | for(auto &l : EdgeData.Lanes) { 68 | auto basespline = ConstructBaseSpline(l); 69 | if(basespline) { 70 | Lanes.Add(basespline); 71 | float width = EdgeData.Type.Width; 72 | if(width < .01) 73 | width = 3 * EdgeData.Type.NumLanes; 74 | ConstructLaneMesh(basespline, width * 100); 75 | } 76 | } 77 | } 78 | 79 | USplineComponent * ASumoEdgeActor::ConstructBaseSpline(FSumoLane &lane) 80 | { 81 | USplineComponent * spline = ConstructSpline(lane.Shape, true, 10); 82 | // snap first spline point to fromJunction z-location 83 | if(SumoNetwork) { 84 | auto startjunction = FromJunctions[EdgeData.FromJunction]; 85 | 86 | } 87 | // snap last spline point to toJunction z-location 88 | 89 | return spline; 90 | } 91 | 92 | void ASumoEdgeActor::ConstructLaneMesh(USplineComponent * basespline, float laneWidth) 93 | { 94 | if(bReparamSpline) { 95 | // calculate reparam steps 96 | float splinelength = basespline->GetSplineLength(); 97 | int numReparam = splinelength / SplineSectionLength; 98 | 99 | // create new vertices along spline 100 | TArray newVerts; 101 | for(int i = 0; i < numReparam; i++) { 102 | FVector rp = basespline->GetLocationAtDistanceAlongSpline(i * SplineSectionLength, ESplineCoordinateSpace::Local); 103 | newVerts.Add(GeoLocation->SnapToGround(rp, 100)); 104 | } 105 | 106 | // add end vertex 107 | FVector ev = basespline->GetLocationAtDistanceAlongSpline(splinelength, ESplineCoordinateSpace::Local); 108 | newVerts.Add(GeoLocation->SnapToGround(ev, 100)); 109 | 110 | // clear spline points in basespline 111 | basespline->ClearSplinePoints(true); 112 | 113 | // add vertices 114 | for(auto v : newVerts) { 115 | basespline->AddSplineLocalPoint(v); 116 | } 117 | 118 | // update spline 119 | basespline->UpdateSpline(); 120 | } else { 121 | SplineSectionLength = basespline->GetSplineLength(); 122 | } 123 | 124 | // determine number of mesh sections 125 | int numMeshSections = basespline->GetNumberOfSplinePoints() - 1; 126 | 127 | for (int i = 0; i < numMeshSections; i++) { 128 | FVector start = basespline->GetLocationAtSplinePoint(i,ESplineCoordinateSpace::World); 129 | FVector end = basespline->GetLocationAtSplinePoint(i+1,ESplineCoordinateSpace::World); 130 | FVector sTangent = basespline->GetTangentAtSplinePoint(i,ESplineCoordinateSpace::World); 131 | FVector eTangent = basespline->GetTangentAtSplinePoint(i+1,ESplineCoordinateSpace::World); 132 | 133 | USplineMeshComponent * mesh = NewObject(this, USplineMeshComponent::StaticClass()); 134 | mesh->SetStartAndEnd(start, sTangent, end, eTangent, true); 135 | mesh->SetSplineUpDir(FVector(0,0,1),true); 136 | mesh->AttachToComponent(GetRootComponent(), FAttachmentTransformRules::KeepRelativeTransform); 137 | mesh->SetForwardAxis(ESplineMeshAxis::Type::X, true); 138 | 139 | // attach static mesh 140 | if(RoadProfileMesh) { 141 | mesh->SetStaticMesh(RoadProfileMesh); 142 | } 143 | 144 | mesh->RegisterComponent(); 145 | LaneMeshes.Add(mesh); 146 | } 147 | } 148 | 149 | FVector ASumoEdgeActor::DetermineEdgeGeoCenter() 150 | { 151 | FVector center = FVector::ZeroVector; 152 | float count = 0; 153 | for(auto &lane : EdgeData.Lanes){ 154 | for(auto &v : lane.Shape){ 155 | center += v; 156 | count+=1; 157 | } 158 | } 159 | 160 | if(count > 1) 161 | center = center / count; 162 | 163 | return center; 164 | } 165 | -------------------------------------------------------------------------------- /Source/SumoUE4/Private/SumoJunctionActor.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Iwer Petersen. All rights reserved. 2 | 3 | 4 | #include "SumoJunctionActor.h" 5 | #include "GenericPlatform/GenericPlatformMath.h" 6 | #include "PolygonHelper.h" 7 | 8 | 9 | // Sets default values 10 | ASumoJunctionActor::ASumoJunctionActor() 11 | { 12 | // Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it. 13 | PrimaryActorTick.bCanEverTick = false; 14 | 15 | ProcMesh = CreateDefaultSubobject(TEXT("ProcMesh")); 16 | PavementHeight = 10; // 10cm 17 | } 18 | 19 | // Called when the game starts or when spawned 20 | void ASumoJunctionActor::BeginPlay() 21 | { 22 | Super::BeginPlay(); 23 | 24 | } 25 | 26 | // Called every frame 27 | void ASumoJunctionActor::Tick(float DeltaTime) 28 | { 29 | Super::Tick(DeltaTime); 30 | 31 | } 32 | void ASumoJunctionActor::OnConstruction(const FTransform &Transform) 33 | { 34 | GeoLocation->GeoRef->Longitude = JunctionData.Location.X; 35 | GeoLocation->GeoRef->Latitude = JunctionData.Location.Y; 36 | 37 | Super::OnConstruction(Transform); 38 | 39 | // cleanup 40 | for(auto &lane : LaneSplines) { 41 | if(lane) 42 | lane->DestroyComponent(); 43 | } 44 | LaneSplines.Empty(); 45 | 46 | ProcMesh->ClearAllMeshSections(); 47 | 48 | ConstructMesh(JunctionData.Shape); 49 | ConstructInternalLanes(); 50 | 51 | } 52 | 53 | void ASumoJunctionActor::ConstructMesh(TArray &vertices) 54 | { 55 | if(!GeoLocation->GeoRef) { 56 | UE_LOG(LogTemp, Warning, TEXT("ASumoJunctionActor: No GeoReference found, cancelling mesh construction!")) 57 | return; 58 | } 59 | 60 | MeshData data; 61 | data.OrigVertexNum = vertices.Num(); 62 | 63 | // these we want to have, remember min & max 64 | double minX, maxX, minY, maxY, maxZ; 65 | minX = minY = std::numeric_limits::max(); 66 | maxX = maxY = maxZ = std::numeric_limits::min(); 67 | 68 | 69 | // make polygon in game coordinates 70 | for(auto &vertex : vertices) { 71 | // convert geocoords to gamecoords 72 | FVector gameloc = GeoLocation->GeoRef->ToGameCoordinate(FVector(vertex.X, vertex.Y, GetActorLocation().Z)); 73 | 74 | // set Z to actorlocation 75 | FVector groundVert = GeoLocation->SnapToGround(gameloc,30); 76 | // subract actor location to make local coordinate 77 | gameloc -= GetActorLocation(); 78 | // add pavement height 79 | gameloc += FVector(0,0,PavementHeight); 80 | 81 | // the vertices we'll use for the mesh 82 | data.Vertices.Add(gameloc); 83 | 84 | // remember min and max; 85 | minX = std::min(gameloc.X, minX); 86 | minY = std::min(gameloc.Y, minY); 87 | maxX = std::max(gameloc.X, maxX); 88 | maxY = std::max(gameloc.Y, maxY); 89 | maxZ = std::max(groundVert.Z-GetActorLocation().Z, maxZ); 90 | } 91 | 92 | // tesselate using earcut 93 | data.TriIndices = PolygonHelper::TessellatePolygon(data.Vertices, TArray(), true); 94 | data.Normals.Init(FVector(0, 0, 1), data.Vertices.Num()); 95 | data.Tangents.Init(FProcMeshTangent(1, 0, 0),data.Vertices.Num()); 96 | data.VertColors.Init(FLinearColor(0, 0, 0, 1), data.Vertices.Num()); 97 | 98 | // UV mapping 99 | data.UV0 = PolygonHelper::FlatUVMap(data.Vertices); 100 | 101 | // create side mesh sections 102 | TArray offsetVertices = PolygonHelper::GenerateOffsetVertices(data.Vertices, 40, PavementHeight); 103 | int baseVertNum = data.Vertices.Num(); 104 | for (int32 i = 0; i < baseVertNum; i++) { 105 | auto p0 = data.Vertices[i]; 106 | auto p1 = data.Vertices[(i + 1) % baseVertNum]; 107 | auto p0a = offsetVertices[i]; 108 | auto p1a = offsetVertices[(i + 1) % baseVertNum]; 109 | AddQuadMeshSection(data, p0, p1, p0a, p1a); 110 | } 111 | 112 | // create the mesh section 113 | ProcMesh->CreateMeshSection_LinearColor(0, data.Vertices, data.TriIndices, data.Normals, data.UV0, data.VertColors, data.Tangents, false); 114 | ProcMesh->AddRelativeLocation(FVector(0,0,maxZ),false); 115 | ProcMesh->AttachToComponent(GetRootComponent(), FAttachmentTransformRules::KeepRelativeTransform); 116 | //ProcMesh->RegisterComponent(); 117 | } 118 | 119 | 120 | /** 121 | * (n) p1 p2 (n+1) 122 | * *-----* 123 | * / \ 124 | * p1a *---------* p2a 125 | * (2n) (2n+1) 126 | */ 127 | void ASumoJunctionActor::AddQuadMeshSection(MeshData &meshdata, const FVector &p1, const FVector &p2, const FVector &p1a, const FVector &p2a) 128 | { 129 | int32 startIdx = meshdata.Vertices.Num(); 130 | 131 | // make vertex array 132 | meshdata.Vertices.Add(p1); // <-- this is at startidx - meshdata.OrigVertexNum 133 | meshdata.Vertices.Add(p2); // <-- this is at startidx - meshdata.OrigVertexNum + 1 134 | meshdata.Vertices.Add(p1a); 135 | meshdata.Vertices.Add(p2a); 136 | 137 | // triangle p1, p2, p2a 138 | meshdata.TriIndices.Append({startIdx, 139 | startIdx + 1, 140 | startIdx + 3}); 141 | // triangle p2a, p1a,p1 142 | meshdata.TriIndices.Append({startIdx + 3, 143 | startIdx + 2, 144 | startIdx}); 145 | 146 | // calculate facenormal 147 | FVector a = p1 - p2; 148 | FVector b = p1 - p1a; 149 | FVector facenormal = FVector::CrossProduct(a,b).GetSafeNormal(.0001); 150 | FVector tangent = a.GetSafeNormal(.0001); 151 | 152 | 153 | // add normals, tangents and colors for each vertex 154 | TArray normals; 155 | TArray colors; 156 | TArray tangents; 157 | normals.Init(facenormal, 4); 158 | meshdata.Normals.Append(normals); 159 | tangents.Init(FProcMeshTangent(tangent, false), 4); 160 | meshdata.Tangents.Append(tangents); 161 | colors.Init(FLinearColor(0,0,0,1), 4); 162 | meshdata.VertColors.Append(colors); 163 | 164 | // calculate uvs by rotating the reference frame to the face normal along p1-p2 165 | // transforming the vertices to this frame which now lie in x-y plane 166 | // and scaling to meter which makes a good texture repetition scale 167 | FVector up(0,0,1); 168 | auto rotator = up.Rotation() - facenormal.Rotation(); 169 | FVector uv1 = rotator.RotateVector(p1) / 100; 170 | FVector uv2 = rotator.RotateVector(p2) / 100; 171 | FVector uv1a = rotator.RotateVector(p1a) / 100; 172 | FVector uv2a = rotator.RotateVector(p2a) / 100; 173 | 174 | meshdata.UV0.Add(FVector2D(uv1)); 175 | meshdata.UV0.Add(FVector2D(uv2)); 176 | meshdata.UV0.Add(FVector2D(uv1a)); 177 | meshdata.UV0.Add(FVector2D(uv2a)); 178 | } 179 | 180 | void ASumoJunctionActor::ConstructInternalLanes() 181 | { 182 | if(!SumoNetwork) { 183 | UE_LOG(LogTemp, Warning, TEXT("ASumoJunctionActor: Cannot construct internal lanes, SumoNetwork Asset not set!")); 184 | return; 185 | } 186 | 187 | for(FString &laneID : JunctionData.InternalLaneIDs) { 188 | // edgeID is laneID minus last "_" 189 | int32 idLen; 190 | if(!laneID.FindLastChar('_', idLen)) { 191 | UE_LOG(LogTemp, Warning, TEXT("ASumoJunctionActor: Could not deduce Edge ID from LaneID %s"), *laneID); 192 | continue; 193 | } 194 | FString edgeID = laneID.Left(idLen); 195 | 196 | if(SumoNetwork->Edges.Contains(edgeID)) { 197 | auto edge = SumoNetwork->Edges[edgeID]; 198 | FSumoLane lane; 199 | for(int i = 0; i < edge.Lanes.Num(); i++) { 200 | if(edge.Lanes[i].ID.Compare(laneID, ESearchCase::Type::CaseSensitive) == 0) { 201 | lane = edge.Lanes[i]; 202 | break; 203 | } 204 | } 205 | // check if lane was found 206 | if(lane.ID.Compare(laneID, ESearchCase::Type::CaseSensitive) != 0) { 207 | UE_LOG(LogTemp, Warning, TEXT("ASumoJunctionActor: Lane %s not found in edge %s"), *laneID, *edgeID); 208 | break; 209 | } 210 | 211 | // construct spline 212 | auto spline = ConstructSpline(lane.Shape, false, PavementHeight); 213 | if(spline) { 214 | LaneSplines.Add(spline); 215 | } 216 | } else { 217 | UE_LOG(LogTemp, Warning, TEXT("ASumoJunctionActor: Edge %s not found in SumoNetwork!"), *edgeID); 218 | } 219 | } 220 | } 221 | -------------------------------------------------------------------------------- /Source/SumoUE4Editor/Private/Helpers/SumoNetworkParser.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Iwer Petersen. All rights reserved. 2 | 3 | #include "SumoNetworkParser.h" 4 | #include "GeoCoordinate.h" 5 | #include "GeoReferenceHelper.h" 6 | #include "GDALHelpers.h" 7 | 8 | USumoNetworkParser::USumoNetworkParser() 9 | : Target(nullptr) 10 | , State(ParsingState::Root) 11 | , CurrentJunction(nullptr) 12 | , CurrentEdge(nullptr) 13 | {} 14 | USumoNetworkParser::~USumoNetworkParser(){} 15 | 16 | bool USumoNetworkParser::ParseFile(const FString &filename, USumoNetworkAsset * asset) { 17 | FText outError; 18 | int32 outErrorNum; 19 | FString Text = ""; 20 | Target = asset; 21 | bool success = FFastXml::ParseXmlFile(this, 22 | *filename, 23 | (TCHAR*)*Text, 24 | nullptr, 25 | false, 26 | false, 27 | outError, 28 | outErrorNum); 29 | return success; 30 | } 31 | 32 | //IFastXMLCallback 33 | bool USumoNetworkParser::ProcessXmlDeclaration(const TCHAR* ElementData, int32 XmlFileLineNumber) { 34 | return true; 35 | } 36 | 37 | bool USumoNetworkParser::ProcessElement(const TCHAR* ElementName, const TCHAR* ElementData, int32 XmlFileLineNumber) { 38 | if(!FCString::Stricmp(ElementName, TEXT("location"))) { 39 | // UE_LOG(LogTemp,Warning,TEXT("Parsing location...")); 40 | State = ParsingState::Location; 41 | } else if(!FCString::Stricmp(ElementName, TEXT("type"))) { 42 | // UE_LOG(LogTemp,Warning,TEXT("Parsing roadtype...")); 43 | CurrentRoadType = new FSumoRoadType(); 44 | State = ParsingState::Type; 45 | } else if(!FCString::Stricmp(ElementName, TEXT("junction"))) { 46 | // UE_LOG(LogTemp,Warning,TEXT("Parsing junction...")); 47 | CurrentJunction = new FSumoJunction(); 48 | State = ParsingState::Junction; 49 | } else if(!FCString::Stricmp(ElementName, TEXT("request"))) { 50 | CurrentRequest = new FSumoRequest(); 51 | State = ParsingState::Request; 52 | } else if(!FCString::Stricmp(ElementName, TEXT("edge"))) { 53 | // UE_LOG(LogTemp,Warning,TEXT("Parsing edge...")); 54 | CurrentEdge = new FSumoEdge(); 55 | State = ParsingState::Edge; 56 | } else if(!FCString::Stricmp(ElementName, TEXT("lane"))) { 57 | // UE_LOG(LogTemp,Warning,TEXT("Parsing lane...")); 58 | CurrentLane = new FSumoLane(); 59 | State = ParsingState::Lane; 60 | } else if(!FCString::Stricmp(ElementName, TEXT("stopOffset"))) { 61 | CurrentStopOffset = new FSumoStopOffset(); 62 | if(State == ParsingState::Edge) { 63 | State = ParsingState::EdgeStopOffset; 64 | } else if(State == ParsingState::Lane) { 65 | State = ParsingState::LaneStopOffset; 66 | } 67 | } else if(!FCString::Stricmp(ElementName, TEXT("tlLogic"))) { 68 | // UE_LOG(LogTemp,Warning,TEXT("Parsing tlLogic...")); 69 | CurrentTLProgram = new FSumoTrafficLightProgram(); 70 | State = ParsingState::TLLogic; 71 | } else if(!FCString::Stricmp(ElementName, TEXT("phase"))) { 72 | CurrentTLPhase = new FSumoTrafficLightPhase(); 73 | State = ParsingState::TLPhase; 74 | } else if(!FCString::Stricmp(ElementName, TEXT("connection"))) { 75 | // UE_LOG(LogTemp,Warning,TEXT("Parsing connection...")); 76 | CurrentConnection = new FSumoConnection(); 77 | State = ParsingState::Connection; 78 | } 79 | return true; 80 | } 81 | 82 | bool USumoNetworkParser::ProcessAttribute(const TCHAR* AttributeName, const TCHAR* AttributeValue) { 83 | FString val(AttributeValue); 84 | 85 | if(State == ParsingState::Location) { 86 | if(!FCString::Stricmp(AttributeName, TEXT("netOffset"))) { 87 | FString xOffStr, yOffStr; 88 | val.Split(TEXT(","), &xOffStr, &yOffStr); 89 | UTMOffsetX = FPlatformString::Atod(*xOffStr); 90 | UTMOffsetY = FPlatformString::Atod(*yOffStr); 91 | // UE_LOG(LogTemp, Warning, TEXT("USumoNetworkParser: Offset: %f, %f"), UTMOffsetX, UTMOffsetY) 92 | } else if(!FCString::Stricmp(AttributeName, TEXT("projParameter"))) { 93 | OGRSpatialReference crs; 94 | if(crs.importFromProj4(TCHAR_TO_ANSI(*val)) != OGRERR_NONE) { 95 | UE_LOG(LogTemp, Error, TEXT("USumoNetworkParser: Failed to import CRS!")); 96 | return false; 97 | } 98 | 99 | if(crs.IsProjected()) { 100 | UTMZone = crs.GetUTMZone(&bNorthernHemisphere); 101 | } else { 102 | UE_LOG(LogTemp, Error, TEXT("USumoNetworkParser: Unexpected GeoReference!")); 103 | return false; 104 | } 105 | 106 | } 107 | } else if(State == ParsingState::Type) { 108 | if(!FCString::Stricmp(AttributeName, TEXT("id"))) { 109 | CurrentRoadType->ID = val; 110 | } else if(!FCString::Stricmp(AttributeName, TEXT("priority"))) { 111 | CurrentRoadType->Priority = FPlatformString::Atoi(AttributeValue); 112 | } else if(!FCString::Stricmp(AttributeName, TEXT("numLanes"))) { 113 | CurrentRoadType->NumLanes = FPlatformString::Atoi(AttributeValue); 114 | } else if(!FCString::Stricmp(AttributeName, TEXT("speed"))) { 115 | CurrentRoadType->Speed = FPlatformString::Atof(AttributeValue); 116 | } else if(!FCString::Stricmp(AttributeName, TEXT("allow"))) { 117 | TArray types; 118 | val.ParseIntoArray(types, TEXT(" "), true); 119 | for(auto &type : types) { 120 | CurrentRoadType->AllowedVehicleTypes.Add(ToVehicleTypeEnum(type)); 121 | } 122 | } else if(!FCString::Stricmp(AttributeName, TEXT("disallow"))) { 123 | TArray types; 124 | val.ParseIntoArray(types, TEXT(" "), true); 125 | for(auto &type : types) { 126 | CurrentRoadType->DisallowedVehicleTypes.Add(ToVehicleTypeEnum(type)); 127 | } 128 | } else if(!FCString::Stricmp(AttributeName, TEXT("oneway"))) { 129 | CurrentRoadType->bOneWay = (FPlatformString::Atoi(AttributeValue) != 0); 130 | } else if(!FCString::Stricmp(AttributeName, TEXT("width"))) { 131 | CurrentRoadType->Width = FPlatformString::Atof(AttributeValue); 132 | } 133 | } else if(State == ParsingState::Junction) { 134 | if(!FCString::Stricmp(AttributeName, TEXT("id"))) { 135 | CurrentJunction->ID = val; 136 | } else if(!FCString::Stricmp(AttributeName, TEXT("type"))) { 137 | if(!FCString::Stricmp(AttributeValue, TEXT("unknown"))) { 138 | CurrentJunction->Type = ESumoJunctionType::SJT_Unknown; 139 | } else if(!FCString::Stricmp(AttributeValue, TEXT("traffic_light"))) { 140 | CurrentJunction->Type = ESumoJunctionType::SJT_TrafficLight; 141 | } else if(!FCString::Stricmp(AttributeValue, TEXT("traffic_light_unregulated"))) { 142 | CurrentJunction->Type = ESumoJunctionType::SJT_TrafficLightUnregulated; 143 | } else if(!FCString::Stricmp(AttributeValue, TEXT("traffic_light_right_on_red"))) { 144 | CurrentJunction->Type = ESumoJunctionType::SJT_TrafficLightRightOnRed; 145 | } else if(!FCString::Stricmp(AttributeValue, TEXT("rail_signal"))) { 146 | CurrentJunction->Type = ESumoJunctionType::SJT_RailSignal; 147 | } else if(!FCString::Stricmp(AttributeValue, TEXT("rail_crossing"))) { 148 | CurrentJunction->Type = ESumoJunctionType::SJT_RailCrossing; 149 | } else if(!FCString::Stricmp(AttributeValue, TEXT("priority"))) { 150 | CurrentJunction->Type = ESumoJunctionType::SJT_Priority; 151 | } else if(!FCString::Stricmp(AttributeValue, TEXT("priority_stop"))) { 152 | CurrentJunction->Type = ESumoJunctionType::SJT_PriorityStop; 153 | } else if(!FCString::Stricmp(AttributeValue, TEXT("allway_stop"))) { 154 | CurrentJunction->Type = ESumoJunctionType::SJT_AllwayStop; 155 | } else if(!FCString::Stricmp(AttributeValue, TEXT("zipper"))) { 156 | CurrentJunction->Type = ESumoJunctionType::SJT_Zipper; 157 | } else if(!FCString::Stricmp(AttributeValue, TEXT("district"))) { 158 | CurrentJunction->Type = ESumoJunctionType::SJT_District; 159 | } else if(!FCString::Stricmp(AttributeValue, TEXT("unregulated"))) { 160 | CurrentJunction->Type = ESumoJunctionType::SJT_Unregulated; 161 | } 162 | } else if(!FCString::Stricmp(AttributeName, TEXT("x"))) { 163 | UTMJunctionLocationX = FPlatformString::Atof(AttributeValue) - UTMOffsetX; 164 | } else if(!FCString::Stricmp(AttributeName, TEXT("y"))) { 165 | UTMJunctionLocationY = FPlatformString::Atof(AttributeValue) - UTMOffsetY; 166 | } else if(!FCString::Stricmp(AttributeName, TEXT("incLanes"))) { 167 | val.ParseIntoArray(CurrentJunction->IncomingLaneIDs, TEXT(" "), true); 168 | } else if(!FCString::Stricmp(AttributeName, TEXT("intLanes"))) { 169 | val.ParseIntoArray(CurrentJunction->InternalLaneIDs, TEXT(" "), true); 170 | } else if(!FCString::Stricmp(AttributeName, TEXT("shape"))) { 171 | CurrentJunction->Shape.Append(ProcessShape(val)); 172 | } 173 | } else if(State == ParsingState::Request) { 174 | if(!FCString::Stricmp(AttributeName, TEXT("index"))) { 175 | CurrentRequest->Index = FPlatformString::Atoi(AttributeValue); 176 | } else if(!FCString::Stricmp(AttributeName, TEXT("response"))) { 177 | CurrentRequest->Response = val; 178 | } else if(!FCString::Stricmp(AttributeName, TEXT("foes"))) { 179 | CurrentRequest->Foes = val; 180 | } else if(!FCString::Stricmp(AttributeName, TEXT("cont"))) { 181 | CurrentRequest->bContinue = FPlatformString::Atoi(AttributeValue) == 0 ? false : true; 182 | } 183 | } else if(State == ParsingState::Edge) { 184 | if(!FCString::Stricmp(AttributeName, TEXT("id"))) { 185 | CurrentEdge->ID = val; 186 | } else if(!FCString::Stricmp(AttributeName, TEXT("from"))) { 187 | CurrentEdge->FromJunction = val; 188 | } else if(!FCString::Stricmp(AttributeName, TEXT("to"))) { 189 | CurrentEdge->ToJunction = val; 190 | } else if(!FCString::Stricmp(AttributeName, TEXT("priority"))) { 191 | CurrentEdge->Priority = FPlatformString::Atoi(AttributeValue); 192 | } else if(!FCString::Stricmp(AttributeName, TEXT("name"))) { 193 | CurrentEdge->Name = val; 194 | } else if(!FCString::Stricmp(AttributeName, TEXT("type"))) { 195 | CurrentEdge->Type = Target->RoadTypes[val]; 196 | } else if(!FCString::Stricmp(AttributeName, TEXT("function"))) { 197 | if(!FCString::Stricmp(AttributeValue, TEXT("normal"))) { 198 | } else if(!FCString::Stricmp(AttributeValue, TEXT("connector"))) { 199 | CurrentEdge->Function = ESumoEdgeFunction::SEF_Connector; 200 | } else if(!FCString::Stricmp(AttributeValue, TEXT("internal"))) { 201 | CurrentEdge->Function = ESumoEdgeFunction::SEF_Internal; 202 | } else if(!FCString::Stricmp(AttributeValue, TEXT("crossing"))) { 203 | CurrentEdge->Function = ESumoEdgeFunction::SEF_Crossing; 204 | } else if(!FCString::Stricmp(AttributeValue, TEXT("walkingarea"))) { 205 | CurrentEdge->Function = ESumoEdgeFunction::SEF_WalkingArea; 206 | } 207 | } 208 | } else if(State == ParsingState::Lane) { 209 | if(!FCString::Stricmp(AttributeName, TEXT("id"))) { 210 | CurrentLane->ID = val; 211 | } else if(!FCString::Stricmp(AttributeName, TEXT("index"))) { 212 | CurrentLane->Index = FPlatformString::Atoi(AttributeValue); 213 | } else if(!FCString::Stricmp(AttributeName, TEXT("speed"))) { 214 | CurrentLane->Speed = FPlatformString::Atof(AttributeValue); 215 | } else if(!FCString::Stricmp(AttributeName, TEXT("length"))) { 216 | CurrentLane->Length = FPlatformString::Atof(AttributeValue); 217 | } else if(!FCString::Stricmp(AttributeName, TEXT("shape"))) { 218 | CurrentLane->Shape.Append(ProcessShape(val)); 219 | } 220 | } else if(State == ParsingState::EdgeStopOffset || State == ParsingState::LaneStopOffset) { 221 | if(!FCString::Stricmp(AttributeName, TEXT("value"))) { 222 | CurrentStopOffset->Value = FPlatformString::Atof(AttributeValue); 223 | } else if(!FCString::Stricmp(AttributeName, TEXT("vClasses"))) { 224 | TArray classStr; 225 | val.ParseIntoArray(classStr, TEXT(" "), true); 226 | for(auto &vehicleclass : classStr) { 227 | CurrentStopOffset->VehicleClasses.Add(ToVehicleTypeEnum(vehicleclass)); 228 | } 229 | } else if(!FCString::Stricmp(AttributeName, TEXT("exceptions"))) { 230 | TArray classStr; 231 | val.ParseIntoArray(classStr, TEXT(" "), true); 232 | for(auto &vehicleclass : classStr) { 233 | CurrentStopOffset->Exceptions.Add(ToVehicleTypeEnum(vehicleclass)); 234 | } 235 | } 236 | 237 | } else if(State == ParsingState::TLLogic) { 238 | if(!FCString::Stricmp(AttributeName, TEXT("id"))) { 239 | CurrentTLProgram->ID = val; 240 | } else if(!FCString::Stricmp(AttributeName, TEXT("type"))) { 241 | if(!FCString::Stricmp(AttributeValue, TEXT("static"))) { 242 | CurrentTLProgram->Type = ESumoTrafficLightProgramType::STLPT_Static; 243 | } else if(!FCString::Stricmp(AttributeValue, TEXT("actuated"))) { 244 | CurrentTLProgram->Type = ESumoTrafficLightProgramType::STLPT_Actuated; 245 | } else if(!FCString::Stricmp(AttributeValue, TEXT("delay_based"))) { 246 | CurrentTLProgram->Type = ESumoTrafficLightProgramType::STLPT_DelayBased; 247 | } 248 | } else if(!FCString::Stricmp(AttributeName, TEXT("programID"))) { 249 | CurrentTLProgram->ProgramID = val; 250 | } else if(!FCString::Stricmp(AttributeName, TEXT("offset"))) { 251 | CurrentTLProgram->TimeOffset = FPlatformString::Atoi(AttributeValue); 252 | } 253 | } else if(State == ParsingState::TLPhase) { 254 | if(!FCString::Stricmp(AttributeName, TEXT("duration"))) { 255 | CurrentTLPhase->Duration = FPlatformString::Atoi(AttributeValue); 256 | } else if(!FCString::Stricmp(AttributeName, TEXT("state"))) { 257 | for (int32 i = 0; i < val.Len(); i++) 258 | { 259 | FString currChar = val.Mid(i, 1); 260 | if (currChar.Equals(TEXT("r"))) { 261 | CurrentTLPhase->States.Add(ESumoTrafficLightSignalState::STLSS_Red); 262 | } else if (currChar.Equals(TEXT("y"))) { 263 | CurrentTLPhase->States.Add(ESumoTrafficLightSignalState::STLSS_Yellow); 264 | } else if (currChar.Equals(TEXT("g"))) { 265 | CurrentTLPhase->States.Add(ESumoTrafficLightSignalState::STLSS_Green); 266 | } else if (currChar.Equals(TEXT("G"))) { 267 | CurrentTLPhase->States.Add(ESumoTrafficLightSignalState::STLSS_GreenPriority); 268 | } else if (currChar.Equals(TEXT("s"))) { 269 | CurrentTLPhase->States.Add(ESumoTrafficLightSignalState::STLSS_RightGreen); 270 | } else if (currChar.Equals(TEXT("u"))) { 271 | CurrentTLPhase->States.Add(ESumoTrafficLightSignalState::STLSS_RedYellow); 272 | } else if (currChar.Equals(TEXT("o"))) { 273 | CurrentTLPhase->States.Add(ESumoTrafficLightSignalState::STLSS_OffBlink); 274 | } else if (currChar.Equals(TEXT("O"))) { 275 | CurrentTLPhase->States.Add(ESumoTrafficLightSignalState::STLSS_Off); 276 | } 277 | 278 | } 279 | } else if(!FCString::Stricmp(AttributeName, TEXT("minDur"))) { 280 | CurrentTLPhase->MinDuration = FPlatformString::Atoi(AttributeValue); 281 | } else if(!FCString::Stricmp(AttributeName, TEXT("maxDur"))) { 282 | CurrentTLPhase->MaxDuration = FPlatformString::Atoi(AttributeValue); 283 | } else if(!FCString::Stricmp(AttributeName, TEXT("name"))) { 284 | CurrentTLPhase->Name = val; 285 | } else if(!FCString::Stricmp(AttributeName, TEXT("next"))) { 286 | TArray indexStr; 287 | val.ParseIntoArray(indexStr, TEXT(" "), true); 288 | for(auto &ind : indexStr) { 289 | CurrentTLPhase->Next.Add(FPlatformString::Atoi(TCHAR_TO_ANSI(*ind))); 290 | } 291 | } 292 | } else if(State == ParsingState::Connection) { 293 | if(!FCString::Stricmp(AttributeName, TEXT("from"))) { 294 | CurrentTLPhase->MinDuration = FPlatformString::Atoi(AttributeValue); 295 | } else if(!FCString::Stricmp(AttributeName, TEXT("to"))) { 296 | } else if(!FCString::Stricmp(AttributeName, TEXT("fromLane"))) { 297 | } else if(!FCString::Stricmp(AttributeName, TEXT("toLane"))) { 298 | } else if(!FCString::Stricmp(AttributeName, TEXT("via"))) { 299 | } else if(!FCString::Stricmp(AttributeName, TEXT("tl"))) { 300 | } else if(!FCString::Stricmp(AttributeName, TEXT("linkIndex"))) { 301 | } else if(!FCString::Stricmp(AttributeName, TEXT("dir"))) { 302 | } else if(!FCString::Stricmp(AttributeName, TEXT("state"))) { 303 | } 304 | } 305 | return true; 306 | } 307 | 308 | bool USumoNetworkParser::ProcessClose(const TCHAR* Element) { 309 | if(!FCString::Stricmp(Element, TEXT("location"))) { 310 | State = ParsingState::Root; 311 | } else if(!FCString::Stricmp(Element, TEXT("type"))) { 312 | // UE_LOG(LogTemp,Warning,TEXT("Adding road type: %s"), *CurrentRoadType->ID); 313 | Target->RoadTypes.Add(CurrentRoadType->ID, *CurrentRoadType); 314 | delete CurrentRoadType; 315 | State = ParsingState::Root; 316 | } else if(!FCString::Stricmp(Element, TEXT("junction"))) { 317 | // UE_LOG(LogTemp,Warning,TEXT("Adding junction: %s at UTM:%f, %f (Offset: %f, %f)"), *CurrentJunction->ID, 318 | // UTMJunctionLocationX, 319 | // UTMJunctionLocationY, 320 | // UTMOffsetX, 321 | // UTMOffsetY); 322 | CurrentJunction->Location = UGeoCoordinate(UTMJunctionLocationX, 323 | UTMJunctionLocationY, 324 | FGeoReferenceHelper::GetEPSGForUTM(UTMZone, bNorthernHemisphere != 0)) 325 | .ToFVector2DInEPSG(UGeoCoordinate::EPSG_WGS84); 326 | Target->Junctions.Add(CurrentJunction->ID, *CurrentJunction); 327 | delete CurrentJunction; 328 | State = ParsingState::Root; 329 | } else if(!FCString::Stricmp(Element, TEXT("request"))) { 330 | CurrentJunction->Requests.Add(*CurrentRequest); 331 | delete CurrentRequest; 332 | State = ParsingState::Junction; 333 | } else if(!FCString::Stricmp(Element, TEXT("edge"))) { 334 | // UE_LOG(LogTemp,Warning,TEXT("Adding edge: %s"), *CurrentEdge->ID); 335 | Target->Edges.Add(CurrentEdge->ID, *CurrentEdge); 336 | delete CurrentEdge; 337 | State = ParsingState::Root; 338 | } else if(!FCString::Stricmp(Element, TEXT("stopOffset")) && State == ParsingState::EdgeStopOffset) { 339 | CurrentEdge->StopOffset = *CurrentStopOffset; 340 | delete CurrentStopOffset; 341 | State = ParsingState::Edge; 342 | } else if(!FCString::Stricmp(Element, TEXT("stopOffset")) && State == ParsingState::LaneStopOffset) { 343 | CurrentLane->StopOffset = *CurrentStopOffset; 344 | delete CurrentStopOffset; 345 | State = ParsingState::Lane; 346 | } else if(!FCString::Stricmp(Element, TEXT("lane"))) { 347 | // UE_LOG(LogTemp,Warning,TEXT("Adding lane: %s"), *CurrentLane->ID); 348 | CurrentEdge->Lanes.Add(*CurrentLane); 349 | delete CurrentLane; 350 | State = ParsingState::Edge; 351 | } else if(!FCString::Stricmp(Element, TEXT("tlLogic"))) { 352 | Target->TrafficLightPrograms.Add(CurrentTLProgram->ID, *CurrentTLProgram); 353 | delete CurrentTLProgram; 354 | State = ParsingState::Root; 355 | } else if(!FCString::Stricmp(Element, TEXT("phase"))) { 356 | CurrentTLProgram->Phases.Add(*CurrentTLPhase); 357 | delete CurrentTLPhase; 358 | State = ParsingState::TLLogic; 359 | } else if(!FCString::Stricmp(Element, TEXT("connection"))) { 360 | Target->Connections.Add(*CurrentConnection); 361 | delete CurrentConnection; 362 | State = ParsingState::Root; 363 | } 364 | 365 | return true; 366 | } 367 | 368 | bool USumoNetworkParser::ProcessComment(const TCHAR* Comment) { 369 | return true; 370 | } 371 | 372 | TEnumAsByte USumoNetworkParser::ToVehicleTypeEnum(FString &typestring) 373 | { 374 | if(!FCString::Stricmp(*typestring, TEXT("passenger"))) { 375 | return ESumoVehicleType::SVT_Passenger; 376 | } else if(!FCString::Stricmp(*typestring, TEXT("private"))) { 377 | return ESumoVehicleType::SVT_Private; 378 | } else if(!FCString::Stricmp(*typestring, TEXT("taxi"))) { 379 | return ESumoVehicleType::SVT_Taxi; 380 | } else if(!FCString::Stricmp(*typestring, TEXT("bus"))) { 381 | return ESumoVehicleType::SVT_Bus; 382 | } else if(!FCString::Stricmp(*typestring, TEXT("coach"))) { 383 | return ESumoVehicleType::SVT_Coach; 384 | } else if(!FCString::Stricmp(*typestring, TEXT("delivery"))) { 385 | return ESumoVehicleType::SVT_Delivery; 386 | } else if(!FCString::Stricmp(*typestring, TEXT("truck"))) { 387 | return ESumoVehicleType::SVT_Truck; 388 | } else if(!FCString::Stricmp(*typestring, TEXT("trailer"))) { 389 | return ESumoVehicleType::SVT_Trailer; 390 | } else if(!FCString::Stricmp(*typestring, TEXT("emergency"))) { 391 | return ESumoVehicleType::SVT_Emergency; 392 | } else if(!FCString::Stricmp(*typestring, TEXT("motorcycle"))) { 393 | return ESumoVehicleType::SVT_Motorcycle; 394 | } else if(!FCString::Stricmp(*typestring, TEXT("moped"))) { 395 | return ESumoVehicleType::SVT_Moped; 396 | } else if(!FCString::Stricmp(*typestring, TEXT("bicycle"))) { 397 | return ESumoVehicleType::SVT_Bicycle; 398 | } else if(!FCString::Stricmp(*typestring, TEXT("pedestrian"))) { 399 | return ESumoVehicleType::SVT_Pedestrian; 400 | } else if(!FCString::Stricmp(*typestring, TEXT("tram"))) { 401 | return ESumoVehicleType::SVT_Tram; 402 | } else if(!FCString::Stricmp(*typestring, TEXT("rail_electric"))) { 403 | return ESumoVehicleType::SVT_RailElectric; 404 | } else if(!FCString::Stricmp(*typestring, TEXT("rail_fast"))) { 405 | return ESumoVehicleType::SVT_RailFast; 406 | } else if(!FCString::Stricmp(*typestring, TEXT("rail_urban"))) { 407 | return ESumoVehicleType::SVT_RailUrban; 408 | } else if(!FCString::Stricmp(*typestring, TEXT("rail"))) { 409 | return ESumoVehicleType::SVT_Rail; 410 | } else if(!FCString::Stricmp(*typestring, TEXT("evehicle"))) { 411 | return ESumoVehicleType::SVT_EVehicle; 412 | } else if(!FCString::Stricmp(*typestring, TEXT("army"))) { 413 | return ESumoVehicleType::SVT_Army; 414 | } else if(!FCString::Stricmp(*typestring, TEXT("ship"))) { 415 | return ESumoVehicleType::SVT_Ship; 416 | } else if(!FCString::Stricmp(*typestring, TEXT("authority"))) { 417 | return ESumoVehicleType::SVT_Authority; 418 | } else if(!FCString::Stricmp(*typestring, TEXT("vip"))) { 419 | return ESumoVehicleType::SVT_Vip; 420 | } else if(!FCString::Stricmp(*typestring, TEXT("hov"))) { 421 | return ESumoVehicleType::SVT_Hov; 422 | } else if(!FCString::Stricmp(*typestring, TEXT("custom1"))) { 423 | return ESumoVehicleType::SVT_Custom1; 424 | } else if(!FCString::Stricmp(*typestring, TEXT("custom2"))) { 425 | return ESumoVehicleType::SVT_Custom2; 426 | } 427 | return ESumoVehicleType::SVT_Passenger; 428 | } 429 | 430 | TArray USumoNetworkParser::ProcessShape(FString &ShapeCoordinates) 431 | { 432 | TArray shapeCoords; 433 | TArray vertices; 434 | ShapeCoordinates.ParseIntoArray(shapeCoords, TEXT(" "), true); 435 | for(FString &coord : shapeCoords) { 436 | TArray components; 437 | coord.ParseIntoArray(components, TEXT(","), true); 438 | double x = FPlatformString::Atod(*components[0]) - UTMOffsetX; 439 | double y = FPlatformString::Atod(*components[1]) - UTMOffsetY; 440 | FVector2D geo = UGeoCoordinate(x, y, FGeoReferenceHelper::GetEPSGForUTM(UTMZone, 441 | bNorthernHemisphere != 0)).ToFVector2DInEPSG(UGeoCoordinate::EPSG_WGS84); 442 | double z = components.Num() > 2 ? FPlatformString::Atod(*components[2]) : 0.0; 443 | vertices.Add(FVector(geo, z)); 444 | // UE_LOG(LogTemp, Warning, TEXT("ShapeCoord: UTM(%d): %.4f,%.4f WGS+Height: %s + %.4f"), UTMZone, x, y, *geo.ToString(), z); 445 | } 446 | return vertices; 447 | } 448 | -------------------------------------------------------------------------------- /Source/SumoUE4/Public/earcut/earcut.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | ISC License 3 | 4 | Copyright (c) 2015, Mapbox 5 | 6 | Permission to use, copy, modify, and/or distribute this software for any purpose 7 | with or without fee is hereby granted, provided that the above copyright notice 8 | and this permission notice appear in all copies. 9 | 10 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 11 | REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND 12 | FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 13 | INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS 14 | OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 15 | TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF 16 | THIS SOFTWARE. 17 | 18 | Source: https://github.com/mapbox/earcut.hpp 19 | */ 20 | 21 | #pragma once 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | namespace mapbox { 30 | 31 | namespace util { 32 | 33 | template struct nth { 34 | inline static typename std::tuple_element::type 35 | get(const T& t) { return std::get(t); }; 36 | }; 37 | 38 | } 39 | 40 | namespace detail { 41 | 42 | template 43 | class Earcut { 44 | public: 45 | std::vector indices; 46 | std::size_t vertices = 0; 47 | 48 | template 49 | void operator()(const Polygon& points); 50 | 51 | private: 52 | struct Node { 53 | Node(N index, double x_, double y_) : i(index), x(x_), y(y_) {} 54 | Node(const Node&) = delete; 55 | Node& operator=(const Node&) = delete; 56 | Node(Node&&) = delete; 57 | Node& operator=(Node&&) = delete; 58 | 59 | const N i; 60 | const double x; 61 | const double y; 62 | 63 | // previous and next vertice nodes in a polygon ring 64 | Node* prev = nullptr; 65 | Node* next = nullptr; 66 | 67 | // z-order curve value 68 | int32_t z = 0; 69 | 70 | // previous and next nodes in z-order 71 | Node* prevZ = nullptr; 72 | Node* nextZ = nullptr; 73 | 74 | // indicates whether this is a steiner point 75 | bool steiner = false; 76 | }; 77 | 78 | template Node* linkedList(const Ring& points, const bool clockwise); 79 | Node* filterPoints(Node* start, Node* end = nullptr); 80 | void earcutLinked(Node* ear, int pass = 0); 81 | bool isEar(Node* ear); 82 | bool isEarHashed(Node* ear); 83 | Node* cureLocalIntersections(Node* start); 84 | void splitEarcut(Node* start); 85 | template Node* eliminateHoles(const Polygon& points, Node* outerNode); 86 | void eliminateHole(Node* hole, Node* outerNode); 87 | Node* findHoleBridge(Node* hole, Node* outerNode); 88 | bool sectorContainsSector(const Node* m, const Node* p); 89 | void indexCurve(Node* start); 90 | Node* sortLinked(Node* list); 91 | int32_t zOrder(const double x_, const double y_); 92 | Node* getLeftmost(Node* start); 93 | bool pointInTriangle(double ax, double ay, double bx, double by, double cx, double cy, double px, double py) const; 94 | bool isValidDiagonal(Node* a, Node* b); 95 | double area(const Node* p, const Node* q, const Node* r) const; 96 | bool equals(const Node* p1, const Node* p2); 97 | bool intersects(const Node* p1, const Node* q1, const Node* p2, const Node* q2); 98 | bool onSegment(const Node* p, const Node* q, const Node* r); 99 | int sign(double val); 100 | bool intersectsPolygon(const Node* a, const Node* b); 101 | bool locallyInside(const Node* a, const Node* b); 102 | bool middleInside(const Node* a, const Node* b); 103 | Node* splitPolygon(Node* a, Node* b); 104 | template Node* insertNode(std::size_t i, const Point& p, Node* last); 105 | void removeNode(Node* p); 106 | 107 | bool hashing; 108 | double minX, maxX; 109 | double minY, maxY; 110 | double inv_size = 0; 111 | 112 | template > 113 | class ObjectPool { 114 | public: 115 | ObjectPool() { } 116 | ObjectPool(std::size_t blockSize_) { 117 | reset(blockSize_); 118 | } 119 | ~ObjectPool() { 120 | clear(); 121 | } 122 | template 123 | T* construct(Args&&... args) { 124 | if (currentIndex >= blockSize) { 125 | currentBlock = alloc_traits::allocate(alloc, blockSize); 126 | allocations.emplace_back(currentBlock); 127 | currentIndex = 0; 128 | } 129 | T* object = ¤tBlock[currentIndex++]; 130 | alloc_traits::construct(alloc, object, std::forward(args)...); 131 | return object; 132 | } 133 | void reset(std::size_t newBlockSize) { 134 | for (auto allocation : allocations) { 135 | alloc_traits::deallocate(alloc, allocation, blockSize); 136 | } 137 | allocations.clear(); 138 | blockSize = std::max(1, newBlockSize); 139 | currentBlock = nullptr; 140 | currentIndex = blockSize; 141 | } 142 | void clear() { reset(blockSize); } 143 | private: 144 | T* currentBlock = nullptr; 145 | std::size_t currentIndex = 1; 146 | std::size_t blockSize = 1; 147 | std::vector allocations; 148 | Alloc alloc; 149 | typedef typename std::allocator_traits alloc_traits; 150 | }; 151 | ObjectPool nodes; 152 | }; 153 | 154 | template template 155 | void Earcut::operator()(const Polygon& points) { 156 | // reset 157 | indices.clear(); 158 | vertices = 0; 159 | 160 | if (points.empty()) return; 161 | 162 | double x; 163 | double y; 164 | int threshold = 80; 165 | std::size_t len = 0; 166 | 167 | for (size_t i = 0; threshold >= 0 && i < points.size(); i++) { 168 | threshold -= static_cast(points[i].size()); 169 | len += points[i].size(); 170 | } 171 | 172 | //estimate size of nodes and indices 173 | nodes.reset(len * 3 / 2); 174 | indices.reserve(len + points[0].size()); 175 | 176 | Node* outerNode = linkedList(points[0], true); 177 | if (!outerNode || outerNode->prev == outerNode->next) return; 178 | 179 | if (points.size() > 1) outerNode = eliminateHoles(points, outerNode); 180 | 181 | // if the shape is not too simple, we'll use z-order curve hash later; calculate polygon bbox 182 | hashing = threshold < 0; 183 | if (hashing) { 184 | Node* p = outerNode->next; 185 | minX = maxX = outerNode->x; 186 | minY = maxY = outerNode->y; 187 | do { 188 | x = p->x; 189 | y = p->y; 190 | minX = std::min(minX, x); 191 | minY = std::min(minY, y); 192 | maxX = std::max(maxX, x); 193 | maxY = std::max(maxY, y); 194 | p = p->next; 195 | } while (p != outerNode); 196 | 197 | // minX, minY and size are later used to transform coords into integers for z-order calculation 198 | inv_size = std::max(maxX - minX, maxY - minY); 199 | inv_size = inv_size != .0 ? (1. / inv_size) : .0; 200 | } 201 | 202 | earcutLinked(outerNode); 203 | 204 | nodes.clear(); 205 | } 206 | 207 | // create a circular doubly linked list from polygon points in the specified winding order 208 | template template 209 | typename Earcut::Node* 210 | Earcut::linkedList(const Ring& points, const bool clockwise) { 211 | using Point = typename Ring::value_type; 212 | double sum = 0; 213 | const std::size_t len = points.size(); 214 | std::size_t i, j; 215 | Node* last = nullptr; 216 | 217 | // calculate original winding order of a polygon ring 218 | for (i = 0, j = len > 0 ? len - 1 : 0; i < len; j = i++) { 219 | const auto& p1 = points[i]; 220 | const auto& p2 = points[j]; 221 | const double p20 = util::nth<0, Point>::get(p2); 222 | const double p10 = util::nth<0, Point>::get(p1); 223 | const double p11 = util::nth<1, Point>::get(p1); 224 | const double p21 = util::nth<1, Point>::get(p2); 225 | sum += (p20 - p10) * (p11 + p21); 226 | } 227 | 228 | // link points into circular doubly-linked list in the specified winding order 229 | if (clockwise == (sum > 0)) { 230 | for (i = 0; i < len; i++) last = insertNode(vertices + i, points[i], last); 231 | } else { 232 | for (i = len; i-- > 0;) last = insertNode(vertices + i, points[i], last); 233 | } 234 | 235 | if (last && equals(last, last->next)) { 236 | removeNode(last); 237 | last = last->next; 238 | } 239 | 240 | vertices += len; 241 | 242 | return last; 243 | } 244 | 245 | // eliminate colinear or duplicate points 246 | template 247 | typename Earcut::Node* 248 | Earcut::filterPoints(Node* start, Node* end) { 249 | if (!end) end = start; 250 | 251 | Node* p = start; 252 | bool again; 253 | do { 254 | again = false; 255 | 256 | if (!p->steiner && (equals(p, p->next) || area(p->prev, p, p->next) == 0)) { 257 | removeNode(p); 258 | p = end = p->prev; 259 | 260 | if (p == p->next) break; 261 | again = true; 262 | 263 | } else { 264 | p = p->next; 265 | } 266 | } while (again || p != end); 267 | 268 | return end; 269 | } 270 | 271 | // main ear slicing loop which triangulates a polygon (given as a linked list) 272 | template 273 | void Earcut::earcutLinked(Node* ear, int pass) { 274 | if (!ear) return; 275 | 276 | // interlink polygon nodes in z-order 277 | if (!pass && hashing) indexCurve(ear); 278 | 279 | Node* stop = ear; 280 | Node* prev; 281 | Node* next; 282 | 283 | int iterations = 0; 284 | 285 | // iterate through ears, slicing them one by one 286 | while (ear->prev != ear->next) { 287 | iterations++; 288 | prev = ear->prev; 289 | next = ear->next; 290 | 291 | if (hashing ? isEarHashed(ear) : isEar(ear)) { 292 | // cut off the triangle 293 | indices.emplace_back(prev->i); 294 | indices.emplace_back(ear->i); 295 | indices.emplace_back(next->i); 296 | 297 | removeNode(ear); 298 | 299 | // skipping the next vertice leads to less sliver triangles 300 | ear = next->next; 301 | stop = next->next; 302 | 303 | continue; 304 | } 305 | 306 | ear = next; 307 | 308 | // if we looped through the whole remaining polygon and can't find any more ears 309 | if (ear == stop) { 310 | // try filtering points and slicing again 311 | if (!pass) earcutLinked(filterPoints(ear), 1); 312 | 313 | // if this didn't work, try curing all small self-intersections locally 314 | else if (pass == 1) { 315 | ear = cureLocalIntersections(filterPoints(ear)); 316 | earcutLinked(ear, 2); 317 | 318 | // as a last resort, try splitting the remaining polygon into two 319 | } else if (pass == 2) splitEarcut(ear); 320 | 321 | break; 322 | } 323 | } 324 | } 325 | 326 | // check whether a polygon node forms a valid ear with adjacent nodes 327 | template 328 | bool Earcut::isEar(Node* ear) { 329 | const Node* a = ear->prev; 330 | const Node* b = ear; 331 | const Node* c = ear->next; 332 | 333 | if (area(a, b, c) >= 0) return false; // reflex, can't be an ear 334 | 335 | // now make sure we don't have other points inside the potential ear 336 | Node* p = ear->next->next; 337 | 338 | while (p != ear->prev) { 339 | if (pointInTriangle(a->x, a->y, b->x, b->y, c->x, c->y, p->x, p->y) && 340 | area(p->prev, p, p->next) >= 0) return false; 341 | p = p->next; 342 | } 343 | 344 | return true; 345 | } 346 | 347 | template 348 | bool Earcut::isEarHashed(Node* ear) { 349 | const Node* a = ear->prev; 350 | const Node* b = ear; 351 | const Node* c = ear->next; 352 | 353 | if (area(a, b, c) >= 0) return false; // reflex, can't be an ear 354 | 355 | // triangle bbox; min & max are calculated like this for speed 356 | const double minTX = std::min(a->x, std::min(b->x, c->x)); 357 | const double minTY = std::min(a->y, std::min(b->y, c->y)); 358 | const double maxTX = std::max(a->x, std::max(b->x, c->x)); 359 | const double maxTY = std::max(a->y, std::max(b->y, c->y)); 360 | 361 | // z-order range for the current triangle bbox; 362 | const int32_t minZ = zOrder(minTX, minTY); 363 | const int32_t maxZ = zOrder(maxTX, maxTY); 364 | 365 | // first look for points inside the triangle in increasing z-order 366 | Node* p = ear->nextZ; 367 | 368 | while (p && p->z <= maxZ) { 369 | if (p != ear->prev && p != ear->next && 370 | pointInTriangle(a->x, a->y, b->x, b->y, c->x, c->y, p->x, p->y) && 371 | area(p->prev, p, p->next) >= 0) return false; 372 | p = p->nextZ; 373 | } 374 | 375 | // then look for points in decreasing z-order 376 | p = ear->prevZ; 377 | 378 | while (p && p->z >= minZ) { 379 | if (p != ear->prev && p != ear->next && 380 | pointInTriangle(a->x, a->y, b->x, b->y, c->x, c->y, p->x, p->y) && 381 | area(p->prev, p, p->next) >= 0) return false; 382 | p = p->prevZ; 383 | } 384 | 385 | return true; 386 | } 387 | 388 | // go through all polygon nodes and cure small local self-intersections 389 | template 390 | typename Earcut::Node* 391 | Earcut::cureLocalIntersections(Node* start) { 392 | Node* p = start; 393 | do { 394 | Node* a = p->prev; 395 | Node* b = p->next->next; 396 | 397 | // a self-intersection where edge (v[i-1],v[i]) intersects (v[i+1],v[i+2]) 398 | if (!equals(a, b) && intersects(a, p, p->next, b) && locallyInside(a, b) && locallyInside(b, a)) { 399 | indices.emplace_back(a->i); 400 | indices.emplace_back(p->i); 401 | indices.emplace_back(b->i); 402 | 403 | // remove two nodes involved 404 | removeNode(p); 405 | removeNode(p->next); 406 | 407 | p = start = b; 408 | } 409 | p = p->next; 410 | } while (p != start); 411 | 412 | return filterPoints(p); 413 | } 414 | 415 | // try splitting polygon into two and triangulate them independently 416 | template 417 | void Earcut::splitEarcut(Node* start) { 418 | // look for a valid diagonal that divides the polygon into two 419 | Node* a = start; 420 | do { 421 | Node* b = a->next->next; 422 | while (b != a->prev) { 423 | if (a->i != b->i && isValidDiagonal(a, b)) { 424 | // split the polygon in two by the diagonal 425 | Node* c = splitPolygon(a, b); 426 | 427 | // filter colinear points around the cuts 428 | a = filterPoints(a, a->next); 429 | c = filterPoints(c, c->next); 430 | 431 | // run earcut on each half 432 | earcutLinked(a); 433 | earcutLinked(c); 434 | return; 435 | } 436 | b = b->next; 437 | } 438 | a = a->next; 439 | } while (a != start); 440 | } 441 | 442 | // link every hole into the outer loop, producing a single-ring polygon without holes 443 | template template 444 | typename Earcut::Node* 445 | Earcut::eliminateHoles(const Polygon& points, Node* outerNode) { 446 | const size_t len = points.size(); 447 | 448 | std::vector queue; 449 | for (size_t i = 1; i < len; i++) { 450 | Node* list = linkedList(points[i], false); 451 | if (list) { 452 | if (list == list->next) list->steiner = true; 453 | queue.push_back(getLeftmost(list)); 454 | } 455 | } 456 | std::sort(queue.begin(), queue.end(), [](const Node* a, const Node* b) { 457 | return a->x < b->x; 458 | }); 459 | 460 | // process holes from left to right 461 | for (size_t i = 0; i < queue.size(); i++) { 462 | eliminateHole(queue[i], outerNode); 463 | outerNode = filterPoints(outerNode, outerNode->next); 464 | } 465 | 466 | return outerNode; 467 | } 468 | 469 | // find a bridge between vertices that connects hole with an outer ring and and link it 470 | template 471 | void Earcut::eliminateHole(Node* hole, Node* outerNode) { 472 | outerNode = findHoleBridge(hole, outerNode); 473 | if (outerNode) { 474 | Node* b = splitPolygon(outerNode, hole); 475 | 476 | // filter out colinear points around cuts 477 | filterPoints(outerNode, outerNode->next); 478 | filterPoints(b, b->next); 479 | } 480 | } 481 | 482 | // David Eberly's algorithm for finding a bridge between hole and outer polygon 483 | template 484 | typename Earcut::Node* 485 | Earcut::findHoleBridge(Node* hole, Node* outerNode) { 486 | Node* p = outerNode; 487 | double hx = hole->x; 488 | double hy = hole->y; 489 | double qx = -std::numeric_limits::infinity(); 490 | Node* m = nullptr; 491 | 492 | // find a segment intersected by a ray from the hole's leftmost Vertex to the left; 493 | // segment's endpoint with lesser x will be potential connection Vertex 494 | do { 495 | if (hy <= p->y && hy >= p->next->y && p->next->y != p->y) { 496 | double x = p->x + (hy - p->y) * (p->next->x - p->x) / (p->next->y - p->y); 497 | if (x <= hx && x > qx) { 498 | qx = x; 499 | if (x == hx) { 500 | if (hy == p->y) return p; 501 | if (hy == p->next->y) return p->next; 502 | } 503 | m = p->x < p->next->x ? p : p->next; 504 | } 505 | } 506 | p = p->next; 507 | } while (p != outerNode); 508 | 509 | if (!m) return 0; 510 | 511 | if (hx == qx) return m; // hole touches outer segment; pick leftmost endpoint 512 | 513 | // look for points inside the triangle of hole Vertex, segment intersection and endpoint; 514 | // if there are no points found, we have a valid connection; 515 | // otherwise choose the Vertex of the minimum angle with the ray as connection Vertex 516 | 517 | const Node* stop = m; 518 | double tanMin = std::numeric_limits::infinity(); 519 | double tanCur = 0; 520 | 521 | p = m; 522 | double mx = m->x; 523 | double my = m->y; 524 | 525 | do { 526 | if (hx >= p->x && p->x >= mx && hx != p->x && 527 | pointInTriangle(hy < my ? hx : qx, hy, mx, my, hy < my ? qx : hx, hy, p->x, p->y)) { 528 | 529 | tanCur = std::abs(hy - p->y) / (hx - p->x); // tangential 530 | 531 | if (locallyInside(p, hole) && 532 | (tanCur < tanMin || (tanCur == tanMin && (p->x > m->x || sectorContainsSector(m, p))))) { 533 | m = p; 534 | tanMin = tanCur; 535 | } 536 | } 537 | 538 | p = p->next; 539 | } while (p != stop); 540 | 541 | return m; 542 | } 543 | 544 | // whether sector in vertex m contains sector in vertex p in the same coordinates 545 | template 546 | bool Earcut::sectorContainsSector(const Node* m, const Node* p) { 547 | return area(m->prev, m, p->prev) < 0 && area(p->next, m, m->next) < 0; 548 | } 549 | 550 | // interlink polygon nodes in z-order 551 | template 552 | void Earcut::indexCurve(Node* start) { 553 | assert(start); 554 | Node* p = start; 555 | 556 | do { 557 | p->z = p->z ? p->z : zOrder(p->x, p->y); 558 | p->prevZ = p->prev; 559 | p->nextZ = p->next; 560 | p = p->next; 561 | } while (p != start); 562 | 563 | p->prevZ->nextZ = nullptr; 564 | p->prevZ = nullptr; 565 | 566 | sortLinked(p); 567 | } 568 | 569 | // Simon Tatham's linked list merge sort algorithm 570 | // http://www.chiark.greenend.org.uk/~sgtatham/algorithms/listsort.html 571 | template 572 | typename Earcut::Node* 573 | Earcut::sortLinked(Node* list) { 574 | assert(list); 575 | Node* p; 576 | Node* q; 577 | Node* e; 578 | Node* tail; 579 | int i, numMerges, pSize, qSize; 580 | int inSize = 1; 581 | 582 | for (;;) { 583 | p = list; 584 | list = nullptr; 585 | tail = nullptr; 586 | numMerges = 0; 587 | 588 | while (p) { 589 | numMerges++; 590 | q = p; 591 | pSize = 0; 592 | for (i = 0; i < inSize; i++) { 593 | pSize++; 594 | q = q->nextZ; 595 | if (!q) break; 596 | } 597 | 598 | qSize = inSize; 599 | 600 | while (pSize > 0 || (qSize > 0 && q)) { 601 | 602 | if (pSize == 0) { 603 | e = q; 604 | q = q->nextZ; 605 | qSize--; 606 | } else if (qSize == 0 || !q) { 607 | e = p; 608 | p = p->nextZ; 609 | pSize--; 610 | } else if (p->z <= q->z) { 611 | e = p; 612 | p = p->nextZ; 613 | pSize--; 614 | } else { 615 | e = q; 616 | q = q->nextZ; 617 | qSize--; 618 | } 619 | 620 | if (tail) tail->nextZ = e; 621 | else list = e; 622 | 623 | e->prevZ = tail; 624 | tail = e; 625 | } 626 | 627 | p = q; 628 | } 629 | 630 | tail->nextZ = nullptr; 631 | 632 | if (numMerges <= 1) return list; 633 | 634 | inSize *= 2; 635 | } 636 | } 637 | 638 | // z-order of a Vertex given coords and size of the data bounding box 639 | template 640 | int32_t Earcut::zOrder(const double x_, const double y_) { 641 | // coords are transformed into non-negative 15-bit integer range 642 | int32_t x = static_cast(32767.0 * (x_ - minX) * inv_size); 643 | int32_t y = static_cast(32767.0 * (y_ - minY) * inv_size); 644 | 645 | x = (x | (x << 8)) & 0x00FF00FF; 646 | x = (x | (x << 4)) & 0x0F0F0F0F; 647 | x = (x | (x << 2)) & 0x33333333; 648 | x = (x | (x << 1)) & 0x55555555; 649 | 650 | y = (y | (y << 8)) & 0x00FF00FF; 651 | y = (y | (y << 4)) & 0x0F0F0F0F; 652 | y = (y | (y << 2)) & 0x33333333; 653 | y = (y | (y << 1)) & 0x55555555; 654 | 655 | return x | (y << 1); 656 | } 657 | 658 | // find the leftmost node of a polygon ring 659 | template 660 | typename Earcut::Node* 661 | Earcut::getLeftmost(Node* start) { 662 | Node* p = start; 663 | Node* leftmost = start; 664 | do { 665 | if (p->x < leftmost->x || (p->x == leftmost->x && p->y < leftmost->y)) 666 | leftmost = p; 667 | p = p->next; 668 | } while (p != start); 669 | 670 | return leftmost; 671 | } 672 | 673 | // check if a point lies within a convex triangle 674 | template 675 | bool Earcut::pointInTriangle(double ax, double ay, double bx, double by, double cx, double cy, double px, double py) const { 676 | return (cx - px) * (ay - py) - (ax - px) * (cy - py) >= 0 && 677 | (ax - px) * (by - py) - (bx - px) * (ay - py) >= 0 && 678 | (bx - px) * (cy - py) - (cx - px) * (by - py) >= 0; 679 | } 680 | 681 | // check if a diagonal between two polygon nodes is valid (lies in polygon interior) 682 | template 683 | bool Earcut::isValidDiagonal(Node* a, Node* b) { 684 | return a->next->i != b->i && a->prev->i != b->i && !intersectsPolygon(a, b) && // dones't intersect other edges 685 | ((locallyInside(a, b) && locallyInside(b, a) && middleInside(a, b) && // locally visible 686 | (area(a->prev, a, b->prev) != 0.0 || area(a, b->prev, b) != 0.0)) || // does not create opposite-facing sectors 687 | (equals(a, b) && area(a->prev, a, a->next) > 0 && area(b->prev, b, b->next) > 0)); // special zero-length case 688 | } 689 | 690 | // signed area of a triangle 691 | template 692 | double Earcut::area(const Node* p, const Node* q, const Node* r) const { 693 | return (q->y - p->y) * (r->x - q->x) - (q->x - p->x) * (r->y - q->y); 694 | } 695 | 696 | // check if two points are equal 697 | template 698 | bool Earcut::equals(const Node* p1, const Node* p2) { 699 | return p1->x == p2->x && p1->y == p2->y; 700 | } 701 | 702 | // check if two segments intersect 703 | template 704 | bool Earcut::intersects(const Node* p1, const Node* q1, const Node* p2, const Node* q2) { 705 | int o1 = sign(area(p1, q1, p2)); 706 | int o2 = sign(area(p1, q1, q2)); 707 | int o3 = sign(area(p2, q2, p1)); 708 | int o4 = sign(area(p2, q2, q1)); 709 | 710 | if (o1 != o2 && o3 != o4) return true; // general case 711 | 712 | if (o1 == 0 && onSegment(p1, p2, q1)) return true; // p1, q1 and p2 are collinear and p2 lies on p1q1 713 | if (o2 == 0 && onSegment(p1, q2, q1)) return true; // p1, q1 and q2 are collinear and q2 lies on p1q1 714 | if (o3 == 0 && onSegment(p2, p1, q2)) return true; // p2, q2 and p1 are collinear and p1 lies on p2q2 715 | if (o4 == 0 && onSegment(p2, q1, q2)) return true; // p2, q2 and q1 are collinear and q1 lies on p2q2 716 | 717 | return false; 718 | } 719 | 720 | // for collinear points p, q, r, check if point q lies on segment pr 721 | template 722 | bool Earcut::onSegment(const Node* p, const Node* q, const Node* r) { 723 | return q->x <= std::max(p->x, r->x) && 724 | q->x >= std::min(p->x, r->x) && 725 | q->y <= std::max(p->y, r->y) && 726 | q->y >= std::min(p->y, r->y); 727 | } 728 | 729 | template 730 | int Earcut::sign(double val) { 731 | return (0.0 < val) - (val < 0.0); 732 | } 733 | 734 | // check if a polygon diagonal intersects any polygon segments 735 | template 736 | bool Earcut::intersectsPolygon(const Node* a, const Node* b) { 737 | const Node* p = a; 738 | do { 739 | if (p->i != a->i && p->next->i != a->i && p->i != b->i && p->next->i != b->i && 740 | intersects(p, p->next, a, b)) return true; 741 | p = p->next; 742 | } while (p != a); 743 | 744 | return false; 745 | } 746 | 747 | // check if a polygon diagonal is locally inside the polygon 748 | template 749 | bool Earcut::locallyInside(const Node* a, const Node* b) { 750 | return area(a->prev, a, a->next) < 0 ? 751 | area(a, b, a->next) >= 0 && area(a, a->prev, b) >= 0 : 752 | area(a, b, a->prev) < 0 || area(a, a->next, b) < 0; 753 | } 754 | 755 | // check if the middle Vertex of a polygon diagonal is inside the polygon 756 | template 757 | bool Earcut::middleInside(const Node* a, const Node* b) { 758 | const Node* p = a; 759 | bool inside = false; 760 | double px = (a->x + b->x) / 2; 761 | double py = (a->y + b->y) / 2; 762 | do { 763 | if (((p->y > py) != (p->next->y > py)) && p->next->y != p->y && 764 | (px < (p->next->x - p->x) * (py - p->y) / (p->next->y - p->y) + p->x)) 765 | inside = !inside; 766 | p = p->next; 767 | } while (p != a); 768 | 769 | return inside; 770 | } 771 | 772 | // link two polygon vertices with a bridge; if the vertices belong to the same ring, it splits 773 | // polygon into two; if one belongs to the outer ring and another to a hole, it merges it into a 774 | // single ring 775 | template 776 | typename Earcut::Node* 777 | Earcut::splitPolygon(Node* a, Node* b) { 778 | Node* a2 = nodes.construct(a->i, a->x, a->y); 779 | Node* b2 = nodes.construct(b->i, b->x, b->y); 780 | Node* an = a->next; 781 | Node* bp = b->prev; 782 | 783 | a->next = b; 784 | b->prev = a; 785 | 786 | a2->next = an; 787 | an->prev = a2; 788 | 789 | b2->next = a2; 790 | a2->prev = b2; 791 | 792 | bp->next = b2; 793 | b2->prev = bp; 794 | 795 | return b2; 796 | } 797 | 798 | // create a node and util::optionally link it with previous one (in a circular doubly linked list) 799 | template template 800 | typename Earcut::Node* 801 | Earcut::insertNode(std::size_t i, const Point& pt, Node* last) { 802 | Node* p = nodes.construct(static_cast(i), util::nth<0, Point>::get(pt), util::nth<1, Point>::get(pt)); 803 | 804 | if (!last) { 805 | p->prev = p; 806 | p->next = p; 807 | 808 | } else { 809 | assert(last); 810 | p->next = last->next; 811 | p->prev = last; 812 | last->next->prev = p; 813 | last->next = p; 814 | } 815 | return p; 816 | } 817 | 818 | template 819 | void Earcut::removeNode(Node* p) { 820 | p->next->prev = p->prev; 821 | p->prev->next = p->next; 822 | 823 | if (p->prevZ) p->prevZ->nextZ = p->nextZ; 824 | if (p->nextZ) p->nextZ->prevZ = p->prevZ; 825 | } 826 | } 827 | 828 | template 829 | std::vector earcut(const Polygon& poly) { 830 | mapbox::detail::Earcut earcut; 831 | earcut(poly); 832 | return std::move(earcut.indices); 833 | } 834 | } 835 | --------------------------------------------------------------------------------