OSGeo / grass

GRASS GIS - free and open-source geospatial processing engine
https://grass.osgeo.org
Other
827 stars 301 forks source link

[Bug] r.info potentially handles statistics in a wrong way #3776

Open wenzeslaus opened 3 months ago

wenzeslaus commented 3 months ago

Describe the bug

r.info checks returned value from Rast_read_rstats to be >0 as if truth-like means have_stats, but according to the documentation, Rast_read_rstats returns 2 on "stats is empty", so setting have_stats = 1 looks wrong.

if (Rast_read_rstats(name, mapset, &rstats) > 0)
                have_stats = 1;

Suggestions welcome on how to test this.

Context: I found this when reviewing #3744.

cwhite911 commented 3 months ago

The raster data in the PERMANENT mapset of the nc_spm_08_grass7 project do not have stats files in cell_misc. This is causing r.info map=<raster> -s to execute the following code block.

 if ((need_stats && !have_stats) || (need_range && !have_range)) {
            DCELL *dbuf, val, min, max;
            int fd, r, c;
            int first = 1;

            Rast_set_input_window(&cellhd);
            dbuf = Rast_allocate_d_input_buf();
            fd = Rast_open_old(name, mapset);
            min = max = 0;

            for (r = 0; r < cellhd.rows; r++) {
                Rast_get_d_row_nomask(fd, dbuf, r);
                for (c = 0; c < cellhd.cols; c++) {
                    val = dbuf[c];
                    if (Rast_is_d_null_value(&val))
                        continue;
                    if (first) {
                        rstats.sum = val;
                        rstats.sumsq = (DCELL)val * val;

                        rstats.count = 1;
                        min = max = val;

                        first = 0;
                    }
                    else {
                        rstats.sum += val;
                        rstats.sumsq += (DCELL)val * val;

                        rstats.count += 1;
                        if (min > val)
                            min = val;
                        if (max < val)
                            max = val;
                    }
                }
            }
            Rast_close(fd);
            G_free(dbuf);

            if (data_type == CELL_TYPE) {
                Rast_init_range(&crange);
                if (rstats.count > 0) {
                    Rast_update_range((CELL)min, &crange);
                    Rast_update_range((CELL)max, &crange);
                }
            }
            else {
                Rast_init_fp_range(&range);
                if (rstats.count > 0) {
                    Rast_update_fp_range(min, &range);
                    Rast_update_fp_range(max, &range);
                }
            }
        }

In this case we are calculating the raster stats instead of reading them from cell_misc/stats. This makes sense because the code to write the rasters stats file is new than the sample grassdata project nc_spm_08_grass7.

We can update the nc_spm_08_grass7 data and update r.info to write the stats file if it is not found to update legacy raster data.