line.c 18 KB

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