read_pg.c 51 KB

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