/*! \file lib/vector/Vlib/remove_areas.c \brief Vector library - clean geometry (remove small areas) Higher level functions for reading/writing/manipulating vectors. (C) 2001-2009 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, Markus Metz */ #include #include #include int Vect_remove_small_areas_nat(struct Map_info *, double, struct Map_info *, double *); int Vect_remove_small_areas_ext(struct Map_info *, double, struct Map_info *, double *); /*! \brief Remove small areas from the map map. Centroid of the area and the longest boundary with adjacent area is removed. Map topology must be built GV_BUILD_CENTROIDS. \param[in,out] Map vector map \param thresh maximum area size for removed areas \param[out] Err vector map where removed lines and centroids are written \param removed_area pointer to where total size of removed area is stored or NULL \return number of removed areas */ int Vect_remove_small_areas(struct Map_info *Map, double thresh, struct Map_info *Err, double *removed_area) { if (Map->format == GV_FORMAT_NATIVE) return Vect_remove_small_areas_nat(Map, thresh, Err, removed_area); else return Vect_remove_small_areas_ext(Map, thresh, Err, removed_area); } int Vect_remove_small_areas_ext(struct Map_info *Map, double thresh, struct Map_info *Err, double *removed_area) { int area, nareas; int nremoved = 0; struct ilist *List; struct ilist *AList; struct line_pnts *Points; struct line_cats *Cats; double size_removed = 0.0; List = Vect_new_list(); AList = Vect_new_list(); Points = Vect_new_line_struct(); Cats = Vect_new_cats_struct(); nareas = Vect_get_num_areas(Map); for (area = 1; area <= nareas; area++) { int i, j, centroid, dissolve_neighbour; double length, size; G_percent(area, nareas, 1); G_debug(3, "area = %d", area); if (!Vect_area_alive(Map, area)) continue; size = Vect_get_area_area(Map, area); if (size > thresh) continue; size_removed += size; /* The area is smaller than the limit -> remove */ /* Remove centroid */ centroid = Vect_get_area_centroid(Map, area); if (centroid > 0) { if (Err) { Vect_read_line(Map, Points, Cats, centroid); Vect_write_line(Err, GV_CENTROID, Points, Cats); } Vect_delete_line(Map, centroid); } /* Find the adjacent area with which the longest boundary is shared */ Vect_get_area_boundaries(Map, area, List); /* Create a list of neighbour areas */ Vect_reset_list(AList); for (i = 0; i < List->n_values; i++) { int line, left, right, neighbour; line = List->value[i]; if (!Vect_line_alive(Map, abs(line))) /* Should not happen */ G_fatal_error(_("Area is composed of dead boundary")); Vect_get_line_areas(Map, abs(line), &left, &right); if (line > 0) neighbour = left; else neighbour = right; G_debug(4, " line = %d left = %d right = %d neighbour = %d", line, left, right, neighbour); Vect_list_append(AList, neighbour); /* this checks for duplicity */ } G_debug(3, "num neighbours = %d", AList->n_values); /* Go through the list of neighbours and find that with the longest boundary */ dissolve_neighbour = 0; length = -1.0; for (i = 0; i < AList->n_values; i++) { int neighbour1; double l = 0.0; neighbour1 = AList->value[i]; G_debug(4, " neighbour1 = %d", neighbour1); for (j = 0; j < List->n_values; j++) { int line, left, right, neighbour2; line = List->value[j]; Vect_get_line_areas(Map, abs(line), &left, &right); if (line > 0) neighbour2 = left; else neighbour2 = right; if (neighbour2 == neighbour1) { Vect_read_line(Map, Points, NULL, abs(line)); l += Vect_line_length(Points); } } if (l > length) { length = l; dissolve_neighbour = neighbour1; } } G_debug(3, "dissolve_neighbour = %d", dissolve_neighbour); /* Make list of boundaries to be removed */ Vect_reset_list(AList); for (i = 0; i < List->n_values; i++) { int line, left, right, neighbour; line = List->value[i]; Vect_get_line_areas(Map, abs(line), &left, &right); if (line > 0) neighbour = left; else neighbour = right; G_debug(3, " neighbour = %d", neighbour); if (neighbour == dissolve_neighbour) { Vect_list_append(AList, abs(line)); } } /* Remove boundaries */ for (i = 0; i < AList->n_values; i++) { int line; line = AList->value[i]; if (Err) { Vect_read_line(Map, Points, Cats, line); Vect_write_line(Err, GV_BOUNDARY, Points, Cats); } Vect_delete_line(Map, line); } nremoved++; nareas = Vect_get_num_areas(Map); } if (removed_area) *removed_area = size_removed; G_message(_("%d areas of total size %g removed"), nremoved, size_removed); return (nremoved); } /* much faster version */ int Vect_remove_small_areas_nat(struct Map_info *Map, double thresh, struct Map_info *Err, double *removed_area) { int area, nareas; int nremoved = 0; struct ilist *List; struct ilist *AList; struct ilist *BList; struct ilist *NList; struct ilist *IList; struct line_pnts *Points; struct line_cats *Cats; double size_removed = 0.0; int dissolve_neighbour; int line, left, right, neighbour; int nisles, nnisles; List = Vect_new_list(); AList = Vect_new_list(); BList = Vect_new_list(); NList = Vect_new_list(); IList = Vect_new_list(); Points = Vect_new_line_struct(); Cats = Vect_new_cats_struct(); nareas = Vect_get_num_areas(Map); for (area = 1; area <= nareas; area++) { int i, j, centroid, ncentroid; double length, l, size; int outer_area = -1; int narea, same_atype = 0; G_percent(area, nareas, 1); G_debug(3, "area = %d", area); if (!Vect_area_alive(Map, area)) continue; size = Vect_get_area_area(Map, area); if (size > thresh) continue; size_removed += size; /* The area is smaller than the limit -> remove */ /* Remove centroid */ centroid = Vect_get_area_centroid(Map, area); if (centroid > 0) { if (Err) { Vect_read_line(Map, Points, Cats, centroid); Vect_write_line(Err, GV_CENTROID, Points, Cats); } Vect_delete_line(Map, centroid); } /* Find the adjacent area with which the longest boundary is shared */ Vect_get_area_boundaries(Map, area, List); /* Create a list of neighbour areas */ Vect_reset_list(AList); for (i = 0; i < List->n_values; i++) { line = List->value[i]; if (!Vect_line_alive(Map, abs(line))) /* Should not happen */ G_fatal_error(_("Area is composed of dead boundary")); Vect_get_line_areas(Map, abs(line), &left, &right); if (line > 0) neighbour = left; else neighbour = right; G_debug(4, " line = %d left = %d right = %d neighbour = %d", line, left, right, neighbour); ncentroid = 0; if (neighbour > 0) { ncentroid = Vect_get_area_centroid(Map, neighbour); } if (neighbour < 0) { narea = Vect_get_isle_area(Map, -neighbour); if (narea > 0) ncentroid = Vect_get_area_centroid(Map, narea); } if ((centroid != 0) + (ncentroid != 0) != 1) same_atype = 1; Vect_list_append(AList, neighbour); /* this checks for duplicity */ } G_debug(3, "num neighbours = %d", AList->n_values); if (AList->n_values == 1) same_atype = 0; /* Go through the list of neighbours and find the one with the longest boundary */ dissolve_neighbour = 0; length = -1.0; for (i = 0; i < AList->n_values; i++) { int neighbour1; l = 0.0; neighbour1 = AList->value[i]; G_debug(4, " neighbour1 = %d", neighbour1); if (same_atype) { ncentroid = 0; if (neighbour1 > 0) { ncentroid = Vect_get_area_centroid(Map, neighbour1); } if (neighbour1 < 0) { narea = Vect_get_isle_area(Map, -neighbour1); if (narea > 0) ncentroid = Vect_get_area_centroid(Map, narea); } if ((centroid != 0) + (ncentroid != 0) == 1) continue; } for (j = 0; j < List->n_values; j++) { int neighbour2; line = List->value[j]; Vect_get_line_areas(Map, abs(line), &left, &right); if (line > 0) neighbour2 = left; else neighbour2 = right; if (neighbour2 == neighbour1) { Vect_read_line(Map, Points, NULL, abs(line)); l += Vect_line_length(Points); } } if (l > length) { length = l; dissolve_neighbour = neighbour1; } } G_debug(3, "dissolve_neighbour = %d", dissolve_neighbour); if (dissolve_neighbour == 0) { G_fatal_error("could not find neighbour to dissolve"); } /* Make list of boundaries to be removed */ Vect_reset_list(AList); Vect_reset_list(BList); for (i = 0; i < List->n_values; i++) { line = List->value[i]; Vect_get_line_areas(Map, abs(line), &left, &right); if (line > 0) neighbour = left; else neighbour = right; G_debug(3, " neighbour = %d", neighbour); if (neighbour == dissolve_neighbour) { Vect_list_append(AList, abs(line)); } else Vect_list_append(BList, line); } G_debug(3, "remove %d of %d boundaries", AList->n_values, List->n_values); /* Get isles inside area */ Vect_reset_list(IList); if ((nisles = Vect_get_area_num_isles(Map, area)) > 0) { for (i = 0; i < nisles; i++) { Vect_list_append(IList, Vect_get_area_isle(Map, area, i)); } } /* Remove boundaries */ for (i = 0; i < AList->n_values; i++) { int ret; line = AList->value[i]; if (Err) { Vect_read_line(Map, Points, Cats, line); Vect_write_line(Err, GV_BOUNDARY, Points, Cats); } /* Vect_delete_line(Map, line); */ /* delete the line from coor */ ret = V1_delete_line_nat(Map, Map->plus.Line[line]->offset); if (ret == -1) { G_fatal_error(_("Could not delete line from coor")); } } /* update topo */ if (dissolve_neighbour > 0) { G_debug(3, "dissolve with neighbour area"); /* get neighbour centroid */ centroid = Vect_get_area_centroid(Map, dissolve_neighbour); /* get neighbour isles */ if ((nnisles = Vect_get_area_num_isles(Map, dissolve_neighbour)) > 0) { for (i = 0; i < nnisles; i++) { Vect_list_append(IList, Vect_get_area_isle(Map, dissolve_neighbour, i)); } } /* get neighbour boundaries */ Vect_get_area_boundaries(Map, dissolve_neighbour, NList); /* delete area from topo */ dig_del_area(&(Map->plus), area); /* delete neighbour area from topo */ dig_del_area(&(Map->plus), dissolve_neighbour); /* delete boundaries from topo */ for (i = 0; i < AList->n_values; i++) { struct P_topo_b *topo; struct P_node *Node; line = AList->value[i]; topo = (struct P_topo_b *)Map->plus.Line[line]->topo; Node = Map->plus.Node[topo->N1]; dig_del_line(&(Map->plus), line, Node->x, Node->y, Node->z); } /* build new area from leftover boundaries of deleted area */ for (i = 0; i < BList->n_values; i++) { struct P_topo_b *topo; int new_isle; line = BList->value[i]; topo = Map->plus.Line[abs(line)]->topo; if (topo->left == 0 || topo->right == 0) { new_isle = Vect_build_line_area(Map, abs(line), (line > 0 ? GV_RIGHT : GV_LEFT)); if (new_isle > 0) { if (outer_area > 0) G_fatal_error("dissolve_neighbour > 0, new area has already been created"); outer_area = new_isle; /* reattach centroid */ Map->plus.Area[outer_area]->centroid = centroid; if (centroid > 0) { struct P_topo_c *ctopo = Map->plus.Line[centroid]->topo; ctopo->area = outer_area; } } else if (new_isle < 0) { /* leftover boundary creates a new isle */ Vect_list_append(IList, -new_isle); } else { /* neither area nor isle, should not happen */ G_fatal_error(_("dissolve_neighbour > 0, failed to build new area")); } } /* check */ if (topo->left == 0 || topo->right == 0) G_fatal_error(_("Dissolve with neighbour area: corrupt topology")); } /* build new area from neighbour's boundaries */ for (i = 0; i < NList->n_values; i++) { struct P_topo_b *topo; line = NList->value[i]; if (!Vect_line_alive(Map, abs(line))) continue; topo = Map->plus.Line[abs(line)]->topo; if (topo->left == 0 || topo->right == 0) { int new_isle; new_isle = Vect_build_line_area(Map, abs(line), (line > 0 ? GV_RIGHT : GV_LEFT)); if (new_isle > 0) { if (outer_area > 0) G_fatal_error("dissolve_neighbour > 0, new area has already been created"); outer_area = new_isle; /* reattach centroid */ Map->plus.Area[outer_area]->centroid = centroid; if (centroid > 0) { struct P_topo_c *ctopo = Map->plus.Line[centroid]->topo; ctopo->area = outer_area; } } else if (new_isle < 0) { /* Neigbour's boundary creates a new isle */ Vect_list_append(IList, -new_isle); } else { /* neither area nor isle, should not happen */ G_fatal_error(_("Failed to build new area")); } } if (topo->left == 0 || topo->right == 0) G_fatal_error(_("Dissolve with neighbour area: corrupt topology")); } } /* dissolve with outer isle */ else if (dissolve_neighbour < 0) { G_debug(3, "dissolve with outer isle"); outer_area = Vect_get_isle_area(Map, -dissolve_neighbour); /* get isle boundaries */ Vect_get_isle_boundaries(Map, -dissolve_neighbour, NList); /* delete area from topo */ dig_del_area(&(Map->plus), area); /* delete isle from topo */ dig_del_isle(&(Map->plus), -dissolve_neighbour); /* delete boundaries from topo */ for (i = 0; i < AList->n_values; i++) { struct P_topo_b *topo; struct P_node *Node; line = AList->value[i]; topo = (struct P_topo_b *)Map->plus.Line[line]->topo; Node = Map->plus.Node[topo->N1]; dig_del_line(&(Map->plus), line, Node->x, Node->y, Node->z); } /* build new isle(s) from leftover boundaries */ for (i = 0; i < BList->n_values; i++) { struct P_topo_b *topo; line = BList->value[i]; topo = Map->plus.Line[abs(line)]->topo; if (topo->left == 0 || topo->right == 0) { int new_isle; new_isle = Vect_build_line_area(Map, abs(line), (line > 0 ? GV_RIGHT : GV_LEFT)); if (new_isle < 0) { Vect_list_append(IList, -new_isle); } else { /* area or nothing should not happen */ G_fatal_error(_("Failed to build new isle")); } } /* check */ if (topo->left == 0 || topo->right == 0) G_fatal_error(_("Dissolve with outer isle: corrupt topology")); } /* build new isle(s) from old isle's boundaries */ for (i = 0; i < NList->n_values; i++) { struct P_topo_b *topo; line = NList->value[i]; if (!Vect_line_alive(Map, abs(line))) continue; topo = Map->plus.Line[abs(line)]->topo; if (topo->left == 0 || topo->right == 0) { int new_isle; new_isle = Vect_build_line_area(Map, abs(line), (line > 0 ? GV_RIGHT : GV_LEFT)); if (new_isle < 0) { Vect_list_append(IList, -new_isle); } else { /* area or nothing should not happen */ G_fatal_error(_("Failed to build new isle")); } } /* check */ if (topo->left == 0 || topo->right == 0) G_fatal_error(_("Dissolve with outer isle: corrupt topology")); } } if (dissolve_neighbour > 0 && outer_area <= 0) { G_fatal_error(_("Area merging failed")); } /* attach all isles to outer or new area */ if (outer_area >= 0) { for (i = 0; i < IList->n_values; i++) { if (!Map->plus.Isle[IList->value[i]]) continue; Map->plus.Isle[IList->value[i]]->area = outer_area; if (outer_area > 0) dig_area_add_isle(&(Map->plus), outer_area, IList->value[i]); } } nremoved++; nareas = Vect_get_num_areas(Map); } if (removed_area) *removed_area = size_removed; G_message(_("%d areas of total size %g removed"), nremoved, size_removed); Vect_destroy_list(List); Vect_destroy_list(AList); Vect_destroy_list(BList); Vect_destroy_list(NList); Vect_destroy_list(IList); Vect_destroy_line_struct(Points); Vect_destroy_cats_struct(Cats); return (nremoved); }