|
@@ -0,0 +1,141 @@
|
|
|
+/****************************************************************
|
|
|
+ *
|
|
|
+ * MODULE: v.clean
|
|
|
+ *
|
|
|
+ * AUTHOR(S): Markus Metz
|
|
|
+ *
|
|
|
+ * PURPOSE: Split lines - helper tool for breaking lines
|
|
|
+ *
|
|
|
+ * COPYRIGHT: (C) 2012 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 <stdlib.h>
|
|
|
+#include <math.h>
|
|
|
+#include <grass/gis.h>
|
|
|
+#include <grass/vector.h>
|
|
|
+#include <grass/glocale.h>
|
|
|
+
|
|
|
+int split_line(struct Map_info *Map, int otype, struct line_pnts *Points,
|
|
|
+ struct line_cats *Cats, struct Map_info *Err, double split_distance);
|
|
|
+
|
|
|
+/* split lines
|
|
|
+ * threshold is determined automatically
|
|
|
+ * returns number of split points */
|
|
|
+int split_lines(struct Map_info *Map, int otype, struct Map_info *Err)
|
|
|
+{
|
|
|
+ int line, nlines, n_split_lines, n_splits_total, type;
|
|
|
+ struct line_pnts *Points;
|
|
|
+ struct line_cats *Cats;
|
|
|
+ double area_size, split_distance;
|
|
|
+ struct bound_box box;
|
|
|
+
|
|
|
+ nlines = Vect_get_num_lines(Map);
|
|
|
+ n_split_lines = 0;
|
|
|
+ for (line = 1; line <= nlines; line++) {
|
|
|
+ type = Vect_get_line_type(Map, line);
|
|
|
+ if ((type & otype) && (type & GV_LINES))
|
|
|
+ n_split_lines++;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (n_split_lines < 50)
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ Vect_get_map_box(Map, &box);
|
|
|
+ area_size = sqrt((box.E - box.W) * (box.N - box.S));
|
|
|
+
|
|
|
+ split_distance = area_size / log(n_split_lines);
|
|
|
+ /* divisor is the handle: increase divisor to decrease split_distance */
|
|
|
+ split_distance = split_distance / 10.;
|
|
|
+ G_debug(1, "area size: %f", area_size);
|
|
|
+ G_debug(1, "split distance: %f", split_distance);
|
|
|
+
|
|
|
+ Points = Vect_new_line_struct();
|
|
|
+ Cats = Vect_new_cats_struct();
|
|
|
+ n_splits_total = 0;
|
|
|
+ for (line = 1; line <= nlines; line++) {
|
|
|
+ int n_splits;
|
|
|
+
|
|
|
+ type = Vect_get_line_type(Map, line);
|
|
|
+ if (!((type & otype) && (type & GV_LINES)))
|
|
|
+ continue;
|
|
|
+
|
|
|
+ Vect_read_line(Map, Points, Cats, line);
|
|
|
+
|
|
|
+ /* can't split boundaries with only 2 vertices */
|
|
|
+ if (Points->n_points < 3)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ n_splits = split_line(Map, type, Points, Cats, Err, split_distance);
|
|
|
+
|
|
|
+ if (n_splits)
|
|
|
+ Vect_delete_line(Map, line);
|
|
|
+
|
|
|
+ n_splits_total += n_splits;
|
|
|
+ }
|
|
|
+
|
|
|
+ Vect_destroy_line_struct(Points);
|
|
|
+ Vect_destroy_cats_struct(Cats);
|
|
|
+
|
|
|
+ G_verbose_message(_("Line splits: %d"), n_splits_total);
|
|
|
+
|
|
|
+ return n_splits_total;
|
|
|
+}
|
|
|
+
|
|
|
+/* split a line using split_distance
|
|
|
+ * returns number of split points */
|
|
|
+int split_line(struct Map_info *Map, int otype, struct line_pnts *Points,
|
|
|
+ struct line_cats *Cats, struct Map_info *Err, double split_distance)
|
|
|
+{
|
|
|
+ int i, n_segs = 0;
|
|
|
+ double dist = 0., seg_dist, dx, dy;
|
|
|
+ struct line_pnts *OutPoints;
|
|
|
+
|
|
|
+ /* don't write zero length boundaries */
|
|
|
+ Vect_line_prune(Points);
|
|
|
+ if (Points->n_points < 2)
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ /* can't split boundaries with only 2 vertices */
|
|
|
+ if (Points->n_points == 2) {
|
|
|
+ Vect_write_line(Map, otype, Points, Cats);
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ OutPoints = Vect_new_line_struct();
|
|
|
+ Vect_append_point(OutPoints, Points->x[0], Points->y[0], Points->z[0]);
|
|
|
+ Vect_append_point(OutPoints, Points->x[1], Points->y[1], Points->z[1]);
|
|
|
+ dx = Points->x[1] - Points->x[0];
|
|
|
+ dy = Points->y[1] - Points->y[0];
|
|
|
+ dist = sqrt(dx * dx + dy * dy);
|
|
|
+
|
|
|
+ /* trying to keep line length smaller than split_distance
|
|
|
+ * alternative, less code: write line as soon as split_distance is exceeded */
|
|
|
+ for (i = 2; i < Points->n_points; i++) {
|
|
|
+ dx = Points->x[i] - Points->x[i - 1];
|
|
|
+ dy = Points->y[i] - Points->y[i - 1];
|
|
|
+ seg_dist = sqrt(dx * dx + dy * dy);
|
|
|
+ dist += seg_dist;
|
|
|
+ if (dist > split_distance) {
|
|
|
+ Vect_write_line(Map, otype, OutPoints, Cats);
|
|
|
+ Vect_reset_line(OutPoints);
|
|
|
+ dist = seg_dist;
|
|
|
+ Vect_append_point(OutPoints, Points->x[i - 1], Points->y[i - 1],
|
|
|
+ Points->z[i - 1]);
|
|
|
+ n_segs++;
|
|
|
+ }
|
|
|
+ Vect_append_point(OutPoints, Points->x[i], Points->y[i],
|
|
|
+ Points->z[i]);
|
|
|
+ }
|
|
|
+ /* write out remaining line points only when original line was split */
|
|
|
+ if (n_segs)
|
|
|
+ Vect_write_line(Map, otype, OutPoints, Cats);
|
|
|
+
|
|
|
+ Vect_destroy_line_struct(OutPoints);
|
|
|
+
|
|
|
+ return n_segs;
|
|
|
+}
|