/*! \brief Find area outside island \param Map vector map \param isle isle id \param box isle bbox \return area id \return 0 if not found */ int Vect_isle_find_area(struct Map_info *Map, int isle, const struct bound_box *box) { int i, j, line, sel_area, area, poly; const struct Plus_head *plus; struct P_line *Line; struct P_node *Node; struct P_isle *Isle; struct P_area *Area; struct P_topo_b *topo; struct bound_box *abox, nbox; static struct boxlist *List = NULL; static BOX_SIZE *size_list; static int alloc_size_list = 0; /* see also Vect_find_area() */ /* Note: We should check all isle points (at least) because if topology is not clean * and two areas overlap, isle which is not completely within area may be attached, * but it would take long time */ G_debug(3, "Vect_isle_find_area () island = %d", isle); plus = &(Map->plus); if (plus->Isle[isle] == NULL) { G_warning(_("Request to find area outside nonexistent isle")); return 0; } if (!List) { List = Vect_new_boxlist(1); alloc_size_list = 10; size_list = G_malloc(alloc_size_list * sizeof(BOX_SIZE)); } Isle = plus->Isle[isle]; line = abs(Isle->lines[0]); Line = plus->Line[line]; topo = (struct P_topo_b *)Line->topo; Node = plus->Node[topo->N1]; /* select areas by box */ nbox.E = Node->x; nbox.W = Node->x; nbox.N = Node->y; nbox.S = Node->y; nbox.T = PORT_DOUBLE_MAX; nbox.B = -PORT_DOUBLE_MAX; Vect_select_areas_by_box(Map, &nbox, List); G_debug(3, "%d areas overlap island boundary point", List->n_values); /* sort areas by bbox size * get the smallest area that contains the isle * using the bbox size is working because if 2 areas both contain * the isle, one of these areas must be inside the other area * which means that the bbox of the outer area must be larger than * the bbox of the inner area, and equal bbox sizes are not possible */ if (alloc_size_list < List->n_values) { alloc_size_list = List->n_values; size_list = G_realloc(size_list, alloc_size_list * sizeof(BOX_SIZE)); } j = 0; for (i = 0; i < List->n_values; i++) { abox = &List->box[i]; if (box->E > abox->E || box->W < abox->W || box->N > abox->N || box->S < abox->S) { G_debug(3, " isle not completely inside area box"); continue; } List->id[j] = List->id[i]; List->box[j] = List->box[i]; size_list[j].i = List->id[j]; size_list[j].box = List->box[j]; size_list[j].size = (abox->N - abox->S) * (abox->E - abox->W); j++; } List->n_values = j; if (List->n_values > 1) { if (List->n_values == 2) { /* simple swap */ if (size_list[1].size < size_list[0].size) { size_list[0].i = List->id[1]; size_list[1].i = List->id[0]; size_list[0].box = List->box[1]; size_list[1].box = List->box[0]; } } else qsort(size_list, List->n_values, sizeof(BOX_SIZE), sort_by_size); } sel_area = 0; for (i = 0; i < List->n_values; i++) { area = size_list[i].i; G_debug(3, "area = %d", area); Area = plus->Area[area]; /* Before other tests, simply exclude those areas inside isolated isles formed by one boundary */ if (abs(Isle->lines[0]) == abs(Area->lines[0])) { G_debug(3, " area inside isolated isle"); continue; } /* Check box */ /* Note: If build is run on large files of areas imported from nontopo format (shapefile) * attaching of isles takes very long time because each area is also isle and select by * box all overlapping areas selects all areas with box overlapping first node. * Then reading coordinates for all those areas would take a long time -> check first * if isle's box is completely within area box */ abox = &size_list[i].box; if (box->E > abox->E || box->W < abox->W || box->N > abox->N || box->S < abox->S) { G_debug(3, " isle not completely inside area box"); continue; } poly = Vect_point_in_area_outer_ring(Node->x, Node->y, Map, area, abox); G_debug(3, " poly = %d", poly); if (poly == 1) { /* point in area, but node is not part of area inside isle (would be poly == 2) */ #if 1 /* new version */ /* the bounding box of the smaller area is * 1) inside the bounding box of a larger area and thus * 2) smaller than the bounding box of a larger area */ sel_area = area; break; #else /* old version */ /* In rare case island is inside more areas in that case we have to calculate area * of outer ring and take the smaller */ if (sel_area == 0) { /* first */ sel_area = area; } else { /* is not first */ G_debug(1, "slow version of Vect_isle_find_area()"); if (cur_size < 0) { /* second area */ /* This is slow, but should not be called often */ Vect_get_area_points(Map, sel_area, APoints); /* G_begin_polygon_area_calculations(); cur_size = G_area_of_polygon(APoints->x, APoints->y, APoints->n_points); */ /* this is faster, but there may be latlon problems: the poles */ dig_find_area_poly(APoints, &cur_size); G_debug(3, " first area size = %f (n points = %d)", cur_size, APoints->n_points); } Vect_get_area_points(Map, area, APoints); /* size = G_area_of_polygon(APoints->x, APoints->y, APoints->n_points); */ /* this is faster, but there may be latlon problems: the poles */ dig_find_area_poly(APoints, &size); G_debug(3, " area size = %f (n points = %d)", size, APoints->n_points); if (size > 0 && size < cur_size) { sel_area = area; cur_size = size; /* this can not happen because the first area must be * inside the second area because the node * is inside both areas */ G_warning(_("Larger bbox but smaller area!!!")); } } G_debug(3, "sel_area = %d cur_size = %f", sel_area, cur_size); #endif } } if (sel_area > 0) { G_debug(3, "Island %d in area %d", isle, sel_area); } else { G_debug(3, "Island %d is not in area", isle); } return sel_area; }
/*! \brief Build area on given side of line (GV_LEFT or GV_RIGHT) \param Map pointer to Map_info structure \param iline line id \param side side (GV_LEFT or GV_RIGHT) \return > 0 area id \return < 0 isle id \return 0 not created (may also already exist) */ int Vect_build_line_area(struct Map_info *Map, int iline, int side) { int area, isle, n_lines; struct Plus_head *plus; struct bound_box box; static struct line_pnts *APoints = NULL; plus_t *lines; double area_size; plus = &(Map->plus); G_debug(3, "Vect_build_line_area() line = %d, side = %d", iline, side); if (!APoints) APoints = Vect_new_line_struct(); /* get area */ area = dig_line_get_area(plus, iline, side); if (area != 0) { /* -> there is already an area on this side of the line, skip */ G_debug(3, " area/isle = %d -> skip", area); return 0; } /* build an area with this line */ n_lines = dig_build_area_with_line(plus, iline, side, &lines); G_debug(3, " n_lines = %d", n_lines); if (n_lines < 1) { return 0; } /* area was not built */ /* get line points which forms a boundary of an area */ Vect__get_area_points(Map, lines, n_lines, APoints); dig_line_box(APoints, &box); Vect_line_prune(APoints); if (APoints->n_points < 4) { G_warning(_("Area of size = 0.0 (less than 4 vertices) ignored")); return 0; } /* Area or island ? */ dig_find_area_poly(APoints, &area_size); /* area_size = dig_find_poly_orientation(APoints); */ /* area_size is not real area size, we are only interested in the sign */ G_debug(3, " area/isle size = %f", area_size); if (area_size > 0) { /* CW: area */ /* add area structure to plus */ area = dig_add_area(plus, n_lines, lines, &box); if (area == -1) { /* error */ G_fatal_error(_("Unable to add area (map closed, topo saved)")); } G_debug(3, " -> area %d", area); return area; } else if (area_size < 0) { /* CCW: island */ isle = dig_add_isle(plus, n_lines, lines, &box); if (isle == -1) { /* error */ G_fatal_error(_("Unable to add isle (map closed, topo saved)")); } G_debug(3, " -> isle %d", isle); return -isle; } else { /* TODO: What to do with such areas? Should be areas/isles of size 0 stored, * so that may be found and cleaned by some utility * Note: it would be useful for vertical closed polygons, but such would be added twice * as area */ G_warning(_("Area of size = 0.0 ignored")); } return 0; }
/* area_outer and area_isles[i] must be closed non self-intersecting lines side: 0 - auto, 1 - right, -1 left */ static void buffer_lines(struct line_pnts *area_outer, struct line_pnts **area_isles, int isles_count, int side, double da, double db, double dalpha, int round, int caps, double tol, struct line_pnts **oPoints, struct line_pnts ***iPoints, int *inner_count) { struct planar_graph *pg2; struct line_pnts *sPoints, *cPoints; struct line_pnts **arrPoints; int i, count = 0; int res, winding; int auto_side; int more = 8; int allocated = 0; double px, py; G_debug(3, "buffer_lines()"); auto_side = (side == 0); /* initializations */ sPoints = Vect_new_line_struct(); cPoints = Vect_new_line_struct(); arrPoints = NULL; /* outer contour */ G_debug(3, " processing outer contour"); *oPoints = Vect_new_line_struct(); if (auto_side) side = get_polygon_orientation(area_outer->x, area_outer->y, area_outer->n_points - 1) ? LEFT_SIDE : RIGHT_SIDE; convolution_line(area_outer, da, db, dalpha, side, round, caps, tol, sPoints); pg2 = pg_create(sPoints); extract_outer_contour(pg2, 0, *oPoints); res = extract_inner_contour(pg2, &winding, cPoints); while (res != 0) { if (winding == 0) { int check_poly = 1; double area_size; dig_find_area_poly(cPoints, &area_size); if (area_size == 0) { G_warning(_("zero area size")); check_poly = 0; } if (cPoints->x[0] != cPoints->x[cPoints->n_points - 1] || cPoints->y[0] != cPoints->y[cPoints->n_points - 1]) { G_warning(_("Line was not closed")); check_poly = 0; } if (check_poly && !Vect_point_in_poly(cPoints->x[0], cPoints->y[0], area_outer)) { if (Vect_get_point_in_poly(cPoints, &px, &py) == 0) { if (!point_in_buf(area_outer, px, py, da, db, dalpha)) { add_line_to_array(cPoints, &arrPoints, &count, &allocated, more); cPoints = Vect_new_line_struct(); } } else { G_warning(_("Vect_get_point_in_poly() failed")); } } } res = extract_inner_contour(pg2, &winding, cPoints); } pg_destroy_struct(pg2); /* inner contours */ G_debug(3, " processing inner contours"); for (i = 0; i < isles_count; i++) { if (auto_side) side = get_polygon_orientation(area_isles[i]->x, area_isles[i]->y, area_isles[i]->n_points - 1) ? RIGHT_SIDE : LEFT_SIDE; convolution_line(area_isles[i], da, db, dalpha, side, round, caps, tol, sPoints); pg2 = pg_create(sPoints); extract_outer_contour(pg2, 0, cPoints); res = extract_inner_contour(pg2, &winding, cPoints); while (res != 0) { if (winding == -1) { int check_poly = 1; double area_size; dig_find_area_poly(cPoints, &area_size); if (area_size == 0) { G_warning(_("zero area size")); check_poly = 0; } if (cPoints->x[0] != cPoints->x[cPoints->n_points - 1] || cPoints->y[0] != cPoints->y[cPoints->n_points - 1]) { G_warning(_("Line was not closed")); check_poly = 0; } /* we need to check if the area is in the buffer. I've simplfied convolution_line(), so that it runs faster, however that leads to ocasional problems */ if (check_poly && Vect_point_in_poly (cPoints->x[0], cPoints->y[0], area_isles[i])) { if (Vect_get_point_in_poly(cPoints, &px, &py) == 0) { if (!point_in_buf(area_isles[i], px, py, da, db, dalpha)) { add_line_to_array(cPoints, &arrPoints, &count, &allocated, more); cPoints = Vect_new_line_struct(); } } else { G_warning(_("Vect_get_point_in_poly() failed")); } } } res = extract_inner_contour(pg2, &winding, cPoints); } pg_destroy_struct(pg2); } arrPoints = G_realloc(arrPoints, count * sizeof(struct line_pnts *)); *inner_count = count; *iPoints = arrPoints; Vect_destroy_line_struct(sPoints); Vect_destroy_line_struct(cPoints); G_debug(3, "buffer_lines() ... done"); return; }