/* **************************************************************************** * * MODULE: Vector library * * AUTHOR(S): Original author CERL, probably Dave Gerdes or Mike Higgins. * Update to GRASS 5.7 Radim Blazek and David D. Gray. * * PURPOSE: Higher level functions for reading/writing/manipulating vectors. * * COPYRIGHT: (C) 2001 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. * *****************************************************************************/ #include #include #include #include #include void delete_area_cats_from_cidx ( struct Map_info *Map, int area ) { int i; P_AREA *Area; static struct line_cats *Cats = NULL; G_debug ( 3, "delete_area_cats_from_cidx() area = %d", area ); Area = Map->plus.Area[area]; if ( !Area ) G_fatal_error ("BUG (delete_area_cats_from_cidx): Area %d does not exist", area ); if ( Area->centroid == 0 ) return; if ( !Cats ) Cats = Vect_new_cats_struct (); V2_read_line_nat ( Map, NULL, Cats, Area->centroid ); for ( i = 0 ; i < Cats->n_cats; i++ ) { dig_cidx_del_cat ( &(Map->plus), Cats->field[i], Cats->cat[i], area, GV_AREA ); } } void add_area_cats_to_cidx ( struct Map_info *Map, int area ) { int i; P_AREA *Area; static struct line_cats *Cats = NULL; G_debug ( 3, "add_area_cats_to_cidx() area = %d", area ); Area = Map->plus.Area[area]; if ( !Area ) G_fatal_error ("BUG (add_area_cats_to_cidx): Area %d does not exist", area ); if ( Area->centroid == 0 ) return; if ( !Cats ) Cats = Vect_new_cats_struct (); V2_read_line_nat ( Map, NULL, Cats, Area->centroid ); for ( i = 0 ; i < Cats->n_cats; i++ ) { dig_cidx_add_cat_sorted ( &(Map->plus), Cats->field[i], Cats->cat[i], area, GV_AREA ); } } long V1__rewrite_line_nat ( struct Map_info *Map, long offset, int type, struct line_pnts *points, struct line_cats *cats); /* Writes line to 'coor' file. * * Returns: offset into file * -1 on error */ long V1_write_line_nat ( struct Map_info *Map, int type, struct line_pnts *points, struct line_cats *cats) { long offset; if ( dig_fseek ( &(Map->dig_fp), 0L, SEEK_END) == -1 ) /* set to end of file */ return -1; offset = dig_ftell ( &(Map->dig_fp) ); if ( offset == -1 ) return -1; return V1__rewrite_line_nat (Map, offset, type, points, cats); } /* Writes line to 'coor' file. * * Returns: number of new line * -1 on error */ long V2_write_line_nat ( struct Map_info *Map, int type, struct line_pnts *points, struct line_cats *cats) { int i, s, n, line=0, next_line, area=0, node, side, first, sel_area; int new_area[2]; long offset; struct Plus_head *plus; BOUND_BOX box, abox; P_LINE *Line, *NLine; P_AREA *Area; P_NODE *Node; G_debug ( 3, "V2_write_line_nat()" ); offset = V1_write_line_nat ( Map, type, points, cats); if ( offset < 0 ) return -1; /* Update topology */ plus = &(Map->plus); /* Add line */ if ( plus->built >= GV_BUILD_BASE ) { line = dig_add_line ( plus, type, points, offset ); G_debug ( 3, " line added to topo with id = %d", line ); dig_line_box ( points, &box ); dig_line_set_box (plus, line, &box); if ( line == 1 ) Vect_box_copy (&(plus->box), &box); else Vect_box_extend (&(plus->box), &box); } /* Update areas. Areas are modified if: * 1) first or/and last point are existing nodes -> * - drop areas/islands whose boundaries are neighbour to this boundary at these nodes * - try build areas and islands for this boundary and neighbour boundaries going through these nodes * Question: may be by adding line created new area/isle which doesn't go through nodes of this line * old new line * +----+----+ +----+----+ +----+----+ * | A1 | A2 | + / -> | A1 | /| or + \ -> | A1 | A2 |\ * | | | | | | | | | * +----+----+ +----+----+ +----+----+ * I1 I1 I1 I1 * * - reattache all centroids/isles inside new area(s) * - attach new isle to area outside * 2) line is closed ring (node at the end is new, so it is not case above) * - build new area/isle * - check if it is island or contains island(s) * - reattache all centroids/isles inside new area(s) * - attach new isle to area outside * * Note that 1) and 2) is done by the same code. */ if ( plus->built >= GV_BUILD_AREAS ) { if ( type == GV_BOUNDARY ) { Line = plus->Line[line]; /* Delete neighbour areas/isles */ first = 1; for ( s = 1; s < 3; s++ ) { /* for each node */ if ( s == 1 ) node = Line->N1; /* Node 1 */ else node = Line->N2; G_debug ( 3, " delete neighbour areas/iseles: side = %d node = %d", s, node ); Node = plus->Node[node]; n = 0; for (i = 0; i < Node->n_lines; i++) { NLine = plus->Line[abs(Node->lines[i])]; if ( NLine->type == GV_BOUNDARY ) n++; } G_debug ( 3, " number of boundaries at node = %d", n ); if ( n > 2) { /* more than 2 boundaries at node ( >= 2 old + 1 new ) */ /* Line above (to the right), it is enough to check to the right, because if area/isle * exists it is the same to the left */ if ( s == 1 ) next_line = dig_angle_next_line (plus, line, GV_RIGHT, GV_BOUNDARY ); else next_line = dig_angle_next_line (plus, -line, GV_RIGHT, GV_BOUNDARY ); if ( next_line != 0 ) { /* there is a boundary to the right */ NLine = plus->Line[abs(next_line)]; if ( next_line > 0 ) /* the boundary is connected by 1. node */ area = NLine->right; /* we are interested just in this side (close to our line) */ else if ( next_line < 0 ) /* the boundary is connected by 2. node */ area = NLine->left; G_debug ( 3, " next_line = %d area = %d", next_line, area); if ( area > 0 ) { /* is area */ Vect_get_area_box ( Map, area, &box); if ( first ) { Vect_box_copy ( &abox, &box); first = 0; } else Vect_box_extend ( &abox, &box); if ( plus->update_cidx ) { delete_area_cats_from_cidx ( Map, area ); } dig_del_area ( plus, area ); } else if ( area < 0 ) { /* is isle */ dig_del_isle ( plus, -area ); } } } } /* Build new areas/isles. Thas true that we deleted also adjacent areas/isles, but if * they form new one our boundary must participate, so we need to build areas/isles * just for our boundary */ for (s = 1; s < 3; s++) { if ( s == 1 ) side = GV_LEFT; else side = GV_RIGHT; G_debug ( 3, " build area/isle on side = %d", side ); G_debug ( 3, "Build area for line = %d, side = %d", line, side ); area = Vect_build_line_area ( Map, line, side ); G_debug ( 3, "Build area for line = %d, side = %d", line, side ); if ( area > 0 ) { /* area */ Vect_get_area_box ( Map, area, &box); if ( first ) { Vect_box_copy ( &abox, &box); first = 0; } else Vect_box_extend ( &abox, &box); } else if ( area < 0 ) { /* isle -> must be attached -> add to abox */ Vect_get_isle_box ( Map, -area, &box); if ( first ) { Vect_box_copy ( &abox, &box); first = 0; } else Vect_box_extend ( &abox, &box); } new_area[s-1] = area; } /* Reattach all centroids/isles in deleted areas + new area. * Because isles are selected by box it covers also possible new isle created above */ if ( !first ) { /* i.e. old area/isle was deleted or new one created */ /* Reattache isles */ if ( plus->built >= GV_BUILD_ATTACH_ISLES ) Vect_attach_isles ( Map, &abox ); /* Reattach centroids */ if ( plus->built >= GV_BUILD_CENTROIDS ) Vect_attach_centroids ( Map, &abox ); } /* Add to category index */ if ( plus->update_cidx ) { for (s = 1; s < 3; s++) { if ( new_area[s-1] > 0 ) { add_area_cats_to_cidx ( Map, new_area[s-1] ); } } } } } /* Attach centroid */ if ( plus->built >= GV_BUILD_CENTROIDS ) { if ( type == GV_CENTROID ) { sel_area = Vect_find_area ( Map, points->x[0], points->y[0] ); G_debug ( 3, " new centroid %d is in area %d", line, sel_area); if ( sel_area > 0 ) { Area = plus->Area[sel_area]; Line = plus->Line[line]; if ( Area->centroid == 0 ) { /* first centroid */ G_debug ( 3, " first centroid -> attach to area"); Area->centroid = line; Line->left = sel_area; if ( plus->update_cidx ) { add_area_cats_to_cidx ( Map, sel_area ); } } else { /* duplicate centroid */ G_debug ( 3, " duplicate centroid -> do not attach to area"); Line->left = -sel_area; } } } } /* Add cetegory index */ for ( i = 0 ; i < cats->n_cats; i++ ) { dig_cidx_add_cat_sorted ( plus, cats->field[i], cats->cat[i], line, type ); } G_debug ( 3, "updated lines : %d , updated nodes : %d", plus->n_uplines, plus->n_upnodes ); return line; } /* Rewrites line at the given offset. * If the number of points or cats differs from * the original one or the type is changed: * GV_POINTS -> GV_LINES or GV_LINES -> GV_POINTS, * the old one is deleted and the * new is appended to the end of the file. * * Returns: line offset * -1 on error */ long V1_rewrite_line_nat ( struct Map_info *Map, long offset, int type, struct line_pnts *points, struct line_cats *cats) { int old_type; struct line_pnts *old_points; struct line_cats *old_cats; long new_offset; /* TODO: enable points and cats == NULL */ /* First compare numbers of points and cats with tha old one */ old_points = Vect_new_line_struct (); old_cats = Vect_new_cats_struct (); old_type = V1_read_line_nat ( Map, old_points, old_cats, offset ); if ( old_type == -1 ) return (-1); /* error */ if ( old_type != -2 /* EOF -> write new line */ && points->n_points == old_points->n_points && cats->n_cats == old_cats->n_cats && ( ( (type & GV_POINTS) && (old_type & GV_POINTS) ) || ( (type & GV_LINES ) && (old_type & GV_LINES ) ) ) ) { /* equal -> overwrite the old */ return V1__rewrite_line_nat (Map, offset, type, points, cats); } else { /* differ -> delete the old and append new */ /* delete old */ V1_delete_line_nat ( Map, offset); /* write new */ if ( dig_fseek ( &(Map->dig_fp), 0L, SEEK_END) == -1 ) /* end of file */ return -1; new_offset = dig_ftell ( &(Map->dig_fp) ); if ( new_offset == -1 ) return -1; return V1__rewrite_line_nat (Map, new_offset, type, points, cats); } } /* Rewrites line of given number. * * Returns: number of new line * -1 on error */ int V2_rewrite_line_nat ( struct Map_info *Map, int line, int type, struct line_pnts *points, struct line_cats *cats) { /* TODO: this is just quick shortcut because we have already V2_delete_nat() * and V2_write_nat() this function first deletes old line * and then writes new one. It is not very effective if number of points * and cats was not changed or topology is not changed (nodes not moved, * angles not changed etc.) */ V2_delete_line_nat ( Map, line ); return ( V2_write_line_nat ( Map, type, points, cats) ); } /* Rewrites line at the given offset. * * Returns: line offset * -1 on error */ long V1__rewrite_line_nat ( struct Map_info *Map, long offset, int type, struct line_pnts *points, struct line_cats *cats) { int i, n_points; char rhead, nc; short field; GVFILE *dig_fp; dig_set_cur_port (&(Map->head.port)); dig_fp = &(Map->dig_fp); if ( dig_fseek ( dig_fp, offset, 0) == -1 ) return -1; /* first byte: 0 bit: 1 - alive, 0 - dead * 1 bit: 1 - categories, 0 - no category * 2-3 bit: store type * 4-5 bit: reserved for store type expansion * 6-7 bit: not used */ rhead = (char) dig_type_to_store ( type ); rhead <<= 2; if (cats->n_cats > 0) { rhead |= 0x02; } rhead |= 0x01; /* written/rewritten is always alive */ if (0 >= dig__fwrite_port_C (&rhead, 1, dig_fp)) { return -1; } if (cats->n_cats > 0) { if ( Map->head.Version_Minor == 1 ) { /* coor format 5.1 */ if (0 >= dig__fwrite_port_I ( &(cats->n_cats), 1, dig_fp)) return -1; } else { /* coor format 5.0 */ nc = (char) cats->n_cats; if (0 >= dig__fwrite_port_C (&nc, 1, dig_fp)) return -1; } if (cats->n_cats > 0) { if ( Map->head.Version_Minor == 1 ) { /* coor format 5.1 */ if (0 >= dig__fwrite_port_I (cats->field, cats->n_cats, dig_fp)) return -1; } else { /* coor format 5.0 */ for (i = 0; i < cats->n_cats; i++) { field = (short) cats->field[i]; if (0 >= dig__fwrite_port_S (&field, 1, dig_fp)) return -1; } } if (0 >= dig__fwrite_port_I (cats->cat, cats->n_cats, dig_fp)) return -1; } } if ( type & GV_POINTS ) { n_points = 1; } else { n_points = points->n_points; if (0 >= dig__fwrite_port_I (&n_points, 1, dig_fp) ) return -1; } if (0 >= dig__fwrite_port_D (points->x, n_points, dig_fp)) return -1; if (0 >= dig__fwrite_port_D (points->y, n_points, dig_fp)) return -1; if ( Map->head.with_z ) { if (0 >= dig__fwrite_port_D (points->z, n_points, dig_fp)) return -1; } if ( 0 != dig_fflush (dig_fp) ) return -1; return offset; } /* Deletes line at the given offset. * * Returns: 0 ok * -1 on error */ int V1_delete_line_nat ( struct Map_info *Map, long offset ) { char rhead; GVFILE *dig_fp; G_debug ( 3, "V1_delete_line_nat(), offset = %ld", offset ); dig_set_cur_port (&(Map->head.port)); dig_fp = &(Map->dig_fp); if ( dig_fseek ( dig_fp, offset, 0) == -1 ) return -1; /* read old */ if (0 >= dig__fread_port_C (&rhead, 1, dig_fp)) return (-1); rhead &= 0xFE; if ( dig_fseek (dig_fp, offset, 0) == -1 ) return -1; if (0 >= dig__fwrite_port_C (&rhead, 1, dig_fp)) return -1; if ( 0 != dig_fflush (dig_fp) ) return -1; return 0; } /* Deletes line of given number. * * Returns: 0 ok * -1 on error */ int V2_delete_line_nat ( struct Map_info *Map, int line ) { int ret, i, side, type=0, first=0, next_line, area; P_LINE *Line = NULL; P_AREA *Area; struct Plus_head *plus; BOUND_BOX box, abox; int adjacent[4], n_adjacent=0; static struct line_cats *Cats = NULL; G_debug ( 3, "V2_delete_line_nat(), line = %d", line ); plus = &(Map->plus); if ( plus->built >= GV_BUILD_BASE ) { Line = Map->plus.Line[line]; if ( Line == NULL ) G_fatal_error ("Attempt to delete dead line"); type = Line->type; } if ( !Cats ) { Cats = Vect_new_cats_struct (); } /* Update category index */ if ( plus->update_cidx ) { type = V2_read_line_nat ( Map, NULL, Cats, line ); for ( i = 0 ; i < Cats->n_cats; i++ ) { dig_cidx_del_cat ( plus, Cats->field[i], Cats->cat[i], line, type ); } } /* delete the line from coor */ ret = V1_delete_line_nat (Map, Line->offset); if ( ret == -1 ) { return ret; } /* Update topology */ if ( plus->built >= GV_BUILD_AREAS && type == GV_BOUNDARY ) { /* Store adjacent boundaries at nodes (will be used to rebuild area/isle) */ /* Adjacent are stored: > 0 - we want right side; < 0 - we want left side */ n_adjacent = 0; next_line = dig_angle_next_line (plus, line, GV_RIGHT, GV_BOUNDARY ); if ( next_line != 0 && abs(next_line) != line ) { /* N1, to the right -> we want the right side for > 0 and left for < 0*/ adjacent[n_adjacent] = next_line; n_adjacent++; } next_line = dig_angle_next_line (plus, line, GV_LEFT, GV_BOUNDARY ); if ( next_line != 0 && abs(next_line) != line ) { /* N1, to the left -> we want the left side for > 0 and right for < 0*/ adjacent[n_adjacent] = -next_line; n_adjacent++; } next_line = dig_angle_next_line (plus, -line, GV_RIGHT, GV_BOUNDARY ); if ( next_line != 0 && abs(next_line) != line ) { /* N2, to the right -> we want the right side for > 0 and left for < 0*/ adjacent[n_adjacent] = next_line; n_adjacent++; } next_line = dig_angle_next_line (plus, -line, GV_LEFT, GV_BOUNDARY ); if ( next_line != 0 && abs(next_line) != line ) { /* N2, to the left -> we want the left side for > 0 and right for < 0*/ adjacent[n_adjacent] = -next_line; n_adjacent++; } /* Delete area(s) and islands this line forms */ first = 1; if ( Line->left > 0 ) { /* delete area */ Vect_get_area_box ( Map, Line->left, &box); if ( first ) { Vect_box_copy ( &abox, &box); first = 0; } else Vect_box_extend ( &abox, &box); if ( plus->update_cidx ) { delete_area_cats_from_cidx ( Map, Line->left ); } dig_del_area ( plus, Line->left ); } else if ( Line->left < 0 ) { /* delete isle */ dig_del_isle ( plus, -Line->left ); } if ( Line->right > 0 ) { /* delete area */ Vect_get_area_box ( Map, Line->right, &box); if ( first ) { Vect_box_copy ( &abox, &box); first = 0; } else Vect_box_extend ( &abox, &box); if ( plus->update_cidx ) { delete_area_cats_from_cidx ( Map, Line->right ); } dig_del_area ( plus, Line->right ); } else if ( Line->right < 0 ) { /* delete isle */ dig_del_isle ( plus, -Line->right ); } } /* Delete reference from area */ if ( plus->built >= GV_BUILD_CENTROIDS && type == GV_CENTROID ) { if ( Line->left > 0 ) { G_debug ( 3, "Remove centroid %d from area %d", line, Line->left ); if ( plus->update_cidx ) { delete_area_cats_from_cidx ( Map, Line->left ); } Area = Map->plus.Area[Line->left]; Area->centroid = 0; } } /* delete the line from topo */ dig_del_line ( plus, line ); /* Rebuild areas/isles and attach centroids and isles */ if ( plus->built >= GV_BUILD_AREAS && type == GV_BOUNDARY ) { int *new_areas, nnew_areas; nnew_areas = 0; new_areas = (int *) G_malloc ( 2 * n_adjacent * sizeof(int) ); /* Rebuild areas/isles */ for (i = 0; i < n_adjacent; i++) { if ( adjacent[i] > 0 ) side = GV_RIGHT; else side = GV_LEFT; G_debug ( 3, "Build area for line = %d, side = %d", adjacent[i], side ); area = Vect_build_line_area ( Map, abs(adjacent[i]), side ); if ( area > 0 ) { /* area */ Vect_get_area_box ( Map, area, &box); if ( first ) { Vect_box_copy ( &abox, &box); first = 0; } else Vect_box_extend ( &abox, &box); new_areas[nnew_areas] = area; nnew_areas++; } else if ( area < 0 ) { /* isle -> must be attached -> add to abox */ Vect_get_isle_box ( Map, -area, &box); if ( first ) { Vect_box_copy ( &abox, &box); first = 0; } else Vect_box_extend ( &abox, &box); } } /* Reattach all centroids/isles in deleted areas + new area. * Because isles are selected by box it covers also possible new isle created above */ if ( !first ) { /* i.e. old area/isle was deleted or new one created */ /* Reattache isles */ if ( plus->built >= GV_BUILD_ATTACH_ISLES ) Vect_attach_isles ( Map, &abox ); /* Reattach centroids */ if ( plus->built >= GV_BUILD_CENTROIDS ) Vect_attach_centroids ( Map, &abox ); } if ( plus->update_cidx ) { for (i = 0; i < nnew_areas; i++ ) { add_area_cats_to_cidx ( Map, new_areas[i] ); } } } G_debug ( 3, "updated lines : %d , updated nodes : %d", plus->n_uplines, plus->n_upnodes ); return ret; }