geos.c 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. #include <float.h>
  2. #include <grass/gis.h>
  3. #include <grass/vector.h>
  4. #include <grass/glocale.h>
  5. #include "local_proto.h"
  6. #ifdef HAVE_GEOS
  7. static int ring2pts(const GEOSGeometry *geom, struct line_pnts *Points)
  8. {
  9. int i, ncoords;
  10. double x, y, z;
  11. const GEOSCoordSequence *seq = NULL;
  12. G_debug(3, "ring2pts()");
  13. Vect_reset_line(Points);
  14. if (!geom) {
  15. G_warning(_("Invalid GEOS geometry!"));
  16. return 0;
  17. }
  18. z = 0.0;
  19. ncoords = GEOSGetNumCoordinates(geom);
  20. if (!ncoords) {
  21. G_warning(_("No coordinates in GEOS geometry (can be ok for negative distance)!"));
  22. return 0;
  23. }
  24. seq = GEOSGeom_getCoordSeq(geom);
  25. for (i = 0; i < ncoords; i++) {
  26. GEOSCoordSeq_getX(seq, i, &x);
  27. GEOSCoordSeq_getY(seq, i, &y);
  28. if (x != x || x > DBL_MAX || x < -DBL_MAX)
  29. G_fatal_error(_("Invalid x coordinate %f"), x);
  30. if (y != y || y > DBL_MAX || y < -DBL_MAX)
  31. G_fatal_error(_("Invalid y coordinate %f"), y);
  32. Vect_append_point(Points, x, y, z);
  33. }
  34. return 1;
  35. }
  36. static int geom2ring(GEOSGeometry *geom, struct Map_info *Out,
  37. struct Map_info *Buf,
  38. struct spatial_index *si,
  39. struct line_cats *Cats,
  40. struct buf_contours **arr_bc,
  41. int *buffers_count, int *arr_bc_alloc)
  42. {
  43. int i, nrings, ngeoms, line_id;
  44. const GEOSGeometry *geom2;
  45. struct bound_box bbox;
  46. static struct line_pnts *Points = NULL;
  47. static struct line_cats *BCats = NULL;
  48. struct buf_contours *p = *arr_bc;
  49. G_debug(3, "geom2ring(): GEOS %s", GEOSGeomType(geom));
  50. if (!Points)
  51. Points = Vect_new_line_struct();
  52. if (!BCats)
  53. BCats = Vect_new_cats_struct();
  54. if (GEOSGeomTypeId(geom) == GEOS_LINESTRING ||
  55. GEOSGeomTypeId(geom) == GEOS_LINEARRING) {
  56. if (!ring2pts(geom, Points))
  57. return 0;
  58. Vect_write_line(Out, GV_BOUNDARY, Points, BCats);
  59. line_id = Vect_write_line(Buf, GV_BOUNDARY, Points, Cats);
  60. /* add buffer to spatial index */
  61. Vect_get_line_box(Buf, line_id, &bbox);
  62. Vect_spatial_index_add_item(si, *buffers_count, &bbox);
  63. p[*buffers_count].outer = line_id;
  64. p[*buffers_count].inner_count = 0;
  65. *buffers_count += 1;
  66. if (*buffers_count >= *arr_bc_alloc) {
  67. *arr_bc_alloc += 100;
  68. p = G_realloc(p, *arr_bc_alloc * sizeof(struct buf_contours));
  69. *arr_bc = p;
  70. }
  71. }
  72. else if (GEOSGeomTypeId(geom) == GEOS_POLYGON) {
  73. geom2 = GEOSGetExteriorRing(geom);
  74. if (!ring2pts(geom2, Points))
  75. return 0;
  76. Vect_write_line(Out, GV_BOUNDARY, Points, BCats);
  77. line_id = Vect_write_line(Buf, GV_BOUNDARY, Points, Cats);
  78. /* add buffer to spatial index */
  79. Vect_get_line_box(Buf, line_id, &bbox);
  80. Vect_spatial_index_add_item(si, *buffers_count, &bbox);
  81. p[*buffers_count].outer = line_id;
  82. p[*buffers_count].inner_count = 0;
  83. nrings = GEOSGetNumInteriorRings(geom);
  84. if (nrings > 0) {
  85. p[*buffers_count].inner_count = nrings;
  86. p[*buffers_count].inner = G_malloc(nrings * sizeof(int));
  87. for (i = 0; i < nrings; i++) {
  88. geom2 = GEOSGetInteriorRingN(geom, i);
  89. if (!ring2pts(geom2, Points)) {
  90. G_fatal_error(_("Corrupt GEOS geometry"));
  91. }
  92. Vect_write_line(Out, GV_BOUNDARY, Points, BCats);
  93. line_id = Vect_write_line(Buf, GV_BOUNDARY, Points, BCats);
  94. p[*buffers_count].inner[i] = line_id;
  95. }
  96. }
  97. *buffers_count += 1;
  98. if (*buffers_count >= *arr_bc_alloc) {
  99. *arr_bc_alloc += 100;
  100. p = G_realloc(p, *arr_bc_alloc * sizeof(struct buf_contours));
  101. *arr_bc = p;
  102. }
  103. }
  104. else if (GEOSGeomTypeId(geom) == GEOS_MULTILINESTRING ||
  105. GEOSGeomTypeId(geom) == GEOS_MULTIPOLYGON ||
  106. GEOSGeomTypeId(geom) == GEOS_GEOMETRYCOLLECTION) {
  107. G_debug(3, "GEOS %s", GEOSGeomType(geom));
  108. ngeoms = GEOSGetNumGeometries(geom);
  109. for (i = 0; i < ngeoms; i++) {
  110. geom2 = GEOSGetGeometryN(geom, i);
  111. geom2ring((GEOSGeometry *)geom2, Out, Buf, si, Cats,
  112. arr_bc, buffers_count, arr_bc_alloc);
  113. }
  114. }
  115. else
  116. G_fatal_error(_("Unknown GEOS geometry type"));
  117. return 1;
  118. }
  119. int geos_buffer(struct Map_info *In, struct Map_info *Out,
  120. struct Map_info *Buf, int id, int type, double da,
  121. struct spatial_index *si,
  122. struct line_cats *Cats,
  123. struct buf_contours **arr_bc,
  124. int *buffers_count, int *arr_bc_alloc, int flat, int no_caps)
  125. {
  126. GEOSGeometry *IGeom = NULL;
  127. GEOSGeometry *OGeom = NULL;
  128. G_debug(3, "geos_buffer(): id=%d", id);
  129. if (type == GV_AREA)
  130. IGeom = Vect_read_area_geos(In, id);
  131. else
  132. IGeom = Vect_read_line_geos(In, id, &type);
  133. /* GEOS code comment on the number of quadrant segments:
  134. * A value of 8 gives less than 2% max error in the buffer distance.
  135. * For a max error of < 1%, use QS = 12.
  136. * For a max error of < 0.1%, use QS = 18. */
  137. #ifdef GEOS_3_3
  138. if (flat || no_caps) {
  139. GEOSBufferParams* geos_params = GEOSBufferParams_create();
  140. GEOSBufferParams_setEndCapStyle(geos_params,
  141. no_caps ? GEOSBUF_CAP_FLAT : GEOSBUF_CAP_SQUARE);
  142. OGeom = GEOSBufferWithParams(IGeom, geos_params, da);
  143. GEOSBufferParams_destroy(geos_params);
  144. }
  145. else {
  146. OGeom = GEOSBuffer(IGeom, da, 12);
  147. }
  148. #else
  149. OGeom = GEOSBuffer(IGeom, da, 12);
  150. #endif
  151. if (!OGeom) {
  152. G_fatal_error(_("Buffering failed (feature %d)"), id);
  153. }
  154. geom2ring(OGeom, Out, Buf, si, Cats, arr_bc, buffers_count, arr_bc_alloc);
  155. if (IGeom)
  156. GEOSGeom_destroy(IGeom);
  157. if (OGeom)
  158. GEOSGeom_destroy(OGeom);
  159. return 1;
  160. }
  161. #endif /* HAVE_GEOS */