1 /*!
2   \file lib/vector/Vlib/cindex.c
3 
4   \brief Vector library - category index management
5 
6   Higher level functions for reading/writing/manipulating vectors.
7 
8   (C) 2001-2013 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 Radim Blazek
14   \author Some contribution by Martin Landa <landa.martin gmail.com>
15 */
16 
17 #include <stdlib.h>
18 #include <string.h>
19 #include <sys/types.h>
20 #include <sys/stat.h>
21 #include <unistd.h>
22 #include <grass/vector.h>
23 #include <grass/glocale.h>
24 
25 #include "local_proto.h"
26 
27 #define SEP "------------------------------------------------------------------------------------------\n"
28 
check_status(const struct Map_info * Map)29 static void check_status(const struct Map_info *Map)
30 {
31     if (!Map->plus.cidx_up_to_date)
32 	G_fatal_error(_("Category index is not up to date"));
33 }
34 
check_index(const struct Map_info * Map,int index)35 static void check_index(const struct Map_info *Map, int index)
36 {
37     if (index < 0 || index >= Map->plus.n_cidx)
38         G_fatal_error(_("Layer index out of range"));
39 }
40 
41 /* search for first occurrence of cat in cat index, starting at first */
ci_search_cat(struct Cat_index * ci,int first,int cat)42 static int ci_search_cat(struct Cat_index *ci, int first, int cat)
43 {
44     int lo, hi, mid;
45 
46     lo = first;
47     if (lo < 0)
48 	lo = 0;
49     if (ci->cat[lo][0] > cat)
50 	return -1;
51     if (ci->cat[lo][0] == cat)
52 	return lo;
53 
54     hi = ci->n_cats - 1;
55     if (first > hi)
56 	return -1;
57 
58     /* deferred test for equality */
59     while (lo < hi) {
60 	mid = (lo + hi) >> 1;
61 	if (ci->cat[mid][0] < cat)
62 	    lo = mid + 1;
63 	else
64 	    hi = mid;
65     }
66     if (ci->cat[lo][0] == cat)
67 	return lo;
68 
69     return -1;
70 }
71 
72 /*!
73   \brief Get number of layers in category index
74 
75   \param Map pointer to Map_info structure
76 
77   \return number of layers
78  */
Vect_cidx_get_num_fields(const struct Map_info * Map)79 int Vect_cidx_get_num_fields(const struct Map_info *Map)
80 {
81     check_status(Map);
82 
83     return Map->plus.n_cidx;
84 }
85 
86 /*!
87   \brief Get layer number for given index
88 
89   G_fatal_error() is called when index not found.
90 
91   \param Map pointer to Map_info structure
92   \param index layer index: from 0 to Vect_cidx_get_num_fields() - 1
93 
94   \return layer number
95  */
Vect_cidx_get_field_number(const struct Map_info * Map,int index)96 int Vect_cidx_get_field_number(const struct Map_info *Map, int index)
97 {
98     check_status(Map);
99     check_index(Map, index);
100 
101     return Map->plus.cidx[index].field;
102 }
103 
104 /*!
105   \brief Get layer index for given layer number
106 
107   \param Map pointer to Map_info structure
108   \param field layer number
109 
110   \return layer index
111   \return -1 if not found
112  */
Vect_cidx_get_field_index(const struct Map_info * Map,int field)113 int Vect_cidx_get_field_index(const struct Map_info *Map, int field)
114 {
115     int i;
116     const struct Plus_head *Plus;
117 
118     G_debug(2, "Vect_cidx_get_field_index() field = %d", field);
119 
120     check_status(Map);
121     Plus = &(Map->plus);
122 
123     for (i = 0; i < Plus->n_cidx; i++) {
124 	if (Plus->cidx[i].field == field)
125 	    return i;
126     }
127 
128     return -1;
129 }
130 
131 /*!
132   \brief Get number of unique categories for given layer index
133 
134   G_fatal_error() is called when index not found.
135 
136   \param Map pointer to Map_info structure
137   \param index layer index (starts at 0)
138 
139   \return number of unique categories
140   \return -1 on error
141  */
Vect_cidx_get_num_unique_cats_by_index(const struct Map_info * Map,int index)142 int Vect_cidx_get_num_unique_cats_by_index(const struct Map_info *Map, int index)
143 {
144     check_status(Map);
145     check_index(Map, index);
146 
147     return Map->plus.cidx[index].n_ucats;
148 }
149 
150 /*!
151   \brief Get number of categories for given layer index
152 
153   \param Map pointer to Map_info structure
154   \param index layer index
155 
156   \return number of categories
157   \return -1 on error
158  */
Vect_cidx_get_num_cats_by_index(const struct Map_info * Map,int index)159 int Vect_cidx_get_num_cats_by_index(const struct Map_info *Map, int index)
160 {
161     check_status(Map);
162     check_index(Map, index);
163 
164     return Map->plus.cidx[index].n_cats;
165 }
166 
167 /*!
168   \brief Get number of feature types for given layer index
169 
170   G_fatal_error() is called when index not found.
171 
172   \param Map pointer to Map_info structure
173   \param field_index layer index
174 
175   \return number of feature types
176   \return -1 on error
177  */
Vect_cidx_get_num_types_by_index(const struct Map_info * Map,int field_index)178 int Vect_cidx_get_num_types_by_index(const struct Map_info *Map, int field_index)
179 {
180     check_status(Map);
181     check_index(Map, field_index);
182 
183     return Map->plus.cidx[field_index].n_types;
184 }
185 
186 /*!
187   \brief Get count of feature types for given field and type index
188 
189   \param Map pointer to Map_info structure
190   \param field_index layer index
191   \param type_index type index
192   \param[out] type feature type (GV_POINT, ...)
193   \param[out] count number of features or NULL
194 
195   \return 1 on success
196   \return 0 on error
197 */
Vect_cidx_get_type_count_by_index(const struct Map_info * Map,int field_index,int type_index,int * type,int * count)198 int Vect_cidx_get_type_count_by_index(const struct Map_info *Map, int field_index,
199 				      int type_index, int *type, int *count)
200 {
201     check_status(Map);
202     check_index(Map, field_index);
203 
204     *type = Map->plus.cidx[field_index].type[type_index][0];
205     if (count)
206         *count = Map->plus.cidx[field_index].type[type_index][1];
207 
208     return 1;
209 }
210 
211 /*!
212   \brief Get count of features of certain type by layer and type
213 
214   \param Map pointer to Map_info structure
215   \param field layer number
216   \param type feature type
217 
218   \return feature count
219   \return 0 if no features, no such field or no such type in category index
220  */
Vect_cidx_get_type_count(const struct Map_info * Map,int field,int type)221 int Vect_cidx_get_type_count(const struct Map_info *Map, int field, int type)
222 {
223     int i, fi, count = 0;
224 
225     G_debug(3, "Vect_cidx_get_type_count() field = %d, type = %d", field,
226 	    type);
227 
228     check_status(Map);
229 
230     if ((fi = Vect_cidx_get_field_index(Map, field)) < 0)
231 	return 0;		/* field not found */
232     G_debug(3, "field_index = %d", fi);
233 
234     G_debug(3, "ntypes = %d", Map->plus.cidx[fi].n_types);
235     for (i = 0; i < Map->plus.cidx[fi].n_types; i++) {
236 	int tp, cnt;
237 
238 	tp = Map->plus.cidx[fi].type[i][0];
239 	cnt = Map->plus.cidx[fi].type[i][1];
240 	if (tp & type)
241 	    count += cnt;
242 	G_debug(3, "%d tp = %d, cnt= %d count = %d", i, tp, cnt, count);
243     }
244 
245     return count;
246 }
247 
248 /*!
249   \brief Get category, feature type and id for given layer and category index
250 
251   \param Map pointer to Map_info structure
252   \param field_index layer index
253   \param cat_index category index
254   \param[out] cat category number
255   \param[out] type feature type
256   \param[out] id feature id
257 
258   \return 1 on success
259   \return 0 on error
260 */
Vect_cidx_get_cat_by_index(const struct Map_info * Map,int field_index,int cat_index,int * cat,int * type,int * id)261 int Vect_cidx_get_cat_by_index(const struct Map_info *Map, int field_index,
262 			       int cat_index, int *cat, int *type, int *id)
263 {
264     check_status(Map);		/* This check is slow ? */
265     check_index(Map, field_index);
266 
267     if (cat_index < 0 || cat_index >= Map->plus.cidx[field_index].n_cats)
268 	G_fatal_error(_("Category index out of range"));
269 
270     *cat = Map->plus.cidx[field_index].cat[cat_index][0];
271     *type = Map->plus.cidx[field_index].cat[cat_index][1];
272     *id = Map->plus.cidx[field_index].cat[cat_index][2];
273 
274     return 1;
275 }
276 
277 /*!
278   \brief Get list of unique categories for given layer index
279 
280   \param Map pointer to Map_info structure
281   \param field_index layer index
282   \param[out] list output list of cats
283 
284   \return 1 on success
285   \return 0 on error
286 */
Vect_cidx_get_unique_cats_by_index(struct Map_info * Map,int field_index,struct ilist * list)287 int Vect_cidx_get_unique_cats_by_index(struct Map_info *Map, int field_index, struct ilist *list)
288 {
289     int c;
290     struct Cat_index *ci;
291 
292     check_status(Map);
293     check_index(Map, field_index);
294 
295     ci = &(Map->plus.cidx[field_index]);
296 
297     /* force sorting index -- really needed? */
298     dig_cidx_sort(&(Map->plus));
299 
300     Vect_reset_list(list);
301     if (ci->n_cats > 0)
302         Vect_list_append(list, ci->cat[0][0]);
303     for (c = 1; c < ci->n_cats; c++) {
304         if (ci->cat[c][0] != ci->cat[c - 1][0])
305             Vect_list_append(list, ci->cat[c][0]);
306     }
307 
308     return list->n_values == ci->n_ucats ? 1 : 0;
309 }
310 
311 /*!
312   \brief Find next line/area id for given category, start_index and type_mask
313 
314   \param Map pointer to Map_info structure
315   \param field_index layer index
316   \param cat category number
317   \param type_mask requested feature type
318   \param start_index start search at this index (0 - whole category index)
319   \param[out] type returned type
320   \param[out] id returned line/area id
321 
322   \return index to array
323   \return -1 not found
324 */
Vect_cidx_find_next(const struct Map_info * Map,int field_index,int cat,int type_mask,int start_index,int * type,int * id)325 int Vect_cidx_find_next(const struct Map_info *Map, int field_index, int cat,
326 			int type_mask, int start_index, int *type, int *id)
327 {
328     int cat_index;
329     struct Cat_index *ci;
330 
331     G_debug(3,
332 	    "Vect_cidx_find_next() cat = %d, type_mask = %d, start_index = %d",
333 	    cat, type_mask, start_index);
334 
335     check_status(Map);		/* This check is slow ? */
336     check_index(Map, field_index);
337     *type = *id = 0;
338 
339     /* pointer to category index */
340     ci = &(Map->plus.cidx[field_index]);
341 
342     cat_index = ci_search_cat(ci, start_index, cat);
343     G_debug(3, "cat_index = %d", cat_index);
344 
345     if (cat_index < 0)
346 	return -1;
347 
348     do {
349 	G_debug(3, "  cat_index = %d", cat_index);
350 	if (ci->cat[cat_index][0] == cat && ci->cat[cat_index][1] & type_mask) {
351 	    *type = ci->cat[cat_index][1];
352 	    *id = ci->cat[cat_index][2];
353 	    G_debug(3, "  type match -> record found");
354 	    return cat_index;
355 	}
356 	cat_index++;
357     } while (cat_index < ci->n_cats);
358 
359     return -1;
360 }
361 
362 
363 /*!
364   \brief Find all line/area id's for given category
365 
366   \param Map pointer to Map_info structure
367   \param layer layer number
368   \param type_mask feature type of objects to search for
369   \param cat category number
370   \param[out] lines array of ids of found lines/points
371 */
Vect_cidx_find_all(const struct Map_info * Map,int layer,int type_mask,int cat,struct ilist * lines)372 void Vect_cidx_find_all(const struct Map_info *Map, int layer, int type_mask,
373 			int cat, struct ilist *lines)
374 {
375     int type, line;
376     struct Cat_index *ci;
377     int field_index, idx;
378 
379     Vect_reset_list(lines);
380     field_index = Vect_cidx_get_field_index(Map, layer);
381 
382     if (field_index == -1) {
383 	/* not found */
384 	return;
385     }
386     ci = &(Map->plus.cidx[field_index]);
387 
388     if ((type_mask & GV_AREA) && type_mask != GV_AREA)
389 	G_fatal_error(_("Mixing IDs of areas and primitives"));
390 
391     idx = Vect_cidx_find_next(Map, field_index, cat,
392 			      type_mask, 0, &type, &line);
393 
394     if (idx == -1) {
395 	return;
396     }
397 
398     do {
399 	if (ci->cat[idx][0] != cat) {
400 	    break;
401 	}
402 	if (ci->cat[idx][1] & type_mask) {
403 	    Vect_list_append(lines, ci->cat[idx][2]);
404 	}
405 	idx++;
406     } while (idx < ci->n_cats);
407     return;
408 }
409 
410 /*!
411   \brief Write (dump) category index in text form to file
412 
413   \param Map pointer to Map_info structure
414   \param[out] out output file
415 
416   \return 1 on success
417   \return 0 on error
418 */
Vect_cidx_dump(const struct Map_info * Map,FILE * out)419 int Vect_cidx_dump(const struct Map_info *Map, FILE * out)
420 {
421     int i, field, nfields, ntypes;
422 
423     G_debug(2, "Vect_cidx_dump()");
424 
425     check_status(Map);
426 
427     nfields = Vect_cidx_get_num_fields(Map);
428     fprintf(out, "---------- CATEGORY INDEX DUMP: Number of layers: %d "
429 	    "--------------------------------------\n", nfields);
430 
431     for (i = 0; i < nfields; i++) {
432 	int j, nucats, ncats;
433 
434 	field = Vect_cidx_get_field_number(Map, i);
435 	nucats = Vect_cidx_get_num_unique_cats_by_index(Map, i);
436 	ncats = Vect_cidx_get_num_cats_by_index(Map, i);
437 	ntypes = Vect_cidx_get_num_types_by_index(Map, i);
438 
439 	fprintf(out,
440 		"Layer %6d  number of unique cats: %7d  number of "
441 		"cats: %7d  number of types: %d\n",
442 		field, nucats, ncats, ntypes);
443 	fprintf(out, SEP);
444 
445 	fprintf(out, "            type |     count\n");
446 	for (j = 0; j < ntypes; j++) {
447 	    int type, count;
448 
449 	    Vect_cidx_get_type_count_by_index(Map, i, j, &type, &count);
450 	    fprintf(out, "           %5d | %9d\n", type, count);
451 	}
452 
453 	fprintf(out, " category | type | line/area\n");
454 	for (j = 0; j < ncats; j++) {
455 	    int cat, type, id;
456 
457 	    Vect_cidx_get_cat_by_index(Map, i, j, &cat, &type, &id);
458 	    fprintf(out, "%9d | %4d | %9d\n", cat, type, id);
459 	}
460 
461 	fprintf(out, SEP);
462     }
463 
464     return 1;
465 }
466 
467 /*!
468   \brief Save category index to binary file (cidx)
469 
470   \param Map pointer to Map_info structure
471 
472   \return 0 on success
473   \return 1 on error
474  */
Vect_cidx_save(struct Map_info * Map)475 int Vect_cidx_save(struct Map_info *Map)
476 {
477     struct Plus_head *plus;
478     char path[GPATH_MAX];
479     struct gvfile fp;
480 
481     G_debug(2, "Vect_cidx_save()");
482     check_status(Map);
483 
484     plus = &(Map->plus);
485 
486     dig_file_init(&fp);
487 
488     Vect__get_path(path, Map);
489     fp.file = G_fopen_new(path, GV_CIDX_ELEMENT);
490     if (fp.file == NULL) {
491 	G_warning(_("Unable to create category index file for vector map <%s>"),
492                   Vect_get_name(Map));
493 	return 1;
494     }
495 
496     /* set portable info */
497     dig_init_portable(&(plus->cidx_port), dig__byte_order_out());
498 
499     if (0 > dig_write_cidx(&fp, plus)) {
500 	G_warning(_("Error writing out category index file"));
501 	return 1;
502     }
503 
504     fclose(fp.file);
505 
506     return 0;
507 }
508 
509 /*!
510   \brief Read category index from cidx file if exists
511 
512   \param Map pointer to Map_info structure
513   \param head_only read only header of the file
514 
515   \return 0 on success
516   \return 1 if file does not exist
517   \return -1 error, file exists but cannot be read
518  */
Vect_cidx_open(struct Map_info * Map,int head_only)519 int Vect_cidx_open(struct Map_info *Map, int head_only)
520 {
521     int ret;
522     char file_path[GPATH_MAX], path[GPATH_MAX];
523     struct gvfile fp;
524     struct Plus_head *Plus;
525 
526     G_debug(2, "Vect_cidx_open(): name = %s mapset= %s", Map->name,
527 	    Map->mapset);
528 
529     Plus = &(Map->plus);
530 
531     Vect__get_path(path, Map);
532     Vect__get_element_path(file_path, Map, GV_CIDX_ELEMENT);
533 
534     if (access(file_path, F_OK) != 0) {	/* does not exist */
535 	return 1;
536     }
537 
538     dig_file_init(&fp);
539     fp.file = G_fopen_old(path, GV_CIDX_ELEMENT, Map->mapset);
540 
541     if (fp.file == NULL) {	/* category index file is not available */
542 	G_warning(_("Unable to open category index file for vector map <%s>"),
543 		  Vect_get_full_name(Map));
544 	return -1;
545     }
546 
547     /* load category index to memory */
548     ret = dig_read_cidx(&fp, Plus, head_only);
549 
550     fclose(fp.file);
551 
552     if (ret == 1) {
553 	G_debug(3, "Cannot read cidx");
554 	return -1;
555     }
556 
557     return 0;
558 }
559