/* *************************************************************** * * * * MODULE: v.clean * * * * AUTHOR(S): Radim Blazek * * * * PURPOSE: Clean lines * * * * 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 #include "proto.h" #include int rmdac ( struct Map_info *Out ); void remove_bridges ( struct Map_info *Map, struct Map_info *Err ); int prune ( struct Map_info *, int, double ); int main (int argc, char *argv[]) { struct Map_info In, Out, Err, *pErr; int i, otype, with_z; char *mapset; struct GModule *module; struct Option *in_opt, *out_opt, *type_opt, *tool_opt, *thresh_opt, *err_opt; struct Flag *no_build_flag; int *tools, ntools, atools; double *threshs; int level; int count; double size; G_gisinit(argv[0]); module = G_define_module(); module->keywords = _("vector"); module->description = _("Toolset to clean vector topology"); in_opt = G_define_standard_option(G_OPT_V_INPUT); out_opt = G_define_standard_option(G_OPT_V_OUTPUT); type_opt = G_define_standard_option(G_OPT_V_TYPE) ; err_opt = G_define_standard_option(G_OPT_V_OUTPUT); err_opt->key = "error"; err_opt->description = _("Name of output map where errors are written"); err_opt->required = NO; tool_opt = G_define_option(); tool_opt->key = "tool"; tool_opt->type = TYPE_STRING; tool_opt->required = YES; tool_opt->multiple = YES; tool_opt->options = "break,rmdupl,rmdangle,chdangle,rmbridge,chbridge,snap,rmdac,bpol,prune," "rmarea,rmsa"; tool_opt->description = _("Cleaning tool"); tool_opt->descriptions = _("break;break lines at each intersection;" "rmdupl;remove duplicate lines (pay attention to categories!);" "rmdangle;remove dangles, threshold ignored if < 0;" "chdangle;change the type of boundary dangle to line, " "threshold ignored if < 0, input line type is ignored;" "rmbridge;remove bridges connecting area and island or 2 islands;" "chbridge;change the type of bridges connecting area and island " "or 2 islands from boundary to line;" "snap;snap lines to vertex in threshold;" "rmdac;remove duplicate area centroids ('type' option ignored);" "bpol;break (topologically clean) polygons (imported from " "non topological format (like shapefile). Boundaries are broken on each " "point shared between 2 and more polygons where angles of segments are different;" "prune;remove vertices in threshold from lines and boundaries, " "boundary is pruned only if topology is not damaged (new intersection, " "changed attachement of centroid), first and last segment of the boundary " "is never changed;" "rmarea;remove small areas, the longest boundary with adjacent area is removed;" "rmsa;remove small angles between lines at nodes"); thresh_opt = G_define_option(); thresh_opt ->key = "thresh"; thresh_opt ->type = TYPE_DOUBLE; thresh_opt ->required = NO; thresh_opt ->multiple = YES; thresh_opt ->label = "Threshold"; thresh_opt ->description = _("Threshold in map units, one value for each tool (default: 0.0[,0.0,...])"); no_build_flag = G_define_flag (); no_build_flag->key = 'b'; no_build_flag->description = _("Do not rebuild and store the topology at the end"); if (G_parser (argc, argv)) exit(EXIT_FAILURE); otype = Vect_option_to_types ( type_opt ); Vect_check_input_output_name ( in_opt->answer, out_opt->answer, GV_FATAL_EXIT ); if ( err_opt->answer ) { Vect_check_input_output_name ( in_opt->answer, err_opt->answer, GV_FATAL_EXIT ); } atools = 20; tools = (int *) G_malloc ( atools * sizeof(int) ); /* Read tools */ ntools = 0; i = 0; if ( strlen(tool_opt->answer) < 1 ) G_fatal_error (_("You must select at least one tool")); while (tool_opt->answers[i]) { if ( i + 1 >= atools ) { atools += 20; G_realloc ( tools, atools * sizeof(int) ); } G_debug ( 1, "tool : %s", tool_opt->answers[i] ); if ( strcmp ( tool_opt->answers[i], "break" ) == 0 ) tools[ntools] = TOOL_BREAK; else if ( strcmp ( tool_opt->answers[i], "rmdupl" ) == 0 ) tools[ntools] = TOOL_RMDUPL; else if ( strcmp ( tool_opt->answers[i], "rmdangle" ) == 0 ) tools[ntools] = TOOL_RMDANGLE; else if ( strcmp ( tool_opt->answers[i], "chdangle" ) == 0 ) tools[ntools] = TOOL_CHDANGLE; else if ( strcmp ( tool_opt->answers[i], "rmbridge" ) == 0 ) tools[ntools] = TOOL_RMBRIDGE; else if ( strcmp ( tool_opt->answers[i], "chbridge" ) == 0 ) tools[ntools] = TOOL_CHBRIDGE; else if ( strcmp ( tool_opt->answers[i], "snap" ) == 0 ) tools[ntools] = TOOL_SNAP; else if ( strcmp ( tool_opt->answers[i], "rmdac" ) == 0 ) tools[ntools] = TOOL_RMDAC; else if ( strcmp ( tool_opt->answers[i], "bpol" ) == 0 ) tools[ntools] = TOOL_BPOL; else if ( strcmp ( tool_opt->answers[i], "prune" ) == 0 ) tools[ntools] = TOOL_PRUNE; else if ( strcmp ( tool_opt->answers[i], "rmarea" ) == 0 ) tools[ntools] = TOOL_RMAREA; else if ( strcmp ( tool_opt->answers[i], "rmsa" ) == 0 ) tools[ntools] = TOOL_RMSA; else G_fatal_error ( _("Tool doesn't exist") ); ntools++; i++; } G_debug ( 1, "ntools = %d", ntools ); threshs = (double *) G_malloc ( ntools * sizeof(double) ); /* Read thresholds */ for ( i = 0; i < ntools; i++ ) threshs[i] = 0.0; i = 0; while ( thresh_opt->answers && thresh_opt->answers[i] ) { threshs[i] = atof ( thresh_opt->answers[i] ) ; G_debug ( 1, "thresh : %s -> %f ", tool_opt->answers[i], threshs[i] ); if ( tools[i] != TOOL_SNAP && tools[i] != TOOL_RMDANGLE && tools[i] != TOOL_CHDANGLE && tools[i] != TOOL_PRUNE && tools[i] != TOOL_RMAREA ) { G_warning (_("Threshold for tool %d may not be > 0, set to 0"), i + 1); threshs[i] = 0.0; } i++; } /* Print tool table */ G_message(_("+---------------------------------+---------------+")); G_message(_("| Tool | Threshold |")); G_message(_("+---------------------------------+---------------+")); for ( i = 0; i < ntools; i++ ) { switch ( tools[i] ) { case ( TOOL_BREAK ) : fprintf(stderr, _("| Break | ")); break; case ( TOOL_RMDUPL ) : fprintf(stderr, _("| Remove duplicates | ")); break; case ( TOOL_RMDANGLE ) : fprintf(stderr, _("| Remove dangles | ")); break; case ( TOOL_CHDANGLE ) : fprintf(stderr, _("| Change type of boundary dangles | ")); break; case ( TOOL_RMBRIDGE ) : fprintf(stderr, _("| Remove bridges | ")); break; case ( TOOL_CHBRIDGE ) : fprintf(stderr, _("| Change type of boundary bridges | ")); break; case ( TOOL_SNAP ) : fprintf(stderr, _("| Snap vertices | ")); break; case ( TOOL_RMDAC ) : fprintf(stderr, _("| Remove duplicate area centroids | ")); break; case ( TOOL_BPOL ) : fprintf(stderr, _("| Break polygons | ")); break; case ( TOOL_PRUNE ) : fprintf(stderr, _("| Prune | ")); break; case ( TOOL_RMAREA ) : fprintf(stderr, _("| Remove small areas | ")); break; case ( TOOL_RMSA ) : fprintf(stderr, _("| Remove small angles at nodes | ")); break; } G_message(_(" %13e |"), threshs[i]); } G_message(_("+---------------------------------+---------------+")); /* open input vector */ if ((mapset = G_find_vector2 (in_opt->answer, "")) == NULL) { G_fatal_error (_("Could not find input map <%s>"), in_opt->answer); } /* Input vector may be both on level 1 and 2. Level 2 is necessary for * virtual centroids (shapefile/OGR) and level 1 is better if input is too big * and build in previous module (like v.in.ogr or other call to v.clean) would take * a long time */ level = Vect_open_old (&In, in_opt->answer, mapset); with_z = Vect_is_3d (&In); Vect_set_fatal_error (GV_FATAL_PRINT); if ( 0 > Vect_open_new (&Out, out_opt->answer, with_z)) { Vect_close (&In); exit (EXIT_FAILURE); } if ( err_opt->answer ) { Vect_set_fatal_error (GV_FATAL_PRINT); if (0 > Vect_open_new (&Err, err_opt->answer, with_z)) { Vect_close (&In); Vect_close (&Out); exit (EXIT_FAILURE); } pErr = &Err; } else { pErr = NULL; } /* Copy input to output */ G_message(_("Copying vector lines ...")); Vect_copy_head_data (&In, &Out); Vect_hist_copy (&In, &Out); Vect_hist_command ( &Out ); /* This works for both level 1 and 2 */ Vect_copy_map_lines ( &In, &Out ); Vect_copy_tables ( &In, &Out, 0 ); Vect_set_release_support ( &In ); Vect_close (&In); /* Start with GV_BUILD_NONE and for each tool use unly the necessary level! */ G_message(_("--------------------------------------------------")); for ( i = 0; i < ntools ; i++ ) { if ( tools[i] == TOOL_RMDAC || tools[i] == TOOL_PRUNE || tools[i] == TOOL_RMAREA ) { if ( Vect_get_built ( &Out ) >= GV_BUILD_CENTROIDS ) { Vect_build_partial ( &Out, GV_BUILD_CENTROIDS, NULL ); } else { G_message(_("Rebuilding parts of topology ...")); Vect_build_partial ( &Out, GV_BUILD_CENTROIDS, stderr ); G_message(_("--------------------------------------------------")); } } else { if ( Vect_get_built ( &Out ) >= GV_BUILD_BASE ) { Vect_build_partial ( &Out, GV_BUILD_BASE, NULL ); } else { G_message(_("Rebuilding parts of topology ...")); Vect_build_partial ( &Out, GV_BUILD_BASE, stderr ); G_message(_("--------------------------------------------------")); } } switch ( tools[i] ) { case TOOL_BREAK: G_message(_("Tool: Break lines at intersections")); Vect_break_lines ( &Out, otype, pErr, stderr ); break; case TOOL_RMDUPL: G_message(_("Tool: Remove duplicates")); Vect_remove_duplicates ( &Out, otype, pErr, stderr ); break; case TOOL_RMDANGLE: G_message(_("Tool: Remove dangles")); Vect_remove_dangles ( &Out, otype, threshs[i], pErr, stderr ); break; case TOOL_CHDANGLE: G_message(_("Tool: Change type of boundary dangles")); Vect_chtype_dangles ( &Out, threshs[i], pErr, stderr ); break; case TOOL_RMBRIDGE: G_message(_("Tool: Remove bridges")); Vect_remove_bridges ( &Out, pErr, stderr ); break; case TOOL_CHBRIDGE: G_message(_("Tool: Change type of boundary bridges")); Vect_chtype_bridges ( &Out, pErr, stderr ); break; case TOOL_RMDAC: G_message(_("Tool: Remove duplicate area centroids")); rmdac ( &Out ); break; case TOOL_SNAP: G_message(_("Tool: Snap line to vertex in threshold")); Vect_snap_lines ( &Out, otype, threshs[i], pErr, stderr ); break; case TOOL_BPOL: G_message(_("Tool: Break polygons")); Vect_break_polygons ( &Out, otype, pErr, stderr ); break; case TOOL_PRUNE: G_message(_("Tool: Prune lines/boundaries")); prune ( &Out, otype, threshs[i] ); break; case TOOL_RMAREA: G_message(_("Tool: Remove small areas")); count = Vect_remove_small_areas ( &Out, threshs[i], pErr, stderr, &size ); G_message(_("%d areas of total size %g removed"), count, size ); break; case TOOL_RMSA: G_message(_("Tool: Remove small angles at nodes")); count = Vect_clean_small_angles_at_nodes ( &Out, otype, pErr, stderr ); G_message(_("%d modifications done"), count); break; } G_message(_("--------------------------------------------------")); } if ( !no_build_flag->answer ) { G_message(_("Rebuilding topology for output vector ...")); Vect_build_partial (&Out, GV_BUILD_NONE, NULL); Vect_build (&Out, stderr); } else { Vect_build_partial ( &Out, GV_BUILD_NONE, NULL ); /* -> topo not saved */ } Vect_close (&Out); if ( pErr ) { G_message(_("--------------------------------------------------")); G_message(_("Building topology for error vector ...")); Vect_build (pErr, stderr); Vect_close (pErr); } exit(EXIT_SUCCESS); }