#include #include #include #include #include "local_proto.h" #ifdef HAVE_GEOS static int ring2pts(const GEOSGeometry *geom, struct line_pnts *Points) { int i, ncoords; double x, y, z; const GEOSCoordSequence *seq = NULL; G_debug(3, "ring2pts()"); Vect_reset_line(Points); if (!geom) { G_warning(_("Invalid GEOS geometry!")); return 0; } z = 0.0; ncoords = GEOSGetNumCoordinates(geom); if (!ncoords) { G_warning(_("No coordinates in GEOS geometry (can be ok for negative distance)!")); return 0; } seq = GEOSGeom_getCoordSeq(geom); for (i = 0; i < ncoords; i++) { GEOSCoordSeq_getX(seq, i, &x); GEOSCoordSeq_getY(seq, i, &y); if (x != x || x > DBL_MAX || x < -DBL_MAX) G_fatal_error(_("Invalid x coordinate %f"), x); if (y != y || y > DBL_MAX || y < -DBL_MAX) G_fatal_error(_("Invalid y coordinate %f"), y); Vect_append_point(Points, x, y, z); } return 1; } static int geom2ring(GEOSGeometry *geom, struct Map_info *Out, struct Map_info *Buf, struct spatial_index *si, struct line_cats *Cats, struct buf_contours **arr_bc, int *buffers_count, int *arr_bc_alloc) { int i, nrings, ngeoms, line_id; const GEOSGeometry *geom2; struct bound_box bbox; static struct line_pnts *Points = NULL; static struct line_cats *BCats = NULL; struct buf_contours *p = *arr_bc; G_debug(3, "geom2ring(): GEOS %s", GEOSGeomType(geom)); if (!Points) Points = Vect_new_line_struct(); if (!BCats) BCats = Vect_new_cats_struct(); if (GEOSGeomTypeId(geom) == GEOS_LINESTRING || GEOSGeomTypeId(geom) == GEOS_LINEARRING) { if (!ring2pts(geom, Points)) return 0; Vect_write_line(Out, GV_BOUNDARY, Points, BCats); line_id = Vect_write_line(Buf, GV_BOUNDARY, Points, Cats); /* add buffer to spatial index */ Vect_get_line_box(Buf, line_id, &bbox); Vect_spatial_index_add_item(si, *buffers_count, &bbox); p[*buffers_count].outer = line_id; p[*buffers_count].inner_count = 0; *buffers_count += 1; if (*buffers_count >= *arr_bc_alloc) { *arr_bc_alloc += 100; p = G_realloc(p, *arr_bc_alloc * sizeof(struct buf_contours)); *arr_bc = p; } } else if (GEOSGeomTypeId(geom) == GEOS_POLYGON) { geom2 = GEOSGetExteriorRing(geom); if (!ring2pts(geom2, Points)) return 0; Vect_write_line(Out, GV_BOUNDARY, Points, BCats); line_id = Vect_write_line(Buf, GV_BOUNDARY, Points, Cats); /* add buffer to spatial index */ Vect_get_line_box(Buf, line_id, &bbox); Vect_spatial_index_add_item(si, *buffers_count, &bbox); p[*buffers_count].outer = line_id; p[*buffers_count].inner_count = 0; nrings = GEOSGetNumInteriorRings(geom); if (nrings > 0) { p[*buffers_count].inner_count = nrings; p[*buffers_count].inner = G_malloc(nrings * sizeof(int)); for (i = 0; i < nrings; i++) { geom2 = GEOSGetInteriorRingN(geom, i); if (!ring2pts(geom2, Points)) { G_fatal_error(_("Corrupt GEOS geometry")); } Vect_write_line(Out, GV_BOUNDARY, Points, BCats); line_id = Vect_write_line(Buf, GV_BOUNDARY, Points, BCats); p[*buffers_count].inner[i] = line_id; } } *buffers_count += 1; if (*buffers_count >= *arr_bc_alloc) { *arr_bc_alloc += 100; p = G_realloc(p, *arr_bc_alloc * sizeof(struct buf_contours)); *arr_bc = p; } } else if (GEOSGeomTypeId(geom) == GEOS_MULTILINESTRING || GEOSGeomTypeId(geom) == GEOS_MULTIPOLYGON || GEOSGeomTypeId(geom) == GEOS_GEOMETRYCOLLECTION) { G_debug(3, "GEOS %s", GEOSGeomType(geom)); ngeoms = GEOSGetNumGeometries(geom); for (i = 0; i < ngeoms; i++) { geom2 = GEOSGetGeometryN(geom, i); geom2ring((GEOSGeometry *)geom2, Out, Buf, si, Cats, arr_bc, buffers_count, arr_bc_alloc); } } else G_fatal_error(_("Unknown GEOS geometry type")); return 1; } int geos_buffer(struct Map_info *In, struct Map_info *Out, struct Map_info *Buf, int id, int type, double da, struct spatial_index *si, struct line_cats *Cats, struct buf_contours **arr_bc, int *buffers_count, int *arr_bc_alloc, int flat, int no_caps) { GEOSGeometry *IGeom = NULL; GEOSGeometry *OGeom = NULL; G_debug(3, "geos_buffer(): id=%d", id); if (type == GV_AREA) IGeom = Vect_read_area_geos(In, id); else IGeom = Vect_read_line_geos(In, id, &type); /* GEOS code comment on the number of quadrant segments: * A value of 8 gives less than 2% max error in the buffer distance. * For a max error of < 1%, use QS = 12. * For a max error of < 0.1%, use QS = 18. */ #ifdef GEOS_3_3 if (flat || no_caps) { GEOSBufferParams* geos_params = GEOSBufferParams_create(); GEOSBufferParams_setEndCapStyle(geos_params, no_caps ? GEOSBUF_CAP_FLAT : GEOSBUF_CAP_SQUARE); OGeom = GEOSBufferWithParams(IGeom, geos_params, da); GEOSBufferParams_destroy(geos_params); } else { OGeom = GEOSBuffer(IGeom, da, 12); } #else OGeom = GEOSBuffer(IGeom, da, 12); #endif if (!OGeom) { G_fatal_error(_("Buffering failed (feature %d)"), id); } geom2ring(OGeom, Out, Buf, si, Cats, arr_bc, buffers_count, arr_bc_alloc); if (IGeom) GEOSGeom_destroy(IGeom); if (OGeom) GEOSGeom_destroy(OGeom); return 1; } #endif /* HAVE_GEOS */