diff --git a/.github/workflows/gcc.yml b/.github/workflows/gcc.yml index 46a1b7a1baa..033cb796653 100644 --- a/.github/workflows/gcc.yml +++ b/.github/workflows/gcc.yml @@ -48,7 +48,7 @@ jobs: - name: Build env: # TODO: -pedantic-errors here won't go through ./configure (with GNU C) - CFLAGS: "-std=${{ matrix.c }} -fPIC -Wall -Wno-error=maybe-uninitialized" + CFLAGS: "-std=${{ matrix.c }} -fPIC -Wall" # TODO: -pedantic-errors here won't compile CXXFLAGS: "-std=${{ matrix.cpp }} -fPIC -Wall -Wno-error=class-memaccess" run: .github/workflows/build_ubuntu-22.04.sh $HOME/install -Werror diff --git a/gui/wxpython/gui_core/goutput.py b/gui/wxpython/gui_core/goutput.py index 15e1eb737e2..c67659f31f9 100644 --- a/gui/wxpython/gui_core/goutput.py +++ b/gui/wxpython/gui_core/goutput.py @@ -43,7 +43,7 @@ ) from core.globalvar import CheckWxVersion, wxPythonPhoenix from gui_core.prompt import GPromptSTC -from gui_core.wrap import Button, ClearButton, ToggleButton, StaticText, StaticBox +from gui_core.wrap import Button, ClearButton, StaticText, StaticBox from core.settings import UserSettings @@ -154,29 +154,21 @@ def __init__( self.btnOutputSave.SetToolTip(_("Save output window content to the file")) self.btnCmdAbort = Button(parent=self.panelProgress, id=wx.ID_STOP) self.btnCmdAbort.SetToolTip(_("Abort running command")) - self.btnCmdProtocol = ToggleButton( - parent=self.panelOutput, - id=wx.ID_ANY, - label=_("&Log file"), - size=self.btnCmdClear.GetSize(), - ) - self.btnCmdProtocol.SetToolTip( - _( - "Toggle to save list of executed commands into " - "a file; content saved when switching off." - ) + self.btnCmdExportHistory = Button(parent=self.panelOutput, id=wx.ID_ANY) + self.btnCmdExportHistory.SetLabel(_("&Export history")) + self.btnCmdExportHistory.SetToolTip( + _("Export history of executed commands to a file") ) - self.cmdFileProtocol = None if not self._gcstyle & GC_PROMPT: self.btnCmdClear.Hide() - self.btnCmdProtocol.Hide() + self.btnCmdExportHistory.Hide() self.btnCmdClear.Bind(wx.EVT_BUTTON, self.cmdPrompt.OnCmdErase) self.btnOutputClear.Bind(wx.EVT_BUTTON, self.OnOutputClear) self.btnOutputSave.Bind(wx.EVT_BUTTON, self.OnOutputSave) self.btnCmdAbort.Bind(wx.EVT_BUTTON, self._gconsole.OnCmdAbort) - self.btnCmdProtocol.Bind(wx.EVT_TOGGLEBUTTON, self.OnCmdProtocol) + self.btnCmdExportHistory.Bind(wx.EVT_BUTTON, self.OnCmdExportHistory) self._layout() @@ -234,7 +226,7 @@ def _layout(self): ) cmdBtnSizer.Add( - self.btnCmdProtocol, + self.btnCmdExportHistory, proportion=1, flag=wx.ALIGN_CENTER | wx.ALIGN_CENTER_VERTICAL @@ -473,54 +465,27 @@ def OnCmdProgress(self, event): self.progressbar.SetValue(event.value) event.Skip() - def CmdProtocolSave(self): - """Save list of manually entered commands into a text log file""" - if self.cmdFileProtocol is None: - return # it should not happen - - try: - with open(self.cmdFileProtocol, "a") as output: - cmds = self.cmdPrompt.GetCommands() - output.write("\n".join(cmds)) - if len(cmds) > 0: - output.write("\n") - except IOError as e: - GError( - _("Unable to write file '{filePath}'.\n\nDetails: {error}").format( - filePath=self.cmdFileProtocol, error=e - ) - ) - - self.showNotification.emit( - message=_("Command log saved to '{}'".format(self.cmdFileProtocol)) + def OnCmdExportHistory(self, event): + """Export the history of executed commands stored + in a .wxgui_history file to a selected file.""" + dlg = wx.FileDialog( + self, + message=_("Save file as..."), + defaultFile="grass_cmd_log.txt", + wildcard=_("{txt} (*.txt)|*.txt|{files} (*)|*").format( + txt=_("Text files"), files=_("Files") + ), + style=wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT, ) - self.cmdFileProtocol = None - - def OnCmdProtocol(self, event=None): - """Save commands into file""" - if not event.IsChecked(): - # stop capturing commands, save list of commands to the - # protocol file - self.CmdProtocolSave() - else: - # start capturing commands - self.cmdPrompt.ClearCommands() - # ask for the file - dlg = wx.FileDialog( - self, - message=_("Save file as..."), - defaultFile="grass_cmd_log.txt", - wildcard=_("%(txt)s (*.txt)|*.txt|%(files)s (*)|*") - % {"txt": _("Text files"), "files": _("Files")}, - style=wx.FD_SAVE, - ) - if dlg.ShowModal() == wx.ID_OK: - self.cmdFileProtocol = dlg.GetPath() - else: - wx.CallAfter(self.btnCmdProtocol.SetValue, False) - dlg.Destroy() + if dlg.ShowModal() == wx.ID_OK: + path = dlg.GetPath() + if self.cmdPrompt.CopyHistory(path): + self.showNotification.emit( + message=_("Command history saved to '{}'".format(path)) + ) + dlg.Destroy() event.Skip() def OnCmdRun(self, event): diff --git a/gui/wxpython/gui_core/prompt.py b/gui/wxpython/gui_core/prompt.py index 4b12bd3af64..eef1d2e001b 100644 --- a/gui/wxpython/gui_core/prompt.py +++ b/gui/wxpython/gui_core/prompt.py @@ -22,6 +22,7 @@ import difflib import codecs import sys +import shutil import wx import wx.stc @@ -33,7 +34,7 @@ from core import globalvar from core import utils -from core.gcmd import EncodeString, DecodeString +from core.gcmd import EncodeString, DecodeString, GError class GPrompt(object): @@ -139,6 +140,27 @@ def _runCmd(self, cmdString): self.OnCmdErase(None) self.ShowStatusText("") + def CopyHistory(self, targetFile): + """Copy history file to the target location. + Returns True if file is successfully copied.""" + env = grass.gisenv() + historyFile = os.path.join( + env["GISDBASE"], + env["LOCATION_NAME"], + env["MAPSET"], + ".wxgui_history", + ) + try: + shutil.copyfile(historyFile, targetFile) + except (IOError, OSError) as e: + GError( + _("Unable to copy file {} to {}'.\n\nDetails: {}").format( + historyFile, targetFile, e + ) + ) + return False + return True + def GetCommands(self): """Get list of launched commands""" return self.commands diff --git a/gui/wxpython/lmgr/frame.py b/gui/wxpython/lmgr/frame.py index 6196e466f1f..abde333d8fa 100644 --- a/gui/wxpython/lmgr/frame.py +++ b/gui/wxpython/lmgr/frame.py @@ -2257,10 +2257,6 @@ def OnCloseWindowOrExit(self, event): def _closeWindow(self, event): """Close wxGUI""" - # save command protocol if actived - if self.goutput.btnCmdProtocol.GetValue(): - self.goutput.CmdProtocolSave() - if not self.currentPage: self._auimgr.UnInit() self.Destroy() diff --git a/gui/wxpython/main_window/frame.py b/gui/wxpython/main_window/frame.py index 00f8a4dd0a4..bb9c7c41eb2 100644 --- a/gui/wxpython/main_window/frame.py +++ b/gui/wxpython/main_window/frame.py @@ -2296,10 +2296,6 @@ def OnCloseWindowOrExit(self, event): def _closeWindow(self, event): """Close wxGUI""" - # save command protocol if actived - if self.goutput.btnCmdProtocol.GetValue(): - self.goutput.CmdProtocolSave() - if not self.currentPage: self._auimgr.UnInit() self.Destroy() diff --git a/imagery/i.ortho.photo/lib/orthophoto.h b/imagery/i.ortho.photo/lib/orthophoto.h index 8dc21474876..d5dc65a8add 100644 --- a/imagery/i.ortho.photo/lib/orthophoto.h +++ b/imagery/i.ortho.photo/lib/orthophoto.h @@ -117,8 +117,8 @@ int I_put_con_points(char *, struct Ortho_Control_Points *); int I_convert_con_points(char *, struct Ortho_Control_Points *, struct Ortho_Control_Points *, double[3], double[3]); /* georef.c */ -int I_compute_ref_equations(struct Ortho_Photo_Points *, double *, double *, - double *, double *); +int I_compute_ref_equations(struct Ortho_Photo_Points *, double[3], double[3], + double[3], double[3]); /* orthoref.c */ int I_compute_ortho_equations(struct Ortho_Control_Points *, struct Ortho_Camera_File_Ref *, @@ -140,7 +140,7 @@ int I_put_ref_points(char *, struct Ortho_Photo_Points *); /* cam_info.h */ int I_read_cam_info(FILE *, struct Ortho_Camera_File_Ref *); -int I_new_fid_point(struct Ortho_Camera_File_Ref *, char *, double, double); +int I_new_fid_point(struct Ortho_Camera_File_Ref *, char[30], double, double); int I_write_cam_info(FILE *, struct Ortho_Camera_File_Ref *); int I_get_cam_info(char *, struct Ortho_Camera_File_Ref *); int I_put_cam_info(char *, struct Ortho_Camera_File_Ref *); diff --git a/imagery/i.segment/iseg.h b/imagery/i.segment/iseg.h index e66fae657ab..37c2c80b3d6 100644 --- a/imagery/i.segment/iseg.h +++ b/imagery/i.segment/iseg.h @@ -141,7 +141,7 @@ int open_files(struct globals *); /* create_isegs.c */ int create_isegs(struct globals *); -void find_four_neighbors(int, int, int[][2]); +void find_four_neighbors(int, int, int[8][2]); void find_eight_neighbors(int, int, int[8][2]); double calculate_euclidean_similarity(struct ngbr_stats *, struct ngbr_stats *, struct globals *); diff --git a/include/grass/defs/ogsf.h b/include/grass/defs/ogsf.h index a1fa48f672e..67a5b5917c0 100644 --- a/include/grass/defs/ogsf.h +++ b/include/grass/defs/ogsf.h @@ -517,7 +517,7 @@ void gsd_scale(float, float, float); void gsd_translate(float, float, float); void gsd_rot(float, char); void gsd_checkwindow(int *, int *, double *, double *); -int gsd_checkpoint(float *, int *, int *, double *, double *); +int gsd_checkpoint(float[4], int[4], int[4], double[16], double[16]); void gsd_litvert_func(float *, unsigned long, float *); void gsd_litvert_func2(float *, unsigned long, float *); void gsd_vert_func(float *); diff --git a/lib/ogsf/gpd.c b/lib/ogsf/gpd.c index 8e3bafef53c..e55e04a5603 100644 --- a/lib/ogsf/gpd.c +++ b/lib/ogsf/gpd.c @@ -214,7 +214,7 @@ void gpd_obj(geosurf *gs, gvstyle *style, Point3 pt) */ int gpd_2dsite(geosite *gp, geosurf *gs, int do_fast) { - float site[3], konst; + float site[4], konst; int src, check; geopoint *gpt; typbuff *buf; @@ -311,7 +311,7 @@ int gpd_2dsite(geosite *gp, geosurf *gs, int do_fast) */ int gpd_3dsite(geosite *gp, float xo, float yo, int do_fast) { - float site[3], tz; + float site[4], tz; int check; geopoint *gpt; GLdouble modelMatrix[16], projMatrix[16]; diff --git a/lib/ogsf/gs2.c b/lib/ogsf/gs2.c index d05e8ba0204..0e66fecbcd0 100644 --- a/lib/ogsf/gs2.c +++ b/lib/ogsf/gs2.c @@ -3282,7 +3282,7 @@ int GS_get_distance_alongsurf(int hs, float x1, float y1, float x2, float y2, float *dist, int use_exag) { geosurf *gs; - float p1[2], p2[2]; + Point3 p1, p2; gs = gs_get_surf(hs); if (gs == NULL) { diff --git a/raster/r.circle/main.c b/raster/r.circle/main.c index 1558662ef18..4b41a78c7c6 100644 --- a/raster/r.circle/main.c +++ b/raster/r.circle/main.c @@ -23,7 +23,7 @@ #include #include -static double distance(double *, double *, double, double, int); +static double distance(double[2], double[2], double, double, int); #ifndef HUGE_VAL #define HUGE_VAL 1.7976931348623157e+308 diff --git a/raster/r.proj/r.proj.html b/raster/r.proj/r.proj.html index 18c8101ba9d..eed560213b3 100644 --- a/raster/r.proj/r.proj.html +++ b/raster/r.proj/r.proj.html @@ -102,7 +102,7 @@

Design of r.proj

the cell based on a weighted distance average of the 4 surrounding cells in the input map. The method=bicubic method determines the new value of the cell based on a weighted distance average of the 16 -surrounding cells in the input map. The method=lanzcos method +surrounding cells in the input map. The method=lanczos method determines the new value of the cell based on a weighted distance average of the 25 surrounding cells in the input map. Compared to bicubic, lanczos puts a higher weight on cells close to the center and a diff --git a/raster/r.resamp.filter/Makefile b/raster/r.resamp.filter/Makefile index d366e95e6a7..1b110a5d3f3 100644 --- a/raster/r.resamp.filter/Makefile +++ b/raster/r.resamp.filter/Makefile @@ -2,8 +2,9 @@ MODULE_TOPDIR = ../.. PGM = r.resamp.filter -LIBES = $(RASTERLIB) $(GISLIB) $(MATHLIB) +LIBES = $(RASTERLIB) $(GISLIB) $(MATHLIB) $(OMPLIB) DEPENDENCIES = $(RASTERDEP) $(GISDEP) +EXTRA_CFLAGS = $(OMPCFLAGS) include $(MODULE_TOPDIR)/include/Make/Module.make diff --git a/raster/r.resamp.filter/benchmark/benchmark_r_resamp_filter.py b/raster/r.resamp.filter/benchmark/benchmark_r_resamp_filter.py new file mode 100644 index 00000000000..8c7799e74fc --- /dev/null +++ b/raster/r.resamp.filter/benchmark/benchmark_r_resamp_filter.py @@ -0,0 +1,59 @@ +"""Benchmarking of r.resamp.filter + +@author Aaron Saw Min Sern +""" + +from grass.exceptions import CalledModuleError +from grass.pygrass.modules import Module +from subprocess import DEVNULL + +import grass.benchmark as bm + + +def main(): + results = [] + + # Users can add more or modify existing reference maps + benchmark(7071, "r.resamp.filter_50M", results) + benchmark(10000, "r.resamp.filter_100M", results) + benchmark(14142, "r.resamp.filter_200M", results) + benchmark(20000, "r.resamp.filter_400M", results) + + bm.nprocs_plot(results, filename="benchmark.svg") + + +def benchmark(size, label, results): + reference = "r_resamp_filter_reference_map" + output = "benchmark_r_resamp_filter_nprocs" + + generate_map(rows=size, cols=size, fname=reference) + Module("g.region", flags="pa", s=0, n=size, w=0, e=size, res=1) + module = Module( + "r.resamp.filter", + input=reference, + filter=["gauss", "box"], + output=output, + radius=[6, 9], + memory=500, + run_=False, + stdout_=DEVNULL, + overwrite=True, + ) + results.append(bm.benchmark_nprocs(module, label=label, max_nprocs=8, repeat=3)) + Module("g.remove", quiet=True, flags="f", type="raster", name=reference) + Module("g.remove", quiet=True, flags="f", type="raster", name=output) + + +def generate_map(rows, cols, fname): + Module("g.region", flags="pa", s=0, n=rows, w=0, e=cols, res=3) + # Generate using r.random.surface if r.surf.fractal fails + try: + print("Generating reference map using r.surf.fractal...") + Module("r.surf.fractal", output=fname) + except CalledModuleError: + print("r.surf.fractal fails, using r.random.surface instead...") + Module("r.random.surface", output=fname) + + +if __name__ == "__main__": + main() diff --git a/raster/r.resamp.filter/main.c b/raster/r.resamp.filter/main.c index 7ad3f609056..53cae46c1bb 100644 --- a/raster/r.resamp.filter/main.c +++ b/raster/r.resamp.filter/main.c @@ -2,14 +2,18 @@ * * MODULE: r.resamp.filter * AUTHOR(S): Glynn Clements + * Aaron Saw Min Sern (OpenMP parallelization) * PURPOSE: - * COPYRIGHT: (C) 2010 by Glynn Clements and the GRASS Development Team + * COPYRIGHT: (C) 2010-2023 by Glynn Clements and 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. * *****************************************************************************/ +#if defined(_OPENMP) +#include +#endif #include #include @@ -103,7 +107,7 @@ static const struct filter_type menu[] = { {"hann", f_hann, 0}, {"hamming", f_hamming, 0}, {"blackman", f_blackman, 0}, - {NULL}, + {NULL, NULL, 0}, }; static char *build_filter_list(void) @@ -146,16 +150,17 @@ struct filter { #define MAX_FILTERS 8 -static int infile, outfile; +static int *infile, outfile; static struct filter filters[MAX_FILTERS]; static int num_filters; static int nulls; static struct Cell_head dst_w, src_w; static double f_x_radius, f_y_radius; static int row_scale, col_scale; -static DCELL *inbuf; +static DCELL **inbuf; static DCELL *outbuf; -static DCELL **bufs; +static DCELL ***bufs; +static int bufrows; static double *h_weights; static double *v_weights; static int *mapcol0, *mapcol1; @@ -312,60 +317,87 @@ static void v_filter(DCELL *dst, DCELL **src, int row, int rows) static void filter(void) { - int cur_row = 0; - int num_rows = 0; + int written_row = 0; + int computed_row = 0; int row; make_h_weights(); make_v_weights(); - for (row = 0; row < dst_w.rows; row++) { - int row0 = maprow0[row]; - int row1 = maprow1[row]; - int rows = row1 - row0; - int i; + while (written_row < dst_w.rows) { + int range = bufrows; - G_percent(row, dst_w.rows, 2); + if (range > dst_w.rows - written_row) { + range = dst_w.rows - written_row; + } + int start = written_row; + int end = written_row + range; + +#pragma omp parallel private(row) + { + int read_row = 0; + int num_rows = 0; + int t_id = 0; + +#if defined(_OPENMP) + t_id = omp_get_thread_num(); +#endif + +#pragma omp for schedule(static, 1) + for (row = start; row < end; row++) { + int row0 = maprow0[row]; + int row1 = maprow1[row]; + int rows = row1 - row0; + int i; + + G_percent(computed_row, dst_w.rows, 2); + + if (row0 >= read_row && row0 < read_row + num_rows) { + int m = row0 - read_row; + int n = read_row + num_rows - row0; + int i; + + for (i = 0; i < n; i++) { + DCELL *tmp = bufs[t_id][i]; + + bufs[t_id][i] = bufs[t_id][m + i]; + bufs[t_id][m + i] = tmp; + } + + read_row = row0; + num_rows = n; + } + else { + read_row = row0; + num_rows = 0; + } - if (row0 >= cur_row && row0 < cur_row + num_rows) { - int m = row0 - cur_row; - int n = cur_row + num_rows - row0; - int i; + for (i = num_rows; i < rows; i++) { + G_debug(5, "read: %p = %d", bufs[t_id][i], row0 + i); + /* enlarging the source window to the North and South is + * not possible for global maps in ll */ + if (row0 + i >= 0 && row0 + i < src_w.rows) + Rast_get_d_row(infile[t_id], inbuf[t_id], row0 + i); + else + Rast_set_d_null_value(inbuf[t_id], src_w.cols); + h_filter(bufs[t_id][i], inbuf[t_id]); + } - for (i = 0; i < n; i++) { - DCELL *tmp = bufs[i]; + num_rows = rows; - bufs[i] = bufs[m + i]; - bufs[m + i] = tmp; + v_filter(&outbuf[(row - start) * dst_w.cols], bufs[t_id], row, + rows); +#pragma omp atomic update + computed_row++; } - - cur_row = row0; - num_rows = n; - } - else { - cur_row = row0; - num_rows = 0; } - for (i = num_rows; i < rows; i++) { - G_debug(5, "read: %p = %d", bufs[i], row0 + i); - /* enlarging the source window to the North and South is - * not possible for global maps in ll */ - if (row0 + i >= 0 && row0 + i < src_w.rows) - Rast_get_d_row(infile, inbuf, row0 + i); - else - Rast_set_d_null_value(inbuf, src_w.cols); - h_filter(bufs[i], inbuf); + for (row = start; row < end; row++) { + Rast_put_d_row(outfile, &outbuf[(row - start) * dst_w.cols]); + G_debug(5, "write: %d", row); } - - num_rows = rows; - - v_filter(outbuf, bufs, row, rows); - - Rast_put_d_row(outfile, outbuf); - G_debug(5, "write: %d", row); + written_row = end; } - G_percent(dst_w.rows, dst_w.rows, 2); } @@ -373,13 +405,15 @@ int main(int argc, char *argv[]) { struct GModule *module; struct { - struct Option *rastin, *rastout, *method, *radius, *x_radius, *y_radius; + struct Option *rastin, *rastout, *method, *radius, *x_radius, *y_radius, + *memory, *nprocs; } parm; struct { struct Flag *nulls; } flag; char title[64]; - int i; + int i, t; + int nprocs; G_gisinit(argv[0]); @@ -399,6 +433,7 @@ int main(int argc, char *argv[]) G_add_keyword(_("hermite")); G_add_keyword(_("lanczos")); G_add_keyword(_("sinc")); + G_add_keyword(_("parallel")); module->description = _("Resamples raster map layers using an analytic kernel."); @@ -436,6 +471,9 @@ int main(int argc, char *argv[]) parm.y_radius->multiple = YES; parm.y_radius->description = _("Filter radius (vertical)"); + parm.memory = G_define_standard_option(G_OPT_MEMORYMB); + parm.nprocs = G_define_standard_option(G_OPT_M_NPROCS); + flag.nulls = G_define_flag(); flag.nulls->key = 'n'; flag.nulls->description = _("Propagate NULLs"); @@ -443,6 +481,19 @@ int main(int argc, char *argv[]) if (G_parser(argc, argv)) exit(EXIT_FAILURE); + sscanf(parm.nprocs->answer, "%d", &nprocs); + if (nprocs < 1) { + G_fatal_error(_("<%d> is not valid number of threads."), nprocs); + } +#if defined(_OPENMP) + omp_set_num_threads(nprocs); +#else + if (nprocs != 1) + G_warning(_("GRASS is compiled without OpenMP support. Ignoring " + "threads setting.")); + nprocs = 1; +#endif + if (parm.radius->answer) { if (parm.x_radius->answer || parm.y_radius->answer) G_fatal_error(_("%s= and %s=/%s= are mutually exclusive"), @@ -561,22 +612,36 @@ int main(int argc, char *argv[]) col_scale = 2 + 2 * ceil(f_x_radius / src_w.ew_res); /* allocate buffers for intermediate rows */ - bufs = G_malloc(row_scale * sizeof(DCELL *)); - for (i = 0; i < row_scale; i++) - bufs[i] = Rast_allocate_d_buf(); + bufs = G_malloc(nprocs * sizeof(DCELL **)); + for (t = 0; t < nprocs; t++) { + bufs[t] = G_malloc(row_scale * sizeof(DCELL *)); + for (i = 0; i < row_scale; i++) + bufs[t][i] = Rast_allocate_d_buf(); + } Rast_set_input_window(&src_w); Rast_set_output_window(&dst_w); - inbuf = Rast_allocate_d_input_buf(); - outbuf = Rast_allocate_d_output_buf(); + bufrows = + atoi(parm.memory->answer) * (((1 << 20) / sizeof(DCELL)) / dst_w.cols); + if (bufrows > dst_w.rows) { + bufrows = dst_w.rows; + } + + inbuf = G_malloc(nprocs * sizeof(DCELL *)); + for (t = 0; t < nprocs; t++) + inbuf[t] = Rast_allocate_d_input_buf(); + outbuf = G_calloc((size_t)bufrows * dst_w.cols, sizeof(DCELL)); - infile = Rast_open_old(parm.rastin->answer, ""); + infile = G_malloc(nprocs * sizeof(int)); + for (t = 0; t < nprocs; t++) + infile[t] = Rast_open_old(parm.rastin->answer, ""); outfile = Rast_open_new(parm.rastout->answer, DCELL_TYPE); filter(); - Rast_close(infile); + for (t = 0; t < nprocs; t++) + Rast_close(infile[t]); Rast_close(outfile); /* record map metadata/history info */ diff --git a/raster/r.resamp.filter/r.resamp.filter.html b/raster/r.resamp.filter/r.resamp.filter.html index 164077b6298..4a0e3562cbc 100644 --- a/raster/r.resamp.filter/r.resamp.filter.html +++ b/raster/r.resamp.filter/r.resamp.filter.html @@ -75,6 +75,21 @@

NOTES

attempt to implement the latter would violate the integrity of the interpolation method. +

PERFORMANCE

+

By specifying the number of parallel processes with nprocs option, +r.resamp.filter can run faster, see benchmarks below. + +

+ benchmark for number of cells +
+ Figure: Benchmark shows execution time for different + number of cells. See benchmark script in the source code. +
+

To reduce the memory requirements to minimum, set option memory to zero. +To take advantage of the parallelization, GRASS GIS +needs to compiled with OpenMP enabled. + +

SEE ALSO

diff --git a/raster/r.resamp.filter/r_resamp_filter_benchmark_size.png b/raster/r.resamp.filter/r_resamp_filter_benchmark_size.png new file mode 100644 index 00000000000..db834ce672c Binary files /dev/null and b/raster/r.resamp.filter/r_resamp_filter_benchmark_size.png differ diff --git a/raster/r.resamp.filter/testsuite/test_r_resamp_filter.py b/raster/r.resamp.filter/testsuite/test_r_resamp_filter.py new file mode 100644 index 00000000000..80b84f64e3f --- /dev/null +++ b/raster/r.resamp.filter/testsuite/test_r_resamp_filter.py @@ -0,0 +1,289 @@ +from grass.gunittest.case import TestCase +from grass.gunittest.main import test + + +class TestResampFilter(TestCase): + """ + + Used dataset: nc_spm_08_grass7 + + Test cases: + test_finite_options: Test output with finite kernels only. + test_infinite_box_options: Test output with infinite kernels + in conjunction with the box kernel. + """ + + test_options = { + "test_finite_options": { + "box": { + "n": 5625000, + "null_cells": 0, + "cells": 5625000, + "min": 55.6378765106201, + "max": 156.305896759033, + "range": 100.668020248413, + "mean": 110.375439282307, + "mean_of_abs": 110.375439282307, + "stddev": 20.3084660266678, + "variance": 412.433792356322, + "coeff_var": 18.3994429908677, + "sum": 620861845.962976, + }, + "bartlett": { + "n": 5625000, + "null_cells": 0, + "cells": 5625000, + "min": 55.6260597229004, + "max": 156.316369018555, + "range": 100.690309295654, + "mean": 110.375440204059, + "mean_of_abs": 110.375440204059, + "stddev": 20.3119057626464, + "variance": 412.573515710628, + "coeff_var": 18.4025592333715, + "sum": 620861851.14783, + }, + "hermite": { + "n": 5625000, + "null_cells": 0, + "cells": 5625000, + "min": 55.6203876647949, + "max": 156.322196495117, + "range": 100.701808830322, + "mean": 110.375440282578, + "mean_of_abs": 110.375440282578, + "stddev": 20.3125721916948, + "variance": 412.600589042813, + "coeff_var": 18.403163004099, + "sum": 620861851.589502, + }, + "lanczos1": { + "n": 5625000, + "null_cells": 0, + "cells": 5625000, + "min": 55.6023817623363, + "max": 156.325354289019, + "range": 100.722972526682, + "mean": 110.375440231786, + "mean_of_abs": 110.375440231786, + "stddev": 20.313022054599, + "variance": 412.618864990624, + "coeff_var": 18.4035705877522, + "sum": 620861851.303796, + }, + "lanczos2": { + "n": 5625000, + "null_cells": 0, + "cells": 5625000, + "min": 55.4287505083574, + "max": 156.36299154775, + "range": 100.934241039393, + "mean": 110.375435152297, + "mean_of_abs": 110.375435152297, + "stddev": 20.3151009676525, + "variance": 412.703327325917, + "coeff_var": 18.405454927196, + "sum": 620861822.731673, + }, + "lanczos3": { + "n": 5625000, + "null_cells": 0, + "cells": 5625000, + "min": 55.4381170630086, + "max": 156.356150645782, + "range": 100.918033582773, + "mean": 110.375439667324, + "mean_of_abs": 110.375439667324, + "stddev": 20.3154206838383, + "variance": 412.716317561325, + "coeff_var": 18.4057438367356, + "sum": 620861848.128697, + }, + }, + "test_infinite_box_options": { + "gauss": { + "n": 5625000, + "null_cells": 0, + "cells": 5625000, + "min": 55.6262147993191, + "max": 156.314719743192, + "range": 100.688504943873, + "mean": 110.375440194786, + "mean_of_abs": 110.375440194786, + "stddev": 20.3108768847555, + "variance": 412.531719827694, + "coeff_var": 18.4016270729355, + "sum": 620861851.095669, + }, + "normal": { + "n": 5625000, + "null_cells": 0, + "cells": 5625000, + "min": 55.6349247730616, + "max": 156.307900724069, + "range": 100.672975951008, + "mean": 110.375439623437, + "mean_of_abs": 110.375439623437, + "stddev": 20.3089516110606, + "variance": 412.4535155404, + "coeff_var": 18.3998828728091, + "sum": 620861847.881836, + }, + "sinc": { + "n": 5625000, + "null_cells": 0, + "cells": 5625000, + "min": 55.6260597229004, + "max": 156.316369018555, + "range": 100.690309295654, + "mean": 110.375440204059, + "mean_of_abs": 110.375440204059, + "stddev": 20.3119057626423, + "variance": 412.573515710463, + "coeff_var": 18.4025592333678, + "sum": 620861851.14783, + }, + "hann": { + "n": 5625000, + "null_cells": 0, + "cells": 5625000, + "min": 55.6170862149874, + "max": 156.32277090836, + "range": 100.705684693373, + "mean": 110.375440282543, + "mean_of_abs": 110.375440282543, + "stddev": 20.3126467418446, + "variance": 412.603617658969, + "coeff_var": 18.4032305464401, + "sum": 620861851.589302, + }, + "hamming": { + "n": 5625000, + "null_cells": 0, + "cells": 5625000, + "min": 55.6223234496647, + "max": 156.318925911936, + "range": 100.696602462271, + "mean": 110.375440209185, + "mean_of_abs": 110.375440209185, + "stddev": 20.3116372028825, + "variance": 412.562605861522, + "coeff_var": 18.4023159177328, + "sum": 620861851.176668, + }, + "blackman": { + "n": 5625000, + "null_cells": 0, + "cells": 5625000, + "min": 55.5969230376839, + "max": 156.326357899112, + "range": 100.729434861428, + "mean": 110.375440150324, + "mean_of_abs": 110.375440150324, + "stddev": 20.3132139323513, + "variance": 412.626660261469, + "coeff_var": 18.4037444423197, + "sum": 620861850.845575, + }, + }, + } + + # TODO: replace by unified handing of maps + to_remove = [] + + def check_univar(self, test_case): + """Checks multiple output against unviariate reference from dict""" + for method in self.test_options[test_case]: + self.assertRasterFitsUnivar( + raster="{}_raster_{}".format(test_case, method), + reference=self.test_options[test_case][method], + precision=1e-5, + ) + self.assertRasterFitsUnivar( + raster="{}_raster_threaded_{}".format(test_case, method), + reference=self.test_options[test_case][method], + precision=1e-5, + ) + + @classmethod + def setUpClass(cls): + cls.use_temp_region() + # run with resolution of 6 + cls.runModule("g.region", raster="elevation", res=6) + + @classmethod + def tearDownClass(cls): + cls.del_temp_region() + if cls.to_remove: + cls.runModule( + "g.remove", flags="f", type="raster", name=",".join(cls.to_remove) + ) + + def test_finite_options(self): + """Test output with finite kernels only.""" + test_case = "test_finite_options" + outputs = [ + "{}_raster_{}".format(test_case, filter) + for filter in self.test_options[test_case] + ] + outputs_threaded = [ + "{}_raster_threaded_{}".format(test_case, filter) + for filter in self.test_options[test_case] + ] + self.to_remove.extend(outputs) + self.to_remove.extend(outputs_threaded) + + for filter in self.test_options[test_case]: + self.assertModule( + "r.resamp.filter", + input="elevation", + output="{}_raster_{}".format(test_case, filter), + filter=filter, + radius=10, + ) + self.assertModule( + "r.resamp.filter", + input="elevation", + output="{}_raster_threaded_{}".format(test_case, filter), + filter=filter, + radius=10, + nprocs=8, + ) + self.check_univar(test_case) + + def test_infinite_box_options(self): + """Test output with infinite kernels in conjunction + with the box kernel.""" + test_case = "test_infinite_box_options" + outputs = [ + "{}_raster_{}".format(test_case, filter) + for filter in self.test_options[test_case] + ] + outputs_threaded = [ + "{}_raster_threaded_{}".format(test_case, filter) + for filter in self.test_options[test_case] + ] + self.to_remove.extend(outputs) + self.to_remove.extend(outputs_threaded) + + for filter in self.test_options[test_case]: + self.assertModule( + "r.resamp.filter", + input="elevation", + output="{}_raster_{}".format(test_case, filter), + filter=["box", filter], + radius=[10, 10], + ) + self.assertModule( + "r.resamp.filter", + input="elevation", + output="{}_raster_threaded_{}".format(test_case, filter), + filter=["box", filter], + radius=[10, 10], + nprocs=8, + ) + self.check_univar(test_case) + + +if __name__ == "__main__": + test() diff --git a/raster/r.surf.area/local_proto.h b/raster/r.surf.area/local_proto.h index c4dccbf32e0..2650c95a3e8 100644 --- a/raster/r.surf.area/local_proto.h +++ b/raster/r.surf.area/local_proto.h @@ -5,6 +5,6 @@ void add_row_area(DCELL *, DCELL *, double, struct Cell_head *, double *, double *); void add_null_area(DCELL *, struct Cell_head *, double *); -void v3cross(double[], double[], double[]); -void v3mag(double[], double *); +void v3cross(double[3], double[3], double[3]); +void v3mag(double[3], double *); double conv_value(double, int); diff --git a/raster/r.terraflow/ccforest.h b/raster/r.terraflow/ccforest.h index 4ea7a2949da..c32a1a3f7ba 100644 --- a/raster/r.terraflow/ccforest.h +++ b/raster/r.terraflow/ccforest.h @@ -40,12 +40,6 @@ class keyvalue { T src() const { return key; }; T dst() const { return value; }; - keyvalue operator=(const keyvalue &that) - { - key = that.key; - value = that.value; - return *this; - }; int operator!=(const keyvalue &e2) const { return (key != e2.key) || (value != e2.value); @@ -147,7 +141,12 @@ class ccforest { public: ccforest(); + ccforest(const ccforest &) = delete; + ccforest &operator=(const ccforest &) = delete; + ccforest(ccforest &&) = delete; + ccforest &operator=(ccforest &&) = delete; ~ccforest(); + void insert(const T &i, const T &j); /* insert edge (i,j) */ T findNextRoot(const T &i); /* find root where i >= prev i */ void printRootStream(); diff --git a/raster/r.terraflow/genericWindow.h b/raster/r.terraflow/genericWindow.h index fa6a130d5ee..082d4a4e480 100644 --- a/raster/r.terraflow/genericWindow.h +++ b/raster/r.terraflow/genericWindow.h @@ -84,14 +84,6 @@ class genericWindow { } } - /***************************************************************/ - genericWindow(const genericWindow &win) - { - for (int i = 0; i < 9; i++) { - data[i] = win.data[i]; - } - } - /***************************************************************/ /* get specified neighbour di,dj in {-1,0,1} */ T get(short di, short dj) const diff --git a/raster/r.terraflow/sweep.h b/raster/r.terraflow/sweep.h index d3a21e42ece..fafca84b834 100644 --- a/raster/r.terraflow/sweep.h +++ b/raster/r.terraflow/sweep.h @@ -160,27 +160,28 @@ class gridPosition { /*************************************************************/ class flowPriority { public: - elevation_type h; - toporank_type toporank; + elevation_type h{0.0}; + toporank_type toporank{0}; /* points at same heights are processed in increasing order of their topological rank; overall, this gives topological order and guarantees that flow is never puhsed backwards. Note: of course, this is a way of waving hands on topological sorting. */ - dimension_type i, j; + dimension_type i{0}, j{0}; public: - flowPriority(elevation_type a = 0, toporank_type b = 0, - dimension_type c = 0, dimension_type d = 0) - : h(a), toporank(b), i(c), j(d) - { - } - - flowPriority(const flowPriority &p) - : h(p.h), toporank(p.toporank), i(p.i), j(p.j) + flowPriority() {} + flowPriority(elevation_type a) : h{a} {} + flowPriority(elevation_type a, toporank_type b, dimension_type c, + dimension_type d) + : h{a}, toporank{b}, i{c}, j{d} { } - ~flowPriority() {} + flowPriority(const flowPriority &) = default; + flowPriority &operator=(const flowPriority &) = default; + flowPriority(flowPriority &&) = default; + flowPriority &operator=(flowPriority &&) = default; + ~flowPriority() = default; elevation_type field1() const { return h; } @@ -312,9 +313,6 @@ class sweepItemBaseType { { } - /***************************************************************/ - ~sweepItemBaseType() {} - /***************************************************************/ /* return the elevation window */ genericWindow getElevWindow() const { return elevwin; } @@ -413,12 +411,11 @@ class PrioCmpSweepItem { /************************************************************/ class flowValue { public: - flowaccumulation_type value; + flowaccumulation_type value{0}; public: - flowValue(flowaccumulation_type x = 0) : value(x) {} - - ~flowValue() {} + flowValue() {} + flowValue(flowaccumulation_type x) : value{x} {} flowaccumulation_type get() const { return value; } friend ostream &operator<<(ostream &s, const flowValue &elt) @@ -434,11 +431,6 @@ class flowValue { flowValue elt(elt1.value + elt2.value); return elt; } - flowValue operator=(const flowValue &elt) - { - value = elt.value; - return *this; - } flowValue operator!=(const flowValue &elt) { return value != elt.value; } flowValue operator==(const flowValue &elt) { return value == elt.value; } @@ -463,22 +455,15 @@ class flowValue { /************************************************************/ class flowStructure { private: - flowPriority prio; - flowValue val; + flowPriority prio{}; + flowValue val{}; public: - flowStructure(const flowPriority &p = 0, const flowValue &e = 0) - : prio(p), val(e) + flowStructure() {} + flowStructure(const flowPriority &p, const flowValue &e) : prio{p}, val{e} { } - /* flowStructure(const flowValue &e, const flowPriority &p): - prio(p), val(e) {} - */ - flowStructure(const flowStructure &fl) : prio(fl.prio), val(fl.val) {} - - ~flowStructure() {} - flowPriority getPriority() const { return prio; } flowValue getValue() const { return val; } diff --git a/raster/r.viewshed/grass.cpp b/raster/r.viewshed/grass.cpp index 5b23c2a9227..d27b1431873 100644 --- a/raster/r.viewshed/grass.cpp +++ b/raster/r.viewshed/grass.cpp @@ -210,6 +210,8 @@ size_t init_event_list_in_memory(AEvent *eventList, char *rastName, G_SURFACE_T **inrast; int nrows = Rast_window_rows(); int ncols = Rast_window_cols(); + if (nrows > maxDimension || ncols > maxDimension) + G_fatal_error(_("Grid size exceeds max dimension: %d"), maxDimension); inrast = (G_SURFACE_T **)G_malloc(3 * sizeof(G_SURFACE_T *)); assert(inrast); @@ -238,7 +240,7 @@ size_t init_event_list_in_memory(AEvent *eventList, char *rastName, Rast_get_row(infd, inrast[2], 0, data_type); e.angle = -1; - for (i = 0; i < nrows; i++) { + for (i = 0; i < (dimensionType)nrows; i++) { /*read in the raster row */ G_SURFACE_T *tmprast = inrast[0]; @@ -254,7 +256,7 @@ size_t init_event_list_in_memory(AEvent *eventList, char *rastName, G_percent(i, nrows, 2); /*fill event list with events from this row */ - for (j = 0; j < Rast_window_cols(); j++) { + for (j = 0; j < (dimensionType)ncols; j++) { e.row = i; e.col = j; @@ -431,6 +433,8 @@ AMI_STREAM *init_event_list(char *rastName, Viewpoint *vp, G_SURFACE_T **inrast; int nrows = Rast_window_rows(); int ncols = Rast_window_cols(); + if (nrows > maxDimension || ncols > maxDimension) + G_fatal_error(_("Grid size exceeds max dimension: %d"), maxDimension); inrast = (G_SURFACE_T **)G_malloc(3 * sizeof(G_SURFACE_T *)); assert(inrast); @@ -456,7 +460,7 @@ AMI_STREAM *init_event_list(char *rastName, Viewpoint *vp, e.angle = -1; /*start scanning through the grid */ - for (i = 0; i < nrows; i++) { + for (i = 0; i < (dimensionType)nrows; i++) { G_percent(i, nrows, 2); @@ -473,7 +477,7 @@ AMI_STREAM *init_event_list(char *rastName, Viewpoint *vp, Rast_set_null_value(inrast[2], ncols, data_type); /*fill event list with events from this row */ - for (j = 0; j < ncols; j++) { + for (j = 0; j < (dimensionType)ncols; j++) { e.row = i; e.col = j; @@ -627,11 +631,16 @@ void save_grid_to_GRASS(Grid *grid, char *filename, RASTER_MAP_TYPE type, outrast = Rast_allocate_buf(type); assert(outrast); + int nrows = Rast_window_rows(); + int ncols = Rast_window_cols(); + if (nrows > maxDimension || ncols > maxDimension) + G_fatal_error(_("Grid size exceeds max dimension: %d"), maxDimension); + dimensionType i, j; - for (i = 0; i < Rast_window_rows(); i++) { - G_percent(i, Rast_window_rows(), 5); - for (j = 0; j < Rast_window_cols(); j++) { + for (i = 0; i < (dimensionType)nrows; i++) { + G_percent(i, nrows, 5); + for (j = 0; j < (dimensionType)ncols; j++) { if (is_invisible_nodata(grid->grid_data[i][j])) { writeNodataValue(outrast, j, type); } @@ -705,14 +714,19 @@ void save_vis_elev_to_GRASS(Grid *visgrid, char *elevfname, char *visfname, visrast = Rast_allocate_buf(elev_data_type); assert(visrast); + int nrows = Rast_window_rows(); + int ncols = Rast_window_cols(); + if (nrows > maxDimension || ncols > maxDimension) + G_fatal_error(_("Grid size exceeds max dimension: %d"), maxDimension); + dimensionType i, j; double elev = 0, viewshed_value; - for (i = 0; i < Rast_window_rows(); i++) { + for (i = 0; i < (dimensionType)nrows; i++) { /* get the row from elevation */ Rast_get_row(elevfd, elevrast, i, elev_data_type); - for (j = 0; j < Rast_window_cols(); j++) { + for (j = 0; j < (dimensionType)ncols; j++) { /* read the current elevation value */ int isNull = 0; @@ -840,10 +854,15 @@ void save_io_visibilitygrid_to_GRASS(IOVisibilityGrid *visgrid, char *fname, counter++; } + int nrows = Rast_window_rows(); + int ncols = Rast_window_cols(); + if (nrows > maxDimension || ncols > maxDimension) + G_fatal_error(_("Grid size exceeds max dimension: %d"), maxDimension); + dimensionType i, j; - for (i = 0; i < Rast_window_rows(); i++) { - for (j = 0; j < Rast_window_cols(); j++) { + for (i = 0; i < (dimensionType)nrows; i++) { + for (j = 0; j < (dimensionType)ncols; j++) { if (curResult->row == i && curResult->col == j) { /*cell is recodred in the visibility stream: it must be @@ -940,8 +959,12 @@ void save_io_vis_and_elev_to_GRASS(IOVisibilityGrid *visgrid, char *elevfname, dimensionType i, j; double elev = 0; + int nrows = Rast_window_rows(); + int ncols = Rast_window_cols(); + if (nrows > maxDimension || ncols > maxDimension) + G_fatal_error(_("Grid size exceeds max dimension: %d"), maxDimension); - for (i = 0; i < Rast_window_rows(); i++) { + for (i = 0; i < (dimensionType)nrows; i++) { Rast_get_row(elevfd, elevrast, i, elev_data_type); diff --git a/vector/v.to.rast/do_lines.c b/vector/v.to.rast/do_lines.c index af090a6f73b..0a1d402ca2f 100644 --- a/vector/v.to.rast/do_lines.c +++ b/vector/v.to.rast/do_lines.c @@ -8,7 +8,7 @@ /* function prototypes */ static int plot_line(double *, double *, int, int, int); static int plot_points(double *, double *, int); -static double v2angle(double *, double *, double, double); +static double v2angle(double[2], double[2], double, double); static double deg_angle(double, double, double, double); int do_lines(struct Map_info *Map, struct line_pnts *Points,