#include "worldfile.h" #include #include #include "lodtools.h" namespace mars { template< typename T > inline ObjectStream & operator << (ObjectStream & outs, const CylindricalRegion< T > & rr) { return outs << rr.p << rr.r << rr.z; } template< typename T > inline ObjectStream & operator >> (ObjectStream & ins, CylindricalRegion< T > & rr) { return ins >> rr.p >> rr.r >> rr.z; } } namespace geoworld { TileMapDetails::TileMapDetails( const unsigned short nTileDim, const unsigned int nTilesW, const unsigned int nTilesH, const unsigned short nLOD /*= 0*/ ) : _nTileDim(nTileDim), _nTilesH(nTilesH), _nTilesW(nTilesW), _nLOD(nLOD) { // Must be a power of 2 assert( ((static_cast< unsigned long > (_nTilesW) * _nTileDim - 1) & (static_cast< unsigned long > (_nTilesW) * _nTileDim)) == 0 && ((static_cast< unsigned long > (_nTilesH) * _nTileDim - 1) & (static_cast< unsigned long > (_nTilesH) * _nTileDim)) == 0 ); } FileSource::MineralHostMap::MineralHostMap( mars::ptr< std::fstream > & pfhFile, const unsigned short nLayers, const unsigned short nLOD, const unsigned short nTileDim, const unsigned int nTilesW, const unsigned int nTilesH ) : VariableFileChunk(pfhFile), TileMapDetails(nTileDim, nTilesW, nTilesH, nLOD), _ppLayers(new LayerMap * [nLayers]), count(nLayers), _pfhFile(pfhFile), top(pfhFile, nTileDim, nTilesW, nTilesH, nLOD) { for(unsigned short c = 0; c < nLayers; ++c) _ppLayers[c] = new LayerMap(pfhFile, nLOD, nTileDim, nTilesW, nTilesH); } FileSource::MineralHostMap::~MineralHostMap() { for(unsigned short c = 0; c < count; ++c) delete _ppLayers[c]; delete [] _ppLayers; } void FileSource::MineralHostMap::init( const std::streamoff & szFileOffset, MainDEM * pPrevChunk ) { std::streamoff szc = szFileOffset; top.init(szc); szc = top.end(); for (unsigned short c = 0; c < count; ++c) { _ppLayers[c]->init(szc); szc = _ppLayers[c]->end(); } _szLayers = szc - szFileOffset; FileChunk::init(szFileOffset); VariableFileChunk::init(pPrevChunk); } void FileSource::MineralHostMap::writeVariableData() { _pfhFile->seekp(getFileOffset() + _szLayers, std::ios::beg); mars::ObjectStream outs (_pfhFile.pointer()); std::vector< std::string > vsDefs; for (unsigned short i = 0; i < count; ++i) *_ppLayers[i] >> outs; } void FileSource::MineralHostMap::readVariableData() { _pfhFile->seekg(getFileOffset() + _szLayers, std::ios::beg); mars::ObjectStream ins (_pfhFile.pointer()); std::vector< std::string > vsDefs; for (unsigned short i = 0; i < count; ++i) *_ppLayers[i] << ins; } void FileSource::MineralHostMap::operator >> ( Stratum & stratum ) { assert(stratum.layers == count); top >> stratum.getBase(); for (size_t c = 0; c < count; ++c) { GeoStratumField::View & layer = stratum.field(c); *_ppLayers[c] >> layer; _ppLayers[c] ->putDefs(layer); } stratum.updateIndexMap(); } void FileSource::MineralHostMap::operator << ( const Stratum & stratum ) { assert(stratum.layers == count); top << stratum.getBase(); for (size_t c = 0; c < count; ++c) { const GeoStratumField::View & layer = stratum.field(c); *_ppLayers[c] << layer; _ppLayers[c] ->setDefs(layer); } } FileSource::MineralHostMap::LayerMap::LayerMap( mars::ptr< std::fstream > & pfhFile, const unsigned short nLOD, const unsigned short nTileDim, const unsigned int nTilesW, const unsigned int nTilesH, const std::vector< std::string > & vsDefs /*= std::vector< std::string > ()*/ ) : StreamTileMap(pfhFile, nTileDim, nTilesW, nTilesH, nLOD), _vsDefs(vsDefs) {} void FileSource::MineralHostMap::LayerMap::putDefs( GeoStratumField::View & field ) const { field.clearRegs(); for (DefList::const_iterator i = _vsDefs.begin(); i != _vsDefs.end(); ++i) field.reg(mars::WeakReference< MineralClass > (*i)); } void FileSource::MineralHostMap::LayerMap::setDefs( const GeoStratumField::View & field ) { _vsDefs.clear(); for (GeoStratumField::ElementList::const_iterator i = field.beginElements(); i != field.endElements(); ++i) _vsDefs.push_back(i->getName()); } void FileSource::MineralMap::add( const MineralPtr & pMin, const QuadTreeSpherePacks::MyCylindricalRegion & cr ) { _setMinerals.insert(pMin); _qtMinerals.add(mars::ptr< MineralPtr > (new MineralPtr (pMin)), cr); } void FileSource::MineralMap::writeVariableData() { assert(_pfhFile->tellp() == getFileOffset()); _pfhFile->write(reinterpret_cast< const char * > (&_header), sizeof(_header)); mars::ObjectStream outs (_pfhFile.pointer()); size_t c = 0; MineralIndex mapidxMins; outs << static_cast< size_t > (_setMinerals.size()); for (MineralSet::const_iterator i = _setMinerals.begin(); i != _setMinerals.end(); ++i) { mapidxMins[*i] = c++; outs << i->getName(); } const long nWidth = static_cast< signed long > (getVirtualWidth()), nHeight = static_cast< signed long > (getVirtualHeight()); std::vector< SphereIndexReference > idxSector; std::vector< std::streamoff > idxSectorLists; _header.listsoff = _pfhFile->tellp() - getFileOffset(); for (QuadTreeSpherePacks::MyRectRegion rect(QuadTreeSpherePacks::VECTOR(), QuadTreeSpherePacks::VECTOR() + (getPhysicalTileDim() - 1)); rect.v1.y < nHeight; rect.v1.y += getVirtualTileDim(), rect.v2.y += getVirtualTileDim() ) for (rect.v1.x = 0, rect.v2.x = getPhysicalTileDim() - 1; rect.v1.x < nWidth; rect.v1.x += getVirtualTileDim(), rect.v2.x += getVirtualTileDim() ) { for (QuadTreeSpherePacks::const_EntityRectIterator k = const_cast< const QuadTreeSpherePacks & > (_qtMinerals).contents(QuadTreeSpherePacks::MyRectRegion(rect.v1, rect.v2)); k; ++k) { assert(mapidxMins.find(*k) != mapidxMins.end()); idxSector.push_back(SphereIndexReference(k.region(), mapidxMins[*k])); } idxSectorLists.push_back(_pfhFile->tellp() - getFileOffset()); outs << idxSector; idxSector.clear(); } _header.mapoffs = _pfhFile->tellp() - getFileOffset(); // Adjust the mapped-offsets now that we actually know where the mineral map begins! for (std::vector< std::streamoff > ::iterator i = idxSectorLists.begin(); i != idxSectorLists.end(); ++i) outs << *i; LOG(mars::Log::Debug) << "Mineral map offset: " << _header.mapoffs; LOG(mars::Log::Debug) << "Map index dump: " << idxSectorLists; std::streampos pos0 = _pfhFile->tellp(); _pfhFile->seekp(getFileOffset()); _pfhFile->write(reinterpret_cast< const char * > (&_header), sizeof(_header)); _pfhFile->seekp(pos0); // Must always leave file pointer at the end of this chunk } FileSource::MineralMap::QuadTreeSpherePacks FileSource::MineralMap::createQuadTree( const unsigned short nTileDim, const unsigned int nTilesW, const unsigned int nTilesH ) { const unsigned int nMaxAxis = std::max(nTilesH, nTilesW); return QuadTreeSpherePacks(static_cast< long > (nMaxAxis) * nTileDim, mars::LOG2(nMaxAxis)); } FileSource::MineralMap::MineralMap( mars::ptr< std::fstream > & pfhFile, const std::ios::openmode nOpenMode, const unsigned short nTileDim, const unsigned int nTilesW, const unsigned int nTilesH ) : VariableFileChunk(pfhFile), TileMapDetails(nTileDim, nTilesW, nTilesH), _pfhFile(pfhFile), _qtMinerals(createQuadTree(nTileDim, nTilesW, nTilesH)) {} FileSource::MineralMap::TileResult FileSource::MineralMap::operator () ( const unsigned int i, const unsigned int j ) { SphRefVector results; mars::ObjectStream ins (_pfhFile.pointer()); std::streamoff offList; _pfhFile->seekg( _header.mapoffs + getFileOffset() + ( static_cast< std::streamoff > (j) * getTilesWide() + static_cast< std::streamoff > (i) ) * sizeof(std::streamoff) ); ins >> offList; _pfhFile->seekg(getFileOffset() + offList, std::ios::beg); ins >> results; return TileResult(MinDefCache(&_mins), results); } void FileSource::MineralMap::readVariableData() { assert(_pfhFile->tellg() == getFileOffset()); _pfhFile->read(reinterpret_cast< char * > (&_header), sizeof(_header)); mars::ObjectStream ins (_pfhFile.pointer()); size_t nCount; std::string s; ins >> nCount; _mins.clear(); for (size_t c = 0; c < nCount; ++c) { ins >> s; _mins.push_back(s); } } FileSource::FileSource( mars::ptr< std::fstream > & pfhFile, const std::ios::openmode nMode, const unsigned short nTileDim, const unsigned int nTilesW, const unsigned int nTilesH, const unsigned short nDepth, const unsigned short nGeoHostLOD, const unsigned short nGeoHostLayers) : TileMapDetails(nTileDim, nTilesW, nTilesH), tilemap(pfhFile, nTileDim, nTilesW, nTilesH), geohost(pfhFile, nGeoHostLayers, nGeoHostLOD, nTileDim, nTilesW, nTilesH), minerals(pfhFile, nMode, nTileDim, nTilesW, nTilesH), _pfhFile(pfhFile), _nOpenMode(nMode), _nHeaderEnd(sizeof(filesource::InfoHeader)), _nDepth(nDepth) { assert(getVirtualTileDim() <= MAX_TILE_DIM); assert(getTilesWide() <= MAX_AXIS_TILES && getTilesHigh() <= MAX_AXIS_TILES); assert(nDepth <= MAX_WORLD_DEPTH); _pfhFile->exceptions(std::ios::failbit | std::ios::badbit); tilemap.init(sizeof(filesource::InfoHeader)); geohost.init(tilemap.end(), &tilemap); minerals.init(&geohost); } FileSource::~FileSource() { if (_pfhFile != NULL) { _pfhFile->close(); } } FileSource * FileSource::open( const std::string & sFileName, const std::ios::openmode nMode ) { using namespace std; using namespace filesource; const std::ios::openmode nOpenMode = nMode | ios::binary; mars::ptr< fstream > pfhFile = new fstream(sFileName, nOpenMode); if (!*pfhFile) throw Ex("Failed to open file"); InfoHeader header; pfhFile->read(reinterpret_cast< char * > (&header), sizeof(header)); if (!validate(header, pfhFile)) throw Ex("Failed to validate file"); return new FileSource(pfhFile, nOpenMode, header.tdim, header.tx, header.ty, header.depth, header.geohost.lod, header.geohost.layers); } FileSource * FileSource::create( const std::string & sFileName, const unsigned long nMinWidth, const unsigned long nMinHeight, const unsigned short nDepth, const unsigned short nTileDim, const unsigned short nGeoHostLOD, const unsigned short nGeoHostLayerCount ) { using namespace std; using namespace filesource; const std::ios::openmode nOpenMode = ios::in | ios::out | ios::binary | ios::trunc; mars::ptr< fstream > pfhFile = new fstream(sFileName, nOpenMode); if (!*pfhFile) throw Ex("Failed to open file"); assert(nMinWidth >= nTileDim && nMinHeight >= nTileDim && nTileDim > 0); assert(((nTileDim - 1) & nTileDim) == 0); // Must be a power of 2 InfoHeader header; const unsigned int nTilesW = static_cast< unsigned int > (dimXp2(nMinWidth / nTileDim + 1)), nTilesH = static_cast< unsigned int > (dimXp2(nMinHeight / nTileDim + 1)); header.tx = nTilesW; header.ty = nTilesH; header.tdim = nTileDim; header.depth = nDepth; header.geohost.layers = nGeoHostLayerCount; header.geohost.lod = nGeoHostLOD; pfhFile->write(reinterpret_cast< const char * > (&header), sizeof(header)); return new FileSource(pfhFile, nOpenMode, nTileDim, nTilesW, nTilesH, nDepth, nGeoHostLOD, nGeoHostLayerCount); } bool FileSource::validate( const filesource::InfoHeader & header, mars::ptr< std::fstream > & pfhFile ) { using namespace filesource; const std::streamoff off0 = pfhFile->tellg(); pfhFile->seekg(0, std::ios::end); // TODO: Strong validation for other file layers const bool bFileSize = (pfhFile->tellg() >= (static_cast< std::streampos > (header.tx) * header.ty * mars::SQ(header.tdim + 1) * sizeof(GeoHeightMap::Precision) + sizeof(InfoHeader))); pfhFile->seekg(off0, std::ios::beg); return header.validate() && bFileSize; } void FileSource::commit() { geohost.commit(); minerals.commit(); } void FileSource::load() { geohost.load(); minerals.load(); } void FileSource::applyDEM( const GeoHeightMap * pHM, const float fCoarseness ) { assert ( ((pHM->width - 1) & pHM->width) == 0 && ((pHM->height - 1) & pHM->height) == 0 && pHM->gridtype == mars::Wrap ); // The low LOD HM must have dimensions as a power of 2 assert(pHM->width % getVirtualTileDim() == 0 && pHM->height % getVirtualTileDim() == 0); LODMagnifier< GeoHeightMap::Precision > magnifier ( *pHM, mars::LOG2(static_cast< unsigned long > (getVirtualWidth()) / pHM->width), getVirtualTileDim(), fCoarseness, getVirtualWidth(), getVirtualHeight() ); // TODO: Verify, compare with: /*LODMagnifier< GeoHeightMap::Precision > magnifier ( hmLowLOD, LOG2(static_cast< unsigned long > (nTilesW) * nTileDim / hmLowLOD.width), nTileDim, fCoarseness, getWorldWidth(), getWorldHeight() );*/ magnifier.write(tilemap); } const std::streampos & FileChunk::getFileOffset() const { assert(_szFileOffset > -1); return _szFileOffset; } void FileChunk::setFileOffset( const std::streamoff & szFileOffset ) { _szFileOffset = szFileOffset; } FileChunk::FileChunk() : _szFileOffset (-1) {} void FileChunk::init( const std::streamoff & szFileOffset ) { _szFileOffset = szFileOffset; } bool FileChunk::isSetFileOffset() const { return _szFileOffset != static_cast< std::streampos > (-1); } void StaticFileChunk::setSize( const std::streamoff & szSize ) { _szSize = szSize; } std::streampos StaticFileChunk::end() const { assert(getFileOffset() > -1 && _szSize > -1); return getFileOffset() + _szSize; } VariableFileChunk::VariableFileChunk( const mars::ptr< std::fstream > & pfhFile ) : _pfhFile(pfhFile), _pNextChunk(NULL), _pPrevChunk(NULL), _bDirty(false), _szChunkEnd(-1) {} void VariableFileChunk::init( FileChunk * pPreviousChunk ) { _pPrevChunk = pPreviousChunk; VariableFileChunk * pVariablePrevChunk = dynamic_cast< VariableFileChunk * > (pPreviousChunk); if (pVariablePrevChunk != NULL) pVariablePrevChunk->_pNextChunk = this; } std::streampos VariableFileChunk::end() const { assert(_szChunkEnd > -1); return _szChunkEnd; } void VariableFileChunk::dirty() { _bDirty = true; } void VariableFileChunk::load() { if (_pPrevChunk != NULL) _pfhFile->seekg(_pPrevChunk->end()); setFileOffset(_pfhFile->tellg()); readVariableData(); _szChunkEnd = _pfhFile->tellg(); _bDirty = false; } void VariableFileChunk::commit() { if (_pPrevChunk != NULL) _pfhFile->seekp(_pPrevChunk->end()); setFileOffset(_pfhFile->tellp()); writeVariableData(); _szChunkEnd = _pfhFile->tellp(); _bDirty = false; } void FileSource::MineralHostMap::Offset::operator >> ( Stratum & stratum ) { TopDEM::Block * pHeightBlock = _pMap->createTopDEMBlock(); LayerMap::Block * pLayerBlock = _pMap->createLayerBlock(); _pMap->top.seekg(x, y, std::ios::beg); _pMap->top >> *pHeightBlock; *pHeightBlock >> stratum.getBase(); for (size_t c = 0; c < _pMap->count; ++c) { GeoStratumField::View & stratlayer = stratum.field(c); LayerMap & filelayer = _pMap->layer(c); filelayer.seekg(x, y, std::ios::beg); filelayer >> *pLayerBlock; *pLayerBlock >> stratlayer; filelayer.putDefs(stratlayer); } stratum.updateIndexMap(); delete pLayerBlock; delete pHeightBlock; } void FileSource::MineralHostMap::Offset::operator << ( const Stratum & stratum ) { TopDEM::Block * pHeightBlock = _pMap->createTopDEMBlock(); LayerMap::Block * pLayerBlock = _pMap->createLayerBlock(); _pMap->top.seekp(x, y, std::ios::beg); *pHeightBlock << stratum.getBase(); _pMap->top << *pHeightBlock; for (size_t c = 0; c < _pMap->count; ++c) { const GeoStratumField::View & stratlayer = stratum.field(c); LayerMap & filelayer = _pMap->layer(c); filelayer.seekp(x, y, std::ios::beg); *pLayerBlock << stratlayer; filelayer << *pLayerBlock; filelayer.setDefs(stratlayer); } delete pLayerBlock; delete pHeightBlock; } FileSource::MineralHostMap::Offset::Offset( MineralHostMap * const pMap, const signed int x, const signed int y ) : _pMap(pMap), x(x), y(y) {} }