GDALDataset *GSBGDataset::CreateCopy( const char *pszFilename, GDALDataset *poSrcDS, int bStrict, char **papszOptions, GDALProgressFunc pfnProgress, void *pProgressData ) { if( pfnProgress == NULL ) pfnProgress = GDALDummyProgress; int nBands = poSrcDS->GetRasterCount(); if (nBands == 0) { CPLError( CE_Failure, CPLE_NotSupported, "GSBG driver does not support source dataset with zero band.\n"); return NULL; } else if (nBands > 1) { if( bStrict ) { CPLError( CE_Failure, CPLE_NotSupported, "Unable to create copy, Golden Software Binary Grid " "format only supports one raster band.\n" ); return NULL; } else CPLError( CE_Warning, CPLE_NotSupported, "Golden Software Binary Grid format only supports one " "raster band, first band will be copied.\n" ); } GDALRasterBand *poSrcBand = poSrcDS->GetRasterBand( 1 ); if( poSrcBand->GetXSize() > SHRT_MAX || poSrcBand->GetYSize() > SHRT_MAX ) { CPLError( CE_Failure, CPLE_IllegalArg, "Unable to create grid, Golden Software Binary Grid format " "only supports sizes up to %dx%d. %dx%d not supported.\n", SHRT_MAX, SHRT_MAX, poSrcBand->GetXSize(), poSrcBand->GetYSize() ); return NULL; } if( !pfnProgress( 0.0, NULL, pProgressData ) ) { CPLError( CE_Failure, CPLE_UserInterrupt, "User terminated\n" ); return NULL; } VSILFILE *fp = VSIFOpenL( pszFilename, "w+b" ); if( fp == NULL ) { CPLError( CE_Failure, CPLE_OpenFailed, "Attempt to create file '%s' failed.\n", pszFilename ); return NULL; } GInt16 nXSize = poSrcBand->GetXSize(); GInt16 nYSize = poSrcBand->GetYSize(); double adfGeoTransform[6]; poSrcDS->GetGeoTransform( adfGeoTransform ); double dfMinX = adfGeoTransform[0] + adfGeoTransform[1] / 2; double dfMaxX = adfGeoTransform[1] * (nXSize - 0.5) + adfGeoTransform[0]; double dfMinY = adfGeoTransform[5] * (nYSize - 0.5) + adfGeoTransform[3]; double dfMaxY = adfGeoTransform[3] + adfGeoTransform[5] / 2; CPLErr eErr = WriteHeader( fp, nXSize, nYSize, dfMinX, dfMaxX, dfMinY, dfMaxY, 0.0, 0.0 ); if( eErr != CE_None ) { VSIFCloseL( fp ); return NULL; } /* -------------------------------------------------------------------- */ /* Copy band data. */ /* -------------------------------------------------------------------- */ float *pfData = (float *)VSIMalloc2( nXSize, sizeof( float ) ); if( pfData == NULL ) { VSIFCloseL( fp ); CPLError( CE_Failure, CPLE_OutOfMemory, "Unable to create copy, unable to allocate line buffer.\n" ); return NULL; } int bSrcHasNDValue; float fSrcNoDataValue = poSrcBand->GetNoDataValue( &bSrcHasNDValue ); double dfMinZ = DBL_MAX; double dfMaxZ = -DBL_MAX; for( GInt16 iRow = nYSize - 1; iRow >= 0; iRow-- ) { eErr = poSrcBand->RasterIO( GF_Read, 0, iRow, nXSize, 1, pfData, nXSize, 1, GDT_Float32, 0, 0 ); if( eErr != CE_None ) { VSIFCloseL( fp ); VSIFree( pfData ); return NULL; } for( int iCol=0; iCol<nXSize; iCol++ ) { if( bSrcHasNDValue && pfData[iCol] == fSrcNoDataValue ) { pfData[iCol] = fNODATA_VALUE; } else { if( pfData[iCol] > dfMaxZ ) dfMaxZ = pfData[iCol]; if( pfData[iCol] < dfMinZ ) dfMinZ = pfData[iCol]; } CPL_LSBPTR32( pfData+iCol ); } if( VSIFWriteL( (void *)pfData, 4, nXSize, fp ) != static_cast<unsigned>(nXSize) ) { VSIFCloseL( fp ); VSIFree( pfData ); CPLError( CE_Failure, CPLE_FileIO, "Unable to write grid row. Disk full?\n" ); return NULL; } if( !pfnProgress( static_cast<double>(iRow)/nYSize, NULL, pProgressData ) ) { VSIFCloseL( fp ); VSIFree( pfData ); CPLError( CE_Failure, CPLE_UserInterrupt, "User terminated" ); return NULL; } } VSIFree( pfData ); /* write out the min and max values */ eErr = WriteHeader( fp, nXSize, nYSize, dfMinX, dfMaxX, dfMinY, dfMaxY, dfMinZ, dfMaxZ ); if( eErr != CE_None ) { VSIFCloseL( fp ); return NULL; } VSIFCloseL( fp ); GDALPamDataset *poDstDS = (GDALPamDataset *)GDALOpen( pszFilename, GA_Update ); if( poDstDS == NULL ) { VSIUnlink( pszFilename ); CPLError( CE_Failure, CPLE_FileIO, "Unable to open copy of dataset.\n" ); return NULL; } else if( dynamic_cast<GSBGDataset *>(poDstDS) == NULL ) { VSIUnlink( pszFilename ); delete poDstDS; CPLError( CE_Failure, CPLE_FileIO, "Copy dataset not opened as Golden Surfer Binary Grid!?\n" ); return NULL; } GDALRasterBand *poDstBand = poSrcDS->GetRasterBand(1); if( poDstBand == NULL ) { VSIUnlink( pszFilename ); delete poDstDS; CPLError( CE_Failure, CPLE_FileIO, "Unable to open copy of raster band?\n" ); return NULL; } /* -------------------------------------------------------------------- */ /* Attempt to copy metadata. */ /* -------------------------------------------------------------------- */ if( !bStrict ) CPLPushErrorHandler( CPLQuietErrorHandler ); /* non-zero transform 2 or 4 or negative 1 or 5 not supported natively */ /*if( adfGeoTransform[2] != 0.0 || adfGeoTransform[4] != 0.0 || adfGeoTransform[1] < 0.0 || adfGeoTransform[5] < 0.0 ) poDstDS->GDALPamDataset::SetGeoTransform( adfGeoTransform );*/ const char *szProjectionRef = poSrcDS->GetProjectionRef(); if( *szProjectionRef != '\0' ) poDstDS->SetProjection( szProjectionRef ); char **pszMetadata = poSrcDS->GetMetadata(); if( pszMetadata != NULL ) poDstDS->SetMetadata( pszMetadata ); /* FIXME: Should the dataset description be copied as well, or is it * always the file name? */ poDstBand->SetDescription( poSrcBand->GetDescription() ); int bSuccess; double dfOffset = poSrcBand->GetOffset( &bSuccess ); if( bSuccess && dfOffset != 0.0 ) poDstBand->SetOffset( dfOffset ); double dfScale = poSrcBand->GetScale( &bSuccess ); if( bSuccess && dfScale != 1.0 ) poDstBand->SetScale( dfScale ); GDALColorInterp oColorInterp = poSrcBand->GetColorInterpretation(); if( oColorInterp != GCI_Undefined ) poDstBand->SetColorInterpretation( oColorInterp ); char **pszCatNames = poSrcBand->GetCategoryNames(); if( pszCatNames != NULL) poDstBand->SetCategoryNames( pszCatNames ); GDALColorTable *poColorTable = poSrcBand->GetColorTable(); if( poColorTable != NULL ) poDstBand->SetColorTable( poColorTable ); if( !bStrict ) CPLPopErrorHandler(); return poDstDS; }
/* Builds a ECRGTOCSubDataset from the set of files of the toc entry */ GDALDataset* ECRGTOCSubDataset::Build( const char* pszProductTitle, const char* pszDiscId, int nScale, int nCountSubDataset, const char* pszTOCFilename, const std::vector<FrameDesc>& aosFrameDesc, double dfGlobalMinX, double dfGlobalMinY, double dfGlobalMaxX, double dfGlobalMaxY, double dfGlobalPixelXSize, double dfGlobalPixelYSize) { int i, j; GDALDriver *poDriver; ECRGTOCSubDataset *poVirtualDS; int nSizeX, nSizeY; double adfGeoTransform[6]; poDriver = GetGDALDriverManager()->GetDriverByName("VRT"); if( poDriver == NULL ) return NULL; nSizeX = (int)((dfGlobalMaxX - dfGlobalMinX) / dfGlobalPixelXSize + 0.5); nSizeY = (int)((dfGlobalMaxY - dfGlobalMinY) / dfGlobalPixelYSize + 0.5); /* ------------------------------------ */ /* Create the VRT with the overall size */ /* ------------------------------------ */ poVirtualDS = new ECRGTOCSubDataset( nSizeX, nSizeY ); poVirtualDS->SetProjection(SRS_WKT_WGS84); adfGeoTransform[0] = dfGlobalMinX; adfGeoTransform[1] = dfGlobalPixelXSize; adfGeoTransform[2] = 0; adfGeoTransform[3] = dfGlobalMaxY; adfGeoTransform[4] = 0; adfGeoTransform[5] = -dfGlobalPixelYSize; poVirtualDS->SetGeoTransform(adfGeoTransform); for (i=0;i<3;i++) { poVirtualDS->AddBand(GDT_Byte, NULL); GDALRasterBand *poBand = poVirtualDS->GetRasterBand( i + 1 ); poBand->SetColorInterpretation((GDALColorInterp)(GCI_RedBand+i)); } poVirtualDS->SetDescription(pszTOCFilename); poVirtualDS->SetMetadataItem("PRODUCT_TITLE", pszProductTitle); poVirtualDS->SetMetadataItem("DISC_ID", pszDiscId); if (nScale != -1) poVirtualDS->SetMetadataItem("SCALE", CPLString().Printf("%d", nScale)); /* -------------------------------------------------------------------- */ /* Check for overviews. */ /* -------------------------------------------------------------------- */ poVirtualDS->oOvManager.Initialize( poVirtualDS, CPLString().Printf("%s.%d", pszTOCFilename, nCountSubDataset)); poVirtualDS->papszFileList = poVirtualDS->GDALDataset::GetFileList(); for(i=0;i<(int)aosFrameDesc.size(); i++) { const char* pszName = BuildFullName(pszTOCFilename, aosFrameDesc[i].pszPath, aosFrameDesc[i].pszName); double dfMinX = 0, dfMaxX = 0, dfMinY = 0, dfMaxY = 0, dfPixelXSize = 0, dfPixelYSize = 0; GetExtent(aosFrameDesc[i].pszName, aosFrameDesc[i].nScale, aosFrameDesc[i].nZone, dfMinX, dfMaxX, dfMinY, dfMaxY, dfPixelXSize, dfPixelYSize); int nFrameXSize = (int)((dfMaxX - dfMinX) / dfPixelXSize + 0.5); int nFrameYSize = (int)((dfMaxY - dfMinY) / dfPixelYSize + 0.5); poVirtualDS->papszFileList = CSLAddString(poVirtualDS->papszFileList, pszName); /* We create proxy datasets and raster bands */ /* Using real datasets and raster bands is possible in theory */ /* However for large datasets, a TOC entry can include several hundreds of files */ /* and we finally reach the limit of maximum file descriptors open at the same time ! */ /* So the idea is to warp the datasets into a proxy and open the underlying dataset only when it is */ /* needed (IRasterIO operation). To improve a bit efficiency, we have a cache of opened */ /* underlying datasets */ ECRGTOCProxyRasterDataSet* poDS = new ECRGTOCProxyRasterDataSet( (ECRGTOCSubDataset*)poVirtualDS, pszName, nFrameXSize, nFrameYSize, dfMinX, dfMaxY, dfPixelXSize, dfPixelYSize); for(j=0;j<3;j++) { VRTSourcedRasterBand *poBand = (VRTSourcedRasterBand*) poVirtualDS->GetRasterBand( j + 1 ); /* Place the raster band at the right position in the VRT */ poBand->AddSimpleSource(poDS->GetRasterBand(j + 1), 0, 0, nFrameXSize, nFrameYSize, (int)((dfMinX - dfGlobalMinX) / dfGlobalPixelXSize + 0.5), (int)((dfGlobalMaxY - dfMaxY) / dfGlobalPixelYSize + 0.5), (int)((dfMaxX - dfMinX) / dfGlobalPixelXSize + 0.5), (int)((dfMaxY - dfMinY) / dfGlobalPixelYSize + 0.5)); } /* The ECRGTOCProxyRasterDataSet will be destroyed when its last raster band will be */ /* destroyed */ poDS->Dereference(); } poVirtualDS->SetMetadataItem("INTERLEAVE", "PIXEL", "IMAGE_STRUCTURE"); return poVirtualDS; }
void ccRasterizeTool::generateRaster() const { #ifdef CC_GDAL_SUPPORT if (!m_cloud || !m_grid.isValid()) return; GDALAllRegister(); ccLog::PrintDebug("(GDAL drivers: %i)", GetGDALDriverManager()->GetDriverCount()); const char *pszFormat = "GTiff"; GDALDriver *poDriver = GetGDALDriverManager()->GetDriverByName(pszFormat); if (!poDriver) { ccLog::Error("[GDAL] Driver %s is not supported", pszFormat); return; } char** papszMetadata = poDriver->GetMetadata(); if( !CSLFetchBoolean( papszMetadata, GDAL_DCAP_CREATE, FALSE ) ) { ccLog::Error("[GDAL] Driver %s doesn't support Create() method", pszFormat); return; } //which (and how many) bands shall we create? bool heightBand = true; //height by default bool densityBand = false; bool allSFBands = false; int sfBandIndex = -1; //scalar field index int totalBands = 0; bool interpolateSF = (getTypeOfSFInterpolation() != INVALID_PROJECTION_TYPE); ccPointCloud* pc = m_cloud->isA(CC_TYPES::POINT_CLOUD) ? static_cast<ccPointCloud*>(m_cloud) : 0; bool hasSF = interpolateSF && pc && !m_grid.scalarFields.empty(); RasterExportOptionsDlg reoDlg; reoDlg.dimensionsLabel->setText(QString("%1 x %2").arg(m_grid.width).arg(m_grid.height)); reoDlg.exportHeightsCheckBox->setChecked(heightBand); reoDlg.exportDensityCheckBox->setChecked(densityBand); reoDlg.exportDisplayedSFCheckBox->setEnabled(hasSF); reoDlg.exportAllSFCheckBox->setEnabled(hasSF); reoDlg.exportAllSFCheckBox->setChecked(allSFBands); if (!reoDlg.exec()) return; //we ask the output filename AFTER displaying the export parameters ;) QString outputFilename; { QSettings settings; settings.beginGroup(ccPS::HeightGridGeneration()); QString imageSavePath = settings.value("savePathImage",QApplication::applicationDirPath()).toString(); outputFilename = QFileDialog::getSaveFileName(0,"Save height grid raster",imageSavePath+QString("/raster.tif"),"geotiff (*.tif)"); if (outputFilename.isNull()) return; //save current export path to persistent settings settings.setValue("savePathImage",QFileInfo(outputFilename).absolutePath()); } heightBand = reoDlg.exportHeightsCheckBox->isChecked(); densityBand = reoDlg.exportDensityCheckBox->isChecked(); if (hasSF) { assert(pc); allSFBands = reoDlg.exportAllSFCheckBox->isChecked() && hasSF; if (!allSFBands && reoDlg.exportDisplayedSFCheckBox->isChecked()) { sfBandIndex = pc->getCurrentDisplayedScalarFieldIndex(); if (sfBandIndex < 0) ccLog::Warning("[Rasterize] Cloud has no active (displayed) SF!"); } } totalBands = heightBand ? 1 : 0; if (densityBand) { ++totalBands; } if (allSFBands) { assert(hasSF); for (size_t i=0; i<m_grid.scalarFields.size(); ++i) if (m_grid.scalarFields[i]) ++totalBands; } else if (sfBandIndex >= 0) { ++totalBands; } if (totalBands == 0) { ccLog::Warning("[Rasterize] Warning, can't output a raster with no band! (check export parameters)"); return; } //data type GDALDataType dataType = (std::max(sizeof(PointCoordinateType),sizeof(ScalarType)) > 4 ? GDT_Float64 : GDT_Float32); char **papszOptions = NULL; GDALDataset* poDstDS = poDriver->Create(qPrintable(outputFilename), static_cast<int>(m_grid.width), static_cast<int>(m_grid.height), totalBands, dataType, papszOptions); if (!poDstDS) { ccLog::Error("[GDAL] Failed to create output raster (not enough memory?)"); return; } ccBBox box = getCustomBBox(); assert(box.isValid()); //vertical dimension const unsigned char Z = getProjectionDimension(); assert(Z >= 0 && Z <= 2); const unsigned char X = Z == 2 ? 0 : Z +1; const unsigned char Y = X == 2 ? 0 : X +1; double shiftX = box.minCorner().u[X]; double shiftY = box.minCorner().u[Y]; double stepX = m_grid.gridStep; double stepY = m_grid.gridStep; if (pc) { const CCVector3d& shift = pc->getGlobalShift(); shiftX -= shift.u[X]; shiftY -= shift.u[Y]; double scale = pc->getGlobalScale(); assert(scale != 0); stepX /= scale; stepY /= scale; } double adfGeoTransform[6] = { shiftX, //top left x stepX, //w-e pixel resolution (can be negative) 0, //0 shiftY, //top left y 0, //0 stepY //n-s pixel resolution (can be negative) }; poDstDS->SetGeoTransform( adfGeoTransform ); //OGRSpatialReference oSRS; //oSRS.SetUTM( 11, TRUE ); //oSRS.SetWellKnownGeogCS( "NAD27" ); //char *pszSRS_WKT = NULL; //oSRS.exportToWkt( &pszSRS_WKT ); //poDstDS->SetProjection( pszSRS_WKT ); //CPLFree( pszSRS_WKT ); double* scanline = (double*) CPLMalloc(sizeof(double)*m_grid.width); int currentBand = 0; //exort height band? if (heightBand) { GDALRasterBand* poBand = poDstDS->GetRasterBand(++currentBand); assert(poBand); poBand->SetColorInterpretation(GCI_Undefined); EmptyCellFillOption fillEmptyCellsStrategy = getFillEmptyCellsStrategy(fillEmptyCellsComboBox); double emptyCellHeight = 0; switch (fillEmptyCellsStrategy) { case LEAVE_EMPTY: emptyCellHeight = m_grid.minHeight-1.0; poBand->SetNoDataValue(emptyCellHeight); //should be transparent! break; case FILL_MINIMUM_HEIGHT: emptyCellHeight = m_grid.minHeight; break; case FILL_MAXIMUM_HEIGHT: emptyCellHeight = m_grid.maxHeight; break; case FILL_CUSTOM_HEIGHT: emptyCellHeight = getCustomHeightForEmptyCells(); break; case FILL_AVERAGE_HEIGHT: emptyCellHeight = m_grid.meanHeight; break; default: assert(false); } for (unsigned j=0; j<m_grid.height; ++j) { const RasterCell* aCell = m_grid.data[j]; for (unsigned i=0; i<m_grid.width; ++i,++aCell) { scanline[i] = aCell->h == aCell->h ? aCell->h : emptyCellHeight; } if (poBand->RasterIO( GF_Write, 0, static_cast<int>(j), static_cast<int>(m_grid.width), 1, scanline, static_cast<int>(m_grid.width), 1, GDT_Float64, 0, 0 ) != CE_None) { ccLog::Error("[GDAL] An error occurred while writing the height band!"); if (scanline) CPLFree(scanline); GDALClose( (GDALDatasetH) poDstDS ); return; } } } //export density band if (densityBand) { GDALRasterBand* poBand = poDstDS->GetRasterBand(++currentBand); assert(poBand); poBand->SetColorInterpretation(GCI_Undefined); for (unsigned j=0; j<m_grid.height; ++j) { const RasterCell* aCell = m_grid.data[j]; for (unsigned i=0; i<m_grid.width; ++i,++aCell) { scanline[i] = aCell->nbPoints; } if (poBand->RasterIO( GF_Write, 0, static_cast<int>(j), static_cast<int>(m_grid.width), 1, scanline, static_cast<int>(m_grid.width), 1, GDT_Float64, 0, 0 ) != CE_None) { ccLog::Error("[GDAL] An error occurred while writing the height band!"); if (scanline) CPLFree(scanline); GDALClose( (GDALDatasetH) poDstDS ); return; } } } //export SF bands if (allSFBands || sfBandIndex >= 0) { for (size_t k=0; k<m_grid.scalarFields.size(); ++k) { double* _sfGrid = m_grid.scalarFields[k]; if (_sfGrid && (allSFBands || sfBandIndex == static_cast<int>(k))) //valid SF grid { GDALRasterBand* poBand = poDstDS->GetRasterBand(++currentBand); double sfNanValue = static_cast<double>(CCLib::ScalarField::NaN()); poBand->SetNoDataValue(sfNanValue); //should be transparent! assert(poBand); poBand->SetColorInterpretation(GCI_Undefined); for (unsigned j=0; j<m_grid.height; ++j) { const RasterCell* aCell = m_grid.data[j]; for (unsigned i=0; i<m_grid.width; ++i,++_sfGrid,++aCell) { scanline[i] = aCell->nbPoints ? *_sfGrid : sfNanValue; } if (poBand->RasterIO( GF_Write, 0, static_cast<int>(j), static_cast<int>(m_grid.width), 1, scanline, static_cast<int>(m_grid.width), 1, GDT_Float64, 0, 0 ) != CE_None) { //the corresponding SF should exist on the input cloud CCLib::ScalarField* formerSf = pc->getScalarField(static_cast<int>(k)); assert(formerSf); ccLog::Error(QString("[GDAL] An error occurred while writing the '%1' scalar field band!").arg(formerSf->getName())); k = m_grid.scalarFields.size(); //quick stop break; } } } } } if (scanline) CPLFree(scanline); scanline = 0; /* Once we're done, close properly the dataset */ GDALClose( (GDALDatasetH) poDstDS ); ccLog::Print(QString("[Rasterize] Raster '%1' succesfully saved").arg(outputFilename)); #else assert(false); ccLog::Error("[Rasterize] GDAL not supported by this version! Can't generate a raster..."); #endif }