read_pg.c 43 KB

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