From 3b48cdcb86f30c2259237d7b3a68ee7d17a208bb Mon Sep 17 00:00:00 2001 From: Thomas Bonfort Date: Mon, 20 Dec 2021 15:31:01 +0100 Subject: [PATCH] add Force() to force computation of histogram if not cached --- godal.cpp | 7 +++++-- godal.go | 2 +- godal.h | 2 +- godal_test.go | 8 +++++++- histogram.go | 26 +++++++++++++++++++++++--- 5 files changed, 37 insertions(+), 8 deletions(-) diff --git a/godal.cpp b/godal.cpp index 8eb8209..43a0749 100644 --- a/godal.cpp +++ b/godal.cpp @@ -791,11 +791,14 @@ size_t godalVSIRead(VSILFILE *f, void *buf, int len, char **errmsg) { } void godalRasterHistogram(cctx *ctx, GDALRasterBandH bnd, double *min, double *max, int *buckets, - unsigned long long **values, int bIncludeOutOfRange, int bApproxOK) { + unsigned long long **values, int bIncludeOutOfRange, int bApproxOK, int bForce) { godalWrap(ctx); CPLErr ret = CE_None; if (*buckets == 0) { - ret=GDALGetDefaultHistogramEx(bnd,min,max,buckets,values,1,nullptr,nullptr); + ret=GDALGetDefaultHistogramEx(bnd,min,max,buckets,values,bForce,nullptr,nullptr); + if(ret == CE_Warning && ctx->errMessage == nullptr) { + CPLError(CE_Failure, CPLE_AppDefined, "no cached histogram available"); + } } else { *values = (unsigned long long*) VSIMalloc(*buckets*sizeof(GUIntBig)); ret=GDALGetRasterHistogramEx(bnd,*min,*max,*buckets,*values,bIncludeOutOfRange,bApproxOK,nullptr,nullptr); diff --git a/godal.go b/godal.go index 5fa9f45..e711f1b 100644 --- a/godal.go +++ b/godal.go @@ -421,7 +421,7 @@ func (band Band) Histogram(opts ...HistogramOption) (Histogram, error) { cgc := createCGOContext(nil, hopt.errorHandler) C.godalRasterHistogram(cgc.cPointer(), band.handle(), (*C.double)(&hopt.min), (*C.double)(&hopt.max), (*C.int)(&hopt.buckets), - &values, C.int(hopt.includeOutside), C.int(hopt.approx)) + &values, C.int(hopt.includeOutside), C.int(hopt.approx), C.int(hopt.force)) if err := cgc.close(); err != nil { return Histogram{}, err } diff --git a/godal.h b/godal.h index b29773d..a04f2f9 100644 --- a/godal.h +++ b/godal.h @@ -100,7 +100,7 @@ extern "C" { void godalGetColorTable(GDALRasterBandH bnd, GDALPaletteInterp *interp, int *nEntries, short **entries); void godalSetColorTable(cctx *ctx, GDALRasterBandH bnd, GDALPaletteInterp interp, int nEntries, short *entries); void godalRasterHistogram(cctx *ctx, GDALRasterBandH bnd, double *min, double *max, int *buckets, - unsigned long long **values, int bIncludeOutOfRange, int bApproxOK); + unsigned long long **values, int bIncludeOutOfRange, int bApproxOK, int bForce); VSILFILE *godalVSIOpen(cctx *ctx, const char *name); void godalVSIUnlink(cctx *ctx, const char *name); diff --git a/godal_test.go b/godal_test.go index 18ef385..26e74dc 100644 --- a/godal_test.go +++ b/godal_test.go @@ -395,9 +395,15 @@ func TestHistogram(t *testing.T) { bnd := ds.Bands()[0] _, err := bnd.Histogram() - assert.NoError(t, err) + assert.Error(t, err) + assert.Equal(t, "no cached histogram available", err.Error()) + ehc := eh() hist, err := bnd.Histogram(ErrLogger(ehc.ErrorHandler)) + assert.Error(t, err) + assert.Equal(t, "no cached histogram available", err.Error()) + + hist, err = bnd.Histogram(Force()) assert.NoError(t, err) ll := hist.Len() diff --git a/histogram.go b/histogram.go index 92c6390..8037479 100644 --- a/histogram.go +++ b/histogram.go @@ -42,8 +42,9 @@ func (h Histogram) Bucket(i int) Bucket { } type histogramOpts struct { - approx int - includeOutside int + approx int //bool + force int //bool + includeOutside int //bool min, max float64 buckets int32 errorHandler ErrorHandler @@ -55,6 +56,9 @@ type histogramOpts struct { // - Approximate() to allow the algorithm to operate on a subset of the full resolution data // - Intervals(count int, min,max float64) to compute a histogram with count buckets, spanning [min,max]. // Each bucket will be (max-min)/count wide. If not provided, the default histogram will be returned. +// - Force() to allow gdal to scan the full dataset to extract the histogram if no cached version +// is available. This option is not used if also using Intervals(). The default (i.e. without using +// Force()) is to return an error if a cached histogram is not available. // - IncludeOutOfRange() to populate the first and last bucket with values under/over the specified min/max // when used in conjuntion with Intervals() // - ErrLogger @@ -89,6 +93,20 @@ func Approximate() interface { return approximateOkOption{} } +type forceOption struct{} + +func (aoo forceOption) setHistogramOpt(ho *histogramOpts) { + ho.force = 1 +} + +// Force allows the histogram algorithm to scan the dataset to collect statistics if a cached +// histogram is not available (false by default, may cause a long computation if set) +func Force() interface { + HistogramOption +} { + return forceOption{} +} + type intervalsOption struct { min, max float64 buckets int32 @@ -101,7 +119,9 @@ func (io intervalsOption) setHistogramOpt(ho *histogramOpts) { } // Intervals computes a histogram with count buckets, spanning [min,max]. -// Each bucket will be (max-min)/count wide. If not provided, the default histogram will be returned. +// Each bucket will be (max-min)/count wide. +// If provided, this option will force a dataset rescan which may be time consuming. +// If not provided, the default histogram will be returned func Intervals(count int, min, max float64) interface { HistogramOption } {