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