line.c 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825
  1. /*!
  2. \file line.c
  3. \brief Vector library - geometry manipulation
  4. (C) 2001-2009 by the GRASS Development Team
  5. This program is free software under the GNU General Public License
  6. (>=v2). Read the file COPYING that comes with GRASS for details.
  7. \author Original author CERL, probably Dave Gerdes or Mike Higgins.
  8. \author Update to GRASS 5.7 Radim Blazek and David D. Gray.
  9. */
  10. #include <grass/config.h>
  11. #include <stdlib.h>
  12. #include <math.h>
  13. #include <grass/gis.h>
  14. #include <grass/vector.h>
  15. #include <grass/glocale.h>
  16. /*!
  17. \brief Creates and initializes a struct line_pnts.
  18. Use Vect_new_line_struct() instead.
  19. This structure is used for reading and writing vector lines and
  20. polygons. The library routines handle all memory allocation. If
  21. 3 lines in memory are needed at the same time, then simply 3
  22. line_pnts structures have to be used
  23. \param void
  24. \return pointer to line_pnts
  25. \return NULL on error
  26. */
  27. struct line_pnts *Vect__new_line_struct(void);
  28. /*!
  29. \brief Creates and initializes a struct line_pnts.
  30. This structure is used for reading and writing vector lines and
  31. polygons. The library routines handle all memory allocation. If
  32. 3 lines in memory are needed at the same time, then simply 3
  33. line_pnts structures have to be used
  34. \param void
  35. \return pointer to line_pnts
  36. \return NULL on error
  37. */
  38. struct line_pnts *Vect_new_line_struct()
  39. {
  40. struct line_pnts *p;
  41. if (NULL == (p = Vect__new_line_struct()))
  42. G_fatal_error("Vect_new_line_struct(): %s", _("Out of memory"));
  43. return p;
  44. }
  45. struct line_pnts *Vect__new_line_struct()
  46. {
  47. struct line_pnts *p;
  48. p = (struct line_pnts *)malloc(sizeof(struct line_pnts));
  49. /* alloc_points MUST be initialized to zero */
  50. if (p)
  51. p->alloc_points = p->n_points = 0;
  52. if (p)
  53. p->x = p->y = p->z = NULL;
  54. return p;
  55. }
  56. /*!
  57. \brief Frees all memory associated with a struct line_pnts, including the struct itself
  58. \param p pointer to line_pnts structure
  59. \return 0
  60. */
  61. int Vect_destroy_line_struct(struct line_pnts *p)
  62. {
  63. if (p) { /* probably a moot test */
  64. if (p->alloc_points) {
  65. G_free((char *)p->x);
  66. G_free((char *)p->y);
  67. G_free((char *)p->z);
  68. }
  69. G_free((char *)p);
  70. }
  71. return 0;
  72. }
  73. /*!
  74. \brief Copy points from array to line structure
  75. \param Points line structure
  76. \param x,y,z coordinates
  77. \param number of points to be copied
  78. \return 0 on success
  79. \return -1 on out of memory
  80. */
  81. int
  82. Vect_copy_xyz_to_pnts(struct line_pnts *Points, const double *x, const double *y,
  83. const double *z, int n)
  84. {
  85. register int i;
  86. if (0 > dig_alloc_points(Points, n))
  87. return (-1);
  88. for (i = 0; i < n; i++) {
  89. Points->x[i] = x[i];
  90. Points->y[i] = y[i];
  91. if (z != NULL)
  92. Points->z[i] = z[i];
  93. else
  94. Points->z[i] = 0;
  95. Points->n_points = n;
  96. }
  97. return (0);
  98. }
  99. /*!
  100. \brief Reset line
  101. Make sure line structure is clean to be re-used, i.e. it
  102. has no points associated with it Points must have previously been
  103. created with Vect_new_line_struct().
  104. \param Points line to be reset
  105. \return 0
  106. */
  107. int Vect_reset_line(struct line_pnts *Points)
  108. {
  109. Points->n_points = 0;
  110. return 0;
  111. }
  112. /*!
  113. \brief Appends one point to the end of a line.
  114. Returns new number of points or -1 on out of memory Note, this will
  115. append to whatever is in line struct. If you are re-using a line
  116. struct, be sure to clear out old data first by calling
  117. Vect_reset_line().
  118. \param Points line
  119. \param x,y,z point coordinates to be added
  120. \return number of points
  121. */
  122. int Vect_append_point(struct line_pnts *Points, double x, double y, double z)
  123. {
  124. register int n;
  125. if (0 > dig_alloc_points(Points, Points->n_points + 1))
  126. return (-1);
  127. n = Points->n_points;
  128. Points->x[n] = x;
  129. Points->y[n] = y;
  130. Points->z[n] = z;
  131. return ++(Points->n_points);
  132. }
  133. /*!
  134. \brief Insert new point at index position and move all old points at that position and above up
  135. \param Points line
  136. \param index (from 0 to Points->n_points-1)
  137. \param x,y,z point coordinates
  138. \return number of points
  139. \return -1 on error (alocation)
  140. */
  141. int
  142. Vect_line_insert_point(struct line_pnts *Points, int index, double x,
  143. double y, double z)
  144. {
  145. register int n;
  146. if (index < 0 || index > Points->n_points - 1)
  147. G_fatal_error("%s Vect_line_insert_point()",
  148. _("Index out of range in"));
  149. if (0 > dig_alloc_points(Points, Points->n_points + 1))
  150. return (-1);
  151. /* move up */
  152. for (n = Points->n_points; n > index; n--) {
  153. Points->x[n] = Points->x[n - 1];
  154. Points->y[n] = Points->y[n - 1];
  155. Points->z[n] = Points->z[n - 1];
  156. }
  157. Points->x[index] = x;
  158. Points->y[index] = y;
  159. Points->z[index] = z;
  160. return ++(Points->n_points);
  161. }
  162. /*!
  163. \brief Delete point at given index and move all points above down
  164. \param Points line
  165. \param index (from 0 to Points->n_points-1)
  166. \return number of points
  167. */
  168. int Vect_line_delete_point(struct line_pnts *Points, int index)
  169. {
  170. register int n;
  171. if (index < 0 || index > Points->n_points - 1)
  172. G_fatal_error("%s Vect_line_insert_point()",
  173. _("Index out of range in"));
  174. if (Points->n_points == 0)
  175. return 0;
  176. /* move down */
  177. for (n = index; n < Points->n_points - 1; n++) {
  178. Points->x[n] = Points->x[n + 1];
  179. Points->y[n] = Points->y[n + 1];
  180. Points->z[n] = Points->z[n + 1];
  181. }
  182. return --(Points->n_points);
  183. }
  184. /*!
  185. \brief Remove duplicate points, i.e. zero length segments
  186. \param Points line
  187. \return number of points
  188. */
  189. int Vect_line_prune(struct line_pnts *Points)
  190. {
  191. int i, j;
  192. if (Points->n_points > 0) {
  193. j = 1;
  194. for (i = 1; i < Points->n_points; i++) {
  195. if (Points->x[i] != Points->x[j - 1] ||
  196. Points->y[i] != Points->y[j - 1]
  197. || Points->z[i] != Points->z[j - 1]) {
  198. Points->x[j] = Points->x[i];
  199. Points->y[j] = Points->y[i];
  200. Points->z[j] = Points->z[i];
  201. j++;
  202. }
  203. }
  204. Points->n_points = j;
  205. }
  206. return (Points->n_points);
  207. }
  208. /*!
  209. \brief Remove points in threshold
  210. \param Points line
  211. \param threshold threshold value
  212. \return number of points in result
  213. */
  214. int Vect_line_prune_thresh(struct line_pnts *Points, double threshold)
  215. {
  216. int ret;
  217. ret = dig_prune(Points, threshold);
  218. if (ret < Points->n_points)
  219. Points->n_points = ret;
  220. return (Points->n_points);
  221. }
  222. /*!
  223. \brief Appends points to the end of a line.
  224. Note, this will append to whatever is in line struct. If you are
  225. re-using a line struct, be sure to clear out old data first by
  226. calling Vect_reset_line().
  227. \param Points line
  228. \param APoints points to be included
  229. \param direction direction (GV_FORWARD, GV_BACKWARD)
  230. \return new number of points
  231. \return -1 on out of memory
  232. */
  233. int
  234. Vect_append_points(struct line_pnts *Points, const struct line_pnts *APoints,
  235. int direction)
  236. {
  237. int i, n, on, an;
  238. on = Points->n_points;
  239. an = APoints->n_points;
  240. n = on + an;
  241. /* Should be OK, dig_alloc_points calls realloc */
  242. if (0 > dig_alloc_points(Points, n))
  243. return (-1);
  244. if (direction == GV_FORWARD) {
  245. for (i = 0; i < an; i++) {
  246. Points->x[on + i] = APoints->x[i];
  247. Points->y[on + i] = APoints->y[i];
  248. Points->z[on + i] = APoints->z[i];
  249. }
  250. }
  251. else {
  252. for (i = 0; i < an; i++) {
  253. Points->x[on + i] = APoints->x[an - i - 1];
  254. Points->y[on + i] = APoints->y[an - i - 1];
  255. Points->z[on + i] = APoints->z[an - i - 1];
  256. }
  257. }
  258. Points->n_points = n;
  259. return n;
  260. }
  261. /*!
  262. \brief Copy points from line structure to array
  263. x/y/z arrays MUST be at least as large as Points->n_points
  264. Also note that n is a pointer to int.
  265. \param Points line
  266. \param x,y,z coordinates arrays
  267. \param n number of points
  268. \return number of points copied
  269. */
  270. int
  271. Vect_copy_pnts_to_xyz(const struct line_pnts *Points, double *x, double *y,
  272. double *z, int *n)
  273. {
  274. register int i;
  275. for (i = 0; i < *n; i++) {
  276. x[i] = Points->x[i];
  277. y[i] = Points->y[i];
  278. if (z != NULL)
  279. z[i] = Points->z[i];
  280. *n = Points->n_points;
  281. }
  282. return (Points->n_points);
  283. }
  284. /*!
  285. \brief Find point on line in the specified distance.
  286. From the begining, measured along line.
  287. If the distance is greater than line length or negative, error is returned.
  288. \param Points line
  289. \param distance distance value
  290. \param x,y,z pointers to point coordinates or NULL
  291. \param angle pointer to angle of line in that point (radians, counter clockwise from x axis) or NULL
  292. \param slope pointer to slope angle in radians (positive up)
  293. \return number of segment the point is on (first is 1),
  294. \return 0 error when point is outside the line
  295. */
  296. int
  297. Vect_point_on_line(const struct line_pnts *Points, double distance,
  298. double *x, double *y, double *z, double *angle,
  299. double *slope)
  300. {
  301. int j, np, seg = 0;
  302. double dist = 0, length;
  303. double xp = 0, yp = 0, zp = 0, dx = 0, dy = 0, dz = 0, dxy =
  304. 0, dxyz, k, rest;
  305. G_debug(3, "Vect_point_on_line(): distance = %f", distance);
  306. if ((distance < 0) || (Points->n_points < 2))
  307. return 0;
  308. /* Check if first or last */
  309. length = Vect_line_length(Points);
  310. G_debug(3, " length = %f", length);
  311. if (distance < 0 || distance > length) {
  312. G_debug(3, " -> outside line");
  313. return 0;
  314. }
  315. np = Points->n_points;
  316. if (distance == 0) {
  317. G_debug(3, " -> first point");
  318. xp = Points->x[0];
  319. yp = Points->y[0];
  320. zp = Points->z[0];
  321. dx = Points->x[1] - Points->x[0];
  322. dy = Points->y[1] - Points->y[0];
  323. dz = Points->z[1] - Points->z[0];
  324. dxy = hypot(dx, dy);
  325. seg = 1;
  326. }
  327. else if (distance == length) {
  328. G_debug(3, " -> last point");
  329. xp = Points->x[np - 1];
  330. yp = Points->y[np - 1];
  331. zp = Points->z[np - 1];
  332. dx = Points->x[np - 1] - Points->x[np - 2];
  333. dy = Points->y[np - 1] - Points->y[np - 2];
  334. dz = Points->z[np - 1] - Points->z[np - 2];
  335. dxy = hypot(dx, dy);
  336. seg = np - 1;
  337. }
  338. else {
  339. for (j = 0; j < Points->n_points - 1; j++) {
  340. /* dxyz = G_distance(Points->x[j], Points->y[j],
  341. Points->x[j+1], Points->y[j+1]); */
  342. dx = Points->x[j + 1] - Points->x[j];
  343. dy = Points->y[j + 1] - Points->y[j];
  344. dz = Points->z[j + 1] - Points->z[j];
  345. dxy = hypot(dx, dy);
  346. dxyz = hypot(dxy, dz);
  347. dist += dxyz;
  348. if (dist >= distance) { /* point is on the current line part */
  349. rest = distance - dist + dxyz; /* from first point of segment to point */
  350. k = rest / dxyz;
  351. xp = Points->x[j] + k * dx;
  352. yp = Points->y[j] + k * dy;
  353. zp = Points->z[j] + k * dz;
  354. seg = j + 1;
  355. break;
  356. }
  357. }
  358. }
  359. if (x != NULL)
  360. *x = xp;
  361. if (y != NULL)
  362. *y = yp;
  363. if (z != NULL)
  364. *z = zp;
  365. /* calculate angle */
  366. if (angle != NULL)
  367. *angle = atan2(dy, dx);
  368. /* calculate slope */
  369. if (slope != NULL)
  370. *slope = atan2(dz, dxy);
  371. return seg;
  372. }
  373. /*!
  374. \brief Create line segment.
  375. Creates segment of InPoints from start to end measured along the
  376. line and write it to OutPoints.
  377. If the distance is greater than line length or negative, error is
  378. returned.
  379. \param InPoints input line
  380. \param start segment number
  381. \param end segment number
  382. \param OutPoints output line
  383. \return 1 success
  384. \return 0 error when start > length or end < 0 or start < 0 or end > length
  385. */
  386. int
  387. Vect_line_segment(const struct line_pnts *InPoints, double start, double end,
  388. struct line_pnts *OutPoints)
  389. {
  390. int i, seg1, seg2;
  391. double length, tmp;
  392. double x1, y1, z1, x2, y2, z2;
  393. G_debug(3, "Vect_line_segment(): start = %f, end = %f, n_points = %d",
  394. start, end, InPoints->n_points);
  395. Vect_reset_line(OutPoints);
  396. if (start > end) {
  397. tmp = start;
  398. start = end;
  399. end = tmp;
  400. }
  401. /* Check start/end */
  402. if (end < 0)
  403. return 0;
  404. length = Vect_line_length(InPoints);
  405. if (start > length)
  406. return 0;
  407. /* Find coordinates and segments of start/end */
  408. seg1 = Vect_point_on_line(InPoints, start, &x1, &y1, &z1, NULL, NULL);
  409. seg2 = Vect_point_on_line(InPoints, end, &x2, &y2, &z2, NULL, NULL);
  410. G_debug(3, " -> seg1 = %d seg2 = %d", seg1, seg2);
  411. if (seg1 == 0 || seg2 == 0) {
  412. G_warning(_("Segment outside line, no segment created"));
  413. return 0;
  414. }
  415. Vect_append_point(OutPoints, x1, y1, z1);
  416. for (i = seg1; i < seg2; i++) {
  417. Vect_append_point(OutPoints, InPoints->x[i], InPoints->y[i],
  418. InPoints->z[i]);
  419. };
  420. Vect_append_point(OutPoints, x2, y2, z2);
  421. return 1;
  422. }
  423. /*!
  424. \brief Calculate line length, 3D-length in case of 3D vector line
  425. For Lat-Long use Vect_line_geodesic_length() instead.
  426. \param Points line geometry
  427. \return line length
  428. */
  429. double Vect_line_length(const struct line_pnts *Points)
  430. {
  431. int j;
  432. double dx, dy, dz, len = 0;
  433. if (Points->n_points < 2)
  434. return 0;
  435. for (j = 0; j < Points->n_points - 1; j++) {
  436. dx = Points->x[j + 1] - Points->x[j];
  437. dy = Points->y[j + 1] - Points->y[j];
  438. dz = Points->z[j + 1] - Points->z[j];
  439. len += hypot(hypot(dx, dy), dz);
  440. }
  441. return len;
  442. }
  443. /*!
  444. \brief Calculate line length.
  445. If projection is LL, the length is measured along the geodesic.
  446. \param Points line geometry
  447. \return line length
  448. */
  449. double Vect_line_geodesic_length(const struct line_pnts *Points)
  450. {
  451. int j, dc;
  452. double dx, dy, dz, dxy, len = 0;
  453. dc = G_begin_distance_calculations();
  454. if (Points->n_points < 2)
  455. return 0;
  456. for (j = 0; j < Points->n_points - 1; j++) {
  457. if (dc == 2)
  458. dxy =
  459. G_geodesic_distance(Points->x[j], Points->y[j],
  460. Points->x[j + 1], Points->y[j + 1]);
  461. else {
  462. dx = Points->x[j + 1] - Points->x[j];
  463. dy = Points->y[j + 1] - Points->y[j];
  464. dxy = hypot(dx, dy);
  465. }
  466. dz = Points->z[j + 1] - Points->z[j];
  467. len += hypot(dxy, dz);
  468. }
  469. return len;
  470. }
  471. /*!
  472. \brief Calculate line distance.
  473. Sets (if not null):
  474. - px, py - point on line,
  475. - dist - distance to line,
  476. - spdist - distance to point on line from segment beginning,
  477. - sldist - distance to point on line form line beginning along line
  478. \param points line
  479. \param ux,uy,uz point coordinates
  480. \param with_z flag if to use z coordinate (3D calculation)
  481. \param[out] px,py,pz point on line
  482. \param[out] dist distance to line,
  483. \param[out] spdist distance of point from segment beginning
  484. \param[out] lpdist distance of point from line
  485. \return nearest segment (first is 1)
  486. */
  487. int
  488. Vect_line_distance(const struct line_pnts *points,
  489. double ux, double uy, double uz,
  490. int with_z,
  491. double *px, double *py, double *pz,
  492. double *dist, double *spdist, double *lpdist)
  493. {
  494. register int i;
  495. register double distance;
  496. register double new_dist;
  497. double tpx, tpy, tpz, tdist, tspdist, tlpdist = 0;
  498. double dx, dy, dz;
  499. register int n_points;
  500. int segment;
  501. n_points = points->n_points;
  502. if (n_points == 1) {
  503. distance =
  504. dig_distance2_point_to_line(ux, uy, uz, points->x[0],
  505. points->y[0], points->z[0],
  506. points->x[0], points->y[0],
  507. points->z[0], with_z, NULL, NULL,
  508. NULL, NULL, NULL);
  509. tpx = points->x[0];
  510. tpy = points->y[0];
  511. tpz = points->z[0];
  512. tdist = sqrt(distance);
  513. tspdist = 0;
  514. tlpdist = 0;
  515. segment = 0;
  516. }
  517. else {
  518. distance =
  519. dig_distance2_point_to_line(ux, uy, uz, points->x[0],
  520. points->y[0], points->z[0],
  521. points->x[1], points->y[1],
  522. points->z[1], with_z, NULL, NULL,
  523. NULL, NULL, NULL);
  524. segment = 1;
  525. for (i = 1; i < n_points - 1; i++) {
  526. new_dist = dig_distance2_point_to_line(ux, uy, uz,
  527. points->x[i], points->y[i],
  528. points->z[i],
  529. points->x[i + 1],
  530. points->y[i + 1],
  531. points->z[i + 1], with_z,
  532. NULL, NULL, NULL, NULL,
  533. NULL);
  534. if (new_dist < distance) {
  535. distance = new_dist;
  536. segment = i + 1;
  537. }
  538. }
  539. /* we have segment and now we can recalculate other values (speed) */
  540. new_dist = dig_distance2_point_to_line(ux, uy, uz,
  541. points->x[segment - 1],
  542. points->y[segment - 1],
  543. points->z[segment - 1],
  544. points->x[segment],
  545. points->y[segment],
  546. points->z[segment], with_z,
  547. &tpx, &tpy, &tpz, &tspdist,
  548. NULL);
  549. /* calculate distance from beginning of line */
  550. if (lpdist) {
  551. tlpdist = 0;
  552. for (i = 0; i < segment - 1; i++) {
  553. dx = points->x[i + 1] - points->x[i];
  554. dy = points->y[i + 1] - points->y[i];
  555. if (with_z)
  556. dz = points->z[i + 1] - points->z[i];
  557. else
  558. dz = 0;
  559. tlpdist += hypot(hypot(dx, dy), dz);
  560. }
  561. tlpdist += tspdist;
  562. }
  563. tdist = sqrt(distance);
  564. }
  565. if (px)
  566. *px = tpx;
  567. if (py)
  568. *py = tpy;
  569. if (pz && with_z)
  570. *pz = tpz;
  571. if (dist)
  572. *dist = tdist;
  573. if (spdist)
  574. *spdist = tspdist;
  575. if (lpdist)
  576. *lpdist = tlpdist;
  577. return (segment);
  578. }
  579. /*!
  580. \brief Calculate distance of 2 points
  581. Simply uses Pythagoras.
  582. \param x1,y1,z1 first point
  583. \param x2,y2,z2 second point
  584. \param with_z use z coordinate
  585. \return distance
  586. */
  587. double Vect_points_distance(double x1, double y1, double z1, /* point 1 */
  588. double x2, double y2, double z2, /* point 2 */
  589. int with_z)
  590. {
  591. double dx, dy, dz;
  592. dx = x2 - x1;
  593. dy = y2 - y1;
  594. dz = z2 - z1;
  595. if (with_z)
  596. return hypot(hypot(dx, dy), dz);
  597. else
  598. return hypot(dx, dy);
  599. }
  600. /*!
  601. \brief Get bounding box of line
  602. \param Points line
  603. \param[out] Box bounding box
  604. \return 0
  605. */
  606. int Vect_line_box(const struct line_pnts *Points, struct bound_box * Box)
  607. {
  608. dig_line_box(Points, Box);
  609. return 0;
  610. }
  611. /*!
  612. \brief Reverse the order of vertices
  613. \param Points line to be changed
  614. \return void
  615. */
  616. void Vect_line_reverse(struct line_pnts *Points)
  617. {
  618. int i, j, np;
  619. double x, y, z;
  620. np = (int)Points->n_points / 2;
  621. for (i = 0; i < np; i++) {
  622. j = Points->n_points - i - 1;
  623. x = Points->x[i];
  624. y = Points->y[i];
  625. z = Points->z[i];
  626. Points->x[i] = Points->x[j];
  627. Points->y[i] = Points->y[j];
  628. Points->z[i] = Points->z[j];
  629. Points->x[j] = x;
  630. Points->y[j] = y;
  631. Points->z[j] = z;
  632. }
  633. }
  634. /*!
  635. \brief Fetches FIRST category number for given vector line and field
  636. \param Map vector map
  637. \param line line id
  638. \param field layer number
  639. \return -1 no category
  640. \return category number (>=0)
  641. */
  642. int Vect_get_line_cat(const struct Map_info *Map, int line, int field)
  643. {
  644. static struct line_cats *cats = NULL;
  645. int cat, ltype;
  646. if (cats == NULL)
  647. cats = Vect_new_cats_struct();
  648. ltype = Vect_read_line(Map, NULL, cats, line);
  649. Vect_cat_get(cats, field, &cat);
  650. G_debug(3, "Vect_get_line_cat: display line %d, ltype %d, cat %d", line,
  651. ltype, cat);
  652. return cat;
  653. }