read_pg.c 40 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373
  1. /*!
  2. \file lib/vector/Vlib/read_pg.c
  3. \brief Vector library - reading features (PostGIS format)
  4. Higher level functions for reading/writing/manipulating vectors.
  5. \todo Currently only points, linestrings and polygons are supported,
  6. implement also other types
  7. (C) 2011-2012 by the GRASS Development Team
  8. This program is free software under the GNU General Public License
  9. (>=v2). Read the file COPYING that comes with GRASS for details.
  10. \author Martin Landa <landa.martin gmail.com>
  11. */
  12. #include <stdlib.h>
  13. #include <string.h>
  14. #include <limits.h>
  15. #include <grass/vector.h>
  16. #include <grass/dbmi.h>
  17. #include <grass/glocale.h>
  18. #ifdef HAVE_POSTGRES
  19. #include "pg_local_proto.h"
  20. static unsigned char *wkb_data;
  21. static unsigned int wkb_data_length;
  22. static int read_next_line_pg(struct Map_info *,
  23. struct line_pnts *, struct line_cats *, int);
  24. SF_FeatureType get_feature(struct Format_info_pg *, int, int);
  25. static unsigned char *hex_to_wkb(const char *, int *);
  26. static int point_from_wkb(const unsigned char *, int, int, int,
  27. struct line_pnts *);
  28. static int linestring_from_wkb(const unsigned char *, int, int, int,
  29. struct line_pnts *, int);
  30. static int polygon_from_wkb(const unsigned char *, int, int, int,
  31. struct Format_info_cache *, int *);
  32. static int geometry_collection_from_wkb(const unsigned char *, int, int, int,
  33. struct Format_info_cache *,
  34. struct feat_parts *);
  35. static int error_corrupted_data(const char *);
  36. static void reallocate_cache(struct Format_info_cache *, int);
  37. static void add_fpart(struct feat_parts *, SF_FeatureType, int, int);
  38. static int get_centroid_topo(struct Format_info_pg *, int, struct line_pnts *);
  39. static int get_centroid(struct Map_info *, int, struct line_pnts *);
  40. #endif
  41. /*!
  42. \brief Read next feature from PostGIS layer. Skip
  43. empty features (level 1 without topology).
  44. t
  45. This function implements sequential access.
  46. The action of this routine can be modified by:
  47. - Vect_read_constraint_region()
  48. - Vect_read_constraint_type()
  49. - Vect_remove_constraints()
  50. \param Map pointer to Map_info structure
  51. \param[out] line_p container used to store line points within
  52. (pointer to line_pnts struct)
  53. \param[out] line_c container used to store line categories within
  54. (pointer line_cats struct)
  55. \return feature type
  56. \return -2 no more features (EOF)
  57. \return -1 out of memory
  58. */
  59. int V1_read_next_line_pg(struct Map_info *Map,
  60. struct line_pnts *line_p, struct line_cats *line_c)
  61. {
  62. #ifdef HAVE_POSTGRES
  63. G_debug(3, "V1_read_next_line_pg()");
  64. /* constraints not ignored */
  65. return read_next_line_pg(Map, line_p, line_c, FALSE);
  66. #else
  67. G_fatal_error(_("GRASS is not compiled with PostgreSQL support"));
  68. return -1;
  69. #endif
  70. }
  71. /*!
  72. \brief Read next feature from PostGIS layer on topological level
  73. (simple feature access).
  74. This function implements sequential access.
  75. \param Map pointer to Map_info structure
  76. \param[out] line_p container used to store line points within
  77. (pointer to line_pnts struct)
  78. \param[out] line_c container used to store line categories within
  79. (pointer to line_cats struct)
  80. \return feature type
  81. \return -2 no more features (EOF)
  82. \return -1 on failure
  83. */
  84. int V2_read_next_line_pg(struct Map_info *Map, struct line_pnts *line_p,
  85. struct line_cats *line_c)
  86. {
  87. #ifdef HAVE_POSTGRES
  88. int line, ret;
  89. struct P_line *Line;
  90. struct bound_box lbox, mbox;
  91. G_debug(3, "V2_read_next_line_pg()");
  92. if (Map->constraint.region_flag)
  93. Vect_get_constraint_box(Map, &mbox);
  94. ret = -1;
  95. while (TRUE) {
  96. line = Map->next_line;
  97. if (Map->next_line > Map->plus.n_lines)
  98. return -2;
  99. Line = Map->plus.Line[line];
  100. if (Line == NULL) { /* skip dead features */
  101. Map->next_line++;
  102. continue;
  103. }
  104. if (Map->constraint.type_flag) {
  105. /* skip by type */
  106. if (!(Line->type & Map->constraint.type)) {
  107. Map->next_line++;
  108. continue;
  109. }
  110. }
  111. if (Line->type == GV_CENTROID) {
  112. G_debug(4, "Centroid");
  113. Map->next_line++;
  114. if (line_p != NULL) {
  115. int i, found;
  116. struct bound_box box;
  117. struct boxlist list;
  118. struct P_topo_c *topo = (struct P_topo_c *)Line->topo;
  119. /* get area bbox */
  120. Vect_get_area_box(Map, topo->area, &box);
  121. /* search in spatial index for centroid with area bbox */
  122. dig_init_boxlist(&list, TRUE);
  123. Vect_select_lines_by_box(Map, &box, Line->type, &list);
  124. found = -1;
  125. for (i = 0; i < list.n_values; i++) {
  126. if (list.id[i] == line) {
  127. found = i;
  128. break;
  129. }
  130. }
  131. if (found > -1) {
  132. Vect_reset_line(line_p);
  133. Vect_append_point(line_p, list.box[found].E,
  134. list.box[found].N, 0.0);
  135. }
  136. }
  137. if (line_c != NULL) {
  138. /* cat = FID and offset = FID for centroid */
  139. Vect_reset_cats(line_c);
  140. Vect_cat_set(line_c, 1, (int)Line->offset);
  141. }
  142. ret = GV_CENTROID;
  143. }
  144. else {
  145. /* ignore constraints, Map->next_line incremented */
  146. ret = read_next_line_pg(Map, line_p, line_c, TRUE);
  147. if (ret != Line->type)
  148. G_fatal_error(_("Unexpected feature type (%s) - should be (%d)"),
  149. ret, Line->type);
  150. }
  151. if (Map->constraint.region_flag) {
  152. /* skip by region */
  153. Vect_line_box(line_p, &lbox);
  154. if (!Vect_box_overlap(&lbox, &mbox)) {
  155. continue;
  156. }
  157. }
  158. /* skip by field ignored */
  159. return ret;
  160. }
  161. #else
  162. G_fatal_error(_("GRASS is not compiled with PostgreSQL support"));
  163. #endif
  164. return -1; /* not reached */
  165. }
  166. /*!
  167. \brief Read feature from PostGIS layer at given offset (level 1 without topology)
  168. This function implements random access on level 1.
  169. \param Map pointer to Map_info structure
  170. \param[out] line_p container used to store line points within
  171. (pointer line_pnts struct)
  172. \param[out] line_c container used to store line categories within
  173. (pointer line_cats struct)
  174. \param offset given offset
  175. \return line type
  176. \return 0 dead line
  177. \return -2 no more features
  178. \return -1 out of memory
  179. */
  180. int V1_read_line_pg(struct Map_info *Map,
  181. struct line_pnts *line_p, struct line_cats *line_c,
  182. off_t offset)
  183. {
  184. #ifdef HAVE_POSTGRES
  185. long fid;
  186. int ipart, type;
  187. struct Format_info_pg *pg_info;
  188. pg_info = &(Map->fInfo.pg);
  189. G_debug(3, "V1_read_line_pg(): offset = %lu offset_num = %lu",
  190. (long)offset, (long)pg_info->offset.array_num);
  191. if (offset >= pg_info->offset.array_num)
  192. return -2; /* nothing to read */
  193. if (line_p != NULL)
  194. Vect_reset_line(line_p);
  195. if (line_c != NULL)
  196. Vect_reset_cats(line_c);
  197. fid = pg_info->offset.array[offset];
  198. G_debug(4, " fid = %ld", fid);
  199. /* read feature to cache if necessary */
  200. if (pg_info->cache.fid != fid) {
  201. int type;
  202. G_debug(3, "read (%s) feature (fid = %ld) to cache",
  203. pg_info->table_name, fid);
  204. get_feature(pg_info, fid, -1);
  205. if (pg_info->cache.sf_type == SF_NONE) {
  206. G_warning(_("Feature %d without geometry skipped"), fid);
  207. return -1;
  208. }
  209. type = (int)pg_info->cache.sf_type;
  210. if (type < 0) /* -1 || - 2 */
  211. return type;
  212. }
  213. /* get data from cache */
  214. if (pg_info->cache.sf_type == SF_POINT ||
  215. pg_info->cache.sf_type == SF_LINESTRING)
  216. ipart = 0;
  217. else
  218. ipart = pg_info->offset.array[offset + 1];
  219. type = pg_info->cache.lines_types[ipart];
  220. G_debug(3, "read feature part: %d -> type = %d", ipart, type);
  221. if (line_p)
  222. Vect_append_points(line_p, pg_info->cache.lines[ipart], GV_FORWARD);
  223. if (line_c)
  224. Vect_cat_set(line_c, 1, (int)fid);
  225. return type;
  226. #else
  227. G_fatal_error(_("GRASS is not compiled with PostgreSQL support"));
  228. return -1;
  229. #endif
  230. }
  231. int V2_read_line_pg(struct Map_info *Map, struct line_pnts *line_p,
  232. struct line_cats *line_c, int line)
  233. {
  234. #ifdef HAVE_POSTGRES
  235. int type, fid;
  236. struct Format_info_pg *pg_info;
  237. struct P_line *Line;
  238. pg_info = &(Map->fInfo.pg);
  239. Line = Map->plus.Line[line];
  240. if (Line == NULL) {
  241. G_warning(_("Attempt to read dead feature %d"), line);
  242. return -1;
  243. }
  244. G_debug(4, "V2_read_line_pg() line = %d type = %d offset = %lu",
  245. line, Line->type, Line->offset);
  246. if (!line_p && !line_c)
  247. return Line->type;
  248. if (line_p != NULL)
  249. Vect_reset_line(line_p);
  250. if (line_c != NULL)
  251. Vect_reset_cats(line_c);
  252. if (line_c)
  253. Vect_cat_set(line_c, 1, (int) Line->offset);
  254. if (Line->type == GV_CENTROID) {
  255. if (pg_info->toposchema_name)
  256. return get_centroid_topo(pg_info, line, line_p);
  257. else
  258. return get_centroid(Map, line, line_p);
  259. }
  260. /* get feature id */
  261. if (pg_info->toposchema_name)
  262. fid = Line->offset;
  263. else
  264. fid = pg_info->offset.array[Line->offset];
  265. get_feature(pg_info, fid, Line->type);
  266. if (pg_info->cache.sf_type == SF_NONE) {
  267. G_warning(_("Feature %d without geometry skipped"), Line->offset);
  268. return -1;
  269. }
  270. type = (int)pg_info->cache.sf_type;
  271. if (type < 0) /* -1 || - 2 */
  272. return type;
  273. if (Line->type == GV_BOUNDARY && type == GV_LINE)
  274. type = GV_BOUNDARY;
  275. if (line_p)
  276. Vect_append_points(line_p, pg_info->cache.lines[0], GV_FORWARD);
  277. return type;
  278. #else
  279. G_fatal_error(_("GRASS is not compiled with PostgreSQL support"));
  280. return -1;
  281. #endif
  282. }
  283. #ifdef HAVE_POSTGRES
  284. /*!
  285. \brief Read next feature from PostGIS layer.
  286. \param Map pointer to Map_info structure
  287. \param[out] line_p container used to store line points within
  288. (pointer to line_pnts struct)
  289. \param[out] line_c container used to store line categories within
  290. (pointer line_cats struct)
  291. \param ignore_constraints TRUE to ignore constraints (type, region)
  292. \return feature type
  293. \return -2 no more features (EOF)
  294. \return -1 out of memory
  295. */
  296. int read_next_line_pg(struct Map_info *Map,
  297. struct line_pnts *line_p, struct line_cats *line_c,
  298. int ignore_constraints)
  299. {
  300. int line, itype;
  301. SF_FeatureType sf_type;
  302. struct Format_info_pg *pg_info;
  303. struct bound_box mbox, lbox;
  304. struct line_pnts *iline;
  305. pg_info = &(Map->fInfo.pg);
  306. if (Map->constraint.region_flag && !ignore_constraints)
  307. Vect_get_constraint_box(Map, &mbox);
  308. while (TRUE) {
  309. line = Map->next_line++; /* level 2 only */
  310. /* reset data structures */
  311. if (line_p != NULL)
  312. Vect_reset_line(line_p);
  313. if (line_c != NULL)
  314. Vect_reset_cats(line_c);
  315. /* read feature to cache if necessary */
  316. while (pg_info->cache.lines_next == pg_info->cache.lines_num) {
  317. /* cache feature -> line_p & line_c */
  318. sf_type = get_feature(pg_info, -1, -1);
  319. if (sf_type == SF_NONE) {
  320. G_warning(_("Feature %d without geometry skipped"), line);
  321. return -1;
  322. }
  323. if ((int)sf_type < 0) /* -1 || - 2 */
  324. return (int)sf_type;
  325. if (sf_type == SF_UNKNOWN || sf_type == SF_NONE) {
  326. G_warning(_("Feature without geometry. Skipped."));
  327. pg_info->cache.lines_next = pg_info->cache.lines_num = 0;
  328. continue;
  329. }
  330. G_debug(4, "%d lines read to cache", pg_info->cache.lines_num);
  331. }
  332. /* get data from cache */
  333. itype = pg_info->cache.lines_types[pg_info->cache.lines_next];
  334. iline = pg_info->cache.lines[pg_info->cache.lines_next];
  335. G_debug(4, "read next cached line %d (type = %d)",
  336. pg_info->cache.lines_next, itype);
  337. /* apply constraints */
  338. if (Map->constraint.type_flag && !ignore_constraints) {
  339. /* skip feature by type */
  340. if (!(itype & Map->constraint.type))
  341. continue;
  342. }
  343. if (line_p && Map->constraint.region_flag && !ignore_constraints) {
  344. /* skip feature by region */
  345. Vect_line_box(iline, &lbox);
  346. if (!Vect_box_overlap(&lbox, &mbox))
  347. continue;
  348. }
  349. /* skip feature by field ignored */
  350. if (line_p)
  351. Vect_append_points(line_p, iline, GV_FORWARD);
  352. if (line_c)
  353. Vect_cat_set(line_c, 1, (int)pg_info->cache.fid);
  354. pg_info->cache.lines_next++;
  355. return itype;
  356. }
  357. return -1; /* not reached */
  358. }
  359. /*!
  360. \brief Read feature geometry
  361. Geometry is stored in lines cache.
  362. \param[in,out] pg_info pointer to Format_info_pg struct
  363. \param fid feature id to be read (-1 for next)
  364. \param topotable_name table name for topological access (NULL for pseudo-topological access) - "node" or "edge"
  365. \return simple feature type (SF_POINT, SF_LINESTRING, ...)
  366. \return -1 on error
  367. */
  368. SF_FeatureType get_feature(struct Format_info_pg *pg_info, int fid, int type)
  369. {
  370. char *data;
  371. char stmt[DB_SQL_MAX];
  372. int left_face, right_face;
  373. if (!pg_info->geom_column && !pg_info->topogeom_column) {
  374. G_warning(_("No geometry or topo geometry column defined"));
  375. return -1;
  376. }
  377. if (fid < 1) {
  378. /* next (read n features) */
  379. if (!pg_info->res) {
  380. if (set_initial_query(pg_info, FALSE) == -1)
  381. return -1;
  382. }
  383. }
  384. else {
  385. if (!pg_info->fid_column) {
  386. G_warning(_("Random access not supported. "
  387. "Primary key not defined."));
  388. return -1;
  389. }
  390. if (execute(pg_info->conn, "BEGIN") == -1)
  391. return -1;
  392. if (!pg_info->toposchema_name) {
  393. /* simple feature access */
  394. sprintf(stmt,
  395. "DECLARE %s_%s%p CURSOR FOR SELECT %s FROM \"%s\".\"%s\" "
  396. "WHERE %s = %d", pg_info->schema_name, pg_info->table_name,
  397. pg_info->conn, pg_info->geom_column, pg_info->schema_name,
  398. pg_info->table_name, pg_info->fid_column, fid);
  399. }
  400. else {
  401. /* topological access */
  402. char *topotable_name;
  403. if (type == GV_POINT)
  404. topotable_name = "node";
  405. else if (type & GV_LINES)
  406. topotable_name = "edge";
  407. else {
  408. G_warning(_("Unsupported feature type %d"), type);
  409. execute(pg_info->conn, "ROLLBACK");
  410. return -1;
  411. }
  412. sprintf(stmt,
  413. "DECLARE %s_%s%p CURSOR FOR SELECT geom,left_face,right_face "
  414. " FROM \"%s\".\"%s\" "
  415. "WHERE %s_id = %d", pg_info->schema_name, pg_info->table_name,
  416. pg_info->conn, pg_info->toposchema_name,
  417. topotable_name, topotable_name, fid);
  418. }
  419. if (execute(pg_info->conn, stmt) == -1)
  420. return -1;
  421. sprintf(stmt, "FETCH ALL in %s_%s%p",
  422. pg_info->schema_name, pg_info->table_name, pg_info->conn);
  423. pg_info->res = PQexec(pg_info->conn, stmt);
  424. pg_info->next_line = 0;
  425. }
  426. if (!pg_info->res || PQresultStatus(pg_info->res) != PGRES_TUPLES_OK) {
  427. PQclear(pg_info->res);
  428. G_warning(_("Reading failed: %s"), PQerrorMessage(pg_info->conn));
  429. pg_info->res = NULL;
  430. return -1; /* reading failed */
  431. }
  432. /* do we need to fetch more records ? */
  433. if (PQntuples(pg_info->res) == CURSOR_PAGE &&
  434. PQntuples(pg_info->res) == pg_info->next_line) {
  435. char stmt[DB_SQL_MAX];
  436. PQclear(pg_info->res);
  437. sprintf(stmt, "FETCH %d in %s_%s%p", CURSOR_PAGE,
  438. pg_info->schema_name, pg_info->table_name, pg_info->conn);
  439. pg_info->res = PQexec(pg_info->conn, stmt);
  440. if (!pg_info->res) {
  441. execute(pg_info->conn, "ROLLBACK");
  442. return -1;
  443. }
  444. pg_info->next_line = 0;
  445. }
  446. /* out of results ? */
  447. if (PQntuples(pg_info->res) == pg_info->next_line) {
  448. if (pg_info->res) {
  449. PQclear(pg_info->res);
  450. pg_info->res = NULL;
  451. sprintf(stmt, "CLOSE %s_%s%p",
  452. pg_info->schema_name, pg_info->table_name, pg_info->conn);
  453. if (execute(pg_info->conn, stmt) == -1) {
  454. execute(pg_info->conn, "ROLLBACK");
  455. G_warning(_("Unable to close cursor"));
  456. return -1;
  457. }
  458. execute(pg_info->conn, "COMMIT");
  459. }
  460. return -2;
  461. }
  462. data = (char *)PQgetvalue(pg_info->res, pg_info->next_line, 0);
  463. left_face = right_face = 0;
  464. if (pg_info->toposchema_name) { /* -> PostGIS topology access */
  465. if (fid < 0) { /* sequential access */
  466. left_face = atoi(PQgetvalue(pg_info->res, pg_info->next_line, 2));
  467. right_face = atoi(PQgetvalue(pg_info->res, pg_info->next_line, 3));
  468. }
  469. else { /* random access */
  470. left_face = atoi(PQgetvalue(pg_info->res, pg_info->next_line, 1));
  471. right_face = atoi(PQgetvalue(pg_info->res, pg_info->next_line, 2));
  472. }
  473. }
  474. pg_info->cache.sf_type =
  475. cache_feature(data, FALSE,
  476. left_face != 0 || right_face != 0 ? TRUE : FALSE,
  477. &(pg_info->cache), NULL);
  478. if (fid < 0) {
  479. pg_info->cache.fid =
  480. atoi(PQgetvalue(pg_info->res, pg_info->next_line, 1));
  481. pg_info->next_line++;
  482. }
  483. else {
  484. pg_info->cache.fid = fid;
  485. PQclear(pg_info->res);
  486. pg_info->res = NULL;
  487. sprintf(stmt, "CLOSE %s_%s%p",
  488. pg_info->schema_name, pg_info->table_name, pg_info->conn);
  489. if (execute(pg_info->conn, stmt) == -1) {
  490. G_warning(_("Unable to close cursor"));
  491. return -1;
  492. }
  493. if (execute(pg_info->conn, "COMMIT") == -1)
  494. return -1;
  495. }
  496. return pg_info->cache.sf_type;
  497. }
  498. /*!
  499. \brief Convert HEX to WKB data
  500. \param hex_data HEX data
  501. \param[out] nbytes number of bytes in output buffer
  502. \return pointer to WKB data buffer
  503. */
  504. unsigned char *hex_to_wkb(const char *hex_data, int *nbytes)
  505. {
  506. unsigned int length;
  507. int i;
  508. length = strlen(hex_data) / 2 + 1;
  509. if (length > wkb_data_length) {
  510. wkb_data_length = length;
  511. wkb_data = G_realloc(wkb_data, wkb_data_length);
  512. }
  513. *nbytes = length - 1;
  514. for (i = 0; i < (*nbytes); i++) {
  515. wkb_data[i] =
  516. (unsigned
  517. char)((hex_data[2 * i] >
  518. 'F' ? hex_data[2 * i] - 0x57 : hex_data[2 * i] >
  519. '9' ? hex_data[2 * i] - 0x37 : hex_data[2 * i] -
  520. 0x30) << 4);
  521. wkb_data[i] |=
  522. (unsigned char)(hex_data[2 * i + 1] >
  523. 'F' ? hex_data[2 * i + 1] -
  524. 0x57 : hex_data[2 * i + 1] >
  525. '9' ? hex_data[2 * i + 1] -
  526. 0x37 : hex_data[2 * i + 1] - 0x30);
  527. }
  528. wkb_data[(*nbytes)] = 0;
  529. return wkb_data;
  530. }
  531. /*!
  532. \brief Read geometry from HEX data
  533. This code is inspired by OGRGeometryFactory::createFromWkb() from
  534. GDAL/OGR library.
  535. \param data HEX data
  536. \param skip_polygon skip polygons (level 1)
  537. \param force_boundary force GV_BOUNDARY feature type (for PostGIS topology)
  538. \param[out] cache lines cache
  539. \param[out] fparts used for building pseudo-topology (or NULL)
  540. \return simple feature type
  541. \return SF_UNKNOWN on error
  542. */
  543. SF_FeatureType cache_feature(const char *data, int skip_polygon,
  544. int force_boundary,
  545. struct Format_info_cache *cache,
  546. struct feat_parts * fparts)
  547. {
  548. int ret, byte_order, nbytes, is3D;
  549. unsigned char *wkb_data;
  550. unsigned int wkb_flags;
  551. SF_FeatureType ftype;
  552. /* reset cache */
  553. cache->lines_num = 0;
  554. cache->fid = -1;
  555. /* next to be read from cache */
  556. cache->lines_next = 0;
  557. if (fparts)
  558. fparts->n_parts = 0;
  559. wkb_flags = 0;
  560. wkb_data = hex_to_wkb(data, &nbytes);
  561. if (nbytes < 5) {
  562. /* G_free(wkb_data); */
  563. if (nbytes > 0) {
  564. G_debug(3, "cache_feature(): invalid geometry");
  565. G_warning(_("Invalid WKB content: %d bytes"), nbytes);
  566. return SF_UNKNOWN;
  567. }
  568. else {
  569. G_debug(3, "cache_feature(): no geometry");
  570. return SF_NONE;
  571. }
  572. }
  573. /* parsing M coordinate not supported */
  574. memcpy(&wkb_flags, wkb_data + 1, 4);
  575. byte_order = (wkb_data[0] == 0 ? ENDIAN_BIG : ENDIAN_LITTLE);
  576. if (byte_order == ENDIAN_BIG)
  577. wkb_flags = SWAP32(wkb_flags);
  578. if (wkb_flags & 0x40000000) {
  579. G_warning(_("Reading EWKB with 4-dimensional coordinates (XYZM) "
  580. "is not supported"));
  581. /* G_free(wkb_data); */
  582. return SF_UNKNOWN;
  583. }
  584. /* PostGIS EWKB format includes an SRID, but this won't be
  585. understood by OGR, so if the SRID flag is set, we remove the
  586. SRID (bytes at offset 5 to 8).
  587. */
  588. if (nbytes > 9 &&
  589. ((byte_order == ENDIAN_BIG && (wkb_data[1] & 0x20)) ||
  590. (byte_order == ENDIAN_LITTLE && (wkb_data[4] & 0x20)))) {
  591. memmove(wkb_data + 5, wkb_data + 9, nbytes - 9);
  592. nbytes -= 4;
  593. if (byte_order == ENDIAN_BIG)
  594. wkb_data[1] &= (~0x20);
  595. else
  596. wkb_data[4] &= (~0x20);
  597. }
  598. if (nbytes < 9 && nbytes != -1) {
  599. /* G_free(wkb_data); */
  600. return SF_UNKNOWN;
  601. }
  602. /* Get the geometry feature type. For now we assume that geometry
  603. type is between 0 and 255 so we only have to fetch one byte.
  604. */
  605. if (byte_order == ENDIAN_LITTLE) {
  606. ftype = (SF_FeatureType) wkb_data[1];
  607. is3D = wkb_data[4] & 0x80 || wkb_data[2] & 0x80;
  608. }
  609. else {
  610. ftype = (SF_FeatureType) wkb_data[4];
  611. is3D = wkb_data[1] & 0x80 || wkb_data[3] & 0x80;
  612. }
  613. G_debug(3, "cache_feature(): sf_type = %d", ftype);
  614. /* allocate space in lines cache - be minimalistic
  615. more lines require eg. polygon with more rings, multi-features
  616. or geometry collections
  617. */
  618. if (!cache->lines) {
  619. reallocate_cache(cache, 1);
  620. }
  621. ret = -1;
  622. if (ftype == SF_POINT) {
  623. cache->lines_num = 1;
  624. cache->lines_types[0] = GV_POINT;
  625. ret = point_from_wkb(wkb_data, nbytes, byte_order,
  626. is3D, cache->lines[0]);
  627. add_fpart(fparts, ftype, 0, 1);
  628. }
  629. else if (ftype == SF_LINESTRING) {
  630. cache->lines_num = 1;
  631. if (force_boundary)
  632. cache->lines_types[0] = GV_BOUNDARY;
  633. else
  634. cache->lines_types[0] = GV_LINE;
  635. ret = linestring_from_wkb(wkb_data, nbytes, byte_order,
  636. is3D, cache->lines[0], FALSE);
  637. add_fpart(fparts, ftype, 0, 1);
  638. }
  639. else if (ftype == SF_POLYGON && !skip_polygon) {
  640. int nrings;
  641. ret = polygon_from_wkb(wkb_data, nbytes, byte_order,
  642. is3D, cache, &nrings);
  643. add_fpart(fparts, ftype, 0, nrings);
  644. }
  645. else if (ftype == SF_MULTIPOINT ||
  646. ftype == SF_MULTILINESTRING ||
  647. ftype == SF_MULTIPOLYGON || ftype == SF_GEOMETRYCOLLECTION) {
  648. ret = geometry_collection_from_wkb(wkb_data, nbytes, byte_order,
  649. is3D, cache, fparts);
  650. }
  651. else {
  652. G_warning(_("Unsupported feature type %d"), ftype);
  653. }
  654. /* read next feature from cache */
  655. cache->lines_next = 0;
  656. /* G_free(wkb_data); */
  657. return ret > 0 ? ftype : SF_UNKNOWN;
  658. }
  659. /*!
  660. \brief Read point for WKB data
  661. See OGRPoint::importFromWkb() from GDAL/OGR library
  662. \param wkb_data WKB data
  663. \param nbytes number of bytes (WKB data buffer)
  664. \param byte_order byte order (ENDIAN_LITTLE, ENDIAN_BIG)
  665. \param with_z WITH_Z for 3D data
  666. \param[out] line_p point geometry (pointer to line_pnts struct)
  667. \return wkb size
  668. \return -1 on error
  669. */
  670. int point_from_wkb(const unsigned char *wkb_data, int nbytes, int byte_order,
  671. int with_z, struct line_pnts *line_p)
  672. {
  673. double x, y, z;
  674. if (nbytes < 21 && nbytes != -1)
  675. return -1;
  676. /* get vertex */
  677. memcpy(&x, wkb_data + 5, 8);
  678. memcpy(&y, wkb_data + 5 + 8, 8);
  679. if (byte_order == ENDIAN_BIG) {
  680. SWAPDOUBLE(&x);
  681. SWAPDOUBLE(&y);
  682. }
  683. if (with_z) {
  684. if (nbytes < 29 && nbytes != -1)
  685. return -1;
  686. memcpy(&z, wkb_data + 5 + 16, 8);
  687. if (byte_order == ENDIAN_BIG) {
  688. SWAPDOUBLE(&z);
  689. }
  690. }
  691. else {
  692. z = 0.0;
  693. }
  694. if (line_p) {
  695. Vect_reset_line(line_p);
  696. Vect_append_point(line_p, x, y, z);
  697. }
  698. return 5 + 8 * (with_z == WITH_Z ? 3 : 2);
  699. }
  700. /*!
  701. \brief Read line for WKB data
  702. See OGRLineString::importFromWkb() from GDAL/OGR library
  703. \param wkb_data WKB data
  704. \param nbytes number of bytes (WKB data buffer)
  705. \param byte_order byte order (ENDIAN_LITTLE, ENDIAN_BIG)
  706. \param with_z WITH_Z for 3D data
  707. \param[out] line_p line geometry (pointer to line_pnts struct)
  708. \return wkb size
  709. \return -1 on error
  710. */
  711. int linestring_from_wkb(const unsigned char *wkb_data, int nbytes,
  712. int byte_order, int with_z, struct line_pnts *line_p,
  713. int is_ring)
  714. {
  715. int npoints, point_size, buff_min_size, offset;
  716. int i;
  717. double x, y, z;
  718. if (is_ring)
  719. offset = 5;
  720. else
  721. offset = 0;
  722. if (is_ring && nbytes < 4 && nbytes != -1)
  723. return error_corrupted_data(NULL);
  724. /* get the vertex count */
  725. memcpy(&npoints, wkb_data + (5 - offset), 4);
  726. if (byte_order == ENDIAN_BIG) {
  727. npoints = SWAP32(npoints);
  728. }
  729. /* check if the wkb stream buffer is big enough to store fetched
  730. number of points. 16 or 24 - size of point structure
  731. */
  732. point_size = with_z ? 24 : 16;
  733. if (npoints < 0 || npoints > INT_MAX / point_size)
  734. return error_corrupted_data(NULL);
  735. buff_min_size = point_size * npoints;
  736. if (nbytes != -1 && buff_min_size > nbytes - (9 - offset))
  737. return error_corrupted_data(_("Length of input WKB is too small"));
  738. if (line_p)
  739. Vect_reset_line(line_p);
  740. /* get the vertex */
  741. for (i = 0; i < npoints; i++) {
  742. memcpy(&x, wkb_data + (9 - offset) + i * point_size, 8);
  743. memcpy(&y, wkb_data + (9 - offset) + 8 + i * point_size, 8);
  744. if (with_z)
  745. memcpy(&z, wkb_data + (9 - offset) + 16 + i * point_size, 8);
  746. else
  747. z = 0.0;
  748. if (byte_order == ENDIAN_BIG) {
  749. SWAPDOUBLE(&x);
  750. SWAPDOUBLE(&y);
  751. if (with_z)
  752. SWAPDOUBLE(&z);
  753. }
  754. if (line_p)
  755. Vect_append_point(line_p, x, y, z);
  756. }
  757. return (9 - offset) + (with_z == WITH_Z ? 3 : 2) * 8 * line_p->n_points;
  758. }
  759. /*!
  760. \brief Read polygon for WKB data
  761. See OGRPolygon::importFromWkb() from GDAL/OGR library
  762. \param wkb_data WKB data
  763. \param nbytes number of bytes (WKB data buffer)
  764. \param byte_order byte order (ENDIAN_LITTLE, ENDIAN_BIG)
  765. \param with_z WITH_Z for 3D data
  766. \param[out] line_p array of rings (pointer to line_pnts struct)
  767. \param[out] nrings number of rings
  768. \return wkb size
  769. \return -1 on error
  770. */
  771. int polygon_from_wkb(const unsigned char *wkb_data, int nbytes,
  772. int byte_order, int with_z,
  773. struct Format_info_cache *cache, int *nrings)
  774. {
  775. int data_offset, i, nsize, isize;
  776. struct line_pnts *line_i;
  777. if (nbytes < 9 && nbytes != -1)
  778. return -1;
  779. /* get the ring count */
  780. memcpy(nrings, wkb_data + 5, 4);
  781. if (byte_order == ENDIAN_BIG) {
  782. *nrings = SWAP32(*nrings);
  783. }
  784. if (*nrings < 0) {
  785. return -1;
  786. }
  787. /* reallocate space for islands if needed */
  788. reallocate_cache(cache, *nrings);
  789. cache->lines_num += *nrings;
  790. /* each ring has a minimum of 4 bytes (point count) */
  791. if (nbytes != -1 && nbytes - 9 < (*nrings) * 4) {
  792. return error_corrupted_data(_("Length of input WKB is too small"));
  793. }
  794. data_offset = 9;
  795. if (nbytes != -1)
  796. nbytes -= data_offset;
  797. /* get the rings */
  798. nsize = 9;
  799. for (i = 0; i < (*nrings); i++) {
  800. if (cache->lines_next >= cache->lines_num)
  801. G_fatal_error(_("Invalid cache index %d (max: %d)"),
  802. cache->lines_next, cache->lines_num);
  803. line_i = cache->lines[cache->lines_next];
  804. cache->lines_types[cache->lines_next++] = GV_BOUNDARY;
  805. linestring_from_wkb(wkb_data + data_offset, nbytes, byte_order,
  806. with_z, line_i, TRUE);
  807. if (nbytes != -1) {
  808. isize = 4 + 8 * (with_z == WITH_Z ? 3 : 2) * line_i->n_points;
  809. nbytes -= isize;
  810. }
  811. nsize += isize;
  812. data_offset += isize;
  813. }
  814. return nsize;
  815. }
  816. /*!
  817. \brief Read geometry collection for WKB data
  818. See OGRGeometryCollection::importFromWkbInternal() from GDAL/OGR library
  819. \param wkb_data WKB data
  820. \param nbytes number of bytes (WKB data buffer)
  821. \param byte_order byte order (ENDIAN_LITTLE, ENDIAN_BIG)
  822. \param with_z WITH_Z for 3D data
  823. \param ipart part to cache (starts at 0)
  824. \param[out] cache lines cache
  825. \param[in,out] fparts feature parts (required for building pseudo-topology)
  826. \return number of parts
  827. \return -1 on error
  828. */
  829. int geometry_collection_from_wkb(const unsigned char *wkb_data, int nbytes,
  830. int byte_order, int with_z,
  831. struct Format_info_cache *cache,
  832. struct feat_parts *fparts)
  833. {
  834. int ipart, nparts, data_offset, nsize;
  835. unsigned char *wkb_subdata;
  836. SF_FeatureType ftype;
  837. if (nbytes < 9 && nbytes != -1)
  838. return error_corrupted_data(NULL);
  839. /* get the geometry count */
  840. memcpy(&nparts, wkb_data + 5, 4);
  841. if (byte_order == ENDIAN_BIG) {
  842. nparts = SWAP32(nparts);
  843. }
  844. if (nparts < 0 || nparts > INT_MAX / 9) {
  845. return error_corrupted_data(NULL);
  846. }
  847. G_debug(5, "\t(geometry collections) parts: %d", nparts);
  848. /* each geometry has a minimum of 9 bytes */
  849. if (nbytes != -1 && nbytes - 9 < nparts * 9) {
  850. return error_corrupted_data(_("Length of input WKB is too small"));
  851. }
  852. data_offset = 9;
  853. if (nbytes != -1)
  854. nbytes -= data_offset;
  855. /* reallocate space for parts if needed */
  856. reallocate_cache(cache, nparts);
  857. /* get parts */
  858. for (ipart = 0; ipart < nparts; ipart++) {
  859. wkb_subdata = (unsigned char *)wkb_data + data_offset;
  860. if (nbytes < 9 && nbytes != -1)
  861. return error_corrupted_data(NULL);
  862. if (byte_order == ENDIAN_LITTLE) {
  863. ftype = (SF_FeatureType) wkb_subdata[1];
  864. }
  865. else {
  866. ftype = (SF_FeatureType) wkb_subdata[4];
  867. }
  868. if (ftype == SF_POINT) {
  869. cache->lines_types[cache->lines_next] = GV_POINT;
  870. nsize = point_from_wkb(wkb_subdata, nbytes, byte_order, with_z,
  871. cache->lines[cache->lines_next]);
  872. cache->lines_num++;
  873. add_fpart(fparts, ftype, cache->lines_next, 1);
  874. cache->lines_next++;
  875. }
  876. else if (ftype == SF_LINESTRING) {
  877. cache->lines_types[cache->lines_next] = GV_LINE;
  878. nsize =
  879. linestring_from_wkb(wkb_subdata, nbytes, byte_order, with_z,
  880. cache->lines[cache->lines_next], FALSE);
  881. cache->lines_num++;
  882. add_fpart(fparts, ftype, cache->lines_next, 1);
  883. cache->lines_next++;
  884. }
  885. else if (ftype == SF_POLYGON) {
  886. int idx, nrings;
  887. idx = cache->lines_next;
  888. nsize = polygon_from_wkb(wkb_subdata, nbytes, byte_order,
  889. with_z, cache, &nrings);
  890. add_fpart(fparts, ftype, idx, nrings);
  891. }
  892. else if (ftype == SF_GEOMETRYCOLLECTION ||
  893. ftype == SF_MULTIPOLYGON ||
  894. ftype == SF_MULTILINESTRING || ftype == SF_MULTIPOLYGON) {
  895. geometry_collection_from_wkb(wkb_subdata, nbytes, byte_order,
  896. with_z, cache, fparts);
  897. }
  898. else {
  899. G_warning(_("Unsupported feature type %d"), ftype);
  900. }
  901. if (nbytes != -1) {
  902. nbytes -= nsize;
  903. }
  904. data_offset += nsize;
  905. }
  906. return nparts;
  907. }
  908. /*!
  909. \brief Report error message
  910. \param msg message (NULL)
  911. \return -1
  912. */
  913. int error_corrupted_data(const char *msg)
  914. {
  915. if (msg)
  916. G_warning(_("Corrupted data. %s."), msg);
  917. else
  918. G_warning(_("Corrupted data"));
  919. return -1;
  920. }
  921. /*!
  922. \brief Set initial SQL query for sequential access
  923. \param pg_info pointer to Format_info_pg struct
  924. \return 0 on success
  925. \return -1 on error
  926. */
  927. int set_initial_query(struct Format_info_pg *pg_info, int fetch_all)
  928. {
  929. char stmt[DB_SQL_MAX];
  930. if (execute(pg_info->conn, "BEGIN") == -1)
  931. return -1;
  932. if (!pg_info->toposchema_name) {
  933. /* simple feature access */
  934. sprintf(stmt,
  935. "DECLARE %s_%s%p CURSOR FOR SELECT %s,%s FROM \"%s\".\"%s\"",
  936. pg_info->schema_name, pg_info->table_name, pg_info->conn,
  937. pg_info->geom_column, pg_info->fid_column, pg_info->schema_name,
  938. pg_info->table_name);
  939. }
  940. else {
  941. /* topology access */
  942. sprintf(stmt,
  943. "DECLARE %s_%s%p CURSOR FOR "
  944. "SELECT geom,row_number() OVER "
  945. "(ORDER BY ST_GeometryType(geom) DESC) AS fid,"
  946. "left_face,right_face FROM ("
  947. "SELECT geom,0 AS left_face, 0 AS right_face FROM "
  948. "\"%s\".node WHERE node_id NOT IN "
  949. "(SELECT node FROM (SELECT start_node AS node FROM \"%s\".edge "
  950. "GROUP BY start_node UNION ALL SELECT end_node AS node FROM "
  951. "\"%s\".edge GROUP BY end_node) AS foo) "
  952. "UNION ALL SELECT geom,left_face,right_face FROM \"%s\".edge) AS foo",
  953. pg_info->schema_name, pg_info->table_name, pg_info->conn,
  954. pg_info->toposchema_name, pg_info->toposchema_name,
  955. pg_info->toposchema_name, pg_info->toposchema_name);
  956. }
  957. G_debug(2, "SQL: %s", stmt);
  958. if (execute(pg_info->conn, stmt) == -1) {
  959. execute(pg_info->conn, "ROLLBACK");
  960. return -1;
  961. }
  962. if (fetch_all)
  963. sprintf(stmt, "FETCH ALL in %s_%s%p",
  964. pg_info->schema_name, pg_info->table_name, pg_info->conn);
  965. else
  966. sprintf(stmt, "FETCH %d in %s_%s%p", CURSOR_PAGE,
  967. pg_info->schema_name, pg_info->table_name, pg_info->conn);
  968. pg_info->res = PQexec(pg_info->conn, stmt);
  969. if (!pg_info->res) {
  970. execute(pg_info->conn, "ROLLBACK");
  971. G_warning(_("Unable to get features"));
  972. return -1;
  973. }
  974. pg_info->next_line = 0;
  975. return 0;
  976. }
  977. /*!
  978. \brief Execute SQL statement
  979. See pg_local_proto.h
  980. \param conn pointer to PGconn
  981. \param stmt query
  982. \return 0 on success
  983. \return -1 on error
  984. */
  985. int execute(PGconn * conn, const char *stmt)
  986. {
  987. PGresult *result;
  988. result = NULL;
  989. G_debug(3, "execute(): %s", stmt);
  990. result = PQexec(conn, stmt);
  991. if (!result || PQresultStatus(result) != PGRES_COMMAND_OK) {
  992. PQclear(result);
  993. G_warning(_("Execution failed: %s"), PQerrorMessage(conn));
  994. return -1;
  995. }
  996. PQclear(result);
  997. return 0;
  998. }
  999. /*!
  1000. \brief Reallocate lines cache
  1001. */
  1002. void reallocate_cache(struct Format_info_cache *cache, int num)
  1003. {
  1004. int i;
  1005. if (cache->lines_alloc >= num)
  1006. return;
  1007. if (!cache->lines) {
  1008. /* most of features requires only one line cache */
  1009. cache->lines_alloc = 1;
  1010. }
  1011. else {
  1012. cache->lines_alloc += num;
  1013. }
  1014. cache->lines = (struct line_pnts **)G_realloc(cache->lines,
  1015. cache->lines_alloc *
  1016. sizeof(struct line_pnts *));
  1017. cache->lines_types = (int *)G_realloc(cache->lines_types,
  1018. cache->lines_alloc * sizeof(int));
  1019. if (cache->lines_alloc > 1) {
  1020. for (i = cache->lines_alloc - num; i < cache->lines_alloc; i++) {
  1021. cache->lines[i] = Vect_new_line_struct();
  1022. cache->lines_types[i] = -1;
  1023. }
  1024. }
  1025. else {
  1026. cache->lines[0] = Vect_new_line_struct();
  1027. cache->lines_types[0] = -1;
  1028. }
  1029. }
  1030. void add_fpart(struct feat_parts *fparts, SF_FeatureType ftype,
  1031. int idx, int nlines)
  1032. {
  1033. if (!fparts)
  1034. return;
  1035. if (fparts->a_parts == 0 || fparts->n_parts >= fparts->a_parts) {
  1036. if (fparts->a_parts == 0)
  1037. fparts->a_parts = 1;
  1038. else
  1039. fparts->a_parts += fparts->n_parts;
  1040. fparts->ftype = (SF_FeatureType *) G_realloc(fparts->ftype,
  1041. fparts->a_parts *
  1042. sizeof(SF_FeatureType));
  1043. fparts->nlines =
  1044. (int *)G_realloc(fparts->nlines, fparts->a_parts * sizeof(int));
  1045. fparts->idx =
  1046. (int *)G_realloc(fparts->idx, fparts->a_parts * sizeof(int));
  1047. }
  1048. fparts->ftype[fparts->n_parts] = ftype;
  1049. fparts->idx[fparts->n_parts] = idx;
  1050. fparts->nlines[fparts->n_parts] = nlines;
  1051. fparts->n_parts++;
  1052. }
  1053. /*
  1054. \brief Get centroid from PostGIS topology
  1055. \param pg_info pointer to Format_info_pg
  1056. \param centroid centroid id
  1057. \param[out] line_p output geometry
  1058. \return GV_CENTROID on success
  1059. \return -1 on error
  1060. */
  1061. int get_centroid_topo(struct Format_info_pg *pg_info,
  1062. int centroid, struct line_pnts *line_p)
  1063. {
  1064. char stmt[DB_SQL_MAX];
  1065. char *data;
  1066. PGresult *res;
  1067. sprintf(stmt,
  1068. "SELECT ST_PointOnSurface(geom) AS geom FROM "
  1069. "ST_GetFaceGeometry('%s', %d) AS geom",
  1070. pg_info->toposchema_name, centroid);
  1071. res = PQexec(pg_info->conn, stmt);
  1072. if (!res || PQresultStatus(res) != PGRES_TUPLES_OK ||
  1073. PQntuples(res) != 1) {
  1074. G_warning(_("Unable to read centroid %d: %s"),
  1075. centroid, PQerrorMessage(pg_info->conn));
  1076. if (res)
  1077. PQclear(res);
  1078. return -1;
  1079. }
  1080. data = (char *)PQgetvalue(res, 0, 0);
  1081. PQclear(res);
  1082. if (GV_POINT != cache_feature(data, FALSE, FALSE, &(pg_info->cache), NULL))
  1083. return -1;
  1084. Vect_append_points(line_p, pg_info->cache.lines[0], GV_FORWARD);
  1085. return GV_CENTROID;
  1086. }
  1087. /*
  1088. \brief Get centroid
  1089. \param pg_info pointer to Format_info_pg
  1090. \param centroid centroid id
  1091. \param[out] line_p output geometry
  1092. \return GV_CENTROID on success
  1093. \return -1 on error
  1094. */
  1095. int get_centroid(struct Map_info *Map, int centroid,
  1096. struct line_pnts *line_p)
  1097. {
  1098. int i, found;
  1099. struct bound_box box;
  1100. struct boxlist list;
  1101. struct P_line *Line;
  1102. struct P_topo_c *topo;
  1103. Line = Map->plus.Line[centroid];
  1104. topo = (struct P_topo_c *)Line->topo;
  1105. /* get area bbox */
  1106. Vect_get_area_box(Map, topo->area, &box);
  1107. /* search in spatial index for centroid with area bbox */
  1108. dig_init_boxlist(&list, TRUE);
  1109. Vect_select_lines_by_box(Map, &box, Line->type, &list);
  1110. found = -1;
  1111. for (i = 0; i < list.n_values; i++) {
  1112. if (list.id[i] == centroid) {
  1113. found = i;
  1114. break;
  1115. }
  1116. }
  1117. if (found == -1)
  1118. return -1;
  1119. if (line_p) {
  1120. Vect_reset_line(line_p);
  1121. Vect_append_point(line_p, list.box[found].E, list.box[found].N, 0.0);
  1122. }
  1123. return GV_CENTROID;
  1124. }
  1125. #endif