2
\file lib/vector/Vlib/break_polygons.c
4
4
\brief Vector library - clean geometry (break polygons)
6
6
Higher level functions for reading/writing/manipulating vectors.
8
(C) 2001-2008 by the GRASS Development Team
8
(C) 2001-2009 by the GRASS Development Team
10
This program is free software under the
11
GNU General Public License (>=v2).
12
Read the file COPYING that comes with GRASS
10
This program is free software under the GNU General Public License
11
(>=v2). Read the file COPYING that comes with GRASS for details.
15
13
\author Radim Blazek
14
\author Update for GRASS 7 Markus Metz
20
17
#include <stdlib.h>
22
#include <grass/gis.h>
23
#include <grass/Vect.h>
22
#include <grass/vector.h>
24
23
#include <grass/glocale.h>
27
* atan2() gives angle from x-axis
28
* this is unambiguous only in 2D, not in 3D
30
* one possibility would be to store unit vectors of length 1
32
* double a1[3], a2[3];
34
* length = sqrt(dx * dx + dy * dy + dz * dz);
35
* dx /= length; dy /= length; dz /=length;
36
* a1[0] = dx; a1[1] = dy; a1[2] = dz;
38
* get second dx, dy, dz
39
* length = sqrt(dx * dx + dy * dy + dz * dz);
40
* dx /= length; dy /= length; dz /=length;
41
* a2[0] = dx; a2[1] = dy; a2[2] = dz;
44
* if (a1[0] == a2[0] && a1[1] == a2[1] && a1[2] == a2[2])
46
* disadvantage: increased memory consumption
48
* new function Vect_break_faces() ?
54
double x, y; /* coords */
29
55
double a1, a2; /* angles */
30
56
char cross; /* 0 - do not break, 1 - break */
31
57
char used; /* 0 - was not used to break line, 1 - was used to break line
32
* this is stored because points are automaticaly marked as cross, even if not used
58
* this is stored because points are automatically marked as cross, even if not used
33
59
* later to break lines */
64
double a1, a2; /* angles */
65
char cross; /* 0 - do not break, 1 - break */
66
char used; /* 0 - was not used to break line, 1 - was used to break line
67
* this is stored because points are automatically marked as cross, even if not used
68
* later to break lines */
38
73
/* Function called from RTreeSearch for point found */
39
void srch(int id, int *arg)
74
static int srch(int id, const struct RTree_Rect *rect, int *arg)
45
\brief Break polygons in vector map.
47
Breaks lines specified by type in vector map. Points at intersections may be optionally
48
written to error map. Input map must be opened on level 2 for update at least on GV_BUILD_BASE.
50
Function is optimized for closed polygons rigs (e.g. imported from OGR) but with clean geometry -
51
adjacent polygons mostly have identical boundary. Function creates database of ALL points
52
in the map, and then is looking for those where polygons should be broken.
53
Lines may be broken only at points existing in input map!
55
\param Map input map where polygons will be broken
56
\param type type of line to be broken
57
\param Err vector map where points at intersections will be written or NULL
62
Vect_break_polygons(struct Map_info *Map, int type, struct Map_info *Err)
78
return 0; /* stop searching */
81
/* function used by binary tree to compare items */
82
static int compare_xpnts(const void *Xpnta, const void *Xpntb)
102
G_warning(_("Break polygons: Bug in binary tree!"));
106
/* break polygons using a file-based search index */
107
void Vect_break_polygons_file(struct Map_info *Map, int type, struct Map_info *Err)
64
109
struct line_pnts *BPoints, *Points;
65
struct line_cats *Cats;
110
struct line_cats *Cats, *ErrCats;
66
111
int i, j, k, ret, ltype, broken, last, nlines;
69
int apoints, npoints, nallpoints, nmarks;
114
int npoints, nallpoints, nmarks;
72
116
double dx, dy, a1 = 0, a2 = 0;
73
int closed, last_point, cross;
75
RTree = RTreeNewIndex();
117
int closed, last_point;
121
static struct RTree_Rect rect;
122
static int rect_init = 0;
125
rect.boundary = G_malloc(6 * sizeof(RectReal));
129
G_debug(1, "File-based version of Vect_break_polygons()");
131
filename = G_tempfile();
132
fd = open(filename, O_RDWR | O_CREAT | O_EXCL, 0600);
133
RTree = RTreeCreateTree(fd, 0, 2);
136
filename = G_tempfile();
137
xpntfd = open(filename, O_RDWR | O_CREAT | O_EXCL, 0600);
77
140
BPoints = Vect_new_line_struct();
78
141
Points = Vect_new_line_struct();
79
142
Cats = Vect_new_cats_struct();
143
ErrCats = Vect_new_cats_struct();
81
145
nlines = Vect_get_num_lines(Map);
255
327
RTreeSearch(RTree, &rect, (void *)srch, 0);
256
328
G_debug(3, "fpoint = %d", fpoint);
258
if (XPnts[fpoint].cross) { /* realy use to break line */
259
XPnts[fpoint].used = 1;
262
/* break or write last segment of broken line */
263
if ((j == (Points->n_points - 1) && broken) ||
264
XPnts[fpoint].cross) {
265
Vect_reset_line(BPoints);
266
for (k = last; k <= j; k++) {
267
Vect_append_point(BPoints, Points->x[k], Points->y[k],
271
/* Result may collapse to one point */
272
Vect_line_prune(BPoints);
273
if (BPoints->n_points > 1) {
274
ret = Vect_write_line(Map, ltype, BPoints, Cats);
276
"Line %d written j = %d n_points(orig,pruned) = %d n_points(new) = %d",
277
ret, j, Points->n_points, BPoints->n_points);
281
Vect_delete_line(Map, i); /* not yet deleted */
288
if (!broken && n_orig_points > Points->n_points) { /* was pruned before -> rewrite */
289
if (Points->n_points > 1) {
290
Vect_rewrite_line(Map, i, ltype, Points, Cats);
291
G_debug(3, "Line %d pruned, npoints = %d", i,
295
Vect_delete_line(Map, i);
296
G_debug(3, "Line %d was deleted", i);
300
G_debug(3, "Line %d was not changed", i);
304
/* Write points on breaks */
306
Vect_reset_cats(Cats);
307
for (i = 1; i < npoints; i++) {
309
Vect_reset_line(Points);
310
Vect_append_point(Points, XPnts[i].x, XPnts[i].y, 0);
311
Vect_write_line(Err, GV_POINT, Points, Cats);
317
RTreeDestroyNode(RTree);
331
lseek(xpntfd, (off_t) (fpoint - 1) * sizeof(XPNT2), SEEK_SET);
332
read(xpntfd, &XPnt, sizeof(XPNT2));
334
/* break or write last segment of broken line */
335
if ((j == (Points->n_points - 1) && broken) ||
337
Vect_reset_line(BPoints);
338
for (k = last; k <= j; k++) {
339
Vect_append_point(BPoints, Points->x[k], Points->y[k],
343
/* Result may collapse to one point */
344
Vect_line_prune(BPoints);
345
if (BPoints->n_points > 1) {
346
ret = Vect_write_line(Map, ltype, BPoints, Cats);
348
"Line %d written j = %d n_points(orig,pruned) = %d n_points(new) = %d",
349
ret, j, Points->n_points, BPoints->n_points);
353
Vect_delete_line(Map, i); /* not yet deleted */
355
/* Write points on breaks */
357
if (XPnt.cross && !XPnt.used) {
358
Vect_reset_line(BPoints);
359
Vect_append_point(BPoints, Points->x[j], Points->y[j], 0);
360
Vect_write_line(Err, GV_POINT, BPoints, ErrCats);
365
lseek(xpntfd, (off_t) (fpoint - 1) * sizeof(XPNT2), SEEK_SET);
366
write(xpntfd, &XPnt, sizeof(XPNT2));
375
if (!broken && n_orig_points > Points->n_points) { /* was pruned before -> rewrite */
376
if (Points->n_points > 1) {
377
Vect_rewrite_line(Map, i, ltype, Points, Cats);
378
G_debug(3, "Line %d pruned, npoints = %d", i,
382
Vect_delete_line(Map, i);
383
G_debug(3, "Line %d was deleted", i);
387
G_debug(3, "Line %d was not changed", i);
392
RTreeDestroyTree(RTree);
394
Vect_destroy_line_struct(Points);
395
Vect_destroy_line_struct(BPoints);
396
Vect_destroy_cats_struct(Cats);
397
Vect_destroy_cats_struct(ErrCats);
398
G_verbose_message(_("Breaks: %d"), nbreaks);
402
/* break polygons using a memory-based search index */
403
void Vect_break_polygons_mem(struct Map_info *Map, int type, struct Map_info *Err)
405
struct line_pnts *BPoints, *Points;
406
struct line_cats *Cats, *ErrCats;
407
int i, j, k, ret, ltype, broken, last, nlines;
409
struct RB_TREE *RBTree;
410
int npoints, nallpoints, nmarks;
411
XPNT *XPnt_found, XPnt_search;
412
double dx, dy, a1 = 0, a2 = 0;
413
int closed, last_point, cross;
415
G_debug(1, "Memory-based version of Vect_break_polygons()");
417
RBTree = rbtree_create(compare_xpnts, sizeof(XPNT));
419
BPoints = Vect_new_line_struct();
420
Points = Vect_new_line_struct();
421
Cats = Vect_new_cats_struct();
422
ErrCats = Vect_new_cats_struct();
424
nlines = Vect_get_num_lines(Map);
426
G_debug(3, "nlines = %d", nlines);
427
/* Go through all lines in vector, and add each point to structure of points,
428
* if such point already exists check angles of segments and if differ mark for break */
433
XPnt_search.used = 0;
435
G_message(_("Breaking polygons (pass 1: select break points)..."));
437
for (i = 1; i <= nlines; i++) {
438
G_percent(i, nlines, 1);
439
G_debug(3, "i = %d", i);
440
if (!Vect_line_alive(Map, i))
443
ltype = Vect_read_line(Map, Points, Cats, i);
447
/* This would be confused by duplicate coordinates (angle cannot be calculated) ->
448
* prune line first */
449
Vect_line_prune(Points);
451
/* If first and last point are identical it is close polygon, we dont need to register last point
452
* and we can calculate angle for first.
453
* If first and last point are not identical we have to mark for break both */
454
last_point = Points->n_points - 1;
455
if (Points->x[0] == Points->x[last_point] &&
456
Points->y[0] == Points->y[last_point])
461
for (j = 0; j < Points->n_points; j++) {
462
G_debug(3, "j = %d", j);
465
if (j == last_point && closed)
466
continue; /* do not register last of close polygon */
468
XPnt_search.x = Points->x[j];
469
XPnt_search.y = Points->y[j];
472
XPnt_found = rbtree_find(RBTree, &XPnt_search);
474
if (Points->n_points <= 2 ||
475
(!closed && (j == 0 || j == last_point))) {
476
cross = 1; /* mark for cross in any case */
478
else { /* calculate angles */
480
if (j == 0 && closed) { /* closed polygon */
481
dx = Points->x[last_point] - Points->x[0];
482
dy = Points->y[last_point] - Points->y[0];
484
dx = Points->x[1] - Points->x[0];
485
dy = Points->y[1] - Points->y[0];
489
dx = Points->x[j - 1] - Points->x[j];
490
dy = Points->y[j - 1] - Points->y[j];
492
dx = Points->x[j + 1] - Points->x[j];
493
dy = Points->y[j + 1] - Points->y[j];
498
if (XPnt_found) { /* found */
499
if (XPnt_found->cross == 1)
500
continue; /* already marked */
504
XPnt_found->cross = 1;
508
G_debug(3, "a1 = %f xa1 = %f a2 = %f xa2 = %f", a1,
509
XPnt_found->a1, a2, XPnt_found->a2);
510
if ((a1 == XPnt_found->a1 && a2 == XPnt_found->a2) ||
511
(a1 == XPnt_found->a2 && a2 == XPnt_found->a1)) { /* identical */
515
XPnt_found->cross = 1;
521
if (j == 0 || j == (Points->n_points - 1) ||
522
Points->n_points < 3) {
525
XPnt_search.cross = 1;
531
XPnt_search.cross = 0;
535
rbtree_insert(RBTree, &XPnt_search);
543
G_debug(2, "Break polygons: unique vertices: %ld", (long int)RBTree->count);
545
/* uncomment to check if search tree is healthy */
546
/* if (rbtree_debug(RBTree, RBTree->root) == 0)
547
G_warning("Break polygons: RBTree not ok"); */
549
/* Second loop through lines (existing when loop is started, no need to process lines written again)
550
* and break at points marked for break */
552
G_message(_("Breaking polygons (pass 2: break at selected points)..."));
554
for (i = 1; i <= nlines; i++) {
557
G_percent(i, nlines, 1);
558
G_debug(3, "i = %d", i);
559
if (!Vect_line_alive(Map, i))
562
ltype = Vect_read_line(Map, Points, Cats, i);
565
if (!(ltype & GV_LINES))
566
continue; /* Nonsense to break points */
568
/* Duplicates would result in zero length lines -> prune line first */
569
n_orig_points = Points->n_points;
570
Vect_line_prune(Points);
574
G_debug(3, "n_points = %d", Points->n_points);
575
for (j = 1; j < Points->n_points; j++) {
576
G_debug(3, "j = %d", j);
579
if (Points->n_points <= 1 ||
580
(j == (Points->n_points - 1) && !broken))
583
* last point and line is not broken, do nothing */
585
XPnt_search.x = Points->x[j];
586
XPnt_search.y = Points->y[j];
588
XPnt_found = rbtree_find(RBTree, &XPnt_search);
590
/* all points must be in the search tree, without duplicates */
591
if (XPnt_found == NULL)
592
G_fatal_error(_("Point not in search tree!"));
594
/* break or write last segment of broken line */
595
if ((j == (Points->n_points - 1) && broken) ||
597
Vect_reset_line(BPoints);
598
for (k = last; k <= j; k++) {
599
Vect_append_point(BPoints, Points->x[k], Points->y[k],
603
/* Result may collapse to one point */
604
Vect_line_prune(BPoints);
605
if (BPoints->n_points > 1) {
606
ret = Vect_write_line(Map, ltype, BPoints, Cats);
608
"Line %d written j = %d n_points(orig,pruned) = %d n_points(new) = %d",
609
ret, j, Points->n_points, BPoints->n_points);
613
Vect_delete_line(Map, i); /* not yet deleted */
615
/* Write points on breaks */
617
if (XPnt_found->cross && !XPnt_found->used) {
618
Vect_reset_line(BPoints);
619
Vect_append_point(BPoints, Points->x[j], Points->y[j], 0);
620
Vect_write_line(Err, GV_POINT, BPoints, ErrCats);
622
XPnt_found->used = 1;
630
if (!broken && n_orig_points > Points->n_points) { /* was pruned before -> rewrite */
631
if (Points->n_points > 1) {
632
Vect_rewrite_line(Map, i, ltype, Points, Cats);
633
G_debug(3, "Line %d pruned, npoints = %d", i,
637
Vect_delete_line(Map, i);
638
G_debug(3, "Line %d was deleted", i);
642
G_debug(3, "Line %d was not changed", i);
646
rbtree_destroy(RBTree);
647
Vect_destroy_line_struct(Points);
648
Vect_destroy_line_struct(BPoints);
649
Vect_destroy_cats_struct(Cats);
650
G_verbose_message(_("Breaks: %d"), nbreaks);
654
\brief Break polygons in vector map
656
Breaks lines specified by type in vector map. Points at
657
intersections may be optionally written to error map. Input vector
658
map must be opened on level 2 for update at least on GV_BUILD_BASE.
660
Function is optimized for closed polygons rings (e.g. imported from
661
OGR) but with clean geometry - adjacent polygons mostly have
662
identical boundary. Function creates database of ALL points in the
663
vector map, and then is looking for those where polygons should be
664
broken. Lines may be broken only at points existing in input
667
\param Map input map where polygons will be broken
668
\param type type of line to be broken (GV_LINE or GV_BOUNDARY)
669
\param Err vector map where points at intersections will be written or NULL
671
void Vect_break_polygons(struct Map_info *Map, int type, struct Map_info *Err)
673
if (getenv("GRASS_VECTOR_LOWMEM"))
674
return Vect_break_polygons_file(Map, type, Err);
676
return Vect_break_polygons_mem(Map, type, Err);