/*! \file lib/vector/Vlib/cindex.c \brief Vector library - category index management Higher level functions for reading/writing/manipulating vectors. (C) 2001-2013 by the 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 Radim Blazek \author Some contribution by Martin Landa */ #include #include #include #include #include #include #include #include "local_proto.h" #define SEP "------------------------------------------------------------------------------------------\n" static void check_status(const struct Map_info *Map) { if (!Map->plus.cidx_up_to_date) G_fatal_error(_("Category index is not up to date")); } static void check_index(const struct Map_info *Map, int index) { if (index < 0 || index >= Map->plus.n_cidx) G_fatal_error(_("Layer index out of range")); } /* search for first occurrence of cat in cat index, starting at first */ static int ci_search_cat(struct Cat_index *ci, int first, int cat) { int lo, hi, mid; lo = first; if (lo < 0) lo = 0; if (ci->cat[lo][0] > cat) return -1; if (ci->cat[lo][0] == cat) return lo; hi = ci->n_cats - 1; if (first > hi) return -1; /* deferred test for equality */ while (lo < hi) { mid = (lo + hi) >> 1; if (ci->cat[mid][0] < cat) lo = mid + 1; else hi = mid; } if (ci->cat[lo][0] == cat) return lo; return -1; } /*! \brief Get number of layers in category index \param Map pointer to Map_info structure \return number of layers */ int Vect_cidx_get_num_fields(const struct Map_info *Map) { check_status(Map); return Map->plus.n_cidx; } /*! \brief Get layer number for given index G_fatal_error() is called when index not found. \param Map pointer to Map_info structure \param index layer index: from 0 to Vect_cidx_get_num_fields() - 1 \return layer number */ int Vect_cidx_get_field_number(const struct Map_info *Map, int index) { check_status(Map); check_index(Map, index); return Map->plus.cidx[index].field; } /*! \brief Get layer index for given layer number \param Map pointer to Map_info structure \param field layer number \return layer index \return -1 if not found */ int Vect_cidx_get_field_index(const struct Map_info *Map, int field) { int i; const struct Plus_head *Plus; G_debug(2, "Vect_cidx_get_field_index() field = %d", field); check_status(Map); Plus = &(Map->plus); for (i = 0; i < Plus->n_cidx; i++) { if (Plus->cidx[i].field == field) return i; } return -1; } /*! \brief Get number of unique categories for given layer index G_fatal_error() is called when index not found. \param Map pointer to Map_info structure \param index layer index (starts at 0) \return number of unique categories \return -1 on error */ int Vect_cidx_get_num_unique_cats_by_index(const struct Map_info *Map, int index) { check_status(Map); check_index(Map, index); return Map->plus.cidx[index].n_ucats; } /*! \brief Get number of categories for given layer index \param Map pointer to Map_info structure \param index layer index \return number of categories \return -1 on error */ int Vect_cidx_get_num_cats_by_index(const struct Map_info *Map, int index) { check_status(Map); check_index(Map, index); return Map->plus.cidx[index].n_cats; } /*! \brief Get number of feature types for given layer index G_fatal_error() is called when index not found. \param Map pointer to Map_info structure \param field_index layer index \return number of feature types \return -1 on error */ int Vect_cidx_get_num_types_by_index(const struct Map_info *Map, int field_index) { check_status(Map); check_index(Map, field_index); return Map->plus.cidx[field_index].n_types; } /*! \brief Get count of feature types for given field and type index \param Map pointer to Map_info structure \param field_index layer index \param type_index type index \param[out] type feature type (GV_POINT, ...) \param[out] count number of features or NULL \return 1 on success \return 0 on error */ int Vect_cidx_get_type_count_by_index(const struct Map_info *Map, int field_index, int type_index, int *type, int *count) { check_status(Map); check_index(Map, field_index); *type = Map->plus.cidx[field_index].type[type_index][0]; if (count) *count = Map->plus.cidx[field_index].type[type_index][1]; return 1; } /*! \brief Get count of features of certain type by layer and type \param Map pointer to Map_info structure \param field layer number \param type feature type \return feature count \return 0 if no features, no such field or no such type in category index */ int Vect_cidx_get_type_count(const struct Map_info *Map, int field, int type) { int i, fi, count = 0; G_debug(3, "Vect_cidx_get_type_count() field = %d, type = %d", field, type); check_status(Map); if ((fi = Vect_cidx_get_field_index(Map, field)) < 0) return 0; /* field not found */ G_debug(3, "field_index = %d", fi); G_debug(3, "ntypes = %d", Map->plus.cidx[fi].n_types); for (i = 0; i < Map->plus.cidx[fi].n_types; i++) { int tp, cnt; tp = Map->plus.cidx[fi].type[i][0]; cnt = Map->plus.cidx[fi].type[i][1]; if (tp & type) count += cnt; G_debug(3, "%d tp = %d, cnt= %d count = %d", i, tp, cnt, count); } return count; } /*! \brief Get category, feature type and id for given layer and category index \param Map pointer to Map_info structure \param field_index layer index \param cat_index category index \param[out] cat category number \param[out] type feature type \param[out] id feature id \return 1 on success \return 0 on error */ int Vect_cidx_get_cat_by_index(const struct Map_info *Map, int field_index, int cat_index, int *cat, int *type, int *id) { check_status(Map); /* This check is slow ? */ check_index(Map, field_index); if (cat_index < 0 || cat_index >= Map->plus.cidx[field_index].n_cats) G_fatal_error(_("Category index out of range")); *cat = Map->plus.cidx[field_index].cat[cat_index][0]; *type = Map->plus.cidx[field_index].cat[cat_index][1]; *id = Map->plus.cidx[field_index].cat[cat_index][2]; return 1; } /*! \brief Get list of unique categories for given layer index \param Map pointer to Map_info structure \param field_index layer index \param[out] list output list of cats \return 1 on success \return 0 on error */ int Vect_cidx_get_unique_cats_by_index(struct Map_info *Map, int field_index, struct ilist *list) { int c; struct Cat_index *ci; check_status(Map); check_index(Map, field_index); ci = &(Map->plus.cidx[field_index]); /* force sorting index -- really needed? */ dig_cidx_sort(&(Map->plus)); Vect_reset_list(list); if (ci->n_cats > 0) Vect_list_append(list, ci->cat[0][0]); for (c = 1; c < ci->n_cats; c++) { if (ci->cat[c][0] != ci->cat[c - 1][0]) Vect_list_append(list, ci->cat[c][0]); } return list->n_values == ci->n_ucats ? 1 : 0; } /*! \brief Find next line/area id for given category, start_index and type_mask \param Map pointer to Map_info structure \param field_index layer index \param cat category number \param type_mask requested feature type \param start_index start search at this index (0 - whole category index) \param[out] type returned type \param[out] id returned line/area id \return index to array \return -1 not found */ int Vect_cidx_find_next(const struct Map_info *Map, int field_index, int cat, int type_mask, int start_index, int *type, int *id) { int cat_index; struct Cat_index *ci; G_debug(3, "Vect_cidx_find_next() cat = %d, type_mask = %d, start_index = %d", cat, type_mask, start_index); check_status(Map); /* This check is slow ? */ check_index(Map, field_index); *type = *id = 0; /* pointer to category index */ ci = &(Map->plus.cidx[field_index]); cat_index = ci_search_cat(ci, start_index, cat); G_debug(3, "cat_index = %d", cat_index); if (cat_index < 0) return -1; do { G_debug(3, " cat_index = %d", cat_index); if (ci->cat[cat_index][0] == cat && ci->cat[cat_index][1] & type_mask) { *type = ci->cat[cat_index][1]; *id = ci->cat[cat_index][2]; G_debug(3, " type match -> record found"); return cat_index; } cat_index++; } while (cat_index < ci->n_cats); return -1; } /*! \brief Find all line/area id's for given category \param Map pointer to Map_info structure \param layer layer number \param type_mask feature type of objects to search for \param cat category number \param[out] lines array of ids of found lines/points */ void Vect_cidx_find_all(const struct Map_info *Map, int layer, int type_mask, int cat, struct ilist *lines) { int type, line; struct Cat_index *ci; int field_index, idx; Vect_reset_list(lines); field_index = Vect_cidx_get_field_index(Map, layer); if (field_index == -1) { /* not found */ return; } ci = &(Map->plus.cidx[field_index]); if ((type_mask & GV_AREA) && type_mask != GV_AREA) G_fatal_error(_("Mixing IDs of areas and primitives")); idx = Vect_cidx_find_next(Map, field_index, cat, type_mask, 0, &type, &line); if (idx == -1) { return; } do { if (ci->cat[idx][0] != cat) { break; } if (ci->cat[idx][1] & type_mask) { Vect_list_append(lines, ci->cat[idx][2]); } idx++; } while (idx < ci->n_cats); return; } /*! \brief Write (dump) category index in text form to file \param Map pointer to Map_info structure \param[out] out output file \return 1 on success \return 0 on error */ int Vect_cidx_dump(const struct Map_info *Map, FILE * out) { int i, field, nfields, ntypes; G_debug(2, "Vect_cidx_dump()"); check_status(Map); nfields = Vect_cidx_get_num_fields(Map); fprintf(out, "---------- CATEGORY INDEX DUMP: Number of layers: %d " "--------------------------------------\n", nfields); for (i = 0; i < nfields; i++) { int j, nucats, ncats; field = Vect_cidx_get_field_number(Map, i); nucats = Vect_cidx_get_num_unique_cats_by_index(Map, i); ncats = Vect_cidx_get_num_cats_by_index(Map, i); ntypes = Vect_cidx_get_num_types_by_index(Map, i); fprintf(out, "Layer %6d number of unique cats: %7d number of " "cats: %7d number of types: %d\n", field, nucats, ncats, ntypes); fprintf(out, SEP); fprintf(out, " type | count\n"); for (j = 0; j < ntypes; j++) { int type, count; Vect_cidx_get_type_count_by_index(Map, i, j, &type, &count); fprintf(out, " %5d | %9d\n", type, count); } fprintf(out, " category | type | line/area\n"); for (j = 0; j < ncats; j++) { int cat, type, id; Vect_cidx_get_cat_by_index(Map, i, j, &cat, &type, &id); fprintf(out, "%9d | %4d | %9d\n", cat, type, id); } fprintf(out, SEP); } return 1; } /*! \brief Save category index to binary file (cidx) \param Map pointer to Map_info structure \return 0 on success \return 1 on error */ int Vect_cidx_save(struct Map_info *Map) { struct Plus_head *plus; char path[GPATH_MAX]; struct gvfile fp; G_debug(2, "Vect_cidx_save()"); check_status(Map); plus = &(Map->plus); dig_file_init(&fp); Vect__get_path(path, Map); fp.file = G_fopen_new(path, GV_CIDX_ELEMENT); if (fp.file == NULL) { G_warning(_("Unable to create category index file for vector map <%s>"), Vect_get_name(Map)); return 1; } /* set portable info */ dig_init_portable(&(plus->cidx_port), dig__byte_order_out()); if (0 > dig_write_cidx(&fp, plus)) { G_warning(_("Error writing out category index file")); return 1; } fclose(fp.file); return 0; } /*! \brief Read category index from cidx file if exists \param Map pointer to Map_info structure \param head_only read only header of the file \return 0 on success \return 1 if file does not exist \return -1 error, file exists but cannot be read */ int Vect_cidx_open(struct Map_info *Map, int head_only) { int ret; char file_path[GPATH_MAX], path[GPATH_MAX]; struct gvfile fp; struct Plus_head *Plus; G_debug(2, "Vect_cidx_open(): name = %s mapset= %s", Map->name, Map->mapset); Plus = &(Map->plus); Vect__get_path(path, Map); Vect__get_element_path(file_path, Map, GV_CIDX_ELEMENT); if (access(file_path, F_OK) != 0) { /* does not exist */ return 1; } dig_file_init(&fp); fp.file = G_fopen_old(path, GV_CIDX_ELEMENT, Map->mapset); if (fp.file == NULL) { /* category index file is not available */ G_warning(_("Unable to open category index file for vector map <%s>"), Vect_get_full_name(Map)); return -1; } /* load category index to memory */ ret = dig_read_cidx(&fp, Plus, head_only); fclose(fp.file); if (ret == 1) { G_debug(3, "Cannot read cidx"); return -1; } return 0; }