Skip to content

Commit

Permalink
v.voronoi for areas (#26)
Browse files Browse the repository at this point in the history
v.voronoi

Fix for area skeletons and voronoi diagrams for areas.

Numerical stability of v.voronoi has been improved but is not perfect.
  • Loading branch information
metzm committed Jun 7, 2019
1 parent b1076eb commit 51bca2f
Show file tree
Hide file tree
Showing 7 changed files with 231 additions and 31 deletions.
2 changes: 1 addition & 1 deletion vector/v.voronoi/defs.h
@@ -1,7 +1,7 @@

extern struct Cell_head Window;
extern struct bound_box Box;
extern struct Map_info In, Out;
extern struct Map_info In, Out, Out2;
extern int Type;
extern int Field;
extern int in_area;
Expand Down
112 changes: 110 additions & 2 deletions vector/v.voronoi/main.c
Expand Up @@ -202,6 +202,8 @@ int main(int argc, char **argv)
Vect_region_box(&Window, &Box);
Box.T = 0.5;
Box.B = -0.5;
xcenter = (Box.W + Box.E) / 2.0;
ycenter = (Box.S + Box.N) / 2.0;

freeinit(&sfl, sizeof(struct Site));

Expand All @@ -211,8 +213,17 @@ int main(int argc, char **argv)
else
readsites();

if (Vect_open_new(&Out, opt.out->answer, 0) < 0)
G_fatal_error(_("Unable to create vector map <%s>"), opt.out->answer);
if (in_area) {
if (Vect_open_tmp_new(&Out, NULL, 0) < 0)
G_fatal_error(_("Unable to create temporary vector map"));

if (Vect_open_new(&Out2, opt.out->answer, 0) < 0)
G_fatal_error(_("Unable to create vector map <%s>"), opt.out->answer);
}
else {
if (Vect_open_new(&Out, opt.out->answer, 0) < 0)
G_fatal_error(_("Unable to create vector map <%s>"), opt.out->answer);
}

Vect_hist_copy(&In, &Out);
Vect_hist_command(&Out);
Expand Down Expand Up @@ -299,6 +310,103 @@ int main(int argc, char **argv)
G_free(coor);
}

if (in_area) {
int ltype;

/* Voronoi diagrams for areas */
/* write input boundaries with an area on both sides to output */
if (copybounds() > 0) {
int cat;
double x, y;
double snap_thresh;
int nmod;

/* snap Voronoi edges to boundaries */
snap_thresh = fabs(Box.W);
if (snap_thresh < fabs(Box.E))
snap_thresh = fabs(Box.E);
if (snap_thresh < fabs(Box.N))
snap_thresh = fabs(Box.N);
if (snap_thresh < fabs(Box.S))
snap_thresh = fabs(Box.S);
snap_thresh = 2 * d_ulp(snap_thresh);

/* TODO: snap only Voronoi edges */
Vect_snap_lines(&Out, GV_BOUNDARY, snap_thresh, NULL);
do {
Vect_break_lines(&Out, GV_BOUNDARY, NULL);
Vect_remove_duplicates(&Out, GV_BOUNDARY, NULL);
nmod =
Vect_clean_small_angles_at_nodes(&Out, GV_BOUNDARY, NULL);
} while (nmod > 0);

/* break lines */
Vect_break_lines(&Out, GV_BOUNDARY, NULL);

/* remove edge parts that are within an input area */
nlines = Vect_get_num_lines(&Out);
for (line = 1; line <= nlines; line++) {
if (!Vect_line_alive(&Out, line))
continue;

ltype = Vect_get_line_type(&Out, line);
if (!(ltype & GV_BOUNDARY))
continue;

Vect_read_line(&Out, Points, Cats, line);
cat = 0;
Vect_cat_get(Cats, 1, &cat);
if (cat != 1)
continue;

Vect_line_prune(Points);
if (Points->n_points == 2) {
x = (Points->x[0] + Points->x[1]) / 2.0;
y = (Points->y[0] + Points->y[1]) / 2.0;
}
else {
i = 1;
while (Points->x[0] == Points->x[i] &&
Points->y[0] == Points->y[i] &&
i < Points->n_points - 1) {
i++;
}
i = Points->n_points / 2.;
x = Points->x[i];
y = Points->y[i];
}
if (Vect_find_area(&In, x, y) > 0)
Vect_delete_line(&Out, line);
}
}

/* copy result to final output */
Vect_reset_cats(Cats);

nlines = Vect_get_num_lines(&Out);

for (line = 1; line <= nlines; line++) {

if (!Vect_line_alive(&Out, line))
continue;

ltype = Vect_get_line_type(&Out, line);
if (!(ltype & GV_BOUNDARY))
continue;

Vect_read_line(&Out, Points, NULL, line);
Vect_line_prune(Points);

Vect_write_line(&Out2, GV_BOUNDARY, Points, Cats);
}

/* close temporary vector */
Vect_close(&Out);
/* is this a dirty hack ? */
Out = Out2;
Vect_build_partial(&Out, GV_BUILD_BASE);
}

nfields = Vect_cidx_get_num_fields(&In);
cats = (int **)G_malloc(nfields * sizeof(int *));
ncats = (int *)G_malloc(nfields * sizeof(int));
Expand Down
2 changes: 2 additions & 0 deletions vector/v.voronoi/sw_defs.h
Expand Up @@ -57,6 +57,7 @@ extern struct Site *bottomsite;
extern int nedges;
extern struct Freelist efl;
extern double xmin, xmax, ymin, ymax, deltax, deltay;
extern double xcenter, ycenter;
extern struct Freelist hfl;
extern struct Halfedge *ELleftend, *ELrightend;
extern int ELhashsize;
Expand Down Expand Up @@ -111,6 +112,7 @@ int scomp(const void *, const void *);
struct Site *nextone(void);
int readsites(void);
int readbounds(void);
int copybounds(void);

/* sw_memory.c */
int freeinit(struct Freelist *, int);
Expand Down
61 changes: 56 additions & 5 deletions vector/v.voronoi/sw_main.c
Expand Up @@ -19,6 +19,7 @@ struct Site *bottomsite;
int nedges;
struct Freelist efl;
double xmin, xmax, ymin, ymax, deltax, deltay;
double xcenter, ycenter;
struct Freelist hfl;
struct Halfedge *ELleftend, *ELrightend;
int ELhashsize;
Expand All @@ -30,7 +31,7 @@ int PQmin;

struct Cell_head Window;
struct bound_box Box;
struct Map_info In, Out;
struct Map_info In, Out, Out2;
int Type;
int Field;
int in_area;
Expand Down Expand Up @@ -138,6 +139,9 @@ int addsite(double x, double y, double z, int id)
ymin = ymax = sites[nsites].coord.y;
}

sites[nsites].coord.x -= xcenter;
sites[nsites].coord.y -= ycenter;

nsites++;

return nsites;
Expand Down Expand Up @@ -259,7 +263,7 @@ int readbounds(void)

Vect_set_constraint_type(&In, GV_BOUNDARY);
Vect_set_constraint_field(&In, Field);

l = 0;
maxdist = 0;
for (line = 1; line <= nlines; line++) {
Expand All @@ -272,7 +276,7 @@ int readbounds(void)
continue;

area_id = 0;
if (n_areas(line, &area_id) != 1)
if (n_areas(line, &area_id) < 1)
continue;

Vect_read_line(&In, Points, Cats, line);
Expand All @@ -299,8 +303,14 @@ int readbounds(void)
continue;

area_id = 0;
if (n_areas(line, &area_id) != 1)
continue;
if (skeleton) {
if (n_areas(line, &area_id) < 1)
continue;
}
else {
if (n_areas(line, &area_id) != 1)
continue;
}

Vect_read_line(&In, Points, Cats, line);
Vect_line_prune(Points);
Expand Down Expand Up @@ -484,3 +494,44 @@ int readbounds(void)

return 0;
}

int copybounds(void)
{
int line, nlines, ltype;
int area_id;
int ncopied;
struct line_pnts *Points;
struct line_cats *Cats;

Points = Vect_new_line_struct();
Cats = Vect_new_cats_struct();

nlines = Vect_get_num_lines(&In);
ncopied = 0;

for (line = 1; line <= nlines; line++) {

if (!Vect_line_alive(&In, line))
continue;
ltype = Vect_get_line_type(&In, line);

if (!(ltype & GV_BOUNDARY))
continue;

if (n_areas(line, &area_id) != 2)
continue;

Vect_read_line(&In, Points, Cats, line);
Vect_line_prune(Points);

Vect_reset_cats(Cats);
Vect_cat_set(Cats, 1, 2);
Vect_write_line(&Out, GV_BOUNDARY, Points, Cats);
ncopied++;
}

Vect_destroy_line_struct(Points);
Vect_destroy_cats_struct(Cats);

return ncopied;
}
6 changes: 6 additions & 0 deletions vector/v.voronoi/v.voronoi.html
Expand Up @@ -16,6 +16,12 @@ <h2>DESCRIPTION</h2>

<h2>NOTES</h2>

<em>v.voronoi</em> suffers from numerical instability, results can
sometimes contain many artefacts. When creating Voronoi diagrams for areas
or skeletons for areas, it is highly recommended to simplify the areas first
with <em><a href="v.generalize.html">v.generalize</a></em>.

<p>
Voronoi diagrams may be used for nearest-neighbor flood filling.
Give the centroids attributes (start with
<em><a href="v.db.addcolumn.html">v.db.addcolumn</a></em>),
Expand Down
8 changes: 4 additions & 4 deletions vector/v.voronoi/vo_extend.c
Expand Up @@ -48,7 +48,7 @@ int extend_line(double s, double n, double w, double e,
}

/* south */
nx = (c - b * s) / a;
nx = (c - b * (s - ycenter)) / a + xcenter;
if (Vect_point_in_box(nx, s, 0.0, &Box) &&
((nx > x && knownPointAtLeft) || (nx <= x && !knownPointAtLeft)))
{
Expand All @@ -58,7 +58,7 @@ int extend_line(double s, double n, double w, double e,
}

/* north */
nx = (c - b * n) / a;
nx = (c - b * (n - ycenter)) / a + xcenter;
if (Vect_point_in_box(nx, n, 0.0, &Box) &&
((nx > x && knownPointAtLeft) || (nx <= x && !knownPointAtLeft)))
{
Expand All @@ -69,7 +69,7 @@ int extend_line(double s, double n, double w, double e,

if (knownPointAtLeft) {
/* east */
ny = (c - a * e) / b;
ny = (c - a * (e - xcenter)) / b + ycenter;
if (Vect_point_in_box(e, ny, 0.0, &Box)) {
*c_x = e;
*c_y = ny;
Expand All @@ -78,7 +78,7 @@ int extend_line(double s, double n, double w, double e,
}
else {
/* west */
ny = (c - a * w) / b;
ny = (c - a * (w - xcenter)) / b + ycenter;
if (Vect_point_in_box(w, ny, 0.0, &Box)) {
*c_x = w;
*c_y = ny;
Expand Down

0 comments on commit 51bca2f

Please sign in to comment.