read_pg.c 46 KB

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