read_pg.c 52 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670
  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. if (pg_info->cache.ctype == CACHE_MAP &&
  371. pg_info->cache.fid == -2) {
  372. /* stop reading - last cached line */
  373. return -2;
  374. }
  375. /* cache feature -> line_p & line_c */
  376. sf_type = get_feature(pg_info, -1, -1);
  377. if (sf_type == SF_NONE) {
  378. G_warning(_("Feature %ld without geometry skipped"), pg_info->cache.fid);
  379. return -1;
  380. }
  381. if ((int)sf_type < 0) { /* -1 || - 2 */
  382. if (pg_info->cache.ctype == CACHE_MAP)
  383. pg_info->cache.fid = -2; /* last line cached */
  384. return (int)sf_type;
  385. }
  386. if (sf_type == SF_UNKNOWN || sf_type == SF_NONE) {
  387. G_warning(_("Feature without geometry. Skipped."));
  388. pg_info->cache.lines_next = pg_info->cache.lines_num = 0;
  389. continue;
  390. }
  391. G_debug(4, "%d lines read to cache", pg_info->cache.lines_num);
  392. /* store fid as offset to be used (used for topo access only */
  393. Map->head.last_offset = pg_info->cache.fid;
  394. }
  395. /* get data from cache, skip dead lines (NULL) */
  396. do {
  397. itype = pg_info->cache.lines_types[pg_info->cache.lines_next];
  398. iline = pg_info->cache.lines[pg_info->cache.lines_next];
  399. pg_info->cache.lines_next++; /* read next line from cache */
  400. } while (iline == NULL);
  401. G_debug(4, "read next cached line %d (type = %d)",
  402. pg_info->cache.lines_next, itype);
  403. /* apply constraints */
  404. if (Map->constraint.type_flag && !ignore_constraints) {
  405. /* skip feature by type */
  406. if (!(itype & Map->constraint.type))
  407. continue;
  408. }
  409. if (line_p && Map->constraint.region_flag && !ignore_constraints) {
  410. /* skip feature by region */
  411. Vect_line_box(iline, &lbox);
  412. if (!Vect_box_overlap(&lbox, &mbox))
  413. continue;
  414. }
  415. /* skip feature by field ignored */
  416. if (line_p)
  417. Vect_append_points(line_p, iline, GV_FORWARD);
  418. if (line_c) {
  419. int cat;
  420. if (!pg_info->toposchema_name) { /* simple features access */
  421. cat = (int)pg_info->cache.fid;
  422. }
  423. else { /* PostGIS Topology (cats are cached) */
  424. cat = pg_info->cache.lines_cats[pg_info->cache.lines_next-1];
  425. if (cat == 0) { /* not cached yet */
  426. int col_idx;
  427. col_idx = itype & GV_POINTS ? 2 : 3;
  428. if (!PQgetisnull(pg_info->res, pg_info->cache.lines_next-1, col_idx))
  429. cat = pg_info->cache.lines_cats[Map->next_line-1] =
  430. atoi(PQgetvalue(pg_info->res, pg_info->cache.lines_next-1, col_idx));
  431. else
  432. pg_info->cache.lines_cats[Map->next_line-1] = -1; /* no cat */
  433. }
  434. }
  435. if (cat > 0)
  436. Vect_cat_set(line_c, 1, cat);
  437. }
  438. return itype;
  439. }
  440. return -1; /* not reached */
  441. }
  442. /*!
  443. \brief Read feature geometry
  444. Geometry is stored in lines cache.
  445. \param[in,out] pg_info pointer to Format_info_pg struct
  446. \param fid feature id to be read (-1 for next)
  447. \param type feature type (GV_POINT, GV_LINE, ...) - use only for topological access
  448. \return simple feature type (SF_POINT, SF_LINESTRING, ...)
  449. \return -1 on error
  450. */
  451. SF_FeatureType get_feature(struct Format_info_pg *pg_info, int fid, int type)
  452. {
  453. int seq_type;
  454. int force_type; /* force type (GV_BOUNDARY or GV_CENTROID) for topo access only */
  455. char *data;
  456. if (!pg_info->geom_column && !pg_info->topogeom_column) {
  457. G_warning(_("No geometry or topo geometry column defined"));
  458. return -1;
  459. }
  460. if (fid < 1) { /* sequantial access */
  461. if (pg_info->cursor_name == NULL &&
  462. Vect__open_cursor_next_line_pg(pg_info, FALSE) != 0)
  463. return -1;
  464. }
  465. else { /* random access */
  466. if (!pg_info->fid_column && !pg_info->toposchema_name) {
  467. G_warning(_("Random access not supported. "
  468. "Primary key not defined."));
  469. return -1;
  470. }
  471. #ifdef USE_CURSOR_RND
  472. if (pg_info->cursor_fid > 0)
  473. pg_info->next_line = fid - pg_info->cursor_fid;
  474. else
  475. pg_info->next_line = 0;
  476. if (pg_info->next_line < 0 || pg_info->next_line > CURSOR_PAGE)
  477. Vect__close_cursor_pg(pg_info);
  478. if (pg_info->cursor_name == NULL &&
  479. Vect__open_cursor_line_pg(pg_info, fid, type) != 0)
  480. return -1;
  481. #else
  482. pg_info->next_line = 0;
  483. if (Vect__select_line_pg(pg_info, fid, type) != 0)
  484. return -1;
  485. #endif
  486. }
  487. /* do we need to fetch more records ? */
  488. if (PQntuples(pg_info->res) == CURSOR_PAGE &&
  489. PQntuples(pg_info->res) == pg_info->next_line) {
  490. char stmt[DB_SQL_MAX];
  491. PQclear(pg_info->res);
  492. sprintf(stmt, "FETCH %d in %s", CURSOR_PAGE, pg_info->cursor_name);
  493. G_debug(3, "SQL: %s", stmt);
  494. pg_info->res = PQexec(pg_info->conn, stmt);
  495. if (!pg_info->res || PQresultStatus(pg_info->res) != PGRES_TUPLES_OK) {
  496. error_tuples(pg_info);
  497. return -1;
  498. }
  499. pg_info->next_line = 0;
  500. }
  501. G_debug(3, "get_feature(): next_line = %d", pg_info->next_line);
  502. /* out of results ? */
  503. if (PQntuples(pg_info->res) == pg_info->next_line) {
  504. if (Vect__close_cursor_pg(pg_info) != 0)
  505. return -1; /* failure */
  506. else
  507. return -2; /* nothing to read */
  508. }
  509. force_type = -1;
  510. if (pg_info->toposchema_name) {
  511. if (fid < 0) {
  512. /* sequatial access */
  513. seq_type = atoi(PQgetvalue(pg_info->res, pg_info->next_line, 2));
  514. if (seq_type == GV_BOUNDARY ||
  515. (seq_type == GV_LINE && pg_info->feature_type == SF_POLYGON))
  516. force_type = GV_BOUNDARY;
  517. else if (seq_type == GV_CENTROID)
  518. force_type = GV_CENTROID;
  519. }
  520. else {
  521. /* random access: check topological elemenent type consistency */
  522. if (type & GV_POINTS) {
  523. if (type == GV_POINT &&
  524. strlen(PQgetvalue(pg_info->res, pg_info->next_line, 1)) != 0)
  525. G_warning(_("Inconsistency in topology: detected centroid (should be point)"));
  526. }
  527. else {
  528. int left_face, right_face;
  529. left_face = atoi(PQgetvalue(pg_info->res, pg_info->next_line, 1));
  530. right_face = atoi(PQgetvalue(pg_info->res, pg_info->next_line, 2));
  531. if (type == GV_LINE &&
  532. (left_face != 0 || right_face != 0))
  533. G_warning(_("Inconsistency in topology: detected boundary (should be line)"));
  534. }
  535. }
  536. }
  537. /* get geometry data */
  538. data = (char *)PQgetvalue(pg_info->res, pg_info->next_line, 0);
  539. /* load feature to the cache */
  540. pg_info->cache.sf_type = Vect__cache_feature_pg(data,
  541. FALSE, force_type,
  542. &(pg_info->cache), NULL);
  543. /* cache also categories (only for PostGIS Topology) */
  544. if (pg_info->toposchema_name) {
  545. int col_idx;
  546. col_idx = type & GV_POINTS ? 2 : 3;
  547. if (!PQgetisnull(pg_info->res, pg_info->next_line, col_idx))
  548. pg_info->cache.lines_cats[pg_info->cache.lines_next] =
  549. atoi(PQgetvalue(pg_info->res, pg_info->next_line, col_idx));
  550. else
  551. pg_info->cache.lines_cats[pg_info->cache.lines_next] = -1; /* no cat */
  552. }
  553. /* set feature id */
  554. if (fid < 0) {
  555. pg_info->cache.fid =
  556. atoi(PQgetvalue(pg_info->res, pg_info->next_line, 1));
  557. pg_info->next_line++;
  558. }
  559. else {
  560. pg_info->cache.fid = fid;
  561. }
  562. return pg_info->cache.sf_type;
  563. }
  564. /*!
  565. \brief Convert HEX to WKB data
  566. \param hex_data HEX data
  567. \param[out] nbytes number of bytes in output buffer
  568. \return pointer to WKB data buffer
  569. */
  570. unsigned char *hex_to_wkb(const char *hex_data, int *nbytes)
  571. {
  572. unsigned int length;
  573. int i;
  574. length = strlen(hex_data) / 2 + 1;
  575. if (length > wkb_data_length) {
  576. wkb_data_length = length;
  577. wkb_data = G_realloc(wkb_data, wkb_data_length);
  578. }
  579. *nbytes = length - 1;
  580. for (i = 0; i < (*nbytes); i++) {
  581. wkb_data[i] =
  582. (unsigned char)((hex_data[2 * i] >
  583. 'F' ? hex_data[2 * i] - 0x57 : hex_data[2 * i] >
  584. '9' ? hex_data[2 * i] - 0x37 : hex_data[2 * i] -
  585. 0x30) << 4);
  586. wkb_data[i] |=
  587. (unsigned char)(hex_data[2 * i + 1] >
  588. 'F' ? hex_data[2 * i + 1] -
  589. 0x57 : hex_data[2 * i + 1] >
  590. '9' ? hex_data[2 * i + 1] -
  591. 0x37 : hex_data[2 * i + 1] - 0x30);
  592. }
  593. wkb_data[(*nbytes)] = 0;
  594. return wkb_data;
  595. }
  596. /*!
  597. \brief Read geometry from HEX data
  598. This code is inspired by OGRGeometryFactory::createFromWkb() from
  599. GDAL/OGR library.
  600. \param data HEX data
  601. \param skip_polygon skip polygons (level 1)
  602. \param force_type force GV_BOUNDARY or GV_CENTROID (used for PostGIS topology only)
  603. \param[out] cache lines cache
  604. \param[out] fparts used for building pseudo-topology (or NULL)
  605. \return simple feature type
  606. \return SF_UNKNOWN on error
  607. */
  608. SF_FeatureType Vect__cache_feature_pg(const char *data, int skip_polygon,
  609. int force_type,
  610. struct Format_info_cache *cache,
  611. struct feat_parts * fparts)
  612. {
  613. int ret, byte_order, nbytes, is3D;
  614. unsigned char *wkb_data;
  615. unsigned int wkb_flags;
  616. SF_FeatureType ftype;
  617. /* reset cache */
  618. if (cache->ctype == CACHE_MAP)
  619. cache->lines_num++;
  620. else {
  621. /* next to be read from cache */
  622. cache->lines_next = 0;
  623. cache->lines_num = 1;
  624. }
  625. cache->fid = -1;
  626. if (fparts)
  627. fparts->n_parts = 0;
  628. wkb_flags = 0;
  629. wkb_data = hex_to_wkb(data, &nbytes);
  630. if (nbytes < 5) {
  631. /* G_free(wkb_data); */
  632. if (nbytes > 0) {
  633. G_debug(3, "Vect__cache_feature_pg(): invalid geometry");
  634. G_warning(_("Invalid WKB content: %d bytes"), nbytes);
  635. return SF_UNKNOWN;
  636. }
  637. else {
  638. G_debug(3, "Vect__cache_feature_pg(): no geometry");
  639. return SF_NONE;
  640. }
  641. }
  642. /* parsing M coordinate not supported */
  643. memcpy(&wkb_flags, wkb_data + 1, 4);
  644. byte_order = (wkb_data[0] == 0 ? ENDIAN_BIG : ENDIAN_LITTLE);
  645. if (byte_order == ENDIAN_BIG)
  646. wkb_flags = SWAP32(wkb_flags);
  647. if (wkb_flags & 0x40000000) {
  648. G_warning(_("Reading EWKB with 4-dimensional coordinates (XYZM) "
  649. "is not supported"));
  650. /* G_free(wkb_data); */
  651. return SF_UNKNOWN;
  652. }
  653. /* PostGIS EWKB format includes an SRID, but this won't be
  654. understood by OGR, so if the SRID flag is set, we remove the
  655. SRID (bytes at offset 5 to 8).
  656. */
  657. if (nbytes > 9 &&
  658. ((byte_order == ENDIAN_BIG && (wkb_data[1] & 0x20)) ||
  659. (byte_order == ENDIAN_LITTLE && (wkb_data[4] & 0x20)))) {
  660. memmove(wkb_data + 5, wkb_data + 9, nbytes - 9);
  661. nbytes -= 4;
  662. if (byte_order == ENDIAN_BIG)
  663. wkb_data[1] &= (~0x20);
  664. else
  665. wkb_data[4] &= (~0x20);
  666. }
  667. if (nbytes < 9 && nbytes != -1) {
  668. /* G_free(wkb_data); */
  669. return SF_UNKNOWN;
  670. }
  671. /* Get the geometry feature type. For now we assume that geometry
  672. type is between 0 and 255 so we only have to fetch one byte.
  673. */
  674. if (byte_order == ENDIAN_LITTLE) {
  675. ftype = (SF_FeatureType) wkb_data[1];
  676. is3D = wkb_data[4] & 0x80 || wkb_data[2] & 0x80;
  677. }
  678. else {
  679. ftype = (SF_FeatureType) wkb_data[4];
  680. is3D = wkb_data[1] & 0x80 || wkb_data[3] & 0x80;
  681. }
  682. G_debug(3, "Vect__cache_feature_pg(): sf_type = %d", ftype);
  683. /* allocate space in lines cache - be minimalistic
  684. more lines require eg. polygon with more rings, multi-features
  685. or geometry collections
  686. */
  687. if (cache->ctype == CACHE_MAP) {
  688. reallocate_cache(cache, 1, TRUE);
  689. }
  690. else {
  691. if (!cache->lines) {
  692. reallocate_cache(cache, 1, FALSE);
  693. }
  694. }
  695. ret = -1;
  696. if (ftype == SF_POINT) {
  697. cache->lines_types[cache->lines_num-1] = force_type == GV_CENTROID ? force_type : GV_POINT;
  698. ret = point_from_wkb(wkb_data, nbytes, byte_order,
  699. is3D, cache->lines[cache->lines_num-1]);
  700. add_fpart(fparts, ftype, 0, 1);
  701. }
  702. else if (ftype == SF_LINESTRING) {
  703. cache->lines_types[cache->lines_num-1] = force_type == GV_BOUNDARY ? force_type : GV_LINE;
  704. ret = linestring_from_wkb(wkb_data, nbytes, byte_order,
  705. is3D, cache->lines[cache->lines_num-1], FALSE);
  706. add_fpart(fparts, ftype, 0, 1);
  707. }
  708. else if (ftype == SF_POLYGON && !skip_polygon) {
  709. int nrings;
  710. cache->lines_num = 0; /* reset before reading rings */
  711. ret = polygon_from_wkb(wkb_data, nbytes, byte_order,
  712. is3D, cache, &nrings);
  713. add_fpart(fparts, ftype, 0, nrings);
  714. }
  715. else if (ftype == SF_MULTIPOINT ||
  716. ftype == SF_MULTILINESTRING ||
  717. ftype == SF_MULTIPOLYGON || ftype == SF_GEOMETRYCOLLECTION) {
  718. ret = geometry_collection_from_wkb(wkb_data, nbytes, byte_order,
  719. is3D, cache, fparts);
  720. }
  721. else {
  722. G_warning(_("Unsupported feature type %d"), ftype);
  723. }
  724. if (cache->ctype != CACHE_MAP) {
  725. /* read next feature from cache */
  726. cache->lines_next = 0;
  727. }
  728. /* G_free(wkb_data); */
  729. return ret > 0 ? ftype : SF_UNKNOWN;
  730. }
  731. /*!
  732. \brief Read point for WKB data
  733. See OGRPoint::importFromWkb() from GDAL/OGR library
  734. \param wkb_data WKB data
  735. \param nbytes number of bytes (WKB data buffer)
  736. \param byte_order byte order (ENDIAN_LITTLE, ENDIAN_BIG)
  737. \param with_z WITH_Z for 3D data
  738. \param[out] line_p point geometry (pointer to line_pnts struct)
  739. \return wkb size
  740. \return -1 on error
  741. */
  742. int point_from_wkb(const unsigned char *wkb_data, int nbytes, int byte_order,
  743. int with_z, struct line_pnts *line_p)
  744. {
  745. double x, y, z;
  746. if (nbytes < 21 && nbytes != -1)
  747. return -1;
  748. /* get vertex */
  749. memcpy(&x, wkb_data + 5, 8);
  750. memcpy(&y, wkb_data + 5 + 8, 8);
  751. if (byte_order == ENDIAN_BIG) {
  752. SWAPDOUBLE(&x);
  753. SWAPDOUBLE(&y);
  754. }
  755. if (with_z) {
  756. if (nbytes < 29 && nbytes != -1)
  757. return -1;
  758. memcpy(&z, wkb_data + 5 + 16, 8);
  759. if (byte_order == ENDIAN_BIG) {
  760. SWAPDOUBLE(&z);
  761. }
  762. }
  763. else {
  764. z = 0.0;
  765. }
  766. if (line_p) {
  767. Vect_reset_line(line_p);
  768. Vect_append_point(line_p, x, y, z);
  769. }
  770. return 5 + 8 * (with_z == WITH_Z ? 3 : 2);
  771. }
  772. /*!
  773. \brief Read line for WKB data
  774. See OGRLineString::importFromWkb() from GDAL/OGR library
  775. \param wkb_data WKB data
  776. \param nbytes number of bytes (WKB data buffer)
  777. \param byte_order byte order (ENDIAN_LITTLE, ENDIAN_BIG)
  778. \param with_z WITH_Z for 3D data
  779. \param[out] line_p line geometry (pointer to line_pnts struct)
  780. \return wkb size
  781. \return -1 on error
  782. */
  783. int linestring_from_wkb(const unsigned char *wkb_data, int nbytes,
  784. int byte_order, int with_z, struct line_pnts *line_p,
  785. int is_ring)
  786. {
  787. int npoints, point_size, buff_min_size, offset;
  788. int i;
  789. double x, y, z;
  790. if (is_ring)
  791. offset = 5;
  792. else
  793. offset = 0;
  794. if (is_ring && nbytes < 4 && nbytes != -1)
  795. return error_corrupted_data(NULL);
  796. /* get the vertex count */
  797. memcpy(&npoints, wkb_data + (5 - offset), 4);
  798. if (byte_order == ENDIAN_BIG) {
  799. npoints = SWAP32(npoints);
  800. }
  801. /* check if the wkb stream buffer is big enough to store fetched
  802. number of points. 16 or 24 - size of point structure
  803. */
  804. point_size = with_z ? 24 : 16;
  805. if (npoints < 0 || npoints > INT_MAX / point_size)
  806. return error_corrupted_data(NULL);
  807. buff_min_size = point_size * npoints;
  808. if (nbytes != -1 && buff_min_size > nbytes - (9 - offset))
  809. return error_corrupted_data(_("Length of input WKB is too small"));
  810. if (line_p)
  811. Vect_reset_line(line_p);
  812. /* get the vertex */
  813. for (i = 0; i < npoints; i++) {
  814. memcpy(&x, wkb_data + (9 - offset) + i * point_size, 8);
  815. memcpy(&y, wkb_data + (9 - offset) + 8 + i * point_size, 8);
  816. if (with_z)
  817. memcpy(&z, wkb_data + (9 - offset) + 16 + i * point_size, 8);
  818. else
  819. z = 0.0;
  820. if (byte_order == ENDIAN_BIG) {
  821. SWAPDOUBLE(&x);
  822. SWAPDOUBLE(&y);
  823. if (with_z)
  824. SWAPDOUBLE(&z);
  825. }
  826. if (line_p)
  827. Vect_append_point(line_p, x, y, z);
  828. }
  829. return (9 - offset) + (with_z == WITH_Z ? 3 : 2) * 8 * line_p->n_points;
  830. }
  831. /*!
  832. \brief Read polygon for WKB data
  833. See OGRPolygon::importFromWkb() from GDAL/OGR library
  834. \param wkb_data WKB data
  835. \param nbytes number of bytes (WKB data buffer)
  836. \param byte_order byte order (ENDIAN_LITTLE, ENDIAN_BIG)
  837. \param with_z WITH_Z for 3D data
  838. \param[out] line_p array of rings (pointer to line_pnts struct)
  839. \param[out] nrings number of rings
  840. \return wkb size
  841. \return -1 on error
  842. */
  843. int polygon_from_wkb(const unsigned char *wkb_data, int nbytes,
  844. int byte_order, int with_z,
  845. struct Format_info_cache *cache, int *nrings)
  846. {
  847. int data_offset, i, nsize, isize;
  848. struct line_pnts *line_i;
  849. if (nbytes < 9 && nbytes != -1)
  850. return -1;
  851. /* get the ring count */
  852. memcpy(nrings, wkb_data + 5, 4);
  853. if (byte_order == ENDIAN_BIG) {
  854. *nrings = SWAP32(*nrings);
  855. }
  856. if (*nrings < 0) {
  857. return -1;
  858. }
  859. /* reallocate space for islands if needed */
  860. reallocate_cache(cache, *nrings, FALSE);
  861. cache->lines_num += *nrings;
  862. /* each ring has a minimum of 4 bytes (point count) */
  863. if (nbytes != -1 && nbytes - 9 < (*nrings) * 4) {
  864. return error_corrupted_data(_("Length of input WKB is too small"));
  865. }
  866. data_offset = 9;
  867. if (nbytes != -1)
  868. nbytes -= data_offset;
  869. /* get the rings */
  870. nsize = 9;
  871. for (i = 0; i < (*nrings); i++) {
  872. if (cache->lines_next >= cache->lines_num)
  873. G_fatal_error(_("Invalid cache index %d (max: %d)"),
  874. cache->lines_next, cache->lines_num);
  875. line_i = cache->lines[cache->lines_next];
  876. cache->lines_types[cache->lines_next++] = GV_BOUNDARY;
  877. linestring_from_wkb(wkb_data + data_offset, nbytes, byte_order,
  878. with_z, line_i, TRUE);
  879. if (nbytes != -1) {
  880. isize = 4 + 8 * (with_z == WITH_Z ? 3 : 2) * line_i->n_points;
  881. nbytes -= isize;
  882. }
  883. nsize += isize;
  884. data_offset += isize;
  885. }
  886. return nsize;
  887. }
  888. /*!
  889. \brief Read geometry collection for WKB data
  890. See OGRGeometryCollection::importFromWkbInternal() from GDAL/OGR library
  891. \param wkb_data WKB data
  892. \param nbytes number of bytes (WKB data buffer)
  893. \param byte_order byte order (ENDIAN_LITTLE, ENDIAN_BIG)
  894. \param with_z WITH_Z for 3D data
  895. \param ipart part to cache (starts at 0)
  896. \param[out] cache lines cache
  897. \param[in,out] fparts feature parts (required for building pseudo-topology)
  898. \return number of parts
  899. \return -1 on error
  900. */
  901. int geometry_collection_from_wkb(const unsigned char *wkb_data, int nbytes,
  902. int byte_order, int with_z,
  903. struct Format_info_cache *cache,
  904. struct feat_parts *fparts)
  905. {
  906. int ipart, nparts, data_offset, nsize;
  907. unsigned char *wkb_subdata;
  908. SF_FeatureType ftype;
  909. if (nbytes < 9 && nbytes != -1)
  910. return error_corrupted_data(NULL);
  911. /* get the geometry count */
  912. memcpy(&nparts, wkb_data + 5, 4);
  913. if (byte_order == ENDIAN_BIG) {
  914. nparts = SWAP32(nparts);
  915. }
  916. if (nparts < 0 || nparts > INT_MAX / 9) {
  917. return error_corrupted_data(NULL);
  918. }
  919. G_debug(5, "\t(geometry collections) parts: %d", nparts);
  920. /* each geometry has a minimum of 9 bytes */
  921. if (nbytes != -1 && nbytes - 9 < nparts * 9) {
  922. return error_corrupted_data(_("Length of input WKB is too small"));
  923. }
  924. data_offset = 9;
  925. if (nbytes != -1)
  926. nbytes -= data_offset;
  927. /* reallocate space for parts if needed */
  928. reallocate_cache(cache, nparts, FALSE);
  929. /* get parts */
  930. for (ipart = 0; ipart < nparts; ipart++) {
  931. wkb_subdata = (unsigned char *)wkb_data + data_offset;
  932. if (nbytes < 9 && nbytes != -1)
  933. return error_corrupted_data(NULL);
  934. if (byte_order == ENDIAN_LITTLE) {
  935. ftype = (SF_FeatureType) wkb_subdata[1];
  936. }
  937. else {
  938. ftype = (SF_FeatureType) wkb_subdata[4];
  939. }
  940. if (ftype == SF_POINT) {
  941. cache->lines_types[cache->lines_next] = GV_POINT;
  942. nsize = point_from_wkb(wkb_subdata, nbytes, byte_order, with_z,
  943. cache->lines[cache->lines_next]);
  944. cache->lines_num++;
  945. add_fpart(fparts, ftype, cache->lines_next, 1);
  946. cache->lines_next++;
  947. }
  948. else if (ftype == SF_LINESTRING) {
  949. cache->lines_types[cache->lines_next] = GV_LINE;
  950. nsize =
  951. linestring_from_wkb(wkb_subdata, nbytes, byte_order, with_z,
  952. cache->lines[cache->lines_next], FALSE);
  953. cache->lines_num++;
  954. add_fpart(fparts, ftype, cache->lines_next, 1);
  955. cache->lines_next++;
  956. }
  957. else if (ftype == SF_POLYGON) {
  958. int idx, nrings;
  959. idx = cache->lines_next;
  960. nsize = polygon_from_wkb(wkb_subdata, nbytes, byte_order,
  961. with_z, cache, &nrings);
  962. add_fpart(fparts, ftype, idx, nrings);
  963. }
  964. else if (ftype == SF_GEOMETRYCOLLECTION ||
  965. ftype == SF_MULTIPOLYGON ||
  966. ftype == SF_MULTILINESTRING || ftype == SF_MULTIPOLYGON) {
  967. geometry_collection_from_wkb(wkb_subdata, nbytes, byte_order,
  968. with_z, cache, fparts);
  969. }
  970. else {
  971. G_warning(_("Unsupported feature type %d"), ftype);
  972. }
  973. if (nbytes != -1) {
  974. nbytes -= nsize;
  975. }
  976. data_offset += nsize;
  977. }
  978. return nparts;
  979. }
  980. /*!
  981. \brief Report error message
  982. \param msg message (NULL)
  983. \return -1
  984. */
  985. int error_corrupted_data(const char *msg)
  986. {
  987. if (msg)
  988. G_warning(_("Corrupted data. %s."), msg);
  989. else
  990. G_warning(_("Corrupted data"));
  991. return -1;
  992. }
  993. /*!
  994. \brief Create select cursor for sequential access (internal use only)
  995. Allocated cursor name should be freed by G_free().
  996. \param pg_info pointer to Format_info_pg struct
  997. \param fetch_all TRUE to fetch all records
  998. \param[out] cursor name
  999. \return 0 on success
  1000. \return -1 on failure
  1001. */
  1002. int Vect__open_cursor_next_line_pg(struct Format_info_pg *pg_info, int fetch_all)
  1003. {
  1004. char stmt[DB_SQL_MAX];
  1005. if (Vect__execute_pg(pg_info->conn, "BEGIN") == -1)
  1006. return -1;
  1007. /* set cursor name */
  1008. G_asprintf(&(pg_info->cursor_name),
  1009. "%s_%s_%p", pg_info->schema_name, pg_info->table_name, pg_info->conn);
  1010. if (!pg_info->toposchema_name) {
  1011. /* simple feature access (geom, fid) */
  1012. /* TODO: start_fid */
  1013. sprintf(stmt,
  1014. "DECLARE %s CURSOR FOR SELECT %s,%s FROM \"%s\".\"%s\" ORDER BY %s",
  1015. pg_info->cursor_name, pg_info->geom_column, pg_info->fid_column, pg_info->schema_name,
  1016. pg_info->table_name, pg_info->fid_column);
  1017. }
  1018. else {
  1019. /* topology access (geom,fid,type) */
  1020. /* TODO: optimize SQL statement (for points/centroids) */
  1021. sprintf(stmt,
  1022. "DECLARE %s CURSOR FOR "
  1023. "SELECT geom,id,type,fid FROM ("
  1024. "SELECT tt.node_id AS id,tt.geom, %d AS type, ft.%s AS fid FROM \"%s\".node AS tt "
  1025. "LEFT JOIN \"%s\".\"%s\" AS ft ON (%s).type = 1 AND (%s).id = node_id "
  1026. "WHERE containing_face IS NULL AND node_id NOT IN "
  1027. "(SELECT node FROM (SELECT start_node AS node FROM \"%s\".edge GROUP BY start_node UNION ALL "
  1028. "SELECT end_node AS node FROM \"%s\".edge GROUP BY end_node) AS foo) UNION ALL "
  1029. "SELECT tt.node_id AS id,tt.geom, %d AS type, ft.%s AS fid FROM \"%s\".node AS tt "
  1030. "LEFT JOIN \"%s\".\"%s\" AS ft ON (%s).type = 3 AND (%s).id = containing_face "
  1031. "WHERE containing_face IS NOT NULL AND node_id NOT IN "
  1032. "(SELECT node FROM (SELECT start_node AS node FROM \"%s\".edge GROUP BY start_node UNION ALL "
  1033. "SELECT end_node AS node FROM \"%s\".edge GROUP BY end_node) AS foo) UNION ALL "
  1034. "SELECT tt.edge_id AS id, tt.geom, %d AS type, ft.%s AS fid FROM \"%s\".edge AS tt "
  1035. "LEFT JOIN \"%s\".\"%s\" AS ft ON (%s).type = 2 AND (%s).id = edge_id "
  1036. "WHERE left_face = 0 AND right_face = 0 UNION ALL "
  1037. "SELECT tt.edge_id AS id, tt.geom, %d AS type, ft.%s AS fid FROM \"%s\".edge AS tt "
  1038. "LEFT JOIN \"%s\".\"%s\" AS ft ON (%s).type = 2 AND (%s).id = edge_id "
  1039. "WHERE left_face != 0 OR right_face != 0 ) AS foo ORDER BY type,id",
  1040. pg_info->cursor_name,
  1041. GV_POINT, pg_info->fid_column, pg_info->toposchema_name, pg_info->schema_name, pg_info->table_name,
  1042. pg_info->topogeom_column, pg_info->topogeom_column, pg_info->toposchema_name, pg_info->toposchema_name,
  1043. GV_CENTROID, pg_info->fid_column, pg_info->toposchema_name, pg_info->schema_name, pg_info->table_name,
  1044. pg_info->topogeom_column, pg_info->topogeom_column, pg_info->toposchema_name, pg_info->toposchema_name,
  1045. GV_LINE, pg_info->fid_column, pg_info->toposchema_name, pg_info->schema_name, pg_info->table_name,
  1046. pg_info->topogeom_column, pg_info->topogeom_column,
  1047. GV_BOUNDARY, pg_info->fid_column, pg_info->toposchema_name, pg_info->schema_name, pg_info->table_name,
  1048. pg_info->topogeom_column, pg_info->topogeom_column);
  1049. }
  1050. if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
  1051. Vect__execute_pg(pg_info->conn, "ROLLBACK");
  1052. return -1;
  1053. }
  1054. if (fetch_all)
  1055. sprintf(stmt, "FETCH ALL in %s", pg_info->cursor_name);
  1056. else
  1057. sprintf(stmt, "FETCH %d in %s", CURSOR_PAGE, pg_info->cursor_name);
  1058. G_debug(3, "SQL: %s", stmt);
  1059. pg_info->res = PQexec(pg_info->conn, stmt); /* fetch records from select cursor */
  1060. if (!pg_info->res || PQresultStatus(pg_info->res) != PGRES_TUPLES_OK) {
  1061. error_tuples(pg_info);
  1062. return -1;
  1063. }
  1064. pg_info->next_line = 0;
  1065. return 0;
  1066. }
  1067. /*!
  1068. \brief Open select cursor for random access (internal use only)
  1069. Fetch number of feature (given by CURSOR_PAGE) starting with
  1070. <em>fid</em>.
  1071. Allocated cursor name should be freed by G_free().
  1072. \param pg_info pointer to Format_info_pg struct
  1073. \param fid feature id to get
  1074. \param type feature type
  1075. \return 0 on success
  1076. \return -1 on failure
  1077. */
  1078. int Vect__open_cursor_line_pg(struct Format_info_pg *pg_info, int fid, int type)
  1079. {
  1080. char stmt[DB_SQL_MAX];
  1081. G_debug(3, "Vect__open_cursor_line_pg(): fid range = %d-%d, type = %d",
  1082. fid, fid + CURSOR_PAGE, type);
  1083. if (Vect__execute_pg(pg_info->conn, "BEGIN") == -1)
  1084. return -1;
  1085. pg_info->cursor_fid = fid;
  1086. G_asprintf(&(pg_info->cursor_name),
  1087. "%s_%s_%d_%p", pg_info->schema_name, pg_info->table_name, fid, pg_info->conn);
  1088. if (!pg_info->toposchema_name) {
  1089. /* simple feature access (geom) */
  1090. sprintf(stmt,
  1091. "DECLARE %s CURSOR FOR SELECT %s FROM \"%s\".\"%s\" "
  1092. "WHERE %s BETWEEN %d AND %d ORDER BY %s", pg_info->cursor_name,
  1093. pg_info->geom_column, pg_info->schema_name, pg_info->table_name,
  1094. pg_info->fid_column, fid, fid + CURSOR_PAGE, pg_info->fid_column);
  1095. }
  1096. else {
  1097. /* topological access */
  1098. if (!(type & (GV_POINTS | GV_LINES))) {
  1099. G_warning(_("Unsupported feature type %d"), type);
  1100. Vect__execute_pg(pg_info->conn, "ROLLBACK");
  1101. return -1;
  1102. }
  1103. if (type & GV_POINTS) {
  1104. /* points (geom,containing_face) */
  1105. sprintf(stmt,
  1106. "DECLARE %s CURSOR FOR SELECT geom,containing_face "
  1107. " FROM \"%s\".node WHERE node_id BETWEEN %d AND %d ORDER BY node_id",
  1108. pg_info->cursor_name,
  1109. pg_info->toposchema_name, fid, fid + CURSOR_PAGE);
  1110. }
  1111. else {
  1112. /* edges (geom,left_face,right_face) */
  1113. sprintf(stmt,
  1114. "DECLARE %s CURSOR FOR SELECT geom,left_face,right_face "
  1115. " FROM \"%s\".edge WHERE edge_id BETWEEN %d AND %d ORDER BY edge_id",
  1116. pg_info->cursor_name,
  1117. pg_info->toposchema_name, fid, fid + CURSOR_PAGE);
  1118. }
  1119. }
  1120. if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
  1121. Vect__execute_pg(pg_info->conn, "ROLLBACK");
  1122. return -1;
  1123. }
  1124. pg_info->next_line = 0;
  1125. sprintf(stmt, "FETCH ALL in %s", pg_info->cursor_name);
  1126. pg_info->res = PQexec(pg_info->conn, stmt);
  1127. if (!pg_info->res || PQresultStatus(pg_info->res) != PGRES_TUPLES_OK) {
  1128. error_tuples(pg_info);
  1129. return -1;
  1130. }
  1131. return 0;
  1132. }
  1133. /*!
  1134. \brief Close select cursor
  1135. \param pg_info pointer to Format_info_pg struct
  1136. \return 0 on success
  1137. \return -1 on failure
  1138. */
  1139. int Vect__close_cursor_pg(struct Format_info_pg *pg_info)
  1140. {
  1141. if (pg_info->res) {
  1142. PQclear(pg_info->res);
  1143. pg_info->res = NULL;
  1144. }
  1145. if (pg_info->cursor_name) {
  1146. char stmt[DB_SQL_MAX];
  1147. sprintf(stmt, "CLOSE %s", pg_info->cursor_name);
  1148. if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
  1149. G_warning(_("Unable to close cursor %s"), pg_info->cursor_name);
  1150. return -1;
  1151. }
  1152. Vect__execute_pg(pg_info->conn, "COMMIT");
  1153. G_free(pg_info->cursor_name);
  1154. pg_info->cursor_name = NULL;
  1155. }
  1156. return 0;
  1157. }
  1158. /*!
  1159. \brief Select feature (internal use only)
  1160. \param pg_info pointer to Format_info_pg struct
  1161. \param fid feature id to get
  1162. \param type feature type
  1163. \return 0 on success
  1164. \return -1 on failure
  1165. */
  1166. int Vect__select_line_pg(struct Format_info_pg *pg_info, int fid, int type)
  1167. {
  1168. char stmt[DB_SQL_MAX];
  1169. if (!pg_info->toposchema_name) {
  1170. /* simple feature access */
  1171. sprintf(stmt,
  1172. "SELECT %s FROM \"%s\".\"%s\" WHERE %s = %d",
  1173. pg_info->geom_column, pg_info->schema_name, pg_info->table_name,
  1174. pg_info->fid_column, fid);
  1175. }
  1176. else {
  1177. /* topological access */
  1178. if (!(type & (GV_POINTS | GV_LINES))) {
  1179. G_warning(_("Unsupported feature type %d"), type);
  1180. return -1;
  1181. }
  1182. if (type & GV_POINTS) {
  1183. int topotype;
  1184. char *nodeid;
  1185. if (type == GV_POINT) {
  1186. topotype = 1;
  1187. nodeid = pg_info->fid_column;
  1188. }
  1189. else { /* assuming GV_CENTROID */
  1190. topotype = 3;
  1191. nodeid = "containing_face";
  1192. }
  1193. sprintf(stmt,
  1194. "SELECT tt.geom,tt.containing_face,ft.%s FROM \"%s\".node AS tt "
  1195. "LEFT JOIN \"%s\".\"%s\" AS ft ON (%s).type = %d and (%s).id = %s "
  1196. "WHERE node_id = %d",
  1197. pg_info->fid_column, pg_info->toposchema_name,
  1198. pg_info->schema_name, pg_info->table_name, pg_info->topogeom_column,
  1199. topotype, pg_info->topogeom_column, nodeid, fid);
  1200. }
  1201. else {
  1202. sprintf(stmt,
  1203. "SELECT tt.geom,tt.left_face,tt.right_face,ft.%s FROM \"%s\".edge AS tt "
  1204. "LEFT JOIN \"%s\".\"%s\" AS ft ON (%s).type = 2 and (%s).id = edge_id "
  1205. "WHERE edge_id = %d",
  1206. pg_info->fid_column, pg_info->toposchema_name,
  1207. pg_info->schema_name, pg_info->table_name, pg_info->topogeom_column,
  1208. pg_info->topogeom_column, fid);
  1209. }
  1210. }
  1211. G_debug(3, "SQL: %s", stmt);
  1212. pg_info->next_line = 0;
  1213. pg_info->res = PQexec(pg_info->conn, stmt);
  1214. if (!pg_info->res || PQresultStatus(pg_info->res) != PGRES_TUPLES_OK) {
  1215. error_tuples(pg_info);
  1216. return -1;
  1217. }
  1218. return 0;
  1219. }
  1220. /*!
  1221. \brief Execute SQL statement
  1222. See pg_local_proto.h
  1223. \param conn pointer to PGconn
  1224. \param stmt query
  1225. \return 0 on success
  1226. \return -1 on error
  1227. */
  1228. int Vect__execute_pg(PGconn * conn, const char *stmt)
  1229. {
  1230. PGresult *result;
  1231. result = NULL;
  1232. G_debug(3, "Vect__execute_pg(): %s", stmt);
  1233. result = PQexec(conn, stmt);
  1234. if (!result || PQresultStatus(result) != PGRES_COMMAND_OK) {
  1235. PQclear(result);
  1236. G_warning(_("Execution failed: %s\nReason: %s"), stmt,
  1237. PQerrorMessage(conn));
  1238. return -1;
  1239. }
  1240. PQclear(result);
  1241. return 0;
  1242. }
  1243. /*!
  1244. \brief Execute SQL statement and get value.
  1245. \param conn pointer to PGconn
  1246. \param stmt query
  1247. \return value on success
  1248. \return -1 on error
  1249. */
  1250. int Vect__execute_get_value_pg(PGconn *conn, const char *stmt)
  1251. {
  1252. int ret;
  1253. PGresult *result;
  1254. result = NULL;
  1255. G_debug(3, "Vect__execute_get_value_pg(): %s", stmt);
  1256. result = PQexec(conn, stmt);
  1257. if (!result || PQresultStatus(result) != PGRES_TUPLES_OK ||
  1258. PQntuples(result) != 1) {
  1259. PQclear(result);
  1260. G_warning(_("Execution failed: %s\nReason: %s"), stmt,
  1261. PQerrorMessage(conn));
  1262. return -1;
  1263. }
  1264. ret = atoi(PQgetvalue(result, 0, 0));
  1265. PQclear(result);
  1266. return ret;
  1267. }
  1268. /*!
  1269. \brief Reallocate lines cache
  1270. */
  1271. void reallocate_cache(struct Format_info_cache *cache, int num, int incr)
  1272. {
  1273. int i;
  1274. if (!incr && cache->lines_alloc >= num)
  1275. return;
  1276. if (!incr && !cache->lines) {
  1277. /* most of features requires only one line cache */
  1278. cache->lines_alloc = 1;
  1279. }
  1280. else {
  1281. cache->lines_alloc += num;
  1282. }
  1283. cache->lines = (struct line_pnts **)G_realloc(cache->lines,
  1284. cache->lines_alloc *
  1285. sizeof(struct line_pnts *));
  1286. cache->lines_types = (int *)G_realloc(cache->lines_types,
  1287. cache->lines_alloc * sizeof(int));
  1288. cache->lines_cats = (int *)G_realloc(cache->lines_cats,
  1289. cache->lines_alloc * sizeof(int));
  1290. if (cache->lines_alloc > 1) {
  1291. for (i = cache->lines_alloc - num; i < cache->lines_alloc; i++) {
  1292. cache->lines[i] = Vect_new_line_struct();
  1293. cache->lines_types[i] = -1;
  1294. cache->lines_cats[i] = -1;
  1295. }
  1296. }
  1297. else {
  1298. cache->lines[0] = Vect_new_line_struct();
  1299. cache->lines_types[0] = -1;
  1300. cache->lines_cats[0] = -1;
  1301. }
  1302. }
  1303. void add_fpart(struct feat_parts *fparts, SF_FeatureType ftype,
  1304. int idx, int nlines)
  1305. {
  1306. if (!fparts)
  1307. return;
  1308. if (fparts->a_parts == 0 || fparts->n_parts >= fparts->a_parts) {
  1309. if (fparts->a_parts == 0)
  1310. fparts->a_parts = 1;
  1311. else
  1312. fparts->a_parts += fparts->n_parts;
  1313. fparts->ftype = (SF_FeatureType *) G_realloc(fparts->ftype,
  1314. fparts->a_parts *
  1315. sizeof(SF_FeatureType));
  1316. fparts->nlines =
  1317. (int *)G_realloc(fparts->nlines, fparts->a_parts * sizeof(int));
  1318. fparts->idx =
  1319. (int *)G_realloc(fparts->idx, fparts->a_parts * sizeof(int));
  1320. }
  1321. fparts->ftype[fparts->n_parts] = ftype;
  1322. fparts->idx[fparts->n_parts] = idx;
  1323. fparts->nlines[fparts->n_parts] = nlines;
  1324. fparts->n_parts++;
  1325. }
  1326. /*
  1327. \brief Get centroid
  1328. \param pg_info pointer to Format_info_pg
  1329. \param centroid centroid id
  1330. \param[out] line_p output geometry
  1331. \return GV_CENTROID on success
  1332. \return -1 on error
  1333. */
  1334. int get_centroid(struct Map_info *Map, int centroid,
  1335. struct line_pnts *line_p)
  1336. {
  1337. int i, found;
  1338. struct bound_box box;
  1339. struct boxlist list;
  1340. struct P_line *Line;
  1341. struct P_topo_c *topo;
  1342. Line = Map->plus.Line[centroid];
  1343. topo = (struct P_topo_c *)Line->topo;
  1344. /* get area bbox */
  1345. Vect_get_area_box(Map, topo->area, &box);
  1346. /* search in spatial index for centroid with area bbox */
  1347. dig_init_boxlist(&list, TRUE);
  1348. Vect_select_lines_by_box(Map, &box, Line->type, &list);
  1349. found = -1;
  1350. for (i = 0; i < list.n_values; i++) {
  1351. if (list.id[i] == centroid) {
  1352. found = i;
  1353. break;
  1354. }
  1355. }
  1356. if (found == -1)
  1357. return -1;
  1358. if (line_p) {
  1359. Vect_reset_line(line_p);
  1360. Vect_append_point(line_p, list.box[found].E, list.box[found].N, 0.0);
  1361. }
  1362. return GV_CENTROID;
  1363. }
  1364. void error_tuples(struct Format_info_pg *pg_info)
  1365. {
  1366. Vect__execute_pg(pg_info->conn, "ROLLBACK");
  1367. G_warning(_("Unable to read features. Reason:\n%s"),
  1368. PQresultErrorMessage(pg_info->res));
  1369. if (pg_info->res) {
  1370. PQclear(pg_info->res);
  1371. pg_info->res = NULL;
  1372. }
  1373. }
  1374. #endif