From: Bborie Park Date: Tue, 26 Jun 2012 18:17:31 +0000 (+0000) Subject: Addition of ST_PixelOfValue. Ticket is #1889. X-Git-Tag: 2.1.0beta2~858 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=3908d60bc068b3f22f609963bba395da63d2588c;p=postgis Addition of ST_PixelOfValue. Ticket is #1889. git-svn-id: http://svn.osgeo.org/postgis/trunk@9987 b70326c6-7e19-0410-871a-916f4a2858ee --- diff --git a/NEWS b/NEWS index 49a27141c..02d5aaf43 100644 --- a/NEWS +++ b/NEWS @@ -8,6 +8,7 @@ PostGIS 2.1.0 - #1643, Tiger Geocoder - Tiger 2011 loader (Regina Obe / Paragon Corporation) Funded by Hunter Systems Group - GEOMETRYCOLLECTION support for ST_MakeValid (Sandro Santilli / Vizzuality) + - ST_PixelOfValue (Bborie Park / UC Davis) * Enhancements * - #823, tiger geocoder: Make loader_generate_script download portion less greedy diff --git a/doc/reference_raster.xml b/doc/reference_raster.xml index 0cf002903..e804978b6 100644 --- a/doc/reference_raster.xml +++ b/doc/reference_raster.xml @@ -3686,6 +3686,139 @@ GROUP BY (foo.geomval).val; , + + + + ST_PixelOfValue + + Get the columnx, rowy coordinates of the pixel whose value equals the search value. + + + + + + + setof record ST_PixelOfValue + + raster rast + + + integer nband + + + double precision[] search + + + boolean exclude_nodata_value=true + + + + setof record ST_PixelOfValue + + raster rast + + + double precision[] search + + + boolean exclude_nodata_value=true + + + + setof record ST_PixelOfValue + + raster rast + + + integer nband + + + double precision search + + + boolean exclude_nodata_value=true + + + + setof record ST_PixelOfValue + + raster rast + + + double precision search + + + boolean exclude_nodata_value=true + + + + + + + Description + + Get the columnx, rowy coordinates of the pixel whose value equals the search value. If no band is specified, then band 1 is assumed. + + + + Examples + + +SELECT + (pixels).* +FROM ( + SELECT + ST_PixelOfValue( + ST_SetValue( + ST_SetValue( + ST_SetValue( + ST_SetValue( + ST_SetValue( + ST_AddBand( + ST_MakeEmptyRaster(5, 5, -2, 2, 1, -1, 0, 0, 0), + '8BUI'::text, 1, 0 + ), + 1, 1, 0 + ), + 2, 3, 0 + ), + 3, 5, 0 + ), + 4, 2, 0 + ), + 5, 4, 255 + ) + , 1, ARRAY[1, 255]) AS pixels +) AS foo + + val | x | y +-----+---+--- + 1 | 1 | 2 + 1 | 1 | 3 + 1 | 1 | 4 + 1 | 1 | 5 + 1 | 2 | 1 + 1 | 2 | 2 + 1 | 2 | 4 + 1 | 2 | 5 + 1 | 3 | 1 + 1 | 3 | 2 + 1 | 3 | 3 + 1 | 3 | 4 + 1 | 4 | 1 + 1 | 4 | 3 + 1 | 4 | 4 + 1 | 4 | 5 + 1 | 5 | 1 + 1 | 5 | 2 + 1 | 5 | 3 + 255 | 5 | 4 + 1 | 5 | 5 + + + + + diff --git a/raster/rt_core/rt_api.c b/raster/rt_core/rt_api.c index 101994687..e640824f4 100644 --- a/raster/rt_core/rt_api.c +++ b/raster/rt_core/rt_api.c @@ -2474,6 +2474,78 @@ int rt_band_get_nearest_pixel( return count; } +/** + * Search band for pixel(s) with search values + * + * @param band: the band to query for minimum and maximum pixel values + * @param exclude_nodata_value: if non-zero, ignore nodata values + * @param search_values: array of values to count + * @param search_values_count: the number of search values + * + * @return -1 on error, otherwise number of pixels + */ +int +rt_band_get_pixel_of_value( + rt_band band, int exclude_nodata_value, + double *searchset, int searchcount, + rt_pixel *pixels +) { + int x; + int y; + int i; + double pixval; + int err; + int count = 0; + + rt_pixel pixel = NULL; + + assert(NULL != band); + assert(NULL != pixels); + + for (x = 0; x < band->width; x++) { + for (y = 0; y < band->height; y++) { + err = rt_band_get_pixel(band, x, y, &pixval); + if (err != 0) { + rterror("rt_band_get_pixel_of_value: Cannot get band pixel"); + return -1; + } + else if ( + exclude_nodata_value && + (band->hasnodata != FALSE) && ( + FLT_EQ(pixval, band->nodataval) || + rt_band_clamped_value_is_nodata(band, pixval) == 1 + ) + ) { + continue; + } + + for (i = 0; i < searchcount; i++) { + if (FLT_NEQ(pixval, searchset[i])) + continue; + + /* match found */ + count++; + if (*pixels == NULL) + *pixels = (rt_pixel) rtalloc(sizeof(struct rt_pixel_t) * count); + else + *pixels = (rt_pixel) rtrealloc(*pixels, sizeof(struct rt_pixel_t) * count); + if (*pixels == NULL) { + rterror("rt_band_get_pixel_of_value: Unable to allocate memory for pixel(s)"); + return -1; + } + + pixel = &((*pixels)[count - 1]); + pixel->x = x; + pixel->y = y; + pixel->nodata = 0; + pixel->value = pixval; + } + } + } + + return count; +} + double rt_band_get_nodata(rt_band band) { @@ -2717,7 +2789,6 @@ rt_band_get_summary_stats( int exclude_nodata_value, double sample, int inc_vals, uint64_t *cK, double *cM, double *cQ ) { - uint8_t *data = NULL; uint32_t x = 0; uint32_t y = 0; uint32_t z = 0; @@ -2775,12 +2846,6 @@ rt_band_get_summary_stats( return stats; } - data = rt_band_get_data(band); - if (data == NULL) { - rterror("rt_band_get_summary_stats: Cannot get band data"); - return NULL; - } - hasnodata = rt_band_get_hasnodata_flag(band); if (hasnodata != FALSE) nodata = rt_band_get_nodata(band); diff --git a/raster/rt_core/rt_api.h b/raster/rt_core/rt_api.h index 34c86cc9f..ed2cabe6b 100644 --- a/raster/rt_core/rt_api.h +++ b/raster/rt_core/rt_api.h @@ -582,6 +582,22 @@ int rt_band_get_nearest_pixel( rt_pixel *npixels ); +/** + * Search band for pixel(s) with search values + * + * @param band: the band to query for minimum and maximum pixel values + * @param exclude_nodata_value: if non-zero, ignore nodata values + * @param search_values: array of values to count + * @param search_values_count: the number of search values + * + * @return -1 on error, otherwise number of pixels + */ +int rt_band_get_pixel_of_value( + rt_band band, int exclude_nodata_value, + double *searchset, int searchcount, + rt_pixel *pixels +); + /** * Returns the minimal possible value for the band according to the pixel type. * @param band: the band to get info from diff --git a/raster/rt_pg/rt_pg.c b/raster/rt_pg/rt_pg.c index bc0f6ec94..2bf651cbb 100644 --- a/raster/rt_pg/rt_pg.c +++ b/raster/rt_pg/rt_pg.c @@ -202,6 +202,9 @@ Datum RASTER_setPixelValue(PG_FUNCTION_ARGS); /* Get pixel geographical shape */ Datum RASTER_getPixelPolygon(PG_FUNCTION_ARGS); +/* Get pixels of value */ +Datum RASTER_pixelOfValue(PG_FUNCTION_ARGS); + /* Get nearest value to a point */ Datum RASTER_nearestValue(PG_FUNCTION_ARGS); @@ -2497,6 +2500,241 @@ Datum RASTER_getPixelPolygon(PG_FUNCTION_ARGS) PG_RETURN_POINTER(gser); } +/** + * Get pixels of value + */ +PG_FUNCTION_INFO_V1(RASTER_pixelOfValue); +Datum RASTER_pixelOfValue(PG_FUNCTION_ARGS) +{ + FuncCallContext *funcctx; + TupleDesc tupdesc; + + rt_pixel pixels = NULL; + rt_pixel pixels2 = NULL; + int count = 0; + int i = 0; + int n = 0; + int call_cntr; + int max_calls; + + if (SRF_IS_FIRSTCALL()) { + MemoryContext oldcontext; + + rt_pgraster *pgraster = NULL; + rt_raster raster = NULL; + rt_band band = NULL; + int nband = 1; + int num_bands = 0; + double *search = NULL; + int nsearch = 0; + double val; + bool exclude_nodata_value = TRUE; + + ArrayType *array; + Oid etype; + Datum *e; + bool *nulls; + int16 typlen; + bool typbyval; + char typalign; + int ndims = 1; + int *dims; + int *lbs; + + /* create a function context for cross-call persistence */ + funcctx = SRF_FIRSTCALL_INIT(); + + /* switch to memory context appropriate for multiple function calls */ + oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); + + if (PG_ARGISNULL(0)) { + MemoryContextSwitchTo(oldcontext); + SRF_RETURN_DONE(funcctx); + } + pgraster = (rt_pgraster *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); + raster = rt_raster_deserialize(pgraster, FALSE); + if (!raster) { + elog(ERROR, "RASTER_pixelOfValue: Could not deserialize raster"); + PG_FREE_IF_COPY(pgraster, 0); + MemoryContextSwitchTo(oldcontext); + SRF_RETURN_DONE(funcctx); + } + + /* num_bands */ + num_bands = rt_raster_get_num_bands(raster); + if (num_bands < 1) { + elog(NOTICE, "Raster provided has no bands"); + rt_raster_destroy(raster); + PG_FREE_IF_COPY(pgraster, 0); + MemoryContextSwitchTo(oldcontext); + SRF_RETURN_DONE(funcctx); + } + + /* band index is 1-based */ + if (!PG_ARGISNULL(1)) + nband = PG_GETARG_INT32(1); + if (nband < 1 || nband > num_bands) { + elog(NOTICE, "Invalid band index (must use 1-based). Returning NULL"); + rt_raster_destroy(raster); + PG_FREE_IF_COPY(pgraster, 0); + MemoryContextSwitchTo(oldcontext); + SRF_RETURN_DONE(funcctx); + } + + /* search values */ + array = PG_GETARG_ARRAYTYPE_P(2); + etype = ARR_ELEMTYPE(array); + get_typlenbyvalalign(etype, &typlen, &typbyval, &typalign); + + switch (etype) { + case FLOAT4OID: + case FLOAT8OID: + break; + default: + elog(ERROR, "RASTER_pixelOfValue: Invalid data type for pixel values"); + rt_raster_destroy(raster); + PG_FREE_IF_COPY(pgraster, 0); + MemoryContextSwitchTo(oldcontext); + SRF_RETURN_DONE(funcctx); + break; + } + + ndims = ARR_NDIM(array); + dims = ARR_DIMS(array); + lbs = ARR_LBOUND(array); + + deconstruct_array(array, etype, typlen, typbyval, typalign, &e, + &nulls, &n); + + search = palloc(sizeof(double) * n); + for (i = 0, nsearch = 0; i < n; i++) { + if (nulls[i]) continue; + + switch (etype) { + case FLOAT4OID: + val = (double) DatumGetFloat4(e[i]); + break; + case FLOAT8OID: + val = (double) DatumGetFloat8(e[i]); + break; + } + + search[nsearch] = val; + POSTGIS_RT_DEBUGF(3, "search[%d] = %d", nsearch, search[nsearch]); + nsearch++; + } + + /* not searching for anything */ + if (nsearch < 1) { + elog(NOTICE, "No search values provided. Returning NULL"); + pfree(search); + rt_raster_destroy(raster); + PG_FREE_IF_COPY(pgraster, 0); + MemoryContextSwitchTo(oldcontext); + SRF_RETURN_DONE(funcctx); + } + else if (nsearch < n) + search = repalloc(search, sizeof(double) * nsearch); + + /* exclude_nodata_value flag */ + if (!PG_ARGISNULL(3)) + exclude_nodata_value = PG_GETARG_BOOL(3); + + /* get band */ + band = rt_raster_get_band(raster, nband - 1); + if (!band) { + elog(NOTICE, "Could not find band at index %d. Returning NULL", nband); + rt_raster_destroy(raster); + PG_FREE_IF_COPY(pgraster, 0); + MemoryContextSwitchTo(oldcontext); + SRF_RETURN_DONE(funcctx); + } + + /* get pixels of values */ + count = rt_band_get_pixel_of_value( + band, exclude_nodata_value, + search, nsearch, + &pixels + ); + pfree(search); + rt_band_destroy(band); + rt_raster_destroy(raster); + PG_FREE_IF_COPY(pgraster, 0); + if (count < 1) { + /* error */ + if (count < 0) + elog(NOTICE, "Unable to get the pixels of search values for band at index %d", nband); + /* no nearest pixel */ + else + elog(NOTICE, "No pixels of search values found for band at index %d", nband); + + MemoryContextSwitchTo(oldcontext); + SRF_RETURN_DONE(funcctx); + } + + /* Store needed information */ + funcctx->user_fctx = pixels; + + /* total number of tuples to be returned */ + funcctx->max_calls = count; + + /* Build a tuple descriptor for our result type */ + if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) { + ereport(ERROR, ( + errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg( + "function returning record called in context " + "that cannot accept type record" + ) + )); + } + + BlessTupleDesc(tupdesc); + funcctx->tuple_desc = tupdesc; + + MemoryContextSwitchTo(oldcontext); + } + + /* stuff done on every call of the function */ + funcctx = SRF_PERCALL_SETUP(); + + call_cntr = funcctx->call_cntr; + max_calls = funcctx->max_calls; + tupdesc = funcctx->tuple_desc; + pixels2 = funcctx->user_fctx; + + /* do when there is more left to send */ + if (call_cntr < max_calls) { + int values_length = 3; + Datum values[values_length]; + bool nulls[values_length]; + HeapTuple tuple; + Datum result; + + memset(nulls, FALSE, values_length); + + /* 0-based to 1-based */ + pixels2[call_cntr].x += 1; + pixels2[call_cntr].y += 1; + + values[0] = Float8GetDatum(pixels2[call_cntr].value); + values[1] = Int64GetDatum(pixels2[call_cntr].x); + values[2] = Int64GetDatum(pixels2[call_cntr].y); + + /* build a tuple */ + tuple = heap_form_tuple(tupdesc, values, nulls); + + /* make the tuple into a datum */ + result = HeapTupleGetDatum(tuple); + + SRF_RETURN_NEXT(funcctx, result); + } + else { + pfree(pixels2); + SRF_RETURN_DONE(funcctx); + } +} + /** * Return nearest value to a point */ diff --git a/raster/rt_pg/rtpostgis.sql.in.c b/raster/rt_pg/rtpostgis.sql.in.c index c75338e91..4e1cf499b 100644 --- a/raster/rt_pg/rtpostgis.sql.in.c +++ b/raster/rt_pg/rtpostgis.sql.in.c @@ -2208,6 +2208,58 @@ CREATE OR REPLACE FUNCTION st_value(rast raster, pt geometry, hasnodata boolean AS $$ SELECT st_value($1, 1, $2, $3) $$ LANGUAGE 'sql' IMMUTABLE STRICT; +----------------------------------------------------------------------- +-- ST_PixelOfValue() +----------------------------------------------------------------------- + +CREATE OR REPLACE FUNCTION st_pixelofvalue( + rast raster, + nband integer, + search double precision[], + exclude_nodata_value boolean DEFAULT TRUE, + OUT val double precision, + OUT x integer, + OUT y integer +) + RETURNS SETOF record + AS 'MODULE_PATHNAME', 'RASTER_pixelOfValue' + LANGUAGE 'c' IMMUTABLE STRICT; + +CREATE OR REPLACE FUNCTION st_pixelofvalue( + rast raster, + search double precision[], + exclude_nodata_value boolean DEFAULT TRUE, + OUT val double precision, + OUT x integer, + OUT y integer +) + RETURNS SETOF record + AS $$ SELECT val, x, y FROM st_pixelofvalue($1, 1, $2, $3) $$ + LANGUAGE 'sql' IMMUTABLE STRICT; + +CREATE OR REPLACE FUNCTION st_pixelofvalue( + rast raster, + nband integer, + search double precision, + exclude_nodata_value boolean DEFAULT TRUE, + OUT x integer, + OUT y integer +) + RETURNS SETOF record + AS $$ SELECT x, y FROM st_pixelofvalue($1, $2, ARRAY[$3], $4) $$ + LANGUAGE 'sql' IMMUTABLE STRICT; + +CREATE OR REPLACE FUNCTION st_pixelofvalue( + rast raster, + search double precision, + exclude_nodata_value boolean DEFAULT TRUE, + OUT x integer, + OUT y integer +) + RETURNS SETOF record + AS $$ SELECT x, y FROM st_pixelofvalue($1, 1, ARRAY[$2], $3) $$ + LANGUAGE 'sql' IMMUTABLE STRICT; + ----------------------------------------------------------------------- -- Raster Accessors ST_Georeference() ----------------------------------------------------------------------- diff --git a/raster/test/core/testapi.c b/raster/test/core/testapi.c index 05f1ef062..893b0fba0 100644 --- a/raster/test/core/testapi.c +++ b/raster/test/core/testapi.c @@ -2813,6 +2813,87 @@ static void testNearestPixel() { deepRelease(rast); } +static void testPixelOfValue() { + rt_raster rast; + rt_band band; + uint32_t x, y; + int rtn; + const int maxX = 10; + const int maxY = 10; + rt_pixel pixels = NULL; + + double search0[1] = {0}; + double search1[1] = {1}; + double search2[2] = {3, 5}; + + rast = rt_raster_new(maxX, maxY); + assert(rast); + + band = addBand(rast, PT_32BUI, 1, 0); + CHECK(band); + + for (x = 0; x < maxX; x++) { + for (y = 0; y < maxY; y++) { + rtn = rt_band_set_pixel(band, x, y, 1); + CHECK((rtn != -1)); + } + } + + rt_band_set_pixel(band, 0, 0, 0); + rt_band_set_pixel(band, 3, 0, 0); + rt_band_set_pixel(band, 6, 0, 0); + rt_band_set_pixel(band, 9, 0, 0); + rt_band_set_pixel(band, 1, 2, 0); + rt_band_set_pixel(band, 4, 2, 0); + rt_band_set_pixel(band, 7, 2, 0); + rt_band_set_pixel(band, 2, 4, 0); + rt_band_set_pixel(band, 5, 4, 0); + rt_band_set_pixel(band, 8, 4, 0); + rt_band_set_pixel(band, 0, 6, 0); + rt_band_set_pixel(band, 3, 6, 0); + rt_band_set_pixel(band, 6, 6, 0); + rt_band_set_pixel(band, 9, 6, 0); + rt_band_set_pixel(band, 1, 8, 0); + rt_band_set_pixel(band, 4, 8, 0); + rt_band_set_pixel(band, 7, 8, 0); + + pixels = NULL; + rtn = rt_band_get_pixel_of_value( + band, TRUE, + search1, 1, + &pixels + ); + CHECK((rtn == 83)); + if (rtn) + rtdealloc(pixels); + + pixels = NULL; + rtn = rt_band_get_pixel_of_value( + band, FALSE, + search0, 1, + &pixels + ); + CHECK((rtn == 17)); + if (rtn) + rtdealloc(pixels); + + rt_band_set_pixel(band, 4, 2, 3); + rt_band_set_pixel(band, 7, 2, 5); + rt_band_set_pixel(band, 1, 8, 3); + + pixels = NULL; + rtn = rt_band_get_pixel_of_value( + band, TRUE, + search2, 2, + &pixels + ); + CHECK((rtn == 3)); + if (rtn) + rtdealloc(pixels); + + deepRelease(rast); +} + int main() { @@ -3083,6 +3164,10 @@ main() testNearestPixel(); printf("OK\n"); + printf("Testing rt_band_get_pixel_of_value... "); + testPixelOfValue(); + printf("OK\n"); + deepRelease(raster); return EXIT_SUCCESS; diff --git a/raster/test/regress/Makefile.in b/raster/test/regress/Makefile.in index 1c56bbb74..e64ffcc65 100644 --- a/raster/test/regress/Makefile.in +++ b/raster/test/regress/Makefile.in @@ -81,7 +81,8 @@ TEST_BANDPROPS = \ rt_pixelvalue \ drop_rt_band_properties_test \ rt_neighborhood \ - rt_nearestvalue + rt_nearestvalue \ + rt_pixelofvalue TEST_UTILITY = \ rt_utility \ diff --git a/raster/test/regress/rt_pixelofvalue.sql b/raster/test/regress/rt_pixelofvalue.sql new file mode 100644 index 000000000..23c80747e --- /dev/null +++ b/raster/test/regress/rt_pixelofvalue.sql @@ -0,0 +1,113 @@ +DROP TABLE IF EXISTS raster_pixelofvalue; +CREATE TABLE raster_pixelofvalue ( + rast raster +); +CREATE OR REPLACE FUNCTION make_test_raster() + RETURNS void + AS $$ + DECLARE + width int := 10; + height int := 10; + x int; + y int; + rast raster; + BEGIN + rast := ST_MakeEmptyRaster(width, height, 0, 0, 1, -1, 0, 0, 0); + rast := ST_AddBand(rast, 1, '32BUI', 0, 0); + + FOR x IN 1..width LOOP + FOR y IN 1..height LOOP + IF (x + y) % 2 = 1 THEN + rast := ST_SetValue(rast, 1, x, y, x + y); + END IF; + END LOOP; + END LOOP; + + INSERT INTO raster_pixelofvalue VALUES (rast); + + RETURN; + END; + $$ LANGUAGE 'plpgsql'; +SELECT make_test_raster(); +DROP FUNCTION make_test_raster(); + +SELECT + (pixval).* +FROM ( + SELECT + ST_PixelOfValue( + rast, 1, + ARRAY[3, 11] + ) AS pixval + FROM raster_pixelofvalue +) foo; + +SELECT + (pixval).* +FROM ( + SELECT + ST_PixelOfValue( + rast, 1, + ARRAY[5] + ) AS pixval + FROM raster_pixelofvalue +) foo; + +SELECT + (pixval).* +FROM ( + SELECT + ST_PixelOfValue( + rast, + ARRAY[0] + ) AS pixval + FROM raster_pixelofvalue +) foo; + +SELECT + (pixval).* +FROM ( + SELECT + ST_PixelOfValue( + rast, + ARRAY[0], + FALSE + ) AS pixval + FROM raster_pixelofvalue +) foo; + +SELECT + (pixval).* +FROM ( + SELECT + ST_PixelOfValue( + rast, 1, + 7 + ) AS pixval + FROM raster_pixelofvalue +) foo; + +SELECT + (pixval).* +FROM ( + SELECT + ST_PixelOfValue( + rast, + 2 + ) AS pixval + FROM raster_pixelofvalue +) foo; + +SELECT + (pixval).* +FROM ( + SELECT + ST_PixelOfValue( + rast, + 0, + FALSE + ) AS pixval + FROM raster_pixelofvalue +) foo; + +DROP TABLE IF EXISTS raster_pixelofvalue; diff --git a/raster/test/regress/rt_pixelofvalue_expected b/raster/test/regress/rt_pixelofvalue_expected new file mode 100644 index 000000000..287be4ca4 --- /dev/null +++ b/raster/test/regress/rt_pixelofvalue_expected @@ -0,0 +1,125 @@ +NOTICE: table "raster_pixelofvalue" does not exist, skipping +3|1|2 +11|1|10 +3|2|1 +11|2|9 +11|3|8 +11|4|7 +11|5|6 +11|6|5 +11|7|4 +11|8|3 +11|9|2 +11|10|1 +5|1|4 +5|2|3 +5|3|2 +5|4|1 +NOTICE: No pixels of search values found for band at index 1 +0|1|1 +0|1|3 +0|1|5 +0|1|7 +0|1|9 +0|2|2 +0|2|4 +0|2|6 +0|2|8 +0|2|10 +0|3|1 +0|3|3 +0|3|5 +0|3|7 +0|3|9 +0|4|2 +0|4|4 +0|4|6 +0|4|8 +0|4|10 +0|5|1 +0|5|3 +0|5|5 +0|5|7 +0|5|9 +0|6|2 +0|6|4 +0|6|6 +0|6|8 +0|6|10 +0|7|1 +0|7|3 +0|7|5 +0|7|7 +0|7|9 +0|8|2 +0|8|4 +0|8|6 +0|8|8 +0|8|10 +0|9|1 +0|9|3 +0|9|5 +0|9|7 +0|9|9 +0|10|2 +0|10|4 +0|10|6 +0|10|8 +0|10|10 +1|6 +2|5 +3|4 +4|3 +5|2 +6|1 +NOTICE: No pixels of search values found for band at index 1 +1|1 +1|3 +1|5 +1|7 +1|9 +2|2 +2|4 +2|6 +2|8 +2|10 +3|1 +3|3 +3|5 +3|7 +3|9 +4|2 +4|4 +4|6 +4|8 +4|10 +5|1 +5|3 +5|5 +5|7 +5|9 +6|2 +6|4 +6|6 +6|8 +6|10 +7|1 +7|3 +7|5 +7|7 +7|9 +8|2 +8|4 +8|6 +8|8 +8|10 +9|1 +9|3 +9|5 +9|7 +9|9 +10|2 +10|4 +10|6 +10|8 +10|10