/* * select.c: neighborhood selection for local prediction */ #include #include /* qsort() */ #include /* memcpy() */ #include /* sqrt(), fabs() */ #include "defs.h" void Rprintf(const char *,...); #include "userio.h" #include "data.h" #include "utils.h" #include "debug.h" #include "vario.h" #include "defaults.h" #include "glvars.h" #include "nsearch.h" #include "select.h" static int octant_select(DATA *d, DPOINT *where); static int which_octant(DPOINT *where, DPOINT *p, int mode); int CDECL dist_cmp(const DPOINT **ap, const DPOINT **bp); static void print_selection(DATA *d, DPOINT *where); /* beware-of-side-effect macro: don't call with ++/--'s */ #define DPSWAP(a,b) { if (a != b) { tmp = a; a = b; b = tmp; }} static int select_qtree(DATA *d, DPOINT *where) { if (d->n_list <= 0 || d->id < 0 || d->sel_max == 0) return (d->n_sel = 0); /* * global neighbourhood? */ if (IS_GLOBAL(d) || where == NULL) { d->sel = d->list; d->n_sel = d->n_sel_max = d->n_list; if (DEBUG_SEL) { print_selection(d, where); } return d->n_sel; } /* set up or resize selection list, d->sel */ if (d->sel == NULL || d->sel == d->list) { /* first time or after forced global selection: */ d->sel = (DPOINT **) emalloc(d->n_list * sizeof(DPOINT *)); d->n_sel_max = d->n_list; } else if (d->n_list > d->n_sel_max) { /* no, something has changed... */ d->n_sel_max += MAX(MAX_DATA, d->n_list - d->n_sel_max); d->sel = (DPOINT **) erealloc(d->sel, d->n_sel_max * sizeof(DPOINT *)); } if (d->id > 0) { /* 2nd, 3d,..., n_vars-th variable */ if (gl_coincide == DEF_coincide) gl_coincide = decide_on_coincide(); /* establish first ... */ if (gl_coincide) { int i; DATA **data = get_gstat_data(); d->n_sel = data[0]->n_sel; for (i = 0; i < d->n_sel; i++) /* copy previous selection: */ d->sel[i] = d->list[GET_INDEX(data[0]->sel[i])]; if (DEBUG_SEL) print_selection(d, where); return d->n_sel; /* and we're done!! */ } } /* * So far, no selection has been done. * Let's see if today's job is easily done: */ memcpy(d->sel, d->list, d->n_list * sizeof(DPOINT *)); if (d->sel_rad >= DBL_MAX && d->sel_max >= d->n_list && d->oct_max == 0) { d->n_sel = d->n_list; if (DEBUG_SEL) print_selection(d, where); return d->n_sel; } /* * now we're sure that (a) sel_rad is set, or (b) sel_max < n_list, * or (c) oct_max is set, so let's do the smart thing: */ qtree_select(where, d); return -1; /* more work to do */ } int select_at(DATA *d, DPOINT *where) { /* * fill the array d->sel appropriatly given x,y,z,d->sel_min,d->sel_max * and d->sel_rad: possibly by corresponding semivariance value * first select all points within a distance d->sel_rad from where * then select at max the d->sel_max nearest and return no selection * if there are less then d->sel_min * if "FORCE", then select ALWAYS the d->sel_min nearest points. * * corrected variogram distance feb. 16th 1993 * changed search to normalizing to (0,0,0) first, aug 1993 */ if (d->what_is_u == U_UNKNOWN) d->what_is_u = U_ISDIST; /* we're going to fill this right now */ else if (d->what_is_u != U_ISDIST) ErrMsg(ER_IMPOSVAL, "select_at() needs distances"); if (select_qtree(d,where) != -1) return d->n_sel; /* * so, now we're at the stage where one of the following conditions holds: * (a) we selected all points within d->sel_rad * (b) we selected (at least) the nearest d->sel_max * (c) we selected (forced) at least d->sel_min, possibly beyond d->sel_rad * Now, should we select further, sorting on distance? * */ if (d->vdist) { /* use variogram distance as sort criterium */ int i; for (i = 0; i < d->n_sel; i++) d->sel[i]->u.dist2 = get_semivariance(get_vgm(LTI(d->id,d->id)), where->x - d->sel[i]->x, where->y - d->sel[i]->y, where->z - d->sel[i]->z); } if (d->oct_max) { /* do octant selection */ d->oct_filled = octant_select(d, where); /* sorts, adjusts n_sel and destroys distance order, so only */ if (get_method() == SPREAD) /* then we've got to re-order them */ qsort(d->sel, (size_t) d->n_sel, sizeof(DPOINT *), (int CDECL (*)(const void *,const void *)) dist_cmp); } if (d->vdist) { qsort(d->sel, (size_t) d->n_sel, sizeof(DPOINT *), (int CDECL (*)(const void *, const void *)) dist_cmp); /* pick d->sel_[max|min] nearest: */ if (d->sel_min && d->n_sel == d->sel_min && d->sel[d->n_sel]->u.dist2 > d->sel_rad) /* we forced: */ d->n_sel = d->sel_min; if (d->n_sel > d->sel_max) d->n_sel = d->sel_max; } if (DEBUG_SEL) print_selection(d, where); return d->n_sel; } static int octant_select(DATA *d, DPOINT *where) { /* * selects the d->oct_max nearest points per octant/quadrant/secant, * using euclidian or variogram distance */ int i, j, noct = 1, n, start = 0, end = 0, n_notempty = 0; DPOINT **sel, *tmp; if (d->mode & Z_BIT_SET) noct = 8; else if (d->mode & Y_BIT_SET) noct = 4; else noct = 2; sel = d->sel; for (i = 0; i < noct; i++) { /* for each octant: */ /* start == end: */ for (j = start; j < d->n_sel; j++) { /* for the remaining of sel: */ if (which_octant(where, sel[j], d->mode) == i) { DPSWAP(sel[end], sel[j]); end++; /* j >= end */ } } n = end - start; /* # of pts in octant i */ if (n > 0) n_notempty++; /* Yahoo, another non-empty octant! */ if (n > d->oct_max) { /* to get the closest n: sort sel from start to end: */ qsort(sel + start, (size_t) n, sizeof(DPOINT *), (int CDECL (*)(const void *, const void *)) dist_cmp); /* swap the remaining ones to the end of sel and forget about'm: */ for (j = start + d->oct_max; j < end; j++) { d->n_sel--; DPSWAP(sel[j], sel[d->n_sel]); } /* accept the first d->oct_max: */ start += d->oct_max; /* proceed with the next octant: */ end = start; } else /* accept all n: */ start = end; } if (end != d->n_sel) { Rprintf("end: %d, n_sel: %d\n", end, d->n_sel); ErrMsg(ER_IMPOSVAL, "octant_select(): remaining points"); } return n_notempty; /* # non-empty octants */ } static int which_octant(DPOINT *where, DPOINT *p, int mode) { /* * it's pretty hard to get this right for perfectly alligned 3D data: * in case of omax=1 and only one point at exactly equal distances * in each of the 6 directions, not all 6 are taken. * For 2D (omax=1, 4 points) this works, however. */ double dx, dy, dz; int x = 0, y = 0, z = 0; dx = p->x - where->x; /* < 0 : p in west half */ dy = p->y - where->y; /* < 0 : p in south half */ dz = p->z - where->z; /* < 0 : p in lower half */ if (mode & Z_BIT_SET) z = (dz < 0); if (mode & Y_BIT_SET) { x = (dy < 0 ? dx > 0 : dx >= 0); y = (dx < 0 ? dy >= 0 : dy > 0); } else x = (where->x < p->x); return (x | (y << 1) | (z << 2)); } int CDECL dist_cmp(const DPOINT **pa, const DPOINT **pb) { /* ANSI qsort() conformant dist_cmp */ if ( (*pa)->u.dist2 < (*pb)->u.dist2 ) return -1; if ( (*pa)->u.dist2 > (*pb)->u.dist2 ) return 1; return 0; } static void print_selection(DATA *d, DPOINT *where) { /* Add this statement to filter out * empty selections if (!d->n_sel) return; */ if (where) { printlog("selection at "); logprint_point(where, d); } else printlog("(NULL selection location)"); print_data_selection(d); }