/*! * \file raster/cell_stats.c * * \brief Raster Library - Raster cell statistics * * (C) 2001-2009 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. * * \author Original author CERL */ #include #include #include #define INCR 10 #define SHIFT 6 static const int NCATS = 1 << SHIFT; #define NODE struct Cell_stats_node static int next_node(struct Cell_stats *); static void init_node(NODE *, int, int); /*! * \brief Initialize cell stats * * This routine, which must be called first, initializes the * Cell_stats structure. * * Set the count for NULL-values to zero. * * \param s pointer to Cell_stats structure */ void Rast_init_cell_stats(struct Cell_stats *s) { s->N = 0; s->tlen = INCR; s->node = (NODE *) G_malloc(s->tlen * sizeof(NODE)); s->null_data_count = 0; } /*! * \brief Add data to cell stats * * The n CELL values in the data array are inserted (and * counted) in the Cell_stats structure. * * Look for NULLs and update the NULL-value count. * * \param cell raster values * \param n number of values * \param s pointer to Cell_stats structure which holds cell stats info * * \return 1 on failure * \return 0 on success */ int Rast_update_cell_stats(const CELL * cell, int n, struct Cell_stats *s) { CELL cat; int p, q; int idx, offset; int N; NODE *node, *pnode; NODE *new_node; if (n <= 0) return 1; node = s->node; /* first non-null node is special case */ if ((N = s->N) == 0) { cat = *cell++; while (Rast_is_c_null_value(&cat)) { s->null_data_count++; cat = *cell++; n--; } if (n > 0) { /* if there are some non-null cells */ N = 1; if (cat < 0) { idx = -((-cat) >> SHIFT) - 1; offset = cat + ((-idx) << SHIFT) - 1; } else { idx = cat >> SHIFT; offset = cat - (idx << SHIFT); } fflush(stderr); init_node(&node[1], idx, offset); node[1].right = 0; n--; } } while (n-- > 0) { cat = *cell++; if (Rast_is_c_null_value(&cat)) { s->null_data_count++; continue; } if (cat < 0) { idx = -((-cat) >> SHIFT) - 1; offset = cat + ((-idx) << SHIFT) - 1; } else { idx = cat >> SHIFT; offset = cat - (idx << SHIFT); } q = 1; while (q > 0) { pnode = &node[p = q]; if (pnode->idx == idx) { pnode->count[offset]++; break; } if (pnode->idx > idx) q = pnode->left; /* go left */ else q = pnode->right; /* go right */ } if (q > 0) continue; /* found */ /* new node */ N++; /* grow the tree? */ if (N >= s->tlen) { node = (NODE *) G_realloc((char *)node, sizeof(NODE) * (s->tlen += INCR)); pnode = &node[p]; /* realloc moves node, must reassign pnode */ } /* add node to tree */ init_node(new_node = &node[N], idx, offset); if (pnode->idx > idx) { new_node->right = -p; /* create thread */ pnode->left = N; /* insert left */ } else { new_node->right = pnode->right; /* copy right link/thread */ pnode->right = N; /* add right */ } } /* while n-- > 0 */ s->N = N; s->node = node; return 0; } static void init_node(NODE * node, int idx, int offset) { long *count; int i; count = node->count = (long *)G_calloc(i = NCATS, sizeof(long)); while (i--) *count++ = 0; node->idx = idx; node->count[offset] = 1; node->left = 0; } /*! * \brief Random query of cell stats * * This routine allows a random query of the Cell_stats structure. The * \p count associated with the raster value \p cat is * set. The routine returns 1 if \p cat was found in the * structure, 0 otherwise. * * Allow finding the count for the NULL-value. * * \param cat raster value * \param[out] count count * \param s pointer to Cell_stats structure which holds cell stats info * * \return 1 if found * \return 0 if not found */ int Rast_find_cell_stat(CELL cat, long *count, const struct Cell_stats *s) { int q; int idx; int offset; *count = 0; if (Rast_is_c_null_value(&cat)) { *count = s->null_data_count; return (*count != 0); } if (s->N <= 0) return 0; /* if (cat < 0) { idx = -(-cat/NCATS) - 1; offset = cat - idx*NCATS - 1; } else { idx = cat/NCATS; offset = cat - idx*NCATS; } */ if (cat < 0) { idx = -((-cat) >> SHIFT) - 1; offset = cat + ((-idx) << SHIFT) - 1; } else { idx = cat >> SHIFT; offset = cat - (idx << SHIFT); } q = 1; while (q > 0) { if (s->node[q].idx == idx) { *count = s->node[q].count[offset]; return (*count != 0); } if (s->node[q].idx > idx) q = s->node[q].left; /* go left */ else q = s->node[q].right; /* go right */ } return 0; } /*! * \brief Reset/rewind cell stats * * The structure s is rewound (i.e., positioned at the first * raster category) so that sorted sequential retrieval can begin. * * \param s pointer to Cell_stats structure which holds cell stats info * * \return 0 */ int Rast_rewind_cell_stats(struct Cell_stats *s) { int q; if (s->N <= 0) return 1; /* start at root and go all the way to the left */ s->curp = 1; while ((q = s->node[s->curp].left)) s->curp = q; s->curoffset = -1; return 0; } static int next_node(struct Cell_stats *s) { int q; /* go to the right */ s->curp = s->node[s->curp].right; if (s->curp == 0) /* no more */ return 0; if (s->curp < 0) { /* thread. stop here */ s->curp = -(s->curp); return 1; } while ((q = s->node[s->curp].left)) /* now go all the way left */ s->curp = q; return 1; } /*! * \brief Retrieve sorted cell stats * * Retrieves the next cat, count combination from the * structure. Returns 0 if there are no more items, non-zero if there * are more. For example: * \code struct Cell_stats s; CELL cat; long count; // updating s occurs here Rast_rewind_cell_stats(&s); while (Rast_next_cell_stat(&cat,&count,&s) fprintf(stdout, "%ld %ld\n", (long) cat, count); \endcode * * Do not return a record for the NULL-value * * \param cat raster value * \param[out] count * \param s pointer to Cell_stats structure which holds cell stats info * * \return 0 if there are no more items * \return non-zero if there are more */ int Rast_next_cell_stat(CELL * cat, long *count, struct Cell_stats *s) { int idx; /* first time report stats for null */ /* decided not to return stats for null in this function static int null_reported = 0; if(!null_reported && s->null_data_count > 0) { *count = s->null_data_count; Rast_set_c_null_value(&cat,1); null_reported = 1; return 1; } */ if (s->N <= 0) return 0; for (;;) { s->curoffset++; if (s->curoffset >= NCATS) { if (!next_node(s)) return 0; s->curoffset = -1; continue; } if ((*count = s->node[s->curp].count[s->curoffset])) { idx = s->node[s->curp].idx; /* if (idx < 0) *cat = idx*NCATS + s->curoffset+1; else *cat = idx*NCATS + s->curoffset; */ if (idx < 0) *cat = -((-idx) << SHIFT) + s->curoffset + 1; else *cat = (idx << SHIFT) + s->curoffset; return 1; } } } /*! * \brief Get number of null values. * * Get a number of null values from stats structure. * * Note: when reporting values which appear in a map using * Rast_next_cell_stats(), to get stats for null, call * Rast_get_stats_for_null_value() first, since Rast_next_cell_stats() does * not report stats for null. * * \param count count * \param s pointer to Cell_stats structure which holds cell stats info */ void Rast_get_stats_for_null_value(long *count, const struct Cell_stats *s) { *count = s->null_data_count; } /*! * \brief Free cell stats structure * * The memory associated with structure s is freed. This * routine may be called any time after calling Rast_init_cell_stats(). * * \param s pointer to Cell_stats structure */ void Rast_free_cell_stats(struct Cell_stats *s) { int i; for (i = 1; i <= s->N; i++) G_free(s->node[i].count); G_free(s->node); }