GRASS 8 Programmer's Manual 8.6.0dev(2026)-5f4f7ad06c
Loading...
Searching...
No Matches
vector/Vlib/cats.c
Go to the documentation of this file.
1/*!
2 * \file lib/vector/Vlib/cats.c
3 *
4 * \brief Vector library - Category management
5 *
6 * Higher level functions for reading/writing/manipulating vectors.
7 *
8 * (C) 2001-2012 by the GRASS Development Team
9 *
10 * This program is free software under the GNU General Public License
11 * (>=v2). Read the file COPYING that comes with GRASS for details.
12 *
13 * \author Original author CERL, probably Dave Gerdes or Mike Higgins
14 * \author Update to GRASS 5.7 Radim Blazek and David D. Gray.
15 * \author Various updates by Martin Landa <landa.martin gmail.com>
16 * \author Various updates by Markus Metz
17 */
18
19#include <stdlib.h>
20#include <string.h>
21#include <grass/vector.h>
22#include <grass/dbmi.h>
23#include <grass/glocale.h>
24
25static int cmp(const void *pa, const void *pb);
26static struct line_cats *Vect__new_cats_struct(void);
27
28/*!
29 \brief Creates and initializes line_cats structure.
30
31 This structure is used for reading and writing vector cats. The
32 library routines handle all memory allocation.
33
34 To free allocated memory call Vect_destroy_cats_struct().
35
36 \return struct line_cats *
37 \return NULL on error
38 */
40{
41 struct line_cats *p;
42
43 if (NULL == (p = Vect__new_cats_struct()))
44 G_fatal_error(_("Vect_new_cats_struct(): Out of memory"));
45
46 return p;
47}
48
49/*!
50 \brief Creates and initializes line_cats structure (lower level fn)
51
52 This structure is used for reading and writing vector cats. The
53 library routines handle all memory allocation.
54
55 \return struct line_cats *
56 */
57static struct line_cats *Vect__new_cats_struct(void)
58{
59 struct line_cats *p;
60
61 p = (struct line_cats *)G_malloc(sizeof(struct line_cats));
62
63 /* n_cats MUST be initialized to zero */
64 if (p)
65 p->n_cats = 0;
66
67 if (p)
68 p->alloc_cats = 0;
69
70 return p;
71}
72
73/*!
74 \brief Frees all memory associated with line_cats structure,
75 including the struct itself.
76
77 \param p line_cats structure
78 */
80{
81 if (p) { /* probably a moot test */
82 if (p->n_cats) {
83 G_free((void *)p->field);
84 G_free((void *)p->cat);
85 }
86 G_free((void *)p);
87 }
88}
89
90/*!
91 \brief Add new field/cat to category structure if doesn't exist
92 yet.
93
94 \param[in,out] Cats line_cats structure
95 \param[in] field layer number
96 \param[in] cat category number
97
98 \return number of categories
99 \return 0 if no space for new category in structure, n_cats would be >
100 GV_NCATS_MAX \return -1 on out of memory \return -2 if field out of range: 1
101 - GV_FIELD_MAX or cat out of range: 1 - GV_CAT_MAX
102 */
103int Vect_cat_set(struct line_cats *Cats, int field, int cat)
104{
105 register int n;
106
107 /* check input values */
108 /* compiler may warn:
109 * comparison is always 0 due to limited range of data type
110 * but remember that limit is set to portable data type length
111 * and machine native size may be longer */
112 /*
113 if (field < 1 || field > GV_FIELD_MAX || cat < 0 || cat > GV_CAT_MAX)
114 return (-2);
115 */
116
117 /* go through old cats and find if field/category exists */
118 for (n = 0; n < Cats->n_cats; n++) {
119 if (Cats->field[n] == field && Cats->cat[n] == cat)
120 return (1);
121 }
122
123 /* field was not found so we shall append new cat */
124 /* test if space exist */
125 if (n >= GV_NCATS_MAX) {
127 _("Too many categories (%d), unable to set cat %d (layer %d)"),
128 Cats->n_cats, cat, field);
129 }
130
131 if (Cats->n_cats == Cats->alloc_cats) {
132 if (0 > dig_alloc_cats(Cats, Cats->n_cats + 100))
133 return (-1);
134 }
135
136 n = Cats->n_cats;
137 Cats->field[n] = field;
138 Cats->cat[n] = cat;
139 Cats->n_cats++;
140 return (1);
141}
142
143/*!
144 \brief Get first found category of given field.
145
146 <em>cat</em> is set to first category found or -1 if field was not
147 found
148
149 \param Cats pointer line_cats structure
150 \param field layer number
151 \param[out] cat pointer to variable where cat will be written (can be NULL)
152
153 \return number of found cats for given field (first reported)
154 \return 0 layer does not exist
155 */
156int Vect_cat_get(const struct line_cats *Cats, int field, int *cat)
157{
158 int n, ret;
159
160 /* field was not found */
161 ret = 0;
162 if (cat)
163 *cat = -1;
164
165 /* check input value */
167 return (0);
168
169 /* go through cats and find if field exist */
170 for (n = 0; n < Cats->n_cats; n++) {
171 if (Cats->field[n] == field) {
172 if (cat && ret == 0) {
173 *cat = Cats->cat[n];
174 }
175 ret++;
176 }
177 }
178
179 return ret;
180}
181
182/*!
183 \brief Get list of categories of given field.
184
185 \param Cats line_cats structure
186 \param field layer number
187 \param[out] cats pointer to list where cats will be written
188
189 \return number of found categories
190 \return -1 on invalid field
191 */
192int Vect_field_cat_get(const struct line_cats *Cats, int field,
193 struct ilist *cats)
194{
195 int n;
196
197 /* reset list of categories */
198 Vect_reset_list(cats);
199
200 /* check input value */
202 return -1;
203
204 /* go through cats and find if field exist */
205 for (n = 0; n < Cats->n_cats; n++) {
206 if (Cats->field[n] == field)
207 Vect_list_append(cats, Cats->cat[n]);
208 }
209
210 return cats->n_values;
211}
212
213/*!
214 \brief Delete all categories of given layer
215
216 \param[in,out] Cats line_cats structure
217 \param field layer number
218
219 \return number of categories deleted
220 \return 0 layer does not exist
221 */
223{
224 int n, m, found;
225
226 /* check input value */
227 /*
228 if (field < 1 || field > GV_FIELD_MAX)
229 return (0);
230 */
231
232 /* go through cats and find if field exist */
233 m = 0;
234 for (n = 0; n < Cats->n_cats; n++) {
235 if (Cats->field[n] != field) {
236 Cats->field[m] = Cats->field[n];
237 Cats->cat[m] = Cats->cat[n];
238 m++;
239 }
240 }
241 found = Cats->n_cats - m;
242 Cats->n_cats = m;
243
244 return (found);
245}
246
247/*!
248 \brief Delete field/cat from line_cats structure
249
250 \param[in,out] Cats line_cats structure
251 \param field layer number
252 \param cat category to be deleted or -1 to delete all cats of given field
253
254 \return number of categories deleted
255 \return 0 field/category number does not exist
256 */
258{
259 register int n, m, found;
260
261 /* check input value */
262 /*
263 if (field < 1 || field > GV_FIELD_MAX)
264 return (0);
265 */
266
267 if (cat == -1)
268 return Vect_cat_del(Cats, field);
269
270 /* go through cats and find if field exist */
271 m = 0;
272 for (n = 0; n < Cats->n_cats; n++) {
273 if (Cats->field[n] != field || Cats->cat[n] != cat) {
274 Cats->field[m] = Cats->field[n];
275 Cats->cat[m] = Cats->cat[n];
276 m++;
277 }
278 }
279 found = Cats->n_cats - m;
280 Cats->n_cats = m;
281
282 return (found);
283}
284
285/*!
286 \brief Reset category structure to make sure cats structure is clean to be
287 re-used.
288
289 I.e. it has no cats associated with it. Cats must have
290 previously been created with Vect_new_cats_struct()
291
292 \param[out] Cats line_cats structure
293
294 \return 0
295 */
297{
298 Cats->n_cats = 0;
299
300 return 0;
301}
302
303/*!
304 \brief Allocate memory for cat_list structure.
305
306 \return pointer to allocated structure
307 \return NULL if out of memory
308 */
310{
311 struct cat_list *p;
312
313 p = (struct cat_list *)G_malloc(sizeof(struct cat_list));
314
315 /* n_ranges MUST be initialized to zero */
316 if (p)
317 G_zero(p, sizeof(struct cat_list));
318
319 return p;
320}
321
322/*!
323 \brief Frees allocated cat_list memory.
324
325 \param p pointer to line_cats structure
326 */
328{
329 if (p) { /* probably a moot test */
330 if (p->n_ranges) {
331 G_free((void *)p->min);
332 G_free((void *)p->max);
333 }
334 G_free((void *)p);
335 }
336}
337
338/*!
339 \brief Converts string of categories and cat ranges separated by commas to
340 cat_list.
341
342 \par Examples of string:
343 \verbatim
344 5,6,7
345 3-9
346 2,3,5-9,20\endverbatim
347
348 \par Example:
349 \code
350 ...
351 str = "2,3,5-9,20"
352 cat_list = Vect_new_cat_list()
353
354 Vect_str_to_cat_list(str, cat_list)
355 \endcode
356 \verbatim
357 cat_list->field = 0
358 cat_list->n_ranges = 4
359 cat_list->min = {2, 3, 5, 20}
360 cat_list->max = {2, 3, 9, 20}
361 \endverbatim
362
363 \param str category list as a string
364 \param[in,out] list pointer to cat_list structure
365
366 \return number of errors in ranges
367 */
368int Vect_str_to_cat_list(const char *str, struct cat_list *list)
369{
370 int i, nr, l, err = 0;
371 const char *s, *e;
372 char buf[100];
373 int min, max;
374
375 G_debug(3, "Vect_str_to_cat_list(): str = %s", str);
376
377 list->n_ranges = 0;
378 l = strlen(str);
379
380 /* find number of ranges */
381 nr = 1; /* one range */
382 for (i = 0; i < l; i++)
383 if (str[i] == ',')
384 nr++;
385
386 /* allocate space */
387 if (list->alloc_ranges == 0) {
388 list->min = (int *)G_malloc(nr * sizeof(int));
389 list->max = (int *)G_malloc(nr * sizeof(int));
390 }
391 else if (nr > list->alloc_ranges) {
392 list->min = (int *)G_realloc((void *)list->min, nr * sizeof(int));
393 list->max = (int *)G_realloc((void *)list->max, nr * sizeof(int));
394 }
395
396 /* go through string and read ranges */
397 i = 0;
398 s = str;
399
400 while (s) {
401 e = (char *)strchr(s, ','); /* first comma */
402 if (e) {
403 l = e - s;
404 strncpy(buf, s, l);
405 buf[l] = '\0';
406 s = e + 1;
407 }
408 else {
409 strcpy(buf, s);
410 s = NULL;
411 }
412
413 G_debug(3, " buf = %s", buf);
414 if (sscanf(buf, "%d-%d", &min, &max) == 2) {
415 }
416 else if (sscanf(buf, "%d", &min) == 1)
417 max = min;
418 else { /* error */
419
420 G_warning(_("Unable to convert category string '%s' (from '%s') to "
421 "category range"),
422 buf, str);
423 err++;
424 continue;
425 }
426
427 list->min[i] = min;
428 list->max[i] = max;
429 i++;
430 }
431
432 list->n_ranges = i;
433
434 return (err);
435}
436
437/*!
438 \brief Convert ordered array of integers to cat_list structure.
439
440 \param vals array of integers
441 \param nvals number of values
442 \param[in,out] list pointer to cat_list structure
443
444 \return number of ranges
445 */
446int Vect_array_to_cat_list(const int *vals, int nvals, struct cat_list *list)
447{
448 int i, range;
449
450 G_debug(1, "Vect_array_to_cat_list()");
451 range = -1;
452 for (i = 0; i < nvals; i++) {
453 if (i == 0 || (vals[i] - list->max[range]) > 1) {
454 range++;
455 if (range == list->alloc_ranges) {
456 list->alloc_ranges += 1000;
457 list->min = (int *)G_realloc((void *)list->min,
458 list->alloc_ranges * sizeof(int));
459 list->max = (int *)G_realloc((void *)list->max,
460 list->alloc_ranges * sizeof(int));
461 }
462 list->min[range] = vals[i];
463 list->max[range] = vals[i];
464 }
465 else {
466 list->max[range] = vals[i];
467 }
468 }
469
470 list->n_ranges = range + 1;
471
472 return (list->n_ranges);
473}
474
475/*!
476 \brief Convert cat_list struct to ordered array of unique integers.
477
478 Output array do not contain duplicate items.
479
480 Allocated array should be freed by G_free().
481
482 \param list pointer to cat_list struct
483 \param[out] vals array of integers
484 \param[out] nvals number of values
485
486 \return 0 on success
487 \return -1 on failure
488 */
489int Vect_cat_list_to_array(const struct cat_list *list, int **vals, int *nvals)
490{
491 int i, j, k, n, n_cats, n_ucats, last_cat;
492 int *cats, *ucats;
493
494 G_debug(1, "Vect_cat_list_to_array()");
495
496 *nvals = n_cats = 0;
497 cats = NULL;
498 for (i = 0; i < list->n_ranges; i++) {
499 n = list->max[i] - list->min[i] + 1;
500 if (n < 1) {
501 G_free(cats);
502 return -1;
503 }
504
505 /* realloc array */
506 cats = (int *)G_realloc(cats, sizeof(int) * (n_cats + n));
507
508 for (j = n_cats, k = 0; j < n_cats + n; j++, k++) {
509 cats[j] = list->min[i] + k;
510 }
511 n_cats += n;
512 }
513
514 /* sort array */
515 qsort(cats, n_cats, sizeof(int), cmp);
516
517 /* skip duplicated values */
518 ucats = G_malloc(sizeof(int) * n_cats);
519 last_cat = ucats[0] = cats[0];
520 n_ucats = 1;
521 for (i = 1; i < n_cats; i++) {
522 if (last_cat == cats[i])
523 continue;
524 last_cat = ucats[n_ucats++] = cats[i];
525 }
526 G_free(cats);
527
528 /* reallocate array for unique values */
529 ucats = (int *)G_realloc(ucats, sizeof(int) * n_ucats);
530
531 *nvals = n_ucats;
532 *vals = ucats;
533
534 return 0;
535}
536
537/*!
538 \brief Check if category number is in list.
539
540 \param cat category number
541 \param list cat_list structure
542
543 \return TRUE if cat is in list
544 \return FALSE if not
545 */
546int Vect_cat_in_cat_list(int cat, const struct cat_list *list)
547{
548 int i;
549
550 for (i = 0; i < list->n_ranges; i++)
551 if (cat >= list->min[i] && cat <= list->max[i])
552 return (TRUE);
553
554 return (FALSE);
555}
556
557/*!
558 \brief Set category constraints using 'where' or 'cats' option and layer
559 number.
560
561 \param Map pointer to Map_info structure
562 \param layer layer number
563 \param where where statement
564 \param catstr category list as string
565
566 \return pointer to cat_list structure or NULL
567 */
569 char *where, char *catstr)
570{
571 struct cat_list *list = NULL;
572 int ret;
573
574 if (layer < 1) {
575 G_warning(_("Layer number must be > 0 for category constraints"));
576 /* no valid constraints, all categories qualify */
577 return list;
578 }
579
580 /* where has precedence over cats */
581 if (where) {
582 struct field_info *Fi = NULL;
584 int ncats, *cats = NULL;
585 int i, j;
586
587 if (catstr)
588 G_warning(_("'%s' and '%s' parameters were supplied, cats will be "
589 "ignored"),
590 "where", "cats");
591
592 Fi = Vect_get_field(Map, layer);
593 if (!Fi) {
594 G_fatal_error(_("Database connection not defined for layer %d"),
595 layer);
596 }
597
598 G_verbose_message(_("Loading categories from table <%s>..."),
599 Fi->table);
600
601 driver = db_start_driver_open_database(Fi->driver, Fi->database);
602 if (driver == NULL)
603 G_fatal_error(_("Unable to open database <%s> by driver <%s>"),
604 Fi->database, Fi->driver);
605
606 ncats = db_select_int(driver, Fi->table, Fi->key, where, &cats);
607 if (ncats == -1)
608 G_fatal_error(_("Unable select records from table <%s>"),
609 Fi->table);
611 n_("One category loaded", "%d categories loaded", ncats), ncats);
612
614
615 /* sort */
616 qsort(cats, ncats, sizeof(int), cmp);
617
618 /* remove duplicates */
619 j = 1;
620 for (i = 1; i < ncats; i++) {
621 if (cats[i] != cats[j - 1]) {
622 cats[j] = cats[i];
623 j++;
624 }
625 }
626 ncats = j;
627
628 /* convert to cat list */
630
631 ret = Vect_array_to_cat_list(cats, ncats, list);
632 if (ret == 0)
633 G_warning(_("No categories selected with '%s' option"), "where");
634
635 if (cats)
636 G_free(cats);
637 }
638 else if (catstr) {
640
642 if (ret > 0)
643 G_warning(_("%d errors in '%s' option"), ret, "cats");
644 }
645
646 if (list) {
647 if (list->n_ranges < 1) {
649 list = NULL;
650 }
651 else
652 list->field = layer;
653 }
654
655 return list;
656}
657
658/*!
659 \brief Check if categories match with category constraints.
660
661 \param Cats line_cats structure
662 \param layer layer number
663 \param list cat_list structure
664
665 \return 0 no match, categories are outside constraints
666 \return 1 match, categories are inside constraints
667 */
668/* TODO:
669 * for GRASS 8, change return type:
670 * return a list of all category numbers that match the constraints
671 * return NULL if no category number matches the constraints
672 */
674 struct cat_list *list)
675{
676 int i;
677
678 if (layer < 1) {
679 G_warning(_("Layer number must be > 0 for category constraints"));
680 /* no valid constraint, all categories qualify */
681 return 1;
682 }
683
684 if (list) {
685 for (i = 0; i < Cats->n_cats; i++) {
686 if (Cats->field[i] == layer &&
687 Vect_cat_in_cat_list(Cats->cat[i], list)) {
688 return 1;
689 }
690 }
691 return 0;
692 }
693
694 for (i = 0; i < Cats->n_cats; i++) {
695 if (Cats->field[i] == layer)
696 return 1;
697 }
698
699 return 0;
700}
701
702/*!
703 \brief Check if category is in ordered array of integers.
704
705 \param cat category number
706 \param array ordered array of integers
707 \param ncats number of categories in array
708
709 \return TRUE if cat is in list
710 \return FALSE if it is not
711 */
712int Vect_cat_in_array(int cat, const int *array, int ncats)
713{
714 int *i;
715
716 i = bsearch((void *)&cat, (void *)array, (size_t)ncats, sizeof(int), cmp);
717
718 return (i != NULL);
719}
720
721/* return -1 if *p1 < *p2
722 * return 1 if *p1 > *p2
723 * return 0 if *p1 == *p2 */
724static int cmp(const void *pa, const void *pb)
725{
726 int *p1 = (int *)pa;
727 int *p2 = (int *)pb;
728
729 if (*p1 < *p2)
730 return -1;
731 return (*p1 > *p2);
732}
#define NULL
Definition ccmath.h:32
Main header of GRASS DataBase Management Interface.
int db_close_database_shutdown_driver(dbDriver *)
Close driver/database connection.
Definition db.c:61
int db_select_int(dbDriver *, const char *, const char *, const char *, int **)
Select array of ordered integers from table/column.
dbDriver * db_start_driver_open_database(const char *, const char *)
Open driver/database connection.
Definition db.c:28
void G_zero(void *, int)
Zero out a buffer, buf, of length i.
Definition gis/zero.c:23
void G_free(void *)
Free allocated memory.
Definition gis/alloc.c:147
#define G_realloc(p, n)
Definition defs/gis.h:141
void void void void G_fatal_error(const char *,...) __attribute__((format(printf
void G_warning(const char *,...) __attribute__((format(printf
#define G_malloc(n)
Definition defs/gis.h:139
void void G_verbose_message(const char *,...) __attribute__((format(printf
int G_debug(int, const char *,...) __attribute__((format(printf
struct field_info * Vect_get_field(struct Map_info *, int)
Get information about link to database (by layer number)
Definition field.c:510
int Vect_list_append(struct ilist *, int)
Append new item to the end of list if not yet present.
int Vect_reset_list(struct ilist *)
Reset ilist structure.
#define GV_FIELD_MAX
Maximum field.
#define GV_NCATS_MAX
Maximum number of categories for one element.
int dig_alloc_cats(struct line_cats *, int)
Allocate room for 'num' fields and category arrays in struct line_cats.
#define min(x, y)
Definition draw2.c:29
#define max(x, y)
Definition draw2.c:30
#define TRUE
Definition gis.h:78
#define FALSE
Definition gis.h:82
#define n_(strs, strp, num)
Definition glocale.h:11
#define _(str)
Definition glocale.h:10
#define strcpy
Definition parson.c:66
double l
Definition r_raster.c:39
Vector map info.
Category list.
int n_ranges
Number of ranges.
int * min
Array of minimum values.
int * max
Array of maximum values.
Layer (old: field) information.
List of integers.
Definition gis.h:715
int n_values
Number of values in the list.
Definition gis.h:723
Feature category info.
int * field
Array of layers (fields)
int alloc_cats
Allocated space for categories.
int * cat
Array of categories.
int n_cats
Number of categories attached to element.
Definition manage.h:4
SYMBOL * err(FILE *fp, SYMBOL *s, char *msg)
void Vect_destroy_cats_struct(struct line_cats *p)
Frees all memory associated with line_cats structure, including the struct itself.
int Vect_cat_get(const struct line_cats *Cats, int field, int *cat)
Get first found category of given field.
int Vect_reset_cats(struct line_cats *Cats)
Reset category structure to make sure cats structure is clean to be re-used.
int Vect_field_cat_del(struct line_cats *Cats, int field, int cat)
Delete field/cat from line_cats structure.
int Vect_array_to_cat_list(const int *vals, int nvals, struct cat_list *list)
Convert ordered array of integers to cat_list structure.
int Vect_cat_in_array(int cat, const int *array, int ncats)
Check if category is in ordered array of integers.
int Vect_str_to_cat_list(const char *str, struct cat_list *list)
Converts string of categories and cat ranges separated by commas to cat_list.
struct line_cats * Vect_new_cats_struct(void)
Creates and initializes line_cats structure.
struct cat_list * Vect_new_cat_list(void)
Allocate memory for cat_list structure.
int Vect_cat_in_cat_list(int cat, const struct cat_list *list)
Check if category number is in list.
int Vect_cat_list_to_array(const struct cat_list *list, int **vals, int *nvals)
Convert cat_list struct to ordered array of unique integers.
struct cat_list * Vect_cats_set_constraint(struct Map_info *Map, int layer, char *where, char *catstr)
Set category constraints using 'where' or 'cats' option and layer number.
int Vect_field_cat_get(const struct line_cats *Cats, int field, struct ilist *cats)
Get list of categories of given field.
int Vect_cats_in_constraint(struct line_cats *Cats, int layer, struct cat_list *list)
Check if categories match with category constraints.
int Vect_cat_del(struct line_cats *Cats, int field)
Delete all categories of given layer.
void Vect_destroy_cat_list(struct cat_list *p)
Frees allocated cat_list memory.
int Vect_cat_set(struct line_cats *Cats, int field, int cat)
Add new field/cat to category structure if doesn't exist yet.