main.c 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382
  1. /******************************************************************************
  2. * hull.c <s.hull>
  3. * Creates the convex hull surrounding a sites list
  4. * @Copyright Andrea Aime <aaime@libero.it>
  5. * 23 Sept. 2001
  6. * Updated 19 Dec 2003, Markus Neteler to 5.7
  7. * Last updated 16 jan 2007, Benjamin Ducke to support 3D hull creation
  8. * This file is part of GRASS GIS. It is free software. You can
  9. * redistribute it and/or modify it under the terms of
  10. * the GNU General Public License as published by the Free Software
  11. * Foundation; either version 2 of the License, or (at your option)
  12. * any later version.
  13. * This program is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU General Public License for more details.
  17. ******************************************************************************/
  18. #include <stdlib.h>
  19. #include <stdio.h>
  20. #include <assert.h>
  21. #include <grass/gis.h>
  22. #include <grass/Vect.h>
  23. #include <grass/glocale.h>
  24. #include "chull.h"
  25. struct Point
  26. {
  27. double x;
  28. double y;
  29. double z;
  30. };
  31. int rightTurn(struct Point *P, int i, int j, int k)
  32. {
  33. double a, b, c, d;
  34. a = P[i].x - P[j].x;
  35. b = P[i].y - P[j].y;
  36. c = P[k].x - P[j].x;
  37. d = P[k].y - P[j].y;
  38. return a * d - b * c < 0;
  39. }
  40. int cmpPoints(const void *v1, const void *v2)
  41. {
  42. struct Point *p1, *p2;
  43. p1 = (struct Point *)v1;
  44. p2 = (struct Point *)v2;
  45. if (p1->x > p2->x)
  46. return 1;
  47. else if (p1->x < p2->x)
  48. return -1;
  49. else
  50. return 0;
  51. }
  52. int convexHull(struct Point *P, const int numPoints, int **hull)
  53. {
  54. int pointIdx, upPoints, loPoints;
  55. int *upHull, *loHull;
  56. /* sort points in ascending x order */
  57. qsort(P, numPoints, sizeof(struct Point), cmpPoints);
  58. *hull = (int *)G_malloc(numPoints * 2 * sizeof(int));
  59. /* compute upper hull */
  60. upHull = *hull;
  61. upHull[0] = 0;
  62. upHull[1] = 1;
  63. upPoints = 1;
  64. for (pointIdx = 2; pointIdx < numPoints; pointIdx++) {
  65. upPoints++;
  66. upHull[upPoints] = pointIdx;
  67. while (upPoints > 1 &&
  68. !rightTurn(P, upHull[upPoints], upHull[upPoints - 1],
  69. upHull[upPoints - 2])
  70. ) {
  71. upHull[upPoints - 1] = upHull[upPoints];
  72. upPoints--;
  73. }
  74. }
  75. /*
  76. printf("upPoints: %d\n", upPoints);
  77. for(pointIdx = 0; pointIdx <= upPoints; pointIdx ++)
  78. printf("%d ", upHull[pointIdx]);
  79. printf("\n");
  80. */
  81. /* compute lower hull, overwrite last point of upper hull */
  82. loHull = &(upHull[upPoints]);
  83. loHull[0] = numPoints - 1;
  84. loHull[1] = numPoints - 2;
  85. loPoints = 1;
  86. for (pointIdx = numPoints - 3; pointIdx >= 0; pointIdx--) {
  87. loPoints++;
  88. loHull[loPoints] = pointIdx;
  89. while (loPoints > 1 &&
  90. !rightTurn(P, loHull[loPoints], loHull[loPoints - 1],
  91. loHull[loPoints - 2])
  92. ) {
  93. loHull[loPoints - 1] = loHull[loPoints];
  94. loPoints--;
  95. }
  96. }
  97. G_debug(3, "numPoints:%d loPoints:%d upPoints:%d",
  98. numPoints, loPoints, upPoints);
  99. /*
  100. printf("loPoints: %d\n", loPoints);
  101. for(pointIdx = 0; pointIdx <= loPoints; pointIdx ++)
  102. printf("%d ", loHull[pointIdx]);
  103. printf("\n");
  104. */
  105. /* reclaim uneeded memory */
  106. *hull = (int *)G_realloc(*hull, (loPoints + upPoints) * sizeof(int));
  107. return loPoints + upPoints;
  108. }
  109. void convexHull3d(struct Point *P, const int numPoints, struct Map_info *Map)
  110. {
  111. int error;
  112. int i;
  113. double *px;
  114. double *py;
  115. double *pz;
  116. px = G_malloc(sizeof(double) * numPoints);
  117. py = G_malloc(sizeof(double) * numPoints);
  118. pz = G_malloc(sizeof(double) * numPoints);
  119. for (i = 0; i < numPoints; i++) {
  120. px[i] = (P)[i].x;
  121. py[i] = (P)[i].y;
  122. pz[i] = (P)[i].z;
  123. }
  124. /* make 3D hull */
  125. error = make3DHull(px, py, pz, numPoints, Map);
  126. if (error < 0) {
  127. G_fatal_error("Simple planar hulls not implemented yet");
  128. }
  129. G_free(px);
  130. G_free(py);
  131. G_free(pz);
  132. }
  133. #define ALLOC_CHUNK 256
  134. int loadSiteCoordinates(struct Map_info *Map, struct Point **points, int all,
  135. struct Cell_head *window)
  136. {
  137. int pointIdx = 0;
  138. struct line_pnts *sites;
  139. struct line_cats *cats;
  140. BOUND_BOX box;
  141. int cat, type;
  142. sites = Vect_new_line_struct();
  143. cats = Vect_new_cats_struct();
  144. *points = NULL;
  145. /* copy window to box */
  146. Vect_region_box(window, &box);
  147. while ((type = Vect_read_next_line(Map, sites, cats)) > -1) {
  148. if (type != GV_POINT)
  149. continue;
  150. Vect_cat_get(cats, 1, &cat);
  151. G_debug(4, "Point: %f|%f|%f|#%d", sites->x[0], sites->y[0],
  152. sites->z[0], cat);
  153. if (all ||
  154. Vect_point_in_box(sites->x[0], sites->y[0], sites->z[0], &box)) {
  155. G_debug(4, "Point in the box");
  156. if ((pointIdx % ALLOC_CHUNK) == 0)
  157. *points =
  158. (struct Point *)G_realloc(*points,
  159. (pointIdx +
  160. ALLOC_CHUNK) *
  161. sizeof(struct Point));
  162. (*points)[pointIdx].x = sites->x[0];
  163. (*points)[pointIdx].y = sites->y[0];
  164. (*points)[pointIdx].z = sites->z[0];
  165. pointIdx++;
  166. }
  167. }
  168. if (pointIdx > 0)
  169. *points =
  170. (struct Point *)G_realloc(*points,
  171. (pointIdx + 1) * sizeof(struct Point));
  172. return pointIdx;
  173. }
  174. /*
  175. * Outputs the points that comprises the convex hull as a single closed line
  176. * and the hull baricenter as the label points (as it is a linear combination
  177. * of points on the hull is guaranteed to be inside the hull, follow from the
  178. * definition of convex polygon)
  179. */
  180. int outputHull(struct Map_info *Map, struct Point *P, int *hull,
  181. int numPoints)
  182. {
  183. struct line_pnts *Points;
  184. struct line_cats *Cats;
  185. double *tmpx, *tmpy;
  186. int i, pointIdx;
  187. double xc, yc;
  188. tmpx = (double *)G_malloc((numPoints + 1) * sizeof(double));
  189. tmpy = (double *)G_malloc((numPoints + 1) * sizeof(double));
  190. xc = yc = 0;
  191. for (i = 0; i < numPoints; i++) {
  192. pointIdx = hull[i];
  193. tmpx[i] = P[pointIdx].x;
  194. tmpy[i] = P[pointIdx].y;
  195. /* average coordinates calculation... may introduce a little
  196. numerical error but guaratees that no overflow will occurr */
  197. xc = xc + tmpx[i] / numPoints;
  198. yc = yc + tmpy[i] / numPoints;
  199. }
  200. tmpx[numPoints] = P[hull[0]].x;
  201. tmpy[numPoints] = P[hull[0]].y;
  202. Points = Vect_new_line_struct();
  203. Cats = Vect_new_cats_struct();
  204. Vect_copy_xyz_to_pnts(Points, tmpx, tmpy, 0, numPoints + 1);
  205. G_free(tmpx);
  206. G_free(tmpy);
  207. /* write out convex hull */
  208. Vect_write_line(Map, GV_BOUNDARY, Points, Cats);
  209. /* find and add centroid */
  210. Vect_reset_line(Points);
  211. Vect_append_point(Points, xc, yc, 0.0);
  212. Vect_cat_set(Cats, 1, 1);
  213. Vect_write_line(Map, GV_CENTROID, Points, Cats);
  214. Vect_destroy_line_struct(Points);
  215. return 0;
  216. }
  217. int main(int argc, char **argv)
  218. {
  219. struct GModule *module;
  220. struct Option *input, *output;
  221. struct Flag *all, *flat;
  222. struct Cell_head window;
  223. char *mapset;
  224. char *sitefile;
  225. struct Map_info Map;
  226. struct Point *points; /* point loaded from site file */
  227. int *hull; /* index of points located on the convex hull */
  228. int numSitePoints, numHullPoints;
  229. int MODE2D;
  230. G_gisinit(argv[0]);
  231. module = G_define_module();
  232. module->keywords = _("vector, geometry");
  233. module->description =
  234. _
  235. ("Uses a GRASS vector points map to produce a convex hull vector map.");
  236. input = G_define_standard_option(G_OPT_V_INPUT);
  237. input->description = _("Name of input vector points map");
  238. output = G_define_standard_option(G_OPT_V_OUTPUT);
  239. output->description = _("Name of output vector area map");
  240. all = G_define_flag();
  241. all->key = 'a';
  242. all->description =
  243. _("Use all vector points (do not limit to current region)");
  244. flat = G_define_flag();
  245. flat->key = 'f';
  246. flat->description =
  247. _("Create a 'flat' 2D hull even if the input is 3D points");
  248. if (G_parser(argc, argv))
  249. exit(EXIT_FAILURE);
  250. sitefile = input->answer;
  251. mapset = G_find_vector2(sitefile, "");
  252. if (mapset == NULL)
  253. G_fatal_error(_("Vector map <%s> not found"), sitefile);
  254. Vect_check_input_output_name(input->answer, output->answer,
  255. GV_FATAL_EXIT);
  256. /* open site file */
  257. if (Vect_open_old(&Map, sitefile, mapset) < 0)
  258. G_fatal_error(_("Cannot open vector map <%s>"), sitefile);
  259. /* load site coordinates */
  260. G_get_window(&window);
  261. numSitePoints = loadSiteCoordinates(&Map, &points, all->answer, &window);
  262. if (numSitePoints < 0)
  263. G_fatal_error(_("Error loading vector points map <%s>"), sitefile);
  264. if (numSitePoints < 3)
  265. G_fatal_error(_("Convex hull calculation requires at least three points"));
  266. /* create a 2D or a 3D hull? */
  267. MODE2D = 1;
  268. if (Vect_is_3d(&Map)) {
  269. MODE2D = 0;
  270. }
  271. if (flat->answer) {
  272. MODE2D = 1;
  273. }
  274. /* create vector map */
  275. if (MODE2D) {
  276. if (0 > Vect_open_new(&Map, output->answer, 0)) {
  277. G_fatal_error(_("Cannot open vector map <%s>"), output->answer);
  278. }
  279. }
  280. else {
  281. if (0 > Vect_open_new(&Map, output->answer, 1)) {
  282. G_fatal_error(_("Cannot open vector map <%s>"), output->answer);
  283. }
  284. }
  285. Vect_hist_command(&Map);
  286. if (MODE2D) {
  287. /* compute convex hull */
  288. numHullPoints = convexHull(points, numSitePoints, &hull);
  289. /* output vector map */
  290. outputHull(&Map, points, hull, numHullPoints);
  291. }
  292. else {
  293. /* this does everything for the 3D hull including vector map creation */
  294. convexHull3d(points, numSitePoints, &Map);
  295. }
  296. /* clean up and bye bye */
  297. Vect_build(&Map, stderr);
  298. Vect_close(&Map);
  299. exit(EXIT_SUCCESS);
  300. }