From: Glynn Clements Date: Tue, 5 Mar 2002 16:44:33 +0000 To: Markus Neteler Subject: Re: Question on raster programming Markus Neteler wrote: > may I ask you for some assistance for a simple example on > GRASS raster programming? > I would like to have r.example as reference for raster > programming. The module will do nothing exciting but show > how to deal with the different precisions. > > the line in question is the inner col-loop, where I > copy the cell values from the old to the new map. > It does not compile due to the variable definition. > > In old modules I have seen ugly things to deal with > INT, FCELL and DCELL (lots of cases etc). Probably it > is much more simple using the > > void *inrast; > > definition on top. But then, I don't understand to access > it... Yes, programming newbie question. Perhaps you have the > patience to help me. I have several older modules in the queue > which I would like to release after an update. Before hacking > in lots of case statements, I prefer to learn the up-to-date > method. If you want to handle multiple data types, you generally[1] have to use a switch statement. [1] If you are only copying values, you can use other methods (e.g. memcpy), but if you wish to actually process the cell values, they need to be stored in a variable of the appropriate type. Suppose you wanted the equivalent of: r.mapcalc 'out = f(in)' The main loop would look something like: extern CELL f_c(CELL); extern FCELL f_f(FCELL); extern DCELL f_d(DCELL); ... for (col=0; col < ncols; col++) { CELL c; FCELL f; DCELL d; switch (data_type) { case CELL_TYPE: c = ((CELL *) inrast)[col]; c = f_c(c); ((CELL *) outrast)[col] = c; break; case FCELL_TYPE: f = ((FCELL *) inrast)[col]; f = f_f(f); ((FCELL *) outrast)[col] = f; break; case DCELL_TYPE: d = ((DCELL *) inrast)[col]; d = f_d(d); ((DCELL *) outrast)[col] = d; break; } } In the most extreme case, you might have nested switch statements to deal with all of the different combinations of input/output types. If it isn't important that the operation is done at the map's native precision, a simpler approach is to just use DCELL values and let the G_{get,put}_*_row functions handle the conversion, e.g. DCELL f_d(DCELL x) { /* some function */ return x; } extern DCELL f_d(DCELL); ... DCELL *inrast = G_allocate_d_raster_buf(); DCELL *outrast = G_allocate_d_raster_buf(); for (row = 0; row < nrows; row++) { if (G_get_d_raster_row (infd, inrast, row) < 0) G_fatal_error(...); for (col=0; col < ncols; col++) outrast[col] = f_d(inrast[col]); if (G_put_d_raster_row (outfd, outrast) < 0) G_fatal_error(...); } One other comment about the example: it isn't necessary to use sprintf() to format error messages; G_fatal_error() takes a format string and arguments, e.g. G_fatal_error("cell file [%s] not found", name); -- Glynn Clements From: Glynn Clements Date: Thu, 7 Mar 2002 16:51:43 +0000 To: Markus Neteler Subject: Re: Question on raster programming Markus Neteler wrote: > What do you think about the macro method implemented in > r.sunmask (src/raster/r.sunmask/)? Is that a good > or possibly slowing down the module for FCELL and DCELL > (in fact it's incredible slow, just for INT DEMs it is acceptable). In the case of r.sunmask, the raster_value() macro/function is pointless. The data is always converted to DCELL before being used; the code should just allocate DCELL buffers and read the data with G_get_d_raster_row(). As for speed, I wouldn't have thought that type-handling logic would be significant compared to the amount of work that's done in the various G_get_*_row() functions. Most code should probably just work with DCELL values, and let libgis perform the conversions, unless the code is only applicable to CELL values, or it handles CELL values differently. -- Glynn Clements Markus Neteler wrote: > sorry to bother you again. I am now continuing to write > r.example (which just copies a file) to have an example > on raster programming. Maybe you recall our conversation on > that some time ago (find it attached as well). > > A remaining question is how to define this: > > extern CELL f_c(CELL); > extern FCELL f_f(FCELL); > extern DCELL f_d(DCELL); > > f_c, f_f, f_d > > Sorry for this question, but I am clueless here. For copying a file, these would just be identity functions, e.g. CELL f_c(CELL x) { return x; } Actually, there aren't many things which you could do within that framework. For most practical applications, a cell in the output map would depend upon more than just the corresponding cell in a single input map. You would typically have multiple input maps, and/or use the values of multiple cells in computing the value of each output cell. Some other points: 1. Copying input parameters into a buffer, i.e.: strcpy (name, option.input->answer); strcpy (result, option.output->answer); is usually unnecessary. Furthermore, it's something which should be discouraged, due to the usual problems with fixed-size buffers. 2. Pre-formatting error/warning messages, e.g.: if (mapset == NULL) { char buf[200]; sprintf (buf, "cell file [%s] not found", name); G_fatal_error (buf); } is unnecessary, as G_fatal_error() and G_warning() already do this, e.g. if (!mapset) G_fatal_error("cell file [%s] not found", name); 3. Ideally, all abnormal exits would be via G_fatal_error(), rather than calling exit() directly. 4. The value passed to exit() should be non-negative. exit(-1) is just a confusing way of writing exit(255). -- Glynn Clements