void ccVolumeCalcTool::ceilFillEmptyCellStrategyChanged(int) { ccRasterGrid::EmptyCellFillOption fillEmptyCellsStrategy = getFillEmptyCellsStrategy(fillCeilEmptyCellsComboBox); ceilEmptyValueDoubleSpinBox->setEnabled( ceilComboBox->currentIndex() == 0 || fillEmptyCellsStrategy == ccRasterGrid::FILL_CUSTOM_HEIGHT); gridIsUpToDate(false); }
void ccVolumeCalcTool::groundFillEmptyCellStrategyChanged(int) { EmptyCellFillOption fillEmptyCellsStrategy = getFillEmptyCellsStrategy(fillGroundEmptyCellsComboBox); groundEmptyValueDoubleSpinBox->setEnabled( groundComboBox->currentIndex() == 0 || fillEmptyCellsStrategy == FILL_CUSTOM_HEIGHT); gridIsUpToDate(false); }
void ccRasterizeTool::fillEmptyCellStrategyChanged(int) { EmptyCellFillOption fillEmptyCellsStrategy = getFillEmptyCellsStrategy(fillEmptyCellsComboBox); emptyValueDoubleSpinBox->setEnabled( fillEmptyCellsStrategy == FILL_CUSTOM_HEIGHT || fillEmptyCellsStrategy == INTERPOLATE ); gridIsUpToDate(false); }
cc2Point5DimEditor::EmptyCellFillOption ccRasterizeTool::getFillEmptyCellsStrategyExt( double& emptyCellsHeight, double& minHeight, double& maxHeight) const { EmptyCellFillOption fillEmptyCellsStrategy = getFillEmptyCellsStrategy(fillEmptyCellsComboBox); emptyCellsHeight = 0.0; minHeight = m_grid.minHeight; maxHeight = m_grid.maxHeight; switch (fillEmptyCellsStrategy) { case LEAVE_EMPTY: //nothing to do break; case FILL_MINIMUM_HEIGHT: emptyCellsHeight = m_grid.minHeight; break; case FILL_MAXIMUM_HEIGHT: emptyCellsHeight = m_grid.maxHeight; break; case FILL_CUSTOM_HEIGHT: case INTERPOLATE: { double customEmptyCellsHeight = getCustomHeightForEmptyCells(); //update min and max height by the way (only if there are invalid cells ;) if (m_grid.validCellCount != m_grid.width * m_grid.height) { if (customEmptyCellsHeight <= m_grid.minHeight) minHeight = customEmptyCellsHeight; else if (customEmptyCellsHeight >= m_grid.maxHeight) maxHeight = customEmptyCellsHeight; emptyCellsHeight = customEmptyCellsHeight; } } break; case FILL_AVERAGE_HEIGHT: //'average height' is a kind of 'custom height' so we can fall back to this mode! fillEmptyCellsStrategy = FILL_CUSTOM_HEIGHT; emptyCellsHeight = m_grid.meanHeight; break; default: assert(false); } return fillEmptyCellsStrategy; }
bool ccVolumeCalcTool::updateGrid() { if (!m_cloud2) { assert(false); return false; } //cloud bounding-box --> grid size ccBBox box = getCustomBBox(); if (!box.isValid()) { return false; } unsigned gridWidth = 0, gridHeight = 0; if (!getGridSize(gridWidth, gridHeight)) { return false; } //grid step double gridStep = getGridStep(); assert(gridStep != 0); //ground ccGenericPointCloud* groundCloud = 0; double groundHeight = 0; switch (groundComboBox->currentIndex()) { case 0: groundHeight = groundEmptyValueDoubleSpinBox->value(); break; case 1: groundCloud = m_cloud1 ? m_cloud1 : m_cloud2; break; case 2: groundCloud = m_cloud2; break; default: assert(false); return false; } //ceil ccGenericPointCloud* ceilCloud = 0; double ceilHeight = 0; switch (ceilComboBox->currentIndex()) { case 0: ceilHeight = ceilEmptyValueDoubleSpinBox->value(); break; case 1: ceilCloud = m_cloud1 ? m_cloud1 : m_cloud2; break; case 2: ceilCloud = m_cloud2; break; default: assert(false); return false; } ccVolumeCalcTool::ReportInfo reportInfo; if (ComputeVolume( m_grid, groundCloud, ceilCloud, box, getProjectionDimension(), gridStep, gridWidth, gridHeight, getTypeOfProjection(), getFillEmptyCellsStrategy(fillGroundEmptyCellsComboBox), reportInfo, groundHeight, ceilHeight, this)) { outputReport(reportInfo); return true; } else { return false; } }
bool ccVolumeCalcTool::updateGrid() { if (!m_cloud2) { assert(false); return false; } //cloud bounding-box --> grid size ccBBox box = getCustomBBox(); if (!box.isValid()) { return false; } unsigned gridWidth = 0, gridHeight = 0; if (!getGridSize(gridWidth, gridHeight)) { return false; } //grid size unsigned gridTotalSize = gridWidth * gridHeight; if (gridTotalSize == 1) { if (QMessageBox::question(this, "Unexpected grid size", "The generated grid will only have 1 cell! Do you want to proceed anyway?", QMessageBox::Yes, QMessageBox::No) == QMessageBox::No) return false; } else if (gridTotalSize > 10000000) { if (QMessageBox::question(this, "Big grid size", "The generated grid will have more than 10.000.000 cells! Do you want to proceed anyway?", QMessageBox::Yes, QMessageBox::No) == QMessageBox::No) return false; } //grid step double gridStep = getGridStep(); assert(gridStep != 0); //memory allocation CCVector3d minCorner = CCVector3d::fromArray(box.minCorner().u); if (!m_grid.init(gridWidth, gridHeight, gridStep, minCorner)) { //not enough memory ccLog::Error("Not enough memory"); return false; } //ground ccGenericPointCloud* groundCloud = 0; double groundHeight = 0; switch (groundComboBox->currentIndex()) { case 0: groundHeight = groundEmptyValueDoubleSpinBox->value(); break; case 1: groundCloud = m_cloud1 ? m_cloud1 : m_cloud2; break; case 2: groundCloud = m_cloud2; break; default: assert(false); return false; } //vertical dimension const unsigned char Z = getProjectionDimension(); assert(Z >= 0 && Z <= 2); //per-cell Z computation ccRasterGrid::ProjectionType projectionType = getTypeOfProjection(); ccProgressDialog pDlg(true, this); ccRasterGrid groundRaster; if (groundCloud) { if (!groundRaster.init(gridWidth, gridHeight, gridStep, minCorner)) { //not enough memory ccLog::Error("Not enough memory"); return false; } if (groundRaster.fillWith( groundCloud, Z, projectionType, getFillEmptyCellsStrategy(fillGroundEmptyCellsComboBox) == ccRasterGrid::INTERPOLATE, ccRasterGrid::INVALID_PROJECTION_TYPE, &pDlg)) { groundRaster.fillEmptyCells(getFillEmptyCellsStrategy(fillGroundEmptyCellsComboBox), groundEmptyValueDoubleSpinBox->value()); ccLog::Print(QString("[Volume] Ground raster grid: size: %1 x %2 / heights: [%3 ; %4]").arg(m_grid.width).arg(m_grid.height).arg(m_grid.minHeight).arg(m_grid.maxHeight)); } else { return false; } } //ceil ccGenericPointCloud* ceilCloud = 0; double ceilHeight = 0; switch (ceilComboBox->currentIndex()) { case 0: ceilHeight = ceilEmptyValueDoubleSpinBox->value(); break; case 1: ceilCloud = m_cloud1 ? m_cloud1 : m_cloud2; break; case 2: ceilCloud = m_cloud2; break; default: assert(false); return false; } ccRasterGrid ceilRaster; if (ceilCloud) { if (!ceilRaster.init(gridWidth, gridHeight, gridStep, minCorner)) { //not enough memory ccLog::Error("Not enough memory"); return false; } if (ceilRaster.fillWith(ceilCloud, Z, projectionType, getFillEmptyCellsStrategy(fillCeilEmptyCellsComboBox) == ccRasterGrid::INTERPOLATE, ccRasterGrid::INVALID_PROJECTION_TYPE, &pDlg)) { ceilRaster.fillEmptyCells(getFillEmptyCellsStrategy(fillCeilEmptyCellsComboBox), ceilEmptyValueDoubleSpinBox->value()); ccLog::Print(QString("[Volume] Ceil raster grid: size: %1 x %2 / heights: [%3 ; %4]").arg(m_grid.width).arg(m_grid.height).arg(m_grid.minHeight).arg(m_grid.maxHeight)); } else { return false; } } //update grid and compute volume { pDlg.setMethodTitle(tr("Volume computation")); pDlg.setInfo(tr("Cells: %1 x %2").arg(m_grid.width).arg(m_grid.height)); pDlg.start(); pDlg.show(); QCoreApplication::processEvents(); CCLib::NormalizedProgress nProgress(&pDlg, m_grid.width*m_grid.height); ReportInfo repotInfo; size_t ceilNonMatchingCount = 0; size_t groundNonMatchingCount = 0; size_t cellCount = 0; //at least one of the grid is based on a cloud m_grid.nonEmptyCellCount = 0; for (unsigned i = 0; i < m_grid.height; ++i) { for (unsigned j = 0; j < m_grid.width; ++j) { ccRasterCell& cell = m_grid.rows[i][j]; bool validGround = true; cell.minHeight = groundHeight; if (groundCloud) { cell.minHeight = groundRaster.rows[i][j].h; validGround = std::isfinite(cell.minHeight); } bool validCeil = true; cell.maxHeight = ceilHeight; if (ceilCloud) { cell.maxHeight = ceilRaster.rows[i][j].h; validCeil = std::isfinite(cell.maxHeight); } if (validGround && validCeil) { cell.h = cell.maxHeight - cell.minHeight; cell.nbPoints = 1; repotInfo.volume += cell.h; if (cell.h < 0) { repotInfo.removedVolume -= cell.h; } else if (cell.h > 0) { repotInfo.addedVolume += cell.h; } repotInfo.surface += 1.0; ++m_grid.nonEmptyCellCount; //= matching count ++cellCount; } else { if (validGround) { ++cellCount; ++groundNonMatchingCount; } else if (validCeil) { ++cellCount; ++ceilNonMatchingCount; } cell.h = std::numeric_limits<double>::quiet_NaN(); cell.nbPoints = 0; } cell.avgHeight = (groundHeight + ceilHeight) / 2; cell.stdDevHeight = 0; if (!nProgress.oneStep()) { ccLog::Warning("[Volume] Process cancelled by the user"); return false; } } } m_grid.validCellCount = m_grid.nonEmptyCellCount; //count the average number of valid neighbors { size_t validNeighborsCount = 0; size_t count = 0; for (unsigned i = 1; i < m_grid.height - 1; ++i) { for (unsigned j = 1; j < m_grid.width - 1; ++j) { ccRasterCell& cell = m_grid.rows[i][j]; if (cell.h == cell.h) { for (unsigned k = i - 1; k <= i + 1; ++k) { for (unsigned l = j - 1; l <= j + 1; ++l) { if (k != i || l != j) { ccRasterCell& otherCell = m_grid.rows[k][l]; if (std::isfinite(otherCell.h)) { ++validNeighborsCount; } } } } ++count; } } } if (count) { repotInfo.averageNeighborsPerCell = static_cast<double>(validNeighborsCount) / count; } } repotInfo.matchingPrecent = static_cast<float>(m_grid.validCellCount * 100) / cellCount; repotInfo.groundNonMatchingPercent = static_cast<float>(groundNonMatchingCount * 100) / cellCount; repotInfo.ceilNonMatchingPercent = static_cast<float>(ceilNonMatchingCount * 100) / cellCount; float cellArea = static_cast<float>(m_grid.gridStep * m_grid.gridStep); repotInfo.volume *= cellArea; repotInfo.addedVolume *= cellArea; repotInfo.removedVolume *= cellArea; repotInfo.surface *= cellArea; outputReport(repotInfo); } m_grid.setValid(true); return true; }
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 }
bool ccRasterizeTool::updateGrid(bool interpolateSF/*=false*/) { if (!m_cloud) { assert(false); return false; } //main parameters ProjectionType projectionType = getTypeOfProjection(); ProjectionType sfInterpolation = interpolateSF ? getTypeOfSFInterpolation() : INVALID_PROJECTION_TYPE; //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; //cloud bounding-box --> grid size ccBBox box = getCustomBBox(); if (!box.isValid()) return false; double gridStep = getGridStep(); assert(gridStep != 0); CCVector3d boxDiag( static_cast<double>(box.maxCorner().x) - static_cast<double>(box.minCorner().x), static_cast<double>(box.maxCorner().y) - static_cast<double>(box.minCorner().y), static_cast<double>(box.maxCorner().z) - static_cast<double>(box.minCorner().z) ); if (boxDiag.u[X] <= 0 || boxDiag.u[Y] <= 0) { ccLog::Error("Invalid cloud bounding box!"); return false; } unsigned gridWidth = static_cast<unsigned>(ceil(boxDiag.u[X] / gridStep)); unsigned gridHeight = static_cast<unsigned>(ceil(boxDiag.u[Y] / gridStep)); //grid size unsigned gridTotalSize = gridWidth * gridHeight; if (gridTotalSize == 1) { if (QMessageBox::question(0,"Unexpected grid size","The generated grid will only have 1 cell! Do you want to proceed anyway?",QMessageBox::Yes,QMessageBox::No) == QMessageBox::No) return false; } else if (gridTotalSize > 10000000) { if (QMessageBox::question(0,"Big grid size","The generated grid will have more than 10.000.000 cells! Do you want to proceed anyway?",QMessageBox::Yes,QMessageBox::No) == QMessageBox::No) return false; } removeContourLines(); //memory allocation if (!m_grid.init(gridWidth,gridHeight)) { //not enough memory ccLog::Error("Not enough memory"); return false; } m_grid.gridStep = gridStep; m_grid.minCorner = CCVector3d::fromArray(box.minCorner().u); ccProgressDialog pDlg(true,this); if (m_grid.fillWith(m_cloud, Z, projectionType, getFillEmptyCellsStrategy(fillEmptyCellsComboBox) == INTERPOLATE, sfInterpolation, &pDlg)) { ccLog::Print(QString("[Rasterize] Current raster grid: size: %1 x %2 / heights: [%3 ; %4]").arg(m_grid.width).arg(m_grid.height).arg(m_grid.minHeight).arg(m_grid.maxHeight)); } return true; }
void ccHeightGridGenerationDlg::projectionChanged(int) { emptyValueDoubleSpinBox->setEnabled(getFillEmptyCellsStrategy() == ccHeightGridGeneration::FILL_CUSTOM_HEIGHT); }