/**************************************************************************** * * MODULE: r.resamp.stats * AUTHOR(S): Glynn Clements (original contributor) * Hamish Bowman * PURPOSE: * COPYRIGHT: (C) 2006-2007 by the GRASS Development Team * * This program is free software under the GNU General Public * License (>=v2). Read the file COPYING that comes with GRASS * for details. * *****************************************************************************/ #include #include #include #include #include #include #include static const struct menu { stat_func *method; /* routine to compute new value */ stat_func_w *method_w; /* routine to compute new value (weighted) */ char *name; /* method name */ char *text; /* menu display - full description */ } menu[] = { {c_ave, w_ave, "average", "average (mean) value"}, {c_median, w_median, "median", "median value"}, {c_mode, w_mode, "mode", "most frequently occurring value"}, {c_min, w_min, "minimum", "lowest value"}, {c_max, w_max, "maximum", "highest value"}, {c_range, NULL, "range", "range value"}, {c_quart1, w_quart1, "quart1", "first quartile"}, {c_quart3, w_quart3, "quart3", "third quartile"}, {c_perc90, w_perc90, "perc90", "ninetieth percentile"}, {c_sum, w_sum, "sum", "sum of values"}, {c_var, w_var, "variance", "variance value"}, {c_stddev, w_stddev, "stddev", "standard deviation"}, {c_quant, w_quant, "quantile", "arbitrary quantile"}, {c_count, w_count, "count", "count of non-NULL values"}, {c_divr, NULL, "diversity", "number of different values"}, {NULL, NULL, NULL, NULL} }; static char *build_method_list(void) { char *buf = G_malloc(1024); char *p = buf; int i; for (i = 0; menu[i].name; i++) { char *q; if (i) *p++ = ','; for (q = menu[i].name; *q; p++, q++) *p = *q; } *p = '\0'; return buf; } static int find_method(const char *name) { int i; for (i = 0; menu[i].name; i++) if (strcmp(menu[i].name, name) == 0) return i; return -1; } static int nulls; static int infile, outfile; static struct Cell_head dst_w, src_w; static DCELL *outbuf; static DCELL **bufs; static int method; static const void *closure; static int row_scale, col_scale; static double quantile; static void resamp_unweighted(void) { stat_func *method_fn; DCELL *values; int *col_map, *row_map; int row, col; method_fn = menu[method].method; values = G_malloc(row_scale * col_scale * sizeof(DCELL)); col_map = G_malloc((dst_w.cols + 1) * sizeof(int)); row_map = G_malloc((dst_w.rows + 1) * sizeof(int)); for (col = 0; col <= dst_w.cols; col++) { double x = Rast_col_to_easting(col, &dst_w); /* col_map[col] = (int)floor(Rast_easting_to_col(x, &src_w) + 0.5); */ col_map[col] = (int)floor((x - src_w.west) / src_w.ew_res + 0.5); } for (row = 0; row <= dst_w.rows; row++) { double y = Rast_row_to_northing(row, &dst_w); row_map[row] = (int)floor(Rast_northing_to_row(y, &src_w) + 0.5); } for (row = 0; row < dst_w.rows; row++) { int maprow0 = row_map[row + 0]; int maprow1 = row_map[row + 1]; int count = maprow1 - maprow0; int i; G_percent(row, dst_w.rows, 4); for (i = 0; i < count; i++) Rast_get_d_row(infile, bufs[i], maprow0 + i); for (col = 0; col < dst_w.cols; col++) { int mapcol0 = col_map[col + 0]; int mapcol1 = col_map[col + 1]; int null = 0; int n = 0; int i, j; for (i = maprow0; i < maprow1; i++) for (j = mapcol0; j < mapcol1; j++) { DCELL *src = &bufs[i - maprow0][j]; DCELL *dst = &values[n++]; if (Rast_is_d_null_value(src)) { Rast_set_d_null_value(dst, 1); null = 1; } else *dst = *src; } if (null && nulls) Rast_set_d_null_value(&outbuf[col], 1); else (*method_fn) (&outbuf[col], values, n, closure); } Rast_put_d_row(outfile, outbuf); } } static void resamp_weighted(void) { stat_func_w *method_fn; DCELL(*values)[2]; double *col_map, *row_map; int row, col; method_fn = menu[method].method_w; values = G_malloc(row_scale * col_scale * 2 * sizeof(DCELL)); col_map = G_malloc((dst_w.cols + 1) * sizeof(double)); row_map = G_malloc((dst_w.rows + 1) * sizeof(double)); for (col = 0; col <= dst_w.cols; col++) { double x = Rast_col_to_easting(col, &dst_w); /* col_map[col] = Rast_easting_to_col(x, &src_w); */ col_map[col] = (x - src_w.west) / src_w.ew_res; } for (row = 0; row <= dst_w.rows; row++) { double y = Rast_row_to_northing(row, &dst_w); row_map[row] = Rast_northing_to_row(y, &src_w); } for (row = 0; row < dst_w.rows; row++) { double y0 = row_map[row + 0]; double y1 = row_map[row + 1]; int maprow0 = (int)floor(y0); int maprow1 = (int)ceil(y1); int count = maprow1 - maprow0; int i; G_percent(row, dst_w.rows, 4); for (i = 0; i < count; i++) Rast_get_d_row(infile, bufs[i], maprow0 + i); for (col = 0; col < dst_w.cols; col++) { double x0 = col_map[col + 0]; double x1 = col_map[col + 1]; int mapcol0 = (int)floor(x0); int mapcol1 = (int)ceil(x1); int null = 0; int n = 0; int i, j; for (i = maprow0; i < maprow1; i++) { double ky = (i == maprow0) ? 1 - (y0 - maprow0) : (i == maprow1 - 1) ? 1 - (maprow1 - y1) : 1; for (j = mapcol0; j < mapcol1; j++) { double kx = (j == mapcol0) ? 1 - (x0 - mapcol0) : (j == mapcol1 - 1) ? 1 - (mapcol1 - x1) : 1; DCELL *src = &bufs[i - maprow0][j]; DCELL *dst = &values[n++][0]; if (Rast_is_d_null_value(src)) { Rast_set_d_null_value(&dst[0], 1); null = 1; } else { dst[0] = *src; dst[1] = kx * ky; } } } if (null && nulls) Rast_set_d_null_value(&outbuf[col], 1); else (*method_fn) (&outbuf[col], values, n, closure); } Rast_put_d_row(outfile, outbuf); } } int main(int argc, char *argv[]) { struct GModule *module; struct { struct Option *rastin, *rastout, *method, *quantile; } parm; struct { struct Flag *nulls, *weight; } flag; struct History history; char title[64]; char buf_nsres[100], buf_ewres[100]; struct Colors colors; int row; G_gisinit(argv[0]); module = G_define_module(); G_add_keyword(_("raster")); G_add_keyword(_("resample")); module->description = _("Resamples raster map layers to a coarser grid using aggregation."); parm.rastin = G_define_standard_option(G_OPT_R_INPUT); parm.rastout = G_define_standard_option(G_OPT_R_OUTPUT); parm.method = G_define_option(); parm.method->key = "method"; parm.method->type = TYPE_STRING; parm.method->required = NO; parm.method->description = _("Aggregation method"); parm.method->options = build_method_list(); parm.method->answer = "average"; parm.quantile = G_define_option(); parm.quantile->key = "quantile"; parm.quantile->type = TYPE_DOUBLE; parm.quantile->required = NO; parm.quantile->description = _("Quantile to calculate for method=quantile"); parm.quantile->options = "0.0-1.0"; parm.quantile->answer = "0.5"; flag.nulls = G_define_flag(); flag.nulls->key = 'n'; flag.nulls->description = _("Propagate NULLs"); flag.weight = G_define_flag(); flag.weight->key = 'w'; flag.weight->description = _("Weight according to area (slower)"); if (G_parser(argc, argv)) exit(EXIT_FAILURE); nulls = flag.nulls->answer; method = find_method(parm.method->answer); if (method < 0) G_fatal_error(_("Unknown method <%s>"), parm.method->answer); if (menu[method].method == c_quant) { quantile = atoi(parm.quantile->answer); closure = &quantile; } G_get_set_window(&dst_w); /* set source window to old map */ Rast_get_cellhd(parm.rastin->answer, "", &src_w); if (G_projection() == PROJECTION_LL) { /* try to shift source window to overlap with destination window */ while (src_w.west >= dst_w.east && src_w.east - 360.0 > dst_w.west) { src_w.east -= 360.0; src_w.west -= 360.0; } while (src_w.east <= dst_w.west && src_w.west + 360.0 < dst_w.east) { src_w.east += 360.0; src_w.west += 360.0; } } /* adjust source window to cover destination window */ { int r0 = (int)floor(Rast_northing_to_row(dst_w.north, &src_w)); int r1 = (int)ceil(Rast_northing_to_row(dst_w.south, &src_w)); /* do not use Rast_easting_to_col() because it does ll wrap */ /* int c0 = (int)floor(Rast_easting_to_col(dst_w.west, &src_w)); int c1 = (int)ceil(Rast_easting_to_col(dst_w.east, &src_w)); */ int c0 = (int)floor((dst_w.west - src_w.west) / src_w.ew_res); int c1 = src_w.cols + (int)ceil((dst_w.east - src_w.east) / src_w.ew_res); src_w.south -= src_w.ns_res * (r1 - src_w.rows); src_w.north += src_w.ns_res * (-r0); src_w.west -= src_w.ew_res * (-c0); src_w.east += src_w.ew_res * (c1 - src_w.cols); src_w.rows = r1 - r0; src_w.cols = c1 - c0; } Rast_set_input_window(&src_w); Rast_set_output_window(&dst_w); row_scale = 2 + ceil(dst_w.ns_res / src_w.ns_res); col_scale = 2 + ceil(dst_w.ew_res / src_w.ew_res); /* allocate buffers for input rows */ bufs = G_malloc(row_scale * sizeof(DCELL *)); for (row = 0; row < row_scale; row++) bufs[row] = Rast_allocate_d_input_buf(); /* open old map */ infile = Rast_open_old(parm.rastin->answer, ""); /* allocate output buffer */ outbuf = Rast_allocate_d_output_buf(); /* open new map */ outfile = Rast_open_new(parm.rastout->answer, DCELL_TYPE); if (flag.weight->answer && menu[method].method_w) resamp_weighted(); else resamp_unweighted(); G_percent(dst_w.rows, dst_w.rows, 2); Rast_close(infile); Rast_close(outfile); /* record map metadata/history info */ sprintf(title, "Aggregate resample by %s", parm.method->answer); Rast_put_cell_title(parm.rastout->answer, title); Rast_short_history(parm.rastout->answer, "raster", &history); Rast_set_history(&history, HIST_DATSRC_1, parm.rastin->answer); G_format_resolution(src_w.ns_res, buf_nsres, src_w.proj); G_format_resolution(src_w.ew_res, buf_ewres, src_w.proj); Rast_format_history(&history, HIST_DATSRC_2, "Source map NS res: %s EW res: %s", buf_nsres, buf_ewres); Rast_command_history(&history); Rast_write_history(parm.rastout->answer, &history); /* copy color table from source map */ if (strcmp(parm.method->answer, "sum") != 0 && strcmp(parm.method->answer, "range") != 0 && strcmp(parm.method->answer, "count") != 0 && strcmp(parm.method->answer, "diversity") != 0) { if (Rast_read_colors(parm.rastin->answer, "", &colors) < 0) G_fatal_error(_("Unable to read color table for %s"), parm.rastin->answer); Rast_mark_colors_as_fp(&colors); Rast_write_colors(parm.rastout->answer, G_mapset(), &colors); } return (EXIT_SUCCESS); }