QGIS/external/mdal/frmts/mdal_esri_tin.cpp

333 lines
10 KiB
C++
Raw Normal View History

2019-10-14 09:19:14 +02:00
/*
MDAL - Mesh Data Abstraction Library (MIT License)
Copyright (C) 2019 Vincent Cloarec (vcloarec at gmail dot com)
*/
#include "mdal_esri_tin.hpp"
#include "mdal_logger.hpp"
2019-10-14 09:19:14 +02:00
MDAL::DriverEsriTin::DriverEsriTin(): Driver( "ESRI_TIN",
"Esri TIN",
"*.adf",
Capability::ReadMesh )
{}
MDAL::Driver *MDAL::DriverEsriTin::create()
{
return new DriverEsriTin();
}
std::unique_ptr<MDAL::Mesh> MDAL::DriverEsriTin::load( const std::string &uri, const std::string & )
2019-10-14 09:19:14 +02:00
{
MDAL::Log::resetLastStatus();
2019-10-14 09:19:14 +02:00
try
{
std::list<int> superpointIndexes;
Faces faces;
bool isNativeLittleEndian = MDAL::isNativeLittleEndian();
2019-10-14 09:19:14 +02:00
//read the total number of vertices (including superpoints and isolated vertices)
int32_t totalIndexesCount32;
std::ifstream inDenv( denvFile( uri ), std::ifstream::in | std::ifstream::binary );
if ( !inDenv.is_open() )
{
inDenv.open( denv9File( uri ), std::ifstream::in | std::ifstream::binary );
if ( !inDenv.is_open() )
throw MDAL::Error( MDAL_Status::Err_UnknownFormat, "Could not open file " + uri + " as denv file" );
}
readValue( totalIndexesCount32, inDenv, isNativeLittleEndian );
size_t totalIndexesCount = static_cast<size_t>( totalIndexesCount32 );
/* Round 1 :populates faces with raw indexes from the file
* rawAndCorrectedIndexesMap is used to map raw indexes from the files and corrected indexes
* Corrected indexes take into account the unwanted vertexes (superpoints, isolated vertices)
* Wanted vertices are associated with the corrected index
* Unwanted vertices are associated with the totalIndexesCount value
*/
std::vector<size_t> rawAndCorrectedIndexesMap( totalIndexesCount, totalIndexesCount );
std::ifstream inFaces( faceFile( uri ), std::ifstream::in | std::ifstream::binary );
std::ifstream inMsk( mskFile( uri ), std::ifstream::in | std::ifstream::binary );
std::ifstream inMsx( msxFile( uri ), std::ifstream::in | std::ifstream::binary );
if ( ! inFaces.is_open() )
throw MDAL::Error( MDAL_Status::Err_FileNotFound, "Could not open file " + uri + " as faces file" );
if ( ! inMsk.is_open() )
throw MDAL::Error( MDAL_Status::Err_FileNotFound, "Could not open file " + uri + " as msk file" );
if ( ! inMsx.is_open() )
throw MDAL::Error( MDAL_Status::Err_FileNotFound, "Could not open file " + uri + " as msx file" );
//Find the beginning of data in mskFile
inMsx.seekg( -4, std::ios::end );
int32_t mskBegin;
if ( ! readValue( mskBegin, inMsx, true ) )
2020-04-29 08:59:46 +02:00
throw MDAL::Error( MDAL_Status::Err_UnknownFormat, "Unable to find the beginning of data in msk file" );
//read information in mskFile
inMsk.seekg( -mskBegin * 2, std::ios::end );
int32_t maskIntergerCount;
if ( ! readValue( maskIntergerCount, inMsk, true ) )
throw MDAL::Error( MDAL_Status::Err_UnknownFormat, "Unable to read information in msk file" );
inMsk.ignore( 4 ); //unused 4 bytes
int32_t maskBitsCount;
if ( ! readValue( maskBitsCount, inMsk, true ) )
throw MDAL::Error( MDAL_Status::Err_UnknownFormat, "Unable to read information in msk file" );
int c = 0;
int32_t maskInt = 0;
while ( true )
{
//read mask file
if ( c % 32 == 0 && c < maskBitsCount ) //first bit in the mask array have to be used-->read next maskInt
if ( ! readValue( maskInt, inMsk, true ) )
2020-04-29 08:59:46 +02:00
throw MDAL::Error( MDAL_Status::Err_UnknownFormat, "Unable to read information in msk file" );
Face f;
for ( int i = 0; i < 3; ++i )
{
int32_t index;
if ( ! readValue( index, inFaces, isNativeLittleEndian ) )
break;
f.push_back( static_cast<size_t>( index - 1 ) );
}
if ( f.size() == 0 ) //that's mean this is the end of the file
break;
if ( f.size() < 3 ) //that's mean the face is not complete
throw MDAL::Error( MDAL_Status::Err_UnknownFormat, "Unable to read information in mask file, face is not complete" );
//exclude masked face
if ( !( maskInt & 0x01 ) )
{
faces.push_back( f );
//fill raw indexes
for ( auto ri : f )
{
if ( ri >= totalIndexesCount )
throw MDAL::Error( MDAL_Status::Err_UnknownFormat, "More raw indexes than total number of indexes" );
rawAndCorrectedIndexesMap[ri] = 1;
}
}
c++;
maskInt = maskInt >> 1;
}
inFaces.close();
inMsk.close();
inMsx.close();
//Round 2 :count the number of wanted indexes and fill the rawIndexes value with the correctedIndex
size_t correctedIndexCount = 0;
for ( size_t i = 0; i < rawAndCorrectedIndexesMap.size(); ++i )
{
if ( rawAndCorrectedIndexesMap.at( i ) < totalIndexesCount )
{
rawAndCorrectedIndexesMap[i] = correctedIndexCount;
correctedIndexCount++;
}
}
//Round 3: populate vertices
Vertices vertices( correctedIndexCount );
std::ifstream inXY( xyFile( uri ), std::ifstream::in | std::ifstream::binary );
std::ifstream inZ( zFile( uri ), std::ifstream::in | std::ifstream::binary );
if ( ! inXY.is_open() )
throw MDAL::Error( MDAL_Status::Err_FileNotFound, "Could not open file " + uri + " as xyFile type" );
if ( ! inZ.is_open() )
throw MDAL::Error( MDAL_Status::Err_FileNotFound, "Could not open file " + uri + " as zFile type" );
size_t rawIndex = 0;
while ( rawIndex < rawAndCorrectedIndexesMap.size() )
{
Vertex vert;
2019-10-14 09:19:14 +02:00
if ( !readValue( vert.x, inXY, isNativeLittleEndian ) )
break;
if ( !readValue( vert.y, inXY, isNativeLittleEndian ) )
throw MDAL::Error( MDAL_Status::Err_UnknownFormat, "Could not read value from xy file" );
float zValue;
if ( !readValue( zValue, inZ, isNativeLittleEndian ) )
throw MDAL::Error( MDAL_Status::Err_UnknownFormat, "Could not read value from z file" );
vert.z = double( zValue );
// store the vertex only if it is a wanted index
if ( rawAndCorrectedIndexesMap[rawIndex] < totalIndexesCount )
vertices[rawAndCorrectedIndexesMap[rawIndex]] = vert ;
rawIndex++;
}
inXY.close();
inZ.close();
//Round 4 :apply correction to the face's indexes
for ( auto &face : faces )
for ( auto &fi : face )
fi = rawAndCorrectedIndexesMap[fi];
//create the memory mesh
2019-10-14 09:19:14 +02:00
std::unique_ptr< MemoryMesh > mesh(
new MemoryMesh(
name(),
vertices.size(),
0,
2019-10-14 09:19:14 +02:00
faces.size(),
3,
computeExtent( vertices ),
uri
)
);
//move the faces and the vertices in the mesh
2019-10-14 09:19:14 +02:00
mesh->faces = std::move( faces );
mesh->vertices = std::move( vertices );
//create the "Altitude" dataset
2019-10-14 09:19:14 +02:00
addBedElevationDatasetGroup( mesh.get(), mesh->vertices );
mesh->datasetGroups.back()->setName( "Altitude" );
std::string crs = getCrsWkt( uri );
if ( ! crs.empty() )
mesh->setSourceCrsFromWKT( crs );
return std::unique_ptr<Mesh>( mesh.release() );
}
catch ( MDAL_Status error )
{
MDAL::Log::error( error, name(), "error while loading file " + uri );
return std::unique_ptr<Mesh>();
}
catch ( MDAL::Error err )
{
MDAL::Log::error( err, name() );
2019-10-14 09:19:14 +02:00
return std::unique_ptr<Mesh>();
}
}
bool MDAL::DriverEsriTin::canReadMesh( const std::string &uri )
2019-10-14 09:19:14 +02:00
{
std::string zFileName = zFile( uri );
std::string faceFileName = faceFile( uri );
std::ifstream xyIn( xyFile( uri ), std::ifstream::in | std::ifstream::binary );
if ( ! xyIn.is_open() )
return false;
std::ifstream zIn( zFile( uri ), std::ifstream::in | std::ifstream::binary );
if ( ! zIn.is_open() )
return false;
std::ifstream faceIn( faceFile( uri ), std::ifstream::in | std::ifstream::binary );
if ( ! faceIn.is_open() )
return false;
std::ifstream hullIn( hullFile( uri ), std::ifstream::in | std::ifstream::binary );
if ( ! hullIn.is_open() )
return false;
return true;
}
std::string MDAL::DriverEsriTin::xyFile( const std::string &uri ) const
{
return pathJoin( dirName( uri ), "tnxy.adf" );
}
std::string MDAL::DriverEsriTin::zFile( const std::string &uri ) const
{
return pathJoin( dirName( uri ), "tnz.adf" );
}
std::string MDAL::DriverEsriTin::faceFile( const std::string &uri ) const
{
return pathJoin( dirName( uri ), "tnod.adf" );
}
std::string MDAL::DriverEsriTin::mskFile( const std::string &uri ) const
{
return pathJoin( dirName( uri ), "tmsk.adf" );
}
std::string MDAL::DriverEsriTin::msxFile( const std::string &uri ) const
{
return pathJoin( dirName( uri ), "tmsx.adf" );
}
std::string MDAL::DriverEsriTin::hullFile( const std::string &uri ) const
{
return pathJoin( dirName( uri ), "thul.adf" );
}
std::string MDAL::DriverEsriTin::denvFile( const std::string &uri ) const
{
return pathJoin( dirName( uri ), "tdenv.adf" );
}
std::string MDAL::DriverEsriTin::denv9File( const std::string &uri ) const
{
return pathJoin( dirName( uri ), "tdenv9.adf" );
}
2019-10-14 09:19:14 +02:00
std::string MDAL::DriverEsriTin::crsFile( const std::string &uri ) const
{
return pathJoin( dirName( uri ), "prj.adf" );
}
/*
2019-10-14 09:19:14 +02:00
void MDAL::DriverEsriTin::readSuperpoints( const std::string &uri, std::list<int> &superpointsIndexes ) const
{
superpointsIndexes.clear();
bool isNativeLittleEndian = MDAL::isNativeLittleEndian();
std::ifstream inHull( hullFile( uri ), std::ifstream::in | std::ifstream::binary );
int32_t index;
while ( readValue( index, inHull, isNativeLittleEndian ) && index != -1 )
superpointsIndexes.push_back( index );
superpointsIndexes.sort();
}
std::string MDAL::DriverEsriTin::getTinName( const std::string &uri ) const
{
std::string tinName = uri;
size_t last_slash_idx = tinName.find_last_of( "\\/" );
if ( last_slash_idx == std::string::npos )
return "";
tinName.erase( last_slash_idx, tinName.size() - last_slash_idx );
last_slash_idx = tinName.find_last_of( "\\/" );
if ( last_slash_idx == std::string::npos )
return "";
tinName.erase( 0, last_slash_idx + 1 );
return tinName;
}
*/
2019-10-14 09:19:14 +02:00
std::string MDAL::DriverEsriTin::getCrsWkt( const std::string &uri ) const
{
std::ifstream inCRS( crsFile( uri ), std::ifstream::in );
if ( ! inCRS.is_open() )
return std::string();
std::string crsWkt;
std::getline( inCRS, crsWkt );
if ( crsWkt == "{B286C06B-0879-11D2-AACA-00C04FA33C20}" ) //com class id of the esri UnknownCoordinateSystem class
crsWkt = "";
return crsWkt;
}