QString cc2Point5DimEditor::getGridSizeAsString() const { //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 "invalid grid box"; } 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) ); unsigned gridWidth = static_cast<unsigned>(ceil(boxDiag.u[X] / gridStep)); unsigned gridHeight = static_cast<unsigned>(ceil(boxDiag.u[Y] / gridStep)); return QString("%1 x %2").arg(gridWidth).arg(gridHeight); }
ccPointCloud* cc2Point5DimEditor::convertGridToCloud( const std::vector<ExportableFields>& exportedFields, bool interpolateSF, bool resampleInputCloud, ccGenericPointCloud* inputCloud, bool fillEmptyCells, double emptyCellsHeight) const { if (!m_grid.isValid()) return 0; unsigned pointsCount = (fillEmptyCells ? m_grid.width * m_grid.height : m_grid.validCellCount); if (pointsCount == 0) { ccLog::Warning("[Rasterize] Empty grid!"); return 0; } ccPointCloud* cloudGrid = 0; if (resampleInputCloud) { CCLib::ReferenceCloud refCloud(inputCloud); if (refCloud.reserve(m_grid.nonEmptyCellCount)) { for (unsigned j=0; j<m_grid.height; ++j) { for (unsigned i=0; i<m_grid.width; ++i) { const RasterCell& cell = m_grid.data[j][i]; if (cell.nbPoints) //non empty cell { refCloud.addPointIndex(cell.pointIndex); } } } assert(refCloud.size() != 0); cloudGrid = inputCloud->isA(CC_TYPES::POINT_CLOUD) ? static_cast<ccPointCloud*>(inputCloud)->partialClone(&refCloud) : ccPointCloud::From(&refCloud,inputCloud); cloudGrid->setPointSize(0); //to avoid display issues //even if we have already resampled the original cloud we may have to create new points and/or scalar fields //if (!interpolateSF && !fillEmptyCells) // return cloudGrid; } else { ccLog::Warning("[Rasterize] Not enough memory!"); return 0; } } else { cloudGrid = new ccPointCloud("grid"); } assert(cloudGrid); //shall we generate per-cell fields as well? std::vector<CCLib::ScalarField*> exportedSFs; if (!exportedFields.empty()) { exportedSFs.resize(exportedFields.size(),0); for (size_t i=0; i<exportedFields.size(); ++i) { int sfIndex = -1; switch (exportedFields[i]) { case PER_CELL_HEIGHT: case PER_CELL_COUNT: case PER_CELL_MIN_HEIGHT: case PER_CELL_MAX_HEIGHT: case PER_CELL_AVG_HEIGHT: case PER_CELL_HEIGHT_STD_DEV: case PER_CELL_HEIGHT_RANGE: sfIndex = cloudGrid->addScalarField(qPrintable(GetDefaultFieldName(exportedFields[i]))); break; default: assert(false); break; } if (sfIndex < 0) { ccLog::Warning("[Rasterize] Couldn't allocate scalar field(s)! Try to free some memory ..."); break; } exportedSFs[i] = cloudGrid->getScalarField(sfIndex); assert(exportedSFs[i]); } } //the resampled cloud already contains the points corresponding to 'filled' cells so we will only //need to add the empty ones (if requested) if ((!resampleInputCloud || fillEmptyCells) && !cloudGrid->reserve(pointsCount)) { ccLog::Warning("[Rasterize] Not enough memory!"); delete cloudGrid; return 0; } //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 ccBBox box = getCustomBBox(); assert(box.isValid()); //we work with doubles as grid step can be much smaller than the cloud coordinates! double Py = box.minCorner().u[Y]; //as the 'non empty cells points' are already in the cloud //we must take care of where we put the scalar fields values! unsigned nonEmptyCellIndex = 0; for (unsigned j=0; j<m_grid.height; ++j) { const RasterCell* aCell = m_grid.data[j]; double Px = box.minCorner().u[X]; for (unsigned i=0; i<m_grid.width; ++i,++aCell) { if (aCell->h == aCell->h) //valid cell { //if we haven't resampled the original cloud, we must add the point //corresponding to this non-empty cell if (!resampleInputCloud || aCell->nbPoints == 0) { CCVector3 Pf( static_cast<PointCoordinateType>(Px), static_cast<PointCoordinateType>(Py), static_cast<PointCoordinateType>(aCell->h) ); cloudGrid->addPoint(Pf); } //fill the associated SFs assert(exportedSFs.size() >= exportedFields.size()); assert(!inputCloud || nonEmptyCellIndex < inputCloud->size()); for (size_t i=0; i<exportedSFs.size(); ++i) { CCLib::ScalarField* sf = exportedSFs[i]; ScalarType sVal = NAN_VALUE; switch (exportedFields[i]) { case PER_CELL_HEIGHT: sVal = static_cast<ScalarType>(aCell->h); break; case PER_CELL_COUNT: sVal = static_cast<ScalarType>(aCell->nbPoints); break; case PER_CELL_MIN_HEIGHT: sVal = static_cast<ScalarType>(aCell->minHeight); break; case PER_CELL_MAX_HEIGHT: sVal = static_cast<ScalarType>(aCell->maxHeight); break; case PER_CELL_AVG_HEIGHT: sVal = static_cast<ScalarType>(aCell->avgHeight); break; case PER_CELL_HEIGHT_STD_DEV: sVal = static_cast<ScalarType>(aCell->stdDevHeight); break; case PER_CELL_HEIGHT_RANGE: sVal = static_cast<ScalarType>(aCell->maxHeight - aCell->minHeight); break; default: assert(false); break; } if (resampleInputCloud) sf->setValue(nonEmptyCellIndex,sVal); else sf->addElement(sVal); } ++nonEmptyCellIndex; } else if (fillEmptyCells) //empty cell { //even if we have resampled the original cloud, we must add the point //corresponding to this empty cell { CCVector3 Pf( static_cast<PointCoordinateType>(Px), static_cast<PointCoordinateType>(Py), static_cast<PointCoordinateType>(emptyCellsHeight) ); cloudGrid->addPoint(Pf); } assert(exportedSFs.size() == exportedFields.size()); for (size_t i=0; i<exportedSFs.size(); ++i) { if (!exportedSFs[i]) { continue; } if (exportedFields[i] == PER_CELL_HEIGHT) { //we set the point height to the default height ScalarType s = static_cast<ScalarType>(emptyCellsHeight); exportedSFs[i]->addElement(s); } else { exportedSFs[i]->addElement(NAN_VALUE); } } } Px += m_grid.gridStep; } Py += m_grid.gridStep; } assert(exportedSFs.size() == exportedFields.size()); for (size_t i=0; i<exportedSFs.size(); ++i) { CCLib::ScalarField* sf = exportedSFs[i]; if (sf) { sf->computeMinAndMax(); } } //take care of former scalar fields if (!resampleInputCloud) { if (interpolateSF && inputCloud && inputCloud->isA(CC_TYPES::POINT_CLOUD)) { ccPointCloud* pc = static_cast<ccPointCloud*>(inputCloud); for (size_t k=0; k<m_grid.scalarFields.size(); ++k) { double* _sfGrid = m_grid.scalarFields[k]; if (_sfGrid) //valid SF grid { //the corresponding SF should exist on the input cloud ccScalarField* formerSf = static_cast<ccScalarField*>(pc->getScalarField(static_cast<int>(k))); assert(formerSf); //we try to create an equivalent SF on the output grid int sfIdx = cloudGrid->addScalarField(formerSf->getName()); if (sfIdx < 0) //if we aren't lucky, the input cloud already had a SF with CC_HEIGHT_GRID_FIELD_NAME as name sfIdx = cloudGrid->addScalarField(qPrintable(QString(formerSf->getName()).append(".old"))); if (sfIdx < 0) { ccLog::Warning("[Rasterize] Couldn't allocate a new scalar field for storing SF '%s' values! Try to free some memory ...",formerSf->getName()); } else { ccScalarField* sf = static_cast<ccScalarField*>(cloudGrid->getScalarField(sfIdx)); assert(sf); //set sf values unsigned n = 0; const ScalarType emptyCellSFValue = CCLib::ScalarField::NaN(); 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) { if (aCell->nbPoints) { ScalarType s = static_cast<ScalarType>(*_sfGrid); sf->setValue(n++,s); } else if (fillEmptyCells) { sf->setValue(n++,emptyCellSFValue); } } } sf->computeMinAndMax(); sf->importParametersFrom(formerSf); assert(sf->currentSize() == pointsCount); } } } } } else { for (size_t k=0; k<cloudGrid->getNumberOfScalarFields(); ++k) { CCLib::ScalarField* sf = cloudGrid->getScalarField(static_cast<int>(k)); sf->resize(cloudGrid->size(),true,NAN_VALUE); } } QString gridName = QString("raster(%1)").arg(m_grid.gridStep); if (inputCloud) { gridName.prepend(inputCloud->getName() + QString(".")); } cloudGrid->setName(gridName); return cloudGrid; }
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 ccRasterizeTool::generateContours() { if (!m_grid.isValid() || !m_rasterCloud) { ccLog::Error("Need a valid raster/cloud to compute contours!"); return; } ccScalarField* activeLayer = m_rasterCloud->getCurrentDisplayedScalarField(); if (!activeLayer) { ccLog::Error("No valid/active layer!"); return; } double startValue = contourStartDoubleSpinBox->value(); if (startValue > activeLayer->getMax()) { ccLog::Error("Start value is above the layer maximum value!"); return; } double step = contourStepDoubleSpinBox->value(); assert(step > 0); unsigned levelCount = 1 + static_cast<unsigned>(floor((activeLayer->getMax()-startValue)/step)); removeContourLines(); bool ignoreBorders = ignoreContourBordersCheckBox->isChecked(); unsigned xDim = m_grid.width; unsigned yDim = m_grid.height; int margin = 0; if (!ignoreBorders) { margin = 1; xDim += 2; yDim += 2; } std::vector<double> grid; try { grid.resize(xDim * yDim, 0); } catch (const std::bad_alloc&) { ccLog::Error("Not enough memory!"); if (m_window) m_window->redraw(); return; } //fill grid { bool sparseLayer = (activeLayer->currentSize() != m_grid.height * m_grid.width); double emptyCellsValue = activeLayer->getMin()-1.0; unsigned layerIndex = 0; for (unsigned j=0; j<m_grid.height; ++j) { RasterCell* cell = m_grid.data[j]; double* row = &(grid[(j+margin)*xDim + margin]); for (unsigned i=0; i<m_grid.width; ++i) { if (cell[i].nbPoints || !sparseLayer) { ScalarType value = activeLayer->getValue(layerIndex++); row[i] = ccScalarField::ValidValue(value) ? value : emptyCellsValue; } else { row[i] = emptyCellsValue; } } } } bool memoryError = false; try { Isolines<double> iso(static_cast<int>(xDim),static_cast<int>(yDim)); if (!ignoreBorders) iso.createOnePixelBorder(&(grid.front()),activeLayer->getMin()-1.0); //bounding box 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; int minVertexCount = minVertexCountSpinBox->value(); assert(minVertexCount >= 3); ccProgressDialog pDlg(true,this); pDlg.setMethodTitle("Contour plot"); pDlg.setInfo(qPrintable(QString("Levels: %1\nCells: %2 x %3").arg(levelCount).arg(m_grid.width).arg(m_grid.height))); pDlg.start(); pDlg.show(); QApplication::processEvents(); CCLib::NormalizedProgress nProgress(&pDlg,levelCount); int lineWidth = contourWidthSpinBox->value(); bool colorize = colorizeContoursCheckBox->isChecked(); double v = startValue; while (v <= activeLayer->getMax() && !memoryError) { //extract contour lines for the current level iso.setThreshold(v); int lineCount = iso.find(&(grid.front())); ccLog::PrintDebug(QString("[Rasterize][Isolines] value=%1 : %2 lines").arg(v).arg(lineCount)); //convert them to poylines int realCount = 0; for (int i=0; i<lineCount; ++i) { int vertCount = iso.getContourLength(i); if (vertCount >= minVertexCount) { ccPointCloud* vertices = new ccPointCloud("vertices"); ccPolyline* poly = new ccPolyline(vertices); poly->addChild(vertices); bool isClosed = iso.isContourClosed(i); if (poly->reserve(vertCount) && vertices->reserve(vertCount)) { unsigned localIndex = 0; for (int vi=0; vi<vertCount; ++vi) { double x = iso.getContourX(i,vi) - margin + 0.5; double y = iso.getContourY(i,vi) - margin + 0.5; CCVector3 P; P.u[X] = static_cast<PointCoordinateType>(x * m_grid.gridStep + box.minCorner().u[X]); P.u[Y] = static_cast<PointCoordinateType>(y * m_grid.gridStep + box.minCorner().u[Y]); P.u[Z] = static_cast<PointCoordinateType>(v); vertices->addPoint(P); assert(localIndex < vertices->size()); poly->addPointIndex(localIndex++); } assert(poly); if (poly->size() > 1) { poly->setName(QString("Contour line value=%1 (#%2)").arg(v).arg(++realCount)); poly->setGlobalScale(m_cloud->getGlobalScale()); poly->setGlobalShift(m_cloud->getGlobalShift()); poly->setWidth(lineWidth); poly->setClosed(isClosed); //if we have less vertices, it means we have 'chopped' the original contour poly->setColor(ccColor::darkGrey); if (colorize) { const ColorCompType* col = activeLayer->getColor(v); if (col) poly->setColor(ccColor::Rgb(col)); } poly->showColors(true); vertices->setEnabled(false); //add the 'const altitude' meta-data as well poly->setMetaData(ccPolyline::MetaKeyConstAltitude(),QVariant(v)); if (m_window) m_window->addToOwnDB(poly); m_contourLines.push_back(poly); } else { delete poly; poly = 0; } } else { delete poly; poly = 0; ccLog::Error("Not enough memory!"); memoryError = true; //early stop break; } } } v += step; if (!nProgress.oneStep()) { //process cancelled by user break; } } } catch (const std::bad_alloc&) { ccLog::Error("Not enough memory!"); } ccLog::Print(QString("[Rasterize] %1 iso-lines generated (%2 levels)").arg(m_contourLines.size()).arg(levelCount)); if (!m_contourLines.empty()) { if (memoryError) { removeContourLines(); } else { exportContoursPushButton->setEnabled(true); clearContoursPushButton->setEnabled(true); } } if (m_window) m_window->redraw(); }