// Copyright 2017 Mike Fricker. All Rights Reserved. #include "StreetMapImporting.h" #include "OSMFile.h" FOSMFile::FOSMFile() : ParsingState( ParsingState::Root ) { } FOSMFile::~FOSMFile() { // Clean up time { for( auto* Way : Ways ) { delete Way; } Ways.Empty(); for( auto HashPair : NodeMap ) { FOSMNodeInfo* NodeInfo = HashPair.Value; delete NodeInfo; } NodeMap.Empty(); } } bool FOSMFile::LoadOpenStreetMapFile( FString& OSMFilePath, const bool bIsFilePathActuallyTextBuffer, FFeedbackContext* FeedbackContext ) { const bool bShowSlowTaskDialog = true; const bool bShowCancelButton = true; FText ErrorMessage; int32 ErrorLineNumber; if( FFastXml::ParseXmlFile( this, bIsFilePathActuallyTextBuffer ? nullptr : *OSMFilePath, bIsFilePathActuallyTextBuffer ? OSMFilePath.GetCharArray().GetData() : nullptr, FeedbackContext, bShowSlowTaskDialog, bShowCancelButton, /* Out */ ErrorMessage, /* Out */ ErrorLineNumber ) ) { if( NodeMap.Num() > 0 ) { AverageLatitude /= NodeMap.Num(); AverageLongitude /= NodeMap.Num(); } return true; } if( FeedbackContext != nullptr ) { FeedbackContext->Logf( ELogVerbosity::Error, TEXT( "Failed to load OpenStreetMap XML file ('%s', Line %i)" ), *ErrorMessage.ToString(), ErrorLineNumber ); } return false; } bool FOSMFile::ProcessXmlDeclaration( const TCHAR* ElementData, int32 XmlFileLineNumber ) { // Don't care about XML declaration return true; } bool FOSMFile::ProcessComment( const TCHAR* Comment ) { // Don't care about comments return true; } bool FOSMFile::ProcessElement( const TCHAR* ElementName, const TCHAR* ElementData, int32 XmlFileLineNumber ) { if( ParsingState == ParsingState::Root ) { if( !FCString::Stricmp( ElementName, TEXT( "node" ) ) ) { ParsingState = ParsingState::Node; CurrentNodeInfo = new FOSMNodeInfo(); CurrentNodeInfo->Latitude = 0.0; CurrentNodeInfo->Longitude = 0.0; } else if( !FCString::Stricmp( ElementName, TEXT( "way" ) ) ) { ParsingState = ParsingState::Way; CurrentWayInfo = new FOSMWayInfo(); CurrentWayInfo->Name.Empty(); CurrentWayInfo->Ref.Empty(); CurrentWayInfo->WayType = EOSMWayType::Other; CurrentWayInfo->Height = 0.0; CurrentWayInfo->bIsOneWay = false; // @todo: We're currently ignoring the "visible" tag on ways, which means that roads will always // be included in our data set. It might be nice to make this an import option. } } else if( ParsingState == ParsingState::Way ) { if( !FCString::Stricmp( ElementName, TEXT( "nd" ) ) ) { ParsingState = ParsingState::Way_NodeRef; } else if( !FCString::Stricmp( ElementName, TEXT( "tag" ) ) ) { ParsingState = ParsingState::Way_Tag; } } return true; } bool FOSMFile::ProcessAttribute( const TCHAR* AttributeName, const TCHAR* AttributeValue ) { if( ParsingState == ParsingState::Node ) { if( !FCString::Stricmp( AttributeName, TEXT( "id" ) ) ) { CurrentNodeID = FPlatformString::Atoi64( AttributeValue ); } else if( !FCString::Stricmp( AttributeName, TEXT( "lat" ) ) ) { CurrentNodeInfo->Latitude = FPlatformString::Atod( AttributeValue ); AverageLatitude += CurrentNodeInfo->Latitude; // Update minimum and maximum latitude // @todo: Performance: Instead of computing our own bounding box, we could parse the "minlat" and // "minlon" tags from the OSM file if( CurrentNodeInfo->Latitude < MinLatitude ) { MinLatitude = CurrentNodeInfo->Latitude; } if( CurrentNodeInfo->Latitude > MaxLatitude ) { MaxLatitude = CurrentNodeInfo->Latitude; } } else if( !FCString::Stricmp( AttributeName, TEXT( "lon" ) ) ) { CurrentNodeInfo->Longitude = FPlatformString::Atod( AttributeValue ); AverageLongitude += CurrentNodeInfo->Longitude; // Update minimum and maximum longitude if( CurrentNodeInfo->Longitude < MinLongitude ) { MinLongitude = CurrentNodeInfo->Longitude; } if( CurrentNodeInfo->Longitude > MaxLongitude ) { MaxLongitude = CurrentNodeInfo->Longitude; } } } else if( ParsingState == ParsingState::Way ) { // ... } else if( ParsingState == ParsingState::Way_NodeRef ) { if( !FCString::Stricmp( AttributeName, TEXT( "ref" ) ) ) { FOSMNodeInfo* ReferencedNode = NodeMap.FindRef( FPlatformString::Atoi64( AttributeValue ) ); const int NewNodeIndex = CurrentWayInfo->Nodes.Num(); CurrentWayInfo->Nodes.Add( ReferencedNode ); // Update the node with information about the way that is referencing it { FOSMWayRef NewWayRef; NewWayRef.Way = CurrentWayInfo; NewWayRef.NodeIndex = NewNodeIndex; ReferencedNode->WayRefs.Add( NewWayRef ); } } } else if( ParsingState == ParsingState::Way_Tag ) { if( !FCString::Stricmp( AttributeName, TEXT( "k" ) ) ) { CurrentWayTagKey = AttributeValue; } else if( !FCString::Stricmp( AttributeName, TEXT( "v" ) ) ) { if( !FCString::Stricmp( CurrentWayTagKey, TEXT( "name" ) ) ) { CurrentWayInfo->Name = AttributeValue; } else if( !FCString::Stricmp( CurrentWayTagKey, TEXT( "ref" ) ) ) { CurrentWayInfo->Ref = AttributeValue; } else if( !FCString::Stricmp( CurrentWayTagKey, TEXT( "highway" ) ) ) { EOSMWayType WayType = EOSMWayType::Other; if( !FCString::Stricmp( AttributeValue, TEXT( "motorway" ) ) ) { WayType = EOSMWayType::Motorway; } else if( !FCString::Stricmp( AttributeValue, TEXT( "motorway_link" ) ) ) { WayType = EOSMWayType::Motorway_Link; } else if( !FCString::Stricmp( AttributeValue, TEXT( "trunk" ) ) ) { WayType = EOSMWayType::Trunk; } else if( !FCString::Stricmp( AttributeValue, TEXT( "trunk_link" ) ) ) { WayType = EOSMWayType::Trunk_Link; } else if( !FCString::Stricmp( AttributeValue, TEXT( "primary" ) ) ) { WayType = EOSMWayType::Primary; } else if( !FCString::Stricmp( AttributeValue, TEXT( "primary_link" ) ) ) { WayType = EOSMWayType::Primary_Link; } else if( !FCString::Stricmp( AttributeValue, TEXT( "secondary" ) ) ) { WayType = EOSMWayType::Secondary; } else if( !FCString::Stricmp( AttributeValue, TEXT( "secondary_link" ) ) ) { WayType = EOSMWayType::Secondary_Link; } else if( !FCString::Stricmp( AttributeValue, TEXT( "tertiary" ) ) ) { WayType = EOSMWayType::Tertiary; } else if( !FCString::Stricmp( AttributeValue, TEXT( "tertiary_link" ) ) ) { WayType = EOSMWayType::Tertiary_Link; } else if( !FCString::Stricmp( AttributeValue, TEXT( "residential" ) ) ) { WayType = EOSMWayType::Residential; } else if( !FCString::Stricmp( AttributeValue, TEXT( "service" ) ) ) { WayType = EOSMWayType::Service; } else if( !FCString::Stricmp( AttributeValue, TEXT( "unclassified" ) ) ) { WayType = EOSMWayType::Unclassified; } else if( !FCString::Stricmp( AttributeValue, TEXT( "living_street" ) ) ) { WayType = EOSMWayType::Living_Street; } else if( !FCString::Stricmp( AttributeValue, TEXT( "pedestrian" ) ) ) { WayType = EOSMWayType::Pedestrian; } else if( !FCString::Stricmp( AttributeValue, TEXT( "track" ) ) ) { WayType = EOSMWayType::Track; } else if( !FCString::Stricmp( AttributeValue, TEXT( "bus_guideway" ) ) ) { WayType = EOSMWayType::Bus_Guideway; } else if( !FCString::Stricmp( AttributeValue, TEXT( "raceway" ) ) ) { WayType = EOSMWayType::Raceway; } else if( !FCString::Stricmp( AttributeValue, TEXT( "road" ) ) ) { WayType = EOSMWayType::Road; } else if( !FCString::Stricmp( AttributeValue, TEXT( "footway" ) ) ) { WayType = EOSMWayType::Footway; } else if( !FCString::Stricmp( AttributeValue, TEXT( "cycleway" ) ) ) { WayType = EOSMWayType::Cycleway; } else if( !FCString::Stricmp( AttributeValue, TEXT( "bridleway" ) ) ) { WayType = EOSMWayType::Bridleway; } else if( !FCString::Stricmp( AttributeValue, TEXT( "steps" ) ) ) { WayType = EOSMWayType::Steps; } else if( !FCString::Stricmp( AttributeValue, TEXT( "path" ) ) ) { WayType = EOSMWayType::Path; } else if( !FCString::Stricmp( AttributeValue, TEXT( "proposed" ) ) ) { WayType = EOSMWayType::Proposed; } else if( !FCString::Stricmp( AttributeValue, TEXT( "construction" ) ) ) { WayType = EOSMWayType::Construction; } else { // Other type that we don't recognize yet. See http://wiki.openstreetmap.org/wiki/Key:highway } CurrentWayInfo->WayType = WayType; } else if( !FCString::Stricmp( CurrentWayTagKey, TEXT( "building" ) ) ) { CurrentWayInfo->WayType = EOSMWayType::Building; // Vinfamy added CurrentWayInfo->Building = AttributeValue; if( !FCString::Stricmp( AttributeValue, TEXT( "yes" ) ) ) { CurrentWayInfo->WayType = EOSMWayType::Building; } else { // Other type that we don't recognize yet. See http://wiki.openstreetmap.org/wiki/Key:building } } /** else if( !FCString::Stricmp( CurrentWayTagKey, TEXT( "natural" ) ) ) { if( !FCString::Stricmp( AttributeValue, TEXT( "water" ) ) ) { CurrentWayInfo->WayType = EOSMWayType::Water; } else { // Other natural type that we don't recognize yet. See http://wiki.openstreetmap.org/wiki/Key:building } } else if( !FCString::Stricmp( CurrentWayTagKey, TEXT( "waterway" ) ) ) { CurrentWayInfo->WayType = EOSMWayType::Water; } else if( !FCString::Stricmp( CurrentWayTagKey, TEXT( "water" ) ) ) { CurrentWayInfo->WayType = EOSMWayType::Water; } else if( !FCString::Stricmp( CurrentWayTagKey, TEXT( "landuse" ) ) ) { if( !FCString::Stricmp( AttributeValue, TEXT( "basin" ) ) ) { CurrentWayInfo->WayType = EOSMWayType::Water; } else if( !FCString::Stricmp( AttributeValue, TEXT( "reservoir" ) ) ) { CurrentWayInfo->WayType = EOSMWayType::Water; } else if( !FCString::Stricmp( AttributeValue, TEXT( "pond" ) ) ) { CurrentWayInfo->WayType = EOSMWayType::Water; } } */ else if( !FCString::Stricmp( CurrentWayTagKey, TEXT( "leisure" ) ) ) { if( !FCString::Stricmp( AttributeValue, TEXT( "park" ) ) ) { CurrentWayInfo->WayType = EOSMWayType::Park; } else if( !FCString::Stricmp( AttributeValue, TEXT( "fitness_centre" ) ) ) { CurrentWayInfo->WayType = EOSMWayType::Building; CurrentWayInfo->Amenity = AttributeValue; } else { // Other natural type that we don't recognize yet. See http://wiki.openstreetmap.org/wiki/Key:building } } else if( !FCString::Stricmp( CurrentWayTagKey, TEXT( "height" ) ) ) { // Check to see if there is a space character in the height value. For now, we're looking // for straight-up floating point values. if( !FString( AttributeValue ).Contains( TEXT( " " ) ) ) { // Okay, no space character. So this has got to be a floating point number. The OSM // spec says that the height values are in meters. CurrentWayInfo->Height = FPlatformString::Atod( AttributeValue ); } else { // Looks like the height value contains units of some sort. // @todo: Add support for interpreting unit strings and converting the values } } else if (!FCString::Stricmp(CurrentWayTagKey, TEXT("building:levels"))) { CurrentWayInfo->BuildingLevels = FPlatformString::Atoi(AttributeValue); } // Vinfamy added else if (!FCString::Stricmp(CurrentWayTagKey, TEXT("amenity"))) { CurrentWayInfo->Amenity = AttributeValue; } else if( !FCString::Stricmp( CurrentWayTagKey, TEXT( "oneway" ) ) ) { if( !FCString::Stricmp( AttributeValue, TEXT( "yes" ) ) ) { CurrentWayInfo->bIsOneWay = true; } else { CurrentWayInfo->bIsOneWay = false; } } } } return true; } bool FOSMFile::ProcessClose( const TCHAR* Element ) { if( ParsingState == ParsingState::Node ) { NodeMap.Add( CurrentNodeID, CurrentNodeInfo ); CurrentNodeID = 0; CurrentNodeInfo = nullptr; ParsingState = ParsingState::Root; } else if( ParsingState == ParsingState::Way ) { Ways.Add( CurrentWayInfo ); CurrentWayInfo = nullptr; ParsingState = ParsingState::Root; } else if( ParsingState == ParsingState::Way_NodeRef ) { ParsingState = ParsingState::Way; } else if( ParsingState == ParsingState::Way_Tag ) { CurrentWayTagKey = TEXT( "" ); ParsingState = ParsingState::Way; } return true; }