GRASS GIS 8 Programmer's Manual  8.2.2dev(2023)-3d2c704037
read_pg.c
Go to the documentation of this file.
1 /*!
2  \file lib/vector/Vlib/read_pg.c
3 
4  \brief Vector library - reading features (PostGIS format)
5 
6  Higher level functions for reading/writing/manipulating vectors.
7 
8  \todo Currently only points, linestrings and polygons are supported,
9  implement also other types
10 
11  \todo Support multigeometries
12 
13  \todo PostGIS Topology - fix category handling (read categories
14  from feature table)
15 
16  (C) 2011-2013 by the GRASS Development Team
17 
18  This program is free software under the GNU General Public License
19  (>=v2). Read the file COPYING that comes with GRASS for details.
20 
21  \author Martin Landa <landa.martin gmail.com>
22  */
23 
24 #include <stdlib.h>
25 #include <string.h>
26 #include <limits.h>
27 
28 #include <grass/vector.h>
29 #include <grass/dbmi.h>
30 #include <grass/glocale.h>
31 
32 #include "local_proto.h"
33 
34 #ifdef HAVE_POSTGRES
35 #include "pg_local_proto.h"
36 
37 /* #define USE_CURSOR_RND */
38 
39 static unsigned char *wkb_data;
40 static unsigned int wkb_data_length;
41 
42 static int read_next_line_pg(struct Map_info *,
43  struct line_pnts *, struct line_cats *, int);
44 SF_FeatureType get_feature(struct Map_info *, int, int);
45 static unsigned char *hex_to_wkb(const char *, int *);
46 static int point_from_wkb(const unsigned char *, int, int, int,
47  struct line_pnts *);
48 static int linestring_from_wkb(const unsigned char *, int, int, int,
49  struct line_pnts *, int);
50 static int polygon_from_wkb(const unsigned char *, int, int, int,
51  struct Format_info_cache *, int *);
52 static int geometry_collection_from_wkb(const unsigned char *, int, int, int,
53  struct Format_info_cache *,
54  struct feat_parts *);
55 static int error_corrupted_data(const char *);
56 static void add_fpart(struct feat_parts *, SF_FeatureType, int, int);
57 static int get_centroid(struct Map_info *, int, struct line_pnts *, struct line_cats *);
58 static void error_tuples(struct Format_info_pg *);
59 #endif
60 
61 /*!
62  \brief Read next feature from PostGIS layer. Skip
63  empty features (level 1 without topology).
64  t
65  This function implements sequential access.
66 
67  The action of this routine can be modified by:
68  - Vect_read_constraint_region()
69  - Vect_read_constraint_type()
70  - Vect_remove_constraints()
71 
72  \param Map pointer to Map_info structure
73  \param[out] line_p container used to store line points within
74  (pointer to line_pnts struct)
75  \param[out] line_c container used to store line categories within
76  (pointer line_cats struct)
77 
78  \return feature type
79  \return -2 no more features (EOF)
80  \return -1 out of memory
81  */
83  struct line_pnts *line_p, struct line_cats *line_c)
84 {
85 #ifdef HAVE_POSTGRES
86  G_debug(3, "V1_read_next_line_pg()");
87 
88  /* constraints not ignored */
89  return read_next_line_pg(Map, line_p, line_c, FALSE);
90 #else
91  G_fatal_error(_("GRASS is not compiled with PostgreSQL support"));
92  return -1;
93 #endif
94 }
95 
96 /*!
97  \brief Read next feature from PostGIS layer on topological level
98  (simple feature access).
99 
100  This function implements sequential access.
101 
102  \param Map pointer to Map_info structure
103  \param[out] line_p container used to store line points within
104  (pointer to line_pnts struct)
105  \param[out] line_c container used to store line categories within
106  (pointer to line_cats struct)
107 
108  \return feature type
109  \return -2 no more features (EOF)
110  \return -1 on failure
111  */
112 int V2_read_next_line_pg(struct Map_info *Map, struct line_pnts *line_p,
113  struct line_cats *line_c)
114 {
115 #ifdef HAVE_POSTGRES
116  int line, ret;
117  struct P_line *Line;
118  struct bound_box lbox, mbox;
119 
120  struct Format_info_pg *pg_info;
121 
122  G_debug(3, "V2_read_next_line_pg()");
123 
124  pg_info = &(Map->fInfo.pg);
125 
126  if (Map->constraint.region_flag)
127  Vect_get_constraint_box(Map, &mbox);
128 
129  ret = -1;
130  while (TRUE) {
131  line = Map->next_line;
132 
133  if (Map->next_line > Map->plus.n_lines)
134  return -2;
135 
136  Line = Map->plus.Line[line];
137  if (Line == NULL) { /* skip dead features */
138  Map->next_line++;
139  continue;
140  }
141 
142  if (Map->constraint.type_flag) {
143  /* skip by type */
144  if (!(Line->type & Map->constraint.type)) {
145  Map->next_line++;
146  continue;
147  }
148  }
149 
150  if (!pg_info->toposchema_name &&
151  Line->type == GV_CENTROID) {
152  G_debug(4, "Determine centroid for simple features");
153 
154  if (line_p != NULL) {
155  int i, found;
156  struct bound_box box;
157  struct boxlist list;
158  struct P_topo_c *topo = (struct P_topo_c *)Line->topo;
159 
160  /* get area bbox */
161  Vect_get_area_box(Map, topo->area, &box);
162  /* search in spatial index for centroid with area bbox */
163  dig_init_boxlist(&list, TRUE);
164  Vect_select_lines_by_box(Map, &box, Line->type, &list);
165 
166  found = -1;
167  for (i = 0; i < list.n_values; i++) {
168  if (list.id[i] == line) {
169  found = i;
170  break;
171  }
172  }
173 
174  if (found > -1) {
175  Vect_reset_line(line_p);
176  Vect_append_point(line_p, list.box[found].E,
177  list.box[found].N, 0.0);
178  }
179  }
180  if (line_c != NULL) {
181  /* cat = FID and offset = FID for centroid */
182  Vect_reset_cats(line_c);
183  Vect_cat_set(line_c, 1, (int)Line->offset);
184  }
185 
186  ret = GV_CENTROID;
187  }
188  else {
189  /* ignore constraints */
190  ret = read_next_line_pg(Map, line_p, line_c, TRUE);
191  if (ret != Line->type) {
192  G_warning(_("Unexpected feature type (%d) - should be (%d)"),
193  ret, Line->type);
194  return -1;
195  }
196  }
197 
198  if (Map->constraint.region_flag) {
199  /* skip by region */
200  Vect_line_box(line_p, &lbox);
201  if (!Vect_box_overlap(&lbox, &mbox)) {
202  Map->next_line++;
203  continue;
204  }
205  }
206 
207  /* skip by field ignored */
208 
209  Map->next_line++; /* read next */
210 
211  return ret;
212  }
213 #else
214  G_fatal_error(_("GRASS is not compiled with PostgreSQL support"));
215 #endif
216 
217  return -1; /* not reached */
218 }
219 
220 /*!
221  \brief Read feature from PostGIS layer at given offset (level 1 without topology)
222 
223  This function implements random access on level 1.
224 
225  \param Map pointer to Map_info structure
226  \param[out] line_p container used to store line points within
227  (pointer line_pnts struct)
228  \param[out] line_c container used to store line categories within
229  (pointer line_cats struct)
230  \param offset given offset
231 
232  \return line type
233  \return 0 dead line
234  \return -2 no more features
235  \return -1 out of memory
236  */
237 int V1_read_line_pg(struct Map_info *Map,
238  struct line_pnts *line_p, struct line_cats *line_c,
239  off_t offset)
240 {
241 #ifdef HAVE_POSTGRES
242  long fid;
243  int ipart, type;
244 
245  struct Format_info_pg *pg_info;
246 
247  pg_info = &(Map->fInfo.pg);
248 
249  G_debug(3, "V1_read_line_pg(): offset = %lu offset_num = %lu",
250  (long)offset, (long)pg_info->offset.array_num);
251 
252  if (offset >= pg_info->offset.array_num)
253  return -2; /* nothing to read */
254 
255  if (line_p != NULL)
256  Vect_reset_line(line_p);
257  if (line_c != NULL)
258  Vect_reset_cats(line_c);
259 
260  fid = pg_info->offset.array[offset];
261  G_debug(4, " fid = %ld", fid);
262 
263  /* read feature to cache if necessary */
264  if (pg_info->cache.fid != fid) {
265  int type;
266 
267  G_debug(3, "read (%s) feature (fid = %ld) to cache",
268  pg_info->table_name, fid);
269  get_feature(Map, fid, -1);
270 
271  if (pg_info->cache.sf_type == SF_NONE) {
272  G_warning(_("Feature %ld without geometry skipped"), fid);
273  return -1;
274  }
275 
276  type = (int)pg_info->cache.sf_type;
277  if (type < 0) /* -1 || - 2 */
278  return type;
279  }
280 
281  /* get data from cache */
282  if (pg_info->cache.sf_type == SF_POINT ||
283  pg_info->cache.sf_type == SF_LINESTRING)
284  ipart = 0;
285  else
286  ipart = pg_info->offset.array[offset + 1];
287  type = pg_info->cache.lines_types[ipart];
288  G_debug(3, "read feature part: %d -> type = %d", ipart, type);
289 
290  if (line_p)
291  Vect_append_points(line_p, pg_info->cache.lines[ipart], GV_FORWARD);
292 
293  if (line_c)
294  Vect_cat_set(line_c, 1, (int)fid);
295 
296  return type;
297 #else
298  G_fatal_error(_("GRASS is not compiled with PostgreSQL support"));
299  return -1;
300 #endif
301 }
302 
303 /*!
304  \brief Read feature from PostGIS layer on topological level
305 
306  This function implements random access on level 2.
307 
308  Note: Topology must be built at level >= GV_BUILD_BASE
309 
310  \param Map pointer to Map_info structure
311  \param[out] line_p container used to store line points within (pointer line_pnts struct)
312  \param[out] line_c container used to store line categories within (pointer line_cats struct)
313  \param line feature id to read
314 
315  \return feature type
316  \return 0 dead feature
317  \return -1 on error
318  */
319 int V2_read_line_pg(struct Map_info *Map, struct line_pnts *line_p,
320  struct line_cats *line_c, int line)
321 {
322 #ifdef HAVE_POSTGRES
323  int fid, cache_idx;
324 
325  struct Format_info_pg *pg_info;
326  struct P_line *Line;
327 
328  pg_info = &(Map->fInfo.pg);
329 
330  if (line < 1 || line > Map->plus.n_lines) {
331  G_warning(_("Attempt to access feature with invalid id (%d)"), line);
332  return -1;
333  }
334 
335  Line = Map->plus.Line[line];
336  if (Line == NULL) {
337  G_warning(_("Attempt to access dead feature %d"), line);
338  return 0;
339  }
340 
341  G_debug(4, "V2_read_line_pg() line = %d type = %d offset = %"PRI_OFF_T,
342  line, Line->type, Line->offset);
343 
344  if (!line_p && !line_c)
345  return Line->type;
346 
347  if (line_p)
348  Vect_reset_line(line_p);
349  if (Line->type == GV_CENTROID && !pg_info->toposchema_name) {
350  /* simple features access: get centroid from sidx */
351  return get_centroid(Map, line, line_p, line_c);
352  }
353 
354  /* get feature id */
355  if (pg_info->toposchema_name)
356  fid = Line->offset;
357  else
358  fid = pg_info->offset.array[Line->offset];
359 
360  /* read feature */
361  if (pg_info->cache.ctype == CACHE_MAP) {
362  cache_idx = line - 1;
363 
364  if (cache_idx >= pg_info->cache.lines_num)
365  G_fatal_error(_("Requesting invalid feature from cache (%d). Number of features in cache: %d"),
366  cache_idx, pg_info->cache.lines_num);
367  if (pg_info->cache.lines_types[cache_idx] != Line->type)
368  G_warning(_("Feature %d: unexpected type (%d) - should be %d"),
369  line, pg_info->cache.lines_types[cache_idx], Line->type);
370  }
371  else {
372  get_feature(Map, fid, Line->type);
373  cache_idx = 0;
374  }
375 
376  /* check sf type */
377  if (pg_info->cache.sf_type == SF_NONE) {
378  G_warning(_("Feature %d without geometry skipped"), line);
379  return -1;
380  }
381  if (0 > (int)pg_info->cache.sf_type) /* -1 || - 2 */
382  return -1;
383 
384  if (line_c) {
385  int cat;
386 
387  Vect_reset_cats(line_c);
388  if (!pg_info->toposchema_name) { /* simple features access */
389  cat = fid;
390  }
391  else { /* PostGIS Topology (cats are cached) */
392  cat = pg_info->cache.lines_cats[cache_idx];
393  if (cat == 0) { /* not cached yet */
394  int col_idx;
395 
396  Vect__select_line_pg(pg_info, fid, Line->type);
397 
398  col_idx = Line->type & GV_POINTS ? 2 : 3;
399 
400  if (!PQgetisnull(pg_info->res, 0, col_idx))
401  cat = pg_info->cache.lines_cats[cache_idx] =
402  atoi(PQgetvalue(pg_info->res, 0, col_idx));
403  else
404  pg_info->cache.lines_cats[cache_idx] = -1; /* no cat */
405  }
406  }
407  if (cat > 0)
408  Vect_cat_set(line_c, 1, cat);
409  }
410 
411  if (line_p)
412  Vect_append_points(line_p, pg_info->cache.lines[cache_idx], GV_FORWARD);
413 
414  return Line->type;
415 #else
416  G_fatal_error(_("GRASS is not compiled with PostgreSQL support"));
417  return -1;
418 #endif
419 }
420 
421 #ifdef HAVE_POSTGRES
422 /*!
423  \brief Read next feature from PostGIS layer.
424 
425  \param Map pointer to Map_info structure
426  \param[out] line_p container used to store line points within
427  (pointer to line_pnts struct)
428  \param[out] line_c container used to store line categories within
429  (pointer line_cats struct)
430  \param ignore_constraints TRUE to ignore constraints (type, region)
431 
432  \return feature type
433  \return -2 no more features (EOF)
434  \return -1 out of memory
435  */
436 int read_next_line_pg(struct Map_info *Map,
437  struct line_pnts *line_p, struct line_cats *line_c,
438  int ignore_constraints)
439 {
440  int itype;
441  SF_FeatureType sf_type;
442 
443  struct Format_info_pg *pg_info;
444  struct bound_box mbox, lbox;
445  struct line_pnts *iline;
446 
447  pg_info = &(Map->fInfo.pg);
448 
449  if (Map->constraint.region_flag && !ignore_constraints)
450  Vect_get_constraint_box(Map, &mbox);
451 
452  while (TRUE) {
453  /* reset data structures */
454  if (line_p != NULL)
455  Vect_reset_line(line_p);
456  if (line_c != NULL)
457  Vect_reset_cats(line_c);
458 
459  /* read feature to cache if necessary */
460  while (pg_info->cache.lines_next == pg_info->cache.lines_num) {
461  if (pg_info->cache.ctype == CACHE_MAP &&
462  pg_info->cache.fid == -2) {
463  /* stop reading - last cached line */
464  return -2;
465  }
466 
467  /* cache feature -> line_p & line_c */
468  sf_type = get_feature(Map, -1, -1);
469 
470  if (sf_type == SF_NONE) {
471  G_warning(_("Feature %ld without geometry skipped"), pg_info->cache.fid);
472  return -1;
473  }
474 
475  if ((int)sf_type < 0) { /* -1 || - 2 */
476  if (pg_info->cache.ctype == CACHE_MAP)
477  pg_info->cache.fid = -2; /* last line cached */
478  return (int)sf_type;
479  }
480 
481  if (sf_type == SF_GEOMETRY || sf_type == SF_NONE) {
482  G_warning(_("Feature without geometry. Skipped."));
483  pg_info->cache.lines_next = pg_info->cache.lines_num = 0;
484  continue;
485  }
486 
487  G_debug(4, "%d lines read to cache", pg_info->cache.lines_num);
488  /* store fid as offset to be used (used for topo access only */
489  Map->head.last_offset = pg_info->cache.fid;
490  }
491 
492  /* get data from cache, skip dead lines (NULL) */
493  do {
494  itype = pg_info->cache.lines_types[pg_info->cache.lines_next];
495  iline = pg_info->cache.lines[pg_info->cache.lines_next];
496 
497  pg_info->cache.lines_next++; /* read next line from cache */
498  } while (iline == NULL);
499 
500  G_debug(4, "read next cached line %d (type = %d)",
501  pg_info->cache.lines_next, itype);
502 
503  /* apply constraints */
504  if (Map->constraint.type_flag && !ignore_constraints) {
505  /* skip feature by type */
506  if (!(itype & Map->constraint.type))
507  continue;
508  }
509 
510  if (line_p && Map->constraint.region_flag && !ignore_constraints) {
511  /* skip feature by region */
512  Vect_line_box(iline, &lbox);
513 
514  if (!Vect_box_overlap(&lbox, &mbox))
515  continue;
516  }
517 
518  /* skip feature by field ignored */
519 
520  if (line_p)
521  Vect_append_points(line_p, iline, GV_FORWARD);
522 
523  if (line_c) {
524  int cat;
525  if (!pg_info->toposchema_name) { /* simple features access */
526  cat = (int)pg_info->cache.fid;
527  }
528  else { /* PostGIS Topology (cats are cached) */
529  cat = pg_info->cache.lines_cats[pg_info->cache.lines_next-1];
530  if (cat == 0) { /* not cached yet */
531  int col_idx;
532 
533  col_idx = itype & GV_POINTS ? 2 : 3;
534 
535  if (!PQgetisnull(pg_info->res, pg_info->cache.lines_next-1, col_idx))
536  cat = pg_info->cache.lines_cats[Map->next_line-1] =
537  atoi(PQgetvalue(pg_info->res, pg_info->cache.lines_next-1, col_idx));
538  else
539  pg_info->cache.lines_cats[Map->next_line-1] = -1; /* no cat */
540  }
541  }
542  if (cat > 0)
543  Vect_cat_set(line_c, 1, cat);
544  }
545 
546  return itype;
547  }
548 
549  return -1; /* not reached */
550 }
551 
552 /*!
553  \brief Read feature geometry
554 
555  Geometry is stored in lines cache.
556 
557  \param[in,out] Map pointer to Map_info struct
558  \param fid feature id to be read (-1 for next)
559  \param type feature type (GV_POINT, GV_LINE, ...) - use only for topological access
560 
561  \return simple feature type (SF_POINT, SF_LINESTRING, ...)
562  \return -1 on error
563  */
564 SF_FeatureType get_feature(struct Map_info *Map, int fid, int type)
565 {
566  int seq_type;
567  int force_type; /* force type (GV_BOUNDARY or GV_CENTROID) for topo access only */
568  char *data;
569 
570  struct Format_info_pg *pg_info;
571 
572  pg_info = &(Map->fInfo.pg);
573 
574  if (!pg_info->geom_column && !pg_info->topogeom_column) {
575  G_warning(_("No geometry or topo geometry column defined"));
576  return -1;
577  }
578  if (fid < 1) { /* sequantial access */
579  if (pg_info->cursor_name == NULL &&
580  Vect__open_cursor_next_line_pg(pg_info, FALSE, Map->plus.built) != 0)
581  return -1;
582  }
583  else { /* random access */
584  if (!pg_info->fid_column && !pg_info->toposchema_name) {
585  G_warning(_("Random access not supported. "
586  "Primary key not defined."));
587  return -1;
588  }
589 
590 #ifdef USE_CURSOR_RND
591  if (pg_info->cursor_fid > 0)
592  pg_info->next_line = fid - pg_info->cursor_fid;
593  else
594  pg_info->next_line = 0;
595 
596  if (pg_info->next_line < 0 || pg_info->next_line > CURSOR_PAGE)
597  Vect__close_cursor_pg(pg_info);
598 
599  if (pg_info->cursor_name == NULL &&
600  Vect__open_cursor_line_pg(pg_info, fid, type) != 0)
601  return -1;
602 #else
603  pg_info->next_line = 0;
604  if (Vect__select_line_pg(pg_info, fid, type) != 0)
605  return -1;
606 #endif
607  }
608 
609  /* do we need to fetch more records ? */
610  if (PQntuples(pg_info->res) == CURSOR_PAGE &&
611  PQntuples(pg_info->res) == pg_info->next_line) {
612  char stmt[DB_SQL_MAX];
613 
614  PQclear(pg_info->res);
615 
616  sprintf(stmt, "FETCH %d in %s", CURSOR_PAGE, pg_info->cursor_name);
617  G_debug(3, "SQL: %s", stmt);
618  pg_info->res = PQexec(pg_info->conn, stmt);
619  if (!pg_info->res || PQresultStatus(pg_info->res) != PGRES_TUPLES_OK) {
620  error_tuples(pg_info);
621  return -1;
622  }
623  pg_info->next_line = 0;
624  }
625 
626  G_debug(3, "get_feature(): next_line = %d", pg_info->next_line);
627 
628  /* out of results ? */
629  if (PQntuples(pg_info->res) == pg_info->next_line) {
630  if (Vect__close_cursor_pg(pg_info) != 0)
631  return -1; /* failure */
632  else
633  return -2; /* nothing to read */
634  }
635 
636  force_type = -1;
637  if (pg_info->toposchema_name) {
638  if (fid < 0) {
639  /* sequatial access */
640  seq_type = atoi(PQgetvalue(pg_info->res, pg_info->next_line, 2));
641  if (seq_type == GV_BOUNDARY ||
642  (seq_type == GV_LINE && pg_info->feature_type == SF_POLYGON))
643  force_type = GV_BOUNDARY;
644  else if (seq_type == GV_CENTROID)
645  force_type = GV_CENTROID;
646  }
647  else {
648  /* random access: check topological elemenent type consistency */
649  if (type & GV_POINTS) {
650  if (type == GV_POINT &&
651  strlen(PQgetvalue(pg_info->res, pg_info->next_line, 1)) != 0)
652  G_warning(_("Inconsistency in topology: detected centroid (should be point)"));
653  }
654  else {
655  int left_face, right_face;
656 
657  left_face = atoi(PQgetvalue(pg_info->res, pg_info->next_line, 1));
658  right_face = atoi(PQgetvalue(pg_info->res, pg_info->next_line, 2));
659 
660  if (type == GV_LINE &&
661  (left_face != 0 || right_face != 0))
662  G_warning(_("Inconsistency in topology: detected boundary (should be line)"));
663  }
664  }
665  }
666 
667  /* get geometry data */
668  data = (char *)PQgetvalue(pg_info->res, pg_info->next_line, 0);
669 
670  /* load feature to the cache */
671  pg_info->cache.sf_type = Vect__cache_feature_pg(data,
672  FALSE, force_type,
673  &(pg_info->cache), NULL);
674 
675  /* cache also categories (only for PostGIS Topology) */
676  if (pg_info->toposchema_name) {
677  int cat, col_idx;
678 
679  col_idx = fid < 0 ? 3 : 2; /* TODO: dermine col_idx for random access */
680 
681  if (!PQgetisnull(pg_info->res, pg_info->next_line, col_idx))
682  cat = atoi(PQgetvalue(pg_info->res, pg_info->next_line, col_idx));
683  else
684  cat = -1; /* no cat */
685  pg_info->cache.lines_cats[pg_info->cache.lines_next] = cat;
686  G_debug(3, "line=%d, type=%d -> cat=%d", pg_info->cache.lines_next+1,
687  pg_info->cache.lines_types[pg_info->cache.lines_next], cat);
688  }
689 
690  /* set feature id */
691  if (fid < 0) {
692  pg_info->cache.fid =
693  atoi(PQgetvalue(pg_info->res, pg_info->next_line, 1));
694  pg_info->next_line++;
695  }
696  else {
697  pg_info->cache.fid = fid;
698  }
699 
700  return pg_info->cache.sf_type;
701 }
702 
703 /*!
704  \brief Convert HEX to WKB data
705 
706  \param hex_data HEX data
707  \param[out] nbytes number of bytes in output buffer
708 
709  \return pointer to WKB data buffer
710  */
711 unsigned char *hex_to_wkb(const char *hex_data, int *nbytes)
712 {
713  unsigned int length;
714  int i;
715 
716  length = strlen(hex_data) / 2 + 1;
717  if (length > wkb_data_length) {
718  wkb_data_length = length;
719  wkb_data = G_realloc(wkb_data, wkb_data_length);
720  }
721 
722  *nbytes = length - 1;
723  for (i = 0; i < (*nbytes); i++) {
724  wkb_data[i] =
725  (unsigned char)((hex_data[2 * i] >
726  'F' ? hex_data[2 * i] - 0x57 : hex_data[2 * i] >
727  '9' ? hex_data[2 * i] - 0x37 : hex_data[2 * i] -
728  0x30) << 4);
729  wkb_data[i] |=
730  (unsigned char)(hex_data[2 * i + 1] >
731  'F' ? hex_data[2 * i + 1] -
732  0x57 : hex_data[2 * i + 1] >
733  '9' ? hex_data[2 * i + 1] -
734  0x37 : hex_data[2 * i + 1] - 0x30);
735  }
736 
737  wkb_data[(*nbytes)] = 0;
738 
739  return wkb_data;
740 }
741 
742 /*!
743  \brief Read geometry from HEX data
744 
745  This code is inspired by OGRGeometryFactory::createFromWkb() from
746  GDAL/OGR library.
747 
748  \param data HEX data
749  \param skip_polygon skip polygons (level 1)
750  \param force_type force GV_BOUNDARY or GV_CENTROID (used for PostGIS topology only)
751  \param[out] cache lines cache
752  \param[out] fparts used for building pseudo-topology (or NULL)
753 
754  \return simple feature type
755  \return SF_GEOMETRY on error
756  */
757 SF_FeatureType Vect__cache_feature_pg(const char *data, int skip_polygon,
758  int force_type,
759  struct Format_info_cache *cache,
760  struct feat_parts * fparts)
761 {
762  int ret, byte_order, nbytes, is3D;
763  unsigned char *wkb_data;
764  unsigned int wkb_flags;
765  SF_FeatureType ftype;
766 
767  /* reset cache */
768  if (cache->ctype == CACHE_MAP)
769  cache->lines_num++;
770  else {
771  /* next to be read from cache */
772  cache->lines_next = 0;
773  cache->lines_num = 1;
774  }
775  cache->fid = -1;
776 
777  if (fparts)
778  fparts->n_parts = 0;
779 
780  wkb_flags = 0;
781  wkb_data = hex_to_wkb(data, &nbytes);
782 
783  if (nbytes < 5) {
784  /* G_free(wkb_data); */
785  if (nbytes > 0) {
786  G_debug(3, "Vect__cache_feature_pg(): invalid geometry");
787  G_warning(_("Invalid WKB content: %d bytes"), nbytes);
788  return SF_GEOMETRY;
789  }
790  else {
791  G_debug(3, "Vect__cache_feature_pg(): no geometry");
792  return SF_NONE;
793  }
794  }
795 
796  /* parsing M coordinate not supported */
797  memcpy(&wkb_flags, wkb_data + 1, 4);
798  byte_order = (wkb_data[0] == 0 ? ENDIAN_BIG : ENDIAN_LITTLE);
799  if (byte_order == ENDIAN_BIG)
800  wkb_flags = SWAP32(wkb_flags);
801 
802  if (wkb_flags & 0x40000000) {
803  G_warning(_("Reading EWKB with 4-dimensional coordinates (XYZM) "
804  "is not supported"));
805  /* G_free(wkb_data); */
806  return SF_GEOMETRY;
807  }
808 
809  /* PostGIS EWKB format includes an SRID, but this won't be
810  understood by OGR, so if the SRID flag is set, we remove the
811  SRID (bytes at offset 5 to 8).
812  */
813  if (nbytes > 9 &&
814  ((byte_order == ENDIAN_BIG && (wkb_data[1] & 0x20)) ||
815  (byte_order == ENDIAN_LITTLE && (wkb_data[4] & 0x20)))) {
816  memmove(wkb_data + 5, wkb_data + 9, nbytes - 9);
817  nbytes -= 4;
818  if (byte_order == ENDIAN_BIG)
819  wkb_data[1] &= (~0x20);
820  else
821  wkb_data[4] &= (~0x20);
822  }
823 
824  if (nbytes < 9 && nbytes != -1) {
825  /* G_free(wkb_data); */
826  return SF_GEOMETRY;
827  }
828 
829  /* Get the geometry feature type. For now we assume that geometry
830  type is between 0 and 255 so we only have to fetch one byte.
831  */
832  if (byte_order == ENDIAN_LITTLE) {
833  ftype = (SF_FeatureType) wkb_data[1];
834  is3D = wkb_data[4] & 0x80 || wkb_data[2] & 0x80;
835  }
836  else {
837  ftype = (SF_FeatureType) wkb_data[4];
838  is3D = wkb_data[1] & 0x80 || wkb_data[3] & 0x80;
839  }
840  G_debug(3, "Vect__cache_feature_pg(): sf_type = %d", ftype);
841 
842  /* allocate space in lines cache - be minimalistic
843 
844  more lines require eg. polygon with more rings, multi-features
845  or geometry collections
846  */
847  if (cache->ctype == CACHE_MAP) {
848  Vect__reallocate_cache(cache, 1, TRUE);
849  }
850  else {
851  if (!cache->lines) {
852  Vect__reallocate_cache(cache, 1, FALSE);
853  }
854  }
855 
856  ret = -1;
857  if (ftype == SF_POINT) {
858  cache->lines_types[cache->lines_num-1] = force_type == GV_CENTROID ? force_type : GV_POINT;
859  ret = point_from_wkb(wkb_data, nbytes, byte_order,
860  is3D, cache->lines[cache->lines_num-1]);
861  add_fpart(fparts, ftype, 0, 1);
862  }
863  else if (ftype == SF_LINESTRING) {
864  cache->lines_types[cache->lines_num-1] = force_type == GV_BOUNDARY ? force_type : GV_LINE;
865  ret = linestring_from_wkb(wkb_data, nbytes, byte_order,
866  is3D, cache->lines[cache->lines_num-1], FALSE);
867  add_fpart(fparts, ftype, 0, 1);
868  }
869  else if (ftype == SF_POLYGON && !skip_polygon) {
870  int nrings;
871 
872  cache->lines_num = 0; /* reset before reading rings */
873  ret = polygon_from_wkb(wkb_data, nbytes, byte_order,
874  is3D, cache, &nrings);
875  add_fpart(fparts, ftype, 0, nrings);
876  }
877  else if (ftype == SF_MULTIPOINT ||
878  ftype == SF_MULTILINESTRING ||
879  ftype == SF_MULTIPOLYGON || ftype == SF_GEOMETRYCOLLECTION) {
880  ret = geometry_collection_from_wkb(wkb_data, nbytes, byte_order,
881  is3D, cache, fparts);
882  }
883  else {
884  G_warning(_("Unsupported feature type %d"), ftype);
885  }
886 
887  if (cache->ctype != CACHE_MAP) {
888  /* read next feature from cache */
889  cache->lines_next = 0;
890  }
891 
892  /* G_free(wkb_data); */
893 
894  return ret > 0 ? ftype : SF_GEOMETRY;
895 }
896 
897 /*!
898  \brief Read point for WKB data
899 
900  See OGRPoint::importFromWkb() from GDAL/OGR library
901 
902  \param wkb_data WKB data
903  \param nbytes number of bytes (WKB data buffer)
904  \param byte_order byte order (ENDIAN_LITTLE, ENDIAN_BIG)
905  \param with_z WITH_Z for 3D data
906  \param[out] line_p point geometry (pointer to line_pnts struct)
907 
908  \return wkb size
909  \return -1 on error
910  */
911 int point_from_wkb(const unsigned char *wkb_data, int nbytes, int byte_order,
912  int with_z, struct line_pnts *line_p)
913 {
914  double x, y, z;
915 
916  if (nbytes < 21 && nbytes != -1)
917  return -1;
918 
919  /* get vertex */
920  memcpy(&x, wkb_data + 5, 8);
921  memcpy(&y, wkb_data + 5 + 8, 8);
922 
923  if (byte_order == ENDIAN_BIG) {
924  SWAPDOUBLE(&x);
925  SWAPDOUBLE(&y);
926  }
927 
928  if (with_z) {
929  if (nbytes < 29 && nbytes != -1)
930  return -1;
931 
932  memcpy(&z, wkb_data + 5 + 16, 8);
933  if (byte_order == ENDIAN_BIG) {
934  SWAPDOUBLE(&z);
935  }
936  }
937  else {
938  z = 0.0;
939  }
940 
941  if (line_p) {
942  Vect_reset_line(line_p);
943  Vect_append_point(line_p, x, y, z);
944  }
945 
946  return 5 + 8 * (with_z == WITH_Z ? 3 : 2);
947 }
948 
949 /*!
950  \brief Read line for WKB data
951 
952  See OGRLineString::importFromWkb() from GDAL/OGR library
953 
954  \param wkb_data WKB data
955  \param nbytes number of bytes (WKB data buffer)
956  \param byte_order byte order (ENDIAN_LITTLE, ENDIAN_BIG)
957  \param with_z WITH_Z for 3D data
958  \param[out] line_p line geometry (pointer to line_pnts struct)
959 
960  \return wkb size
961  \return -1 on error
962  */
963 int linestring_from_wkb(const unsigned char *wkb_data, int nbytes,
964  int byte_order, int with_z, struct line_pnts *line_p,
965  int is_ring)
966 {
967  int npoints, point_size, buff_min_size, offset;
968  int i;
969  double x, y, z;
970 
971  if (is_ring)
972  offset = 5;
973  else
974  offset = 0;
975 
976  if (is_ring && nbytes < 4 && nbytes != -1)
977  return error_corrupted_data(NULL);
978 
979  /* get the vertex count */
980  memcpy(&npoints, wkb_data + (5 - offset), 4);
981 
982  if (byte_order == ENDIAN_BIG) {
983  npoints = SWAP32(npoints);
984  }
985 
986  /* check if the wkb stream buffer is big enough to store fetched
987  number of points. 16 or 24 - size of point structure
988  */
989  point_size = with_z ? 24 : 16;
990  if (npoints < 0 || npoints > INT_MAX / point_size)
991  return error_corrupted_data(NULL);
992 
993  buff_min_size = point_size * npoints;
994 
995  if (nbytes != -1 && buff_min_size > nbytes - (9 - offset))
996  return error_corrupted_data(_("Length of input WKB is too small"));
997 
998  if (line_p)
999  Vect_reset_line(line_p);
1000 
1001  /* get the vertex */
1002  for (i = 0; i < npoints; i++) {
1003  memcpy(&x, wkb_data + (9 - offset) + i * point_size, 8);
1004  memcpy(&y, wkb_data + (9 - offset) + 8 + i * point_size, 8);
1005  if (with_z)
1006  memcpy(&z, wkb_data + (9 - offset) + 16 + i * point_size, 8);
1007  else
1008  z = 0.0;
1009 
1010  if (byte_order == ENDIAN_BIG) {
1011  SWAPDOUBLE(&x);
1012  SWAPDOUBLE(&y);
1013  if (with_z)
1014  SWAPDOUBLE(&z);
1015  }
1016 
1017  if (line_p)
1018  Vect_append_point(line_p, x, y, z);
1019  }
1020 
1021  return (9 - offset) + (with_z == WITH_Z ? 3 : 2) * 8 * line_p->n_points;
1022 }
1023 
1024 /*!
1025  \brief Read polygon for WKB data
1026 
1027  See OGRPolygon::importFromWkb() from GDAL/OGR library
1028 
1029  \param wkb_data WKB data
1030  \param nbytes number of bytes (WKB data buffer)
1031  \param byte_order byte order (ENDIAN_LITTLE, ENDIAN_BIG)
1032  \param with_z WITH_Z for 3D data
1033  \param[out] line_p array of rings (pointer to line_pnts struct)
1034  \param[out] nrings number of rings
1035 
1036  \return wkb size
1037  \return -1 on error
1038  */
1039 int polygon_from_wkb(const unsigned char *wkb_data, int nbytes,
1040  int byte_order, int with_z,
1041  struct Format_info_cache *cache, int *nrings)
1042 {
1043  int data_offset, i, nsize, isize;
1044  int num_of_rings;
1045  struct line_pnts *line_i;
1046 
1047  if (nbytes < 9 && nbytes != -1)
1048  return -1;
1049 
1050  /* get the ring count */
1051  memcpy(nrings, wkb_data + 5, 4);
1052  if (byte_order == ENDIAN_BIG) {
1053  *nrings = SWAP32(*nrings);
1054  }
1055  if (*nrings < 0) {
1056  return -1;
1057  }
1058  num_of_rings = *nrings;
1059 
1060  /* reallocate space for islands if needed */
1061  Vect__reallocate_cache(cache, num_of_rings, FALSE);
1062  cache->lines_num += num_of_rings;
1063 
1064  /* each ring has a minimum of 4 bytes (point count) */
1065  if (nbytes != -1 && nbytes - 9 < num_of_rings * 4) {
1066  return error_corrupted_data(_("Length of input WKB is too small"));
1067  }
1068 
1069  data_offset = 9;
1070  if (nbytes != -1)
1071  nbytes -= data_offset;
1072 
1073  /* get the rings */
1074  nsize = 9;
1075  for (i = 0; i < num_of_rings; i++) {
1076  if (cache->lines_next >= cache->lines_num)
1077  G_fatal_error(_("Invalid cache index %d (max: %d)"),
1078  cache->lines_next, cache->lines_num);
1079  line_i = cache->lines[cache->lines_next];
1080  cache->lines_types[cache->lines_next++] = GV_BOUNDARY;
1081 
1082  linestring_from_wkb(wkb_data + data_offset, nbytes, byte_order,
1083  with_z, line_i, TRUE);
1084 
1085  if (nbytes != -1) {
1086  isize = 4 + 8 * (with_z == WITH_Z ? 3 : 2) * line_i->n_points;
1087  nbytes -= isize;
1088  }
1089 
1090  nsize += isize;
1091  data_offset += isize;
1092  }
1093 
1094  return nsize;
1095 }
1096 
1097 /*!
1098  \brief Read geometry collection for WKB data
1099 
1100  See OGRGeometryCollection::importFromWkbInternal() from GDAL/OGR library
1101 
1102  \param wkb_data WKB data
1103  \param nbytes number of bytes (WKB data buffer)
1104  \param byte_order byte order (ENDIAN_LITTLE, ENDIAN_BIG)
1105  \param with_z WITH_Z for 3D data
1106  \param ipart part to cache (starts at 0)
1107  \param[out] cache lines cache
1108  \param[in,out] fparts feature parts (required for building pseudo-topology)
1109 
1110  \return number of parts
1111  \return -1 on error
1112  */
1113 int geometry_collection_from_wkb(const unsigned char *wkb_data, int nbytes,
1114  int byte_order, int with_z,
1115  struct Format_info_cache *cache,
1116  struct feat_parts *fparts)
1117 {
1118  int ipart, nparts, data_offset, nsize;
1119  unsigned char *wkb_subdata;
1120  SF_FeatureType ftype;
1121 
1122  if (nbytes < 9 && nbytes != -1)
1123  return error_corrupted_data(NULL);
1124 
1125  /* get the geometry count */
1126  memcpy(&nparts, wkb_data + 5, 4);
1127  if (byte_order == ENDIAN_BIG) {
1128  nparts = SWAP32(nparts);
1129  }
1130  if (nparts < 0 || nparts > INT_MAX / 9) {
1131  return error_corrupted_data(NULL);
1132  }
1133  G_debug(5, "\t(geometry collections) parts: %d", nparts);
1134 
1135  /* each geometry has a minimum of 9 bytes */
1136  if (nbytes != -1 && nbytes - 9 < nparts * 9) {
1137  return error_corrupted_data(_("Length of input WKB is too small"));
1138  }
1139 
1140  data_offset = 9;
1141  if (nbytes != -1)
1142  nbytes -= data_offset;
1143 
1144  /* reallocate space for parts if needed */
1145  Vect__reallocate_cache(cache, nparts, FALSE);
1146 
1147  /* get parts */
1148  for (ipart = 0; ipart < nparts; ipart++) {
1149  wkb_subdata = (unsigned char *)wkb_data + data_offset;
1150  if (nbytes < 9 && nbytes != -1)
1151  return error_corrupted_data(NULL);
1152 
1153  if (byte_order == ENDIAN_LITTLE) {
1154  ftype = (SF_FeatureType) wkb_subdata[1];
1155  }
1156  else {
1157  ftype = (SF_FeatureType) wkb_subdata[4];
1158  }
1159 
1160  if (ftype == SF_POINT) {
1161  cache->lines_types[cache->lines_next] = GV_POINT;
1162  nsize = point_from_wkb(wkb_subdata, nbytes, byte_order, with_z,
1163  cache->lines[cache->lines_next]);
1164  cache->lines_num++;
1165  add_fpart(fparts, ftype, cache->lines_next, 1);
1166  cache->lines_next++;
1167  }
1168  else if (ftype == SF_LINESTRING) {
1169  cache->lines_types[cache->lines_next] = GV_LINE;
1170  nsize =
1171  linestring_from_wkb(wkb_subdata, nbytes, byte_order, with_z,
1172  cache->lines[cache->lines_next], FALSE);
1173  cache->lines_num++;
1174  add_fpart(fparts, ftype, cache->lines_next, 1);
1175  cache->lines_next++;
1176  }
1177  else if (ftype == SF_POLYGON) {
1178  int idx, nrings;
1179 
1180  idx = cache->lines_next;
1181  nsize = polygon_from_wkb(wkb_subdata, nbytes, byte_order,
1182  with_z, cache, &nrings);
1183  add_fpart(fparts, ftype, idx, nrings);
1184  }
1185  else if (ftype == SF_GEOMETRYCOLLECTION ||
1186  ftype == SF_MULTIPOLYGON ||
1187  ftype == SF_MULTILINESTRING || ftype == SF_MULTIPOLYGON) {
1188  geometry_collection_from_wkb(wkb_subdata, nbytes, byte_order,
1189  with_z, cache, fparts);
1190  }
1191  else {
1192  G_warning(_("Unsupported feature type %d"), ftype);
1193  }
1194 
1195  if (nbytes != -1) {
1196  nbytes -= nsize;
1197  }
1198 
1199  data_offset += nsize;
1200  }
1201 
1202  return nparts;
1203 }
1204 
1205 /*!
1206  \brief Report error message
1207 
1208  \param msg message (NULL)
1209 
1210  \return -1
1211  */
1212 int error_corrupted_data(const char *msg)
1213 {
1214  if (msg)
1215  G_warning(_("Corrupted data. %s."), msg);
1216  else
1217  G_warning(_("Corrupted data"));
1218 
1219  return -1;
1220 }
1221 
1222 /*!
1223  \brief Create select cursor for sequential access (internal use only)
1224 
1225  Allocated cursor name should be freed by G_free().
1226 
1227  \param pg_info pointer to Format_info_pg struct
1228  \param fetch_all TRUE to fetch all records
1229  \param[out] cursor name
1230 
1231  \return 0 on success
1232  \return -1 on failure
1233 */
1234 int Vect__open_cursor_next_line_pg(struct Format_info_pg *pg_info, int fetch_all, int built_level)
1235 {
1236  char stmt[DB_SQL_MAX];
1237 
1238  if (Vect__execute_pg(pg_info->conn, "BEGIN") == -1)
1239  return -1;
1240 
1241  /* set cursor name */
1242  G_asprintf(&(pg_info->cursor_name),
1243  "%s_%s_%p", pg_info->schema_name, pg_info->table_name, pg_info->conn);
1244 
1245  if (!pg_info->toposchema_name) {
1246  /* simple feature access (geom, fid) */
1247  /* TODO: start_fid */
1248  if (pg_info->where) {
1249  /* set attribute filter if where sql statement defined */
1250  char **tokens = G_tokenize(pg_info->where, "=");
1251  if (G_number_of_tokens(tokens) != 2) {
1252  G_warning(_("Unable to parse '%s'"), pg_info->where);
1253  return -1;
1254  }
1255  sprintf(stmt,
1256  "DECLARE %s CURSOR FOR SELECT \"%s\",\"%s\" FROM \"%s\".\"%s\" WHERE \"%s\"=%s ORDER BY \"%s\"",
1257  pg_info->cursor_name, pg_info->geom_column, pg_info->fid_column, pg_info->schema_name,
1258  pg_info->table_name, tokens[0], tokens[1], pg_info->fid_column);
1259  G_free_tokens(tokens);
1260  }
1261  else {
1262  sprintf(stmt,
1263  "DECLARE %s CURSOR FOR SELECT \"%s\",\"%s\" FROM \"%s\".\"%s\" ORDER BY \"%s\"",
1264  pg_info->cursor_name, pg_info->geom_column, pg_info->fid_column, pg_info->schema_name,
1265  pg_info->table_name, pg_info->fid_column);
1266  }
1267  }
1268  else {
1269  /* topology access (geom,id,fid,type) */
1270  /* TODO: optimize SQL statement (for points/centroids) */
1271  sprintf(stmt,
1272  "DECLARE %s CURSOR FOR "
1273  "SELECT geom,id,type,fid FROM ("
1274  "SELECT tt.node_id AS id,tt.geom, %d AS type, ft.%s AS fid FROM \"%s\".node AS tt "
1275  "LEFT JOIN \"%s\".\"%s\" AS ft ON (%s).type = 1 AND (%s).id = node_id "
1276  "WHERE containing_face IS NULL AND node_id NOT IN "
1277  "(SELECT node FROM (SELECT start_node AS node FROM \"%s\".edge GROUP BY start_node UNION ALL "
1278  "SELECT end_node AS node FROM \"%s\".edge GROUP BY end_node) AS foo) UNION ALL "
1279  "SELECT tt.node_id AS id,tt.geom, %d AS type, ft.%s AS fid FROM \"%s\".node AS tt "
1280  "LEFT JOIN \"%s\".\"%s\" AS ft ON (%s).type = 3 AND (%s).id = %s "
1281  "WHERE containing_face IS NOT NULL AND node_id NOT IN "
1282  "(SELECT node FROM (SELECT start_node AS node FROM \"%s\".edge GROUP BY start_node UNION ALL "
1283  "SELECT end_node AS node FROM \"%s\".edge GROUP BY end_node) AS foo) UNION ALL "
1284  "SELECT tt.edge_id AS id, tt.geom, %d AS type, ft.%s AS fid FROM \"%s\".edge AS tt "
1285  "LEFT JOIN \"%s\".\"%s\" AS ft ON (%s).type = 2 AND (%s).id = edge_id "
1286  "WHERE left_face = 0 AND right_face = 0 UNION ALL "
1287  "SELECT tt.edge_id AS id, tt.geom, %d AS type, ft.%s AS fid FROM \"%s\".edge AS tt "
1288  "LEFT JOIN \"%s\".\"%s\" AS ft ON (%s).type = 2 AND (%s).id = edge_id "
1289  "WHERE left_face != 0 OR right_face != 0 ) AS foo ORDER BY type,id",
1290  pg_info->cursor_name,
1291  GV_POINT, pg_info->fid_column, pg_info->toposchema_name, pg_info->schema_name, pg_info->table_name,
1292  pg_info->topogeom_column, pg_info->topogeom_column, pg_info->toposchema_name, pg_info->toposchema_name,
1293  GV_CENTROID, pg_info->fid_column, pg_info->toposchema_name, pg_info->schema_name, pg_info->table_name,
1294  pg_info->topogeom_column, pg_info->topogeom_column,
1295  built_level >= GV_BUILD_CENTROIDS ? "containing_face" : "node_id",
1296  pg_info->toposchema_name, pg_info->toposchema_name,
1297  GV_LINE, pg_info->fid_column, pg_info->toposchema_name, pg_info->schema_name, pg_info->table_name,
1298  pg_info->topogeom_column, pg_info->topogeom_column,
1299  GV_BOUNDARY, pg_info->fid_column, pg_info->toposchema_name, pg_info->schema_name, pg_info->table_name,
1300  pg_info->topogeom_column, pg_info->topogeom_column);
1301  }
1302  if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
1303  Vect__execute_pg(pg_info->conn, "ROLLBACK");
1304  return -1;
1305  }
1306 
1307  if (fetch_all)
1308  sprintf(stmt, "FETCH ALL in %s", pg_info->cursor_name);
1309  else
1310  sprintf(stmt, "FETCH %d in %s", CURSOR_PAGE, pg_info->cursor_name);
1311  G_debug(3, "SQL: %s", stmt);
1312  pg_info->res = PQexec(pg_info->conn, stmt); /* fetch records from select cursor */
1313  if (!pg_info->res || PQresultStatus(pg_info->res) != PGRES_TUPLES_OK) {
1314  error_tuples(pg_info);
1315  return -1;
1316  }
1317  pg_info->next_line = 0;
1318 
1319  return 0;
1320 }
1321 
1322 /*!
1323  \brief Open select cursor for random access (internal use only)
1324 
1325  Fetch number of feature (given by CURSOR_PAGE) starting with
1326  <em>fid</em>.
1327 
1328  Allocated cursor name should be freed by G_free().
1329 
1330  \param pg_info pointer to Format_info_pg struct
1331  \param fid feature id to get
1332  \param type feature type
1333 
1334  \return 0 on success
1335  \return -1 on failure
1336 */
1337 int Vect__open_cursor_line_pg(struct Format_info_pg *pg_info, int fid, int type)
1338 {
1339  char stmt[DB_SQL_MAX];
1340 
1341  G_debug(3, "Vect__open_cursor_line_pg(): fid range = %d-%d, type = %d",
1342  fid, fid + CURSOR_PAGE, type);
1343 
1344  if (Vect__execute_pg(pg_info->conn, "BEGIN") == -1)
1345  return -1;
1346 
1347  pg_info->cursor_fid = fid;
1348  G_asprintf(&(pg_info->cursor_name),
1349  "%s_%s_%d_%p", pg_info->schema_name, pg_info->table_name, fid, pg_info->conn);
1350 
1351  if (!pg_info->toposchema_name) {
1352  /* simple feature access (geom) */
1353  sprintf(stmt,
1354  "DECLARE %s CURSOR FOR SELECT %s FROM \"%s\".\"%s\" "
1355  "WHERE %s BETWEEN %d AND %d ORDER BY %s", pg_info->cursor_name,
1356  pg_info->geom_column, pg_info->schema_name, pg_info->table_name,
1357  pg_info->fid_column, fid, fid + CURSOR_PAGE, pg_info->fid_column);
1358  }
1359  else {
1360  /* topological access */
1361  if (!(type & (GV_POINTS | GV_LINES))) {
1362  G_warning(_("Unsupported feature type %d"), type);
1363  Vect__execute_pg(pg_info->conn, "ROLLBACK");
1364  return -1;
1365  }
1366 
1367  if (type & GV_POINTS) {
1368  /* points (geom,containing_face) */
1369  sprintf(stmt,
1370  "DECLARE %s CURSOR FOR SELECT geom,containing_face "
1371  " FROM \"%s\".node WHERE node_id BETWEEN %d AND %d ORDER BY node_id",
1372  pg_info->cursor_name,
1373  pg_info->toposchema_name, fid, fid + CURSOR_PAGE);
1374  }
1375  else {
1376  /* edges (geom,left_face,right_face) */
1377  sprintf(stmt,
1378  "DECLARE %s CURSOR FOR SELECT geom,left_face,right_face "
1379  " FROM \"%s\".edge WHERE edge_id BETWEEN %d AND %d ORDER BY edge_id",
1380  pg_info->cursor_name,
1381  pg_info->toposchema_name, fid, fid + CURSOR_PAGE);
1382  }
1383  }
1384  if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
1385  Vect__execute_pg(pg_info->conn, "ROLLBACK");
1386  return -1;
1387  }
1388  pg_info->next_line = 0;
1389 
1390  sprintf(stmt, "FETCH ALL in %s", pg_info->cursor_name);
1391  pg_info->res = PQexec(pg_info->conn, stmt);
1392  if (!pg_info->res || PQresultStatus(pg_info->res) != PGRES_TUPLES_OK) {
1393  error_tuples(pg_info);
1394  return -1;
1395  }
1396 
1397  return 0;
1398 }
1399 
1400 /*!
1401  \brief Close select cursor
1402 
1403  \param pg_info pointer to Format_info_pg struct
1404 
1405  \return 0 on success
1406  \return -1 on failure
1407 */
1409 {
1410  if (pg_info->res) {
1411  PQclear(pg_info->res);
1412  pg_info->res = NULL;
1413  }
1414 
1415  if (pg_info->cursor_name) {
1416  char stmt[DB_SQL_MAX];
1417 
1418  sprintf(stmt, "CLOSE %s", pg_info->cursor_name);
1419  if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
1420  G_warning(_("Unable to close cursor %s"), pg_info->cursor_name);
1421  return -1;
1422  }
1423  Vect__execute_pg(pg_info->conn, "COMMIT");
1424  G_free(pg_info->cursor_name);
1425  pg_info->cursor_name = NULL;
1426  }
1427 
1428  return 0;
1429 }
1430 
1431 /*!
1432  \brief Select feature (internal use only)
1433 
1434  \param pg_info pointer to Format_info_pg struct
1435  \param fid feature id to get
1436  \param type feature type
1437 
1438  \return 0 on success
1439  \return -1 on failure
1440 */
1441 int Vect__select_line_pg(struct Format_info_pg *pg_info, int fid, int type)
1442 {
1443  char stmt[DB_SQL_MAX];
1444 
1445  if (!pg_info->toposchema_name) {
1446  /* simple feature access */
1447  sprintf(stmt,
1448  "SELECT %s FROM \"%s\".\"%s\" WHERE %s = %d",
1449  pg_info->geom_column, pg_info->schema_name, pg_info->table_name,
1450  pg_info->fid_column, fid);
1451  }
1452  else {
1453  /* topological access */
1454  if (!(type & (GV_POINTS | GV_LINES))) {
1455  G_warning(_("Unsupported feature type %d"), type);
1456  return -1;
1457  }
1458 
1459  if (type & GV_POINTS) {
1460  int topotype;
1461  char *nodeid;
1462 
1463  if (type == GV_POINT) {
1464  topotype = 1;
1465  nodeid = pg_info->fid_column;
1466  }
1467  else { /* assuming GV_CENTROID */
1468  topotype = 3;
1469  nodeid = "containing_face";
1470  }
1471 
1472  sprintf(stmt,
1473  "SELECT tt.geom,tt.containing_face,ft.%s FROM \"%s\".node AS tt "
1474  "LEFT JOIN \"%s\".\"%s\" AS ft ON (%s).type = %d and (%s).id = %s "
1475  "WHERE node_id = %d",
1476  pg_info->fid_column, pg_info->toposchema_name,
1477  pg_info->schema_name, pg_info->table_name, pg_info->topogeom_column,
1478  topotype, pg_info->topogeom_column, nodeid, fid);
1479  }
1480  else {
1481  sprintf(stmt,
1482  "SELECT tt.geom,tt.left_face,tt.right_face,ft.%s FROM \"%s\".edge AS tt "
1483  "LEFT JOIN \"%s\".\"%s\" AS ft ON (%s).type = 2 and (%s).id = edge_id "
1484  "WHERE edge_id = %d",
1485  pg_info->fid_column, pg_info->toposchema_name,
1486  pg_info->schema_name, pg_info->table_name, pg_info->topogeom_column,
1487  pg_info->topogeom_column, fid);
1488  }
1489  }
1490  G_debug(3, "SQL: %s", stmt);
1491 
1492  pg_info->next_line = 0;
1493 
1494  pg_info->res = PQexec(pg_info->conn, stmt);
1495  if (!pg_info->res || PQresultStatus(pg_info->res) != PGRES_TUPLES_OK) {
1496  error_tuples(pg_info);
1497  return -1;
1498  }
1499 
1500  return 0;
1501 }
1502 
1503 /*!
1504  \brief Execute SQL statement
1505 
1506  See pg_local_proto.h
1507 
1508  \param conn pointer to PGconn
1509  \param stmt query
1510 
1511  \return 0 on success
1512  \return -1 on error
1513  */
1514 int Vect__execute_pg(PGconn * conn, const char *stmt)
1515 {
1516  PGresult *result;
1517 
1518  result = NULL;
1519 
1520  G_debug(3, "Vect__execute_pg(): %s", stmt);
1521  result = PQexec(conn, stmt);
1522  if (!result || PQresultStatus(result) != PGRES_COMMAND_OK) {
1523  size_t stmt_len;
1524  char stmt_prt[512];
1525 
1526  PQclear(result);
1527  stmt_len = strlen(stmt);
1528  strncpy(stmt_prt, stmt, stmt_len > 511 ? 511 : stmt_len);
1529  stmt_prt[stmt_len > 511 ? 511 : stmt_len] = '\0';
1530  G_warning(_("Execution failed: %s (...)\nReason: %s"), stmt_prt,
1531  PQerrorMessage(conn));
1532  return -1;
1533  }
1534 
1535  PQclear(result);
1536  return 0;
1537 }
1538 
1539 /*!
1540  \brief Execute SQL statement and get value.
1541 
1542  \param conn pointer to PGconn
1543  \param stmt query
1544 
1545  \return value on success
1546  \return -1 on error
1547  */
1548 int Vect__execute_get_value_pg(PGconn *conn, const char *stmt)
1549 {
1550  int ret;
1551  PGresult *result;
1552 
1553  result = NULL;
1554 
1555  G_debug(3, "Vect__execute_get_value_pg(): %s", stmt);
1556  result = PQexec(conn, stmt);
1557  if (!result || PQresultStatus(result) != PGRES_TUPLES_OK ||
1558  PQntuples(result) != 1) {
1559  PQclear(result);
1560 
1561  G_warning(_("Execution failed: %s\nReason: %s"), stmt,
1562  PQerrorMessage(conn));
1563  return -1;
1564  }
1565 
1566  ret = atoi(PQgetvalue(result, 0, 0));
1567  PQclear(result);
1568 
1569  return ret;
1570 }
1571 
1572 /*!
1573  \brief Reallocate lines cache
1574  */
1575 void Vect__reallocate_cache(struct Format_info_cache *cache, int num, int incr)
1576 {
1577  int i;
1578 
1579  if (!incr && cache->lines_alloc >= num)
1580  return;
1581 
1582  if (!incr && !cache->lines) {
1583  /* most of features requires only one line cache */
1584  cache->lines_alloc = 1;
1585  }
1586  else {
1587  cache->lines_alloc += num;
1588  }
1589 
1590  cache->lines = (struct line_pnts **)G_realloc(cache->lines,
1591  cache->lines_alloc *
1592  sizeof(struct line_pnts *));
1593  cache->lines_types = (int *)G_realloc(cache->lines_types,
1594  cache->lines_alloc * sizeof(int));
1595  cache->lines_cats = (int *)G_realloc(cache->lines_cats,
1596  cache->lines_alloc * sizeof(int));
1597 
1598  if (cache->lines_alloc > 1) {
1599  for (i = cache->lines_alloc - num; i < cache->lines_alloc; i++) {
1600  cache->lines[i] = Vect_new_line_struct();
1601  cache->lines_types[i] = -1;
1602  cache->lines_cats[i] = -1;
1603  }
1604  }
1605  else {
1606  cache->lines[0] = Vect_new_line_struct();
1607  cache->lines_types[0] = -1;
1608  cache->lines_cats[0] = -1;
1609  }
1610 }
1611 
1612 void add_fpart(struct feat_parts *fparts, SF_FeatureType ftype,
1613  int idx, int nlines)
1614 {
1615  if (!fparts)
1616  return;
1617 
1618  if (fparts->a_parts == 0 || fparts->n_parts >= fparts->a_parts) {
1619  if (fparts->a_parts == 0)
1620  fparts->a_parts = 1;
1621  else
1622  fparts->a_parts += fparts->n_parts;
1623 
1624  fparts->ftype = (SF_FeatureType *) G_realloc(fparts->ftype,
1625  fparts->a_parts *
1626  sizeof(SF_FeatureType));
1627  fparts->nlines =
1628  (int *)G_realloc(fparts->nlines, fparts->a_parts * sizeof(int));
1629  fparts->idx =
1630  (int *)G_realloc(fparts->idx, fparts->a_parts * sizeof(int));
1631  }
1632 
1633  fparts->ftype[fparts->n_parts] = ftype;
1634  fparts->idx[fparts->n_parts] = idx;
1635  fparts->nlines[fparts->n_parts] = nlines;
1636 
1637  fparts->n_parts++;
1638 }
1639 
1640 /*
1641  \brief Get centroid
1642 
1643  \param pg_info pointer to Format_info_pg
1644  \param centroid centroid id
1645  \param[out] line_p output geometry
1646 
1647  \return GV_CENTROID on success
1648  \return -1 on error
1649 */
1650 int get_centroid(struct Map_info *Map, int centroid,
1651  struct line_pnts *line_p, struct line_cats *line_c)
1652 {
1653  int i, found;
1654  struct bound_box box;
1655  struct boxlist list;
1656  struct P_line *Line;
1657  struct P_topo_c *topo;
1658 
1659  Line = Map->plus.Line[centroid];
1660  topo = (struct P_topo_c *)Line->topo;
1661 
1662  /* get area bbox */
1663  Vect_get_area_box(Map, topo->area, &box);
1664  /* search in spatial index for centroid with area bbox */
1665  dig_init_boxlist(&list, TRUE);
1666  Vect_select_lines_by_box(Map, &box, Line->type, &list);
1667 
1668  found = -1;
1669  for (i = 0; i < list.n_values; i++) {
1670  if (list.id[i] == centroid) {
1671  found = i;
1672  break;
1673  }
1674  }
1675 
1676  if (found == -1)
1677  return -1;
1678 
1679  if (line_p) {
1680  Vect_reset_line(line_p);
1681  Vect_append_point(line_p, list.box[found].E, list.box[found].N, 0.0);
1682  }
1683  if (line_c) {
1684  Vect_cat_set(line_c, 1, Line->offset);
1685  }
1686 
1687  return GV_CENTROID;
1688 }
1689 
1690 void error_tuples(struct Format_info_pg *pg_info)
1691 {
1692  Vect__execute_pg(pg_info->conn, "ROLLBACK");
1693  G_warning(_("Unable to read features. Reason:\n%s"),
1694  PQresultErrorMessage(pg_info->res));
1695 
1696  if (pg_info->res) {
1697  PQclear(pg_info->res);
1698  pg_info->res = NULL;
1699  }
1700 }
1701 #endif
int Vect_get_area_box(const struct Map_info *, int, struct bound_box *)
Get bounding box of area.
int Vect_append_points(struct line_pnts *, const struct line_pnts *, int)
Appends points to the end of a line.
Definition: line.c:337
int * array
Offset list.
Definition: dig_structs.h:446
char * toposchema_name
Topology schema name and id.
Definition: dig_structs.h:699
#define TRUE
Definition: gis.h:59
#define ENDIAN_LITTLE
Endian check.
Definition: gis.h:391
Bounding box.
Definition: dig_structs.h:65
#define GV_FORWARD
Line direction indicator forward/backward.
Definition: dig_defines.h:178
void void void void G_fatal_error(const char *,...) __attribute__((format(printf
if(!(yy_init))
Definition: sqlp.yy.c:775
int built
Highest level of topology currently available.
Definition: dig_structs.h:873
plus_t area
Area number, negative for duplicate centroid.
Definition: dig_structs.h:1537
int Vect__open_cursor_line_pg(struct Format_info_pg *pg_info, int fid, int type)
Open select cursor for random access (internal use only)
Definition: read_pg.c:1337
int * id
Array of ids.
Definition: dig_structs.h:1755
off_t offset
Offset in coor file for line.
Definition: dig_structs.h:1593
PGconn * conn
PGconn object (generated by PQconnectdb)
Definition: dig_structs.h:663
Vector geometry.
Definition: dig_structs.h:1574
struct P_line ** Line
Array of vector geometries.
Definition: dig_structs.h:887
int region_flag
Non-zero value to enable region constraint.
Definition: dig_structs.h:1362
int V1_read_next_line_pg(struct Map_info *Map, struct line_pnts *line_p, struct line_cats *line_c)
Read next feature from PostGIS layer. Skip empty features (level 1 without topology). t This function implements sequential access.
Definition: read_pg.c:82
SF_FeatureType feature_type
Feature type (simple feature access)
Definition: dig_structs.h:635
#define DB_SQL_MAX
Definition: dbmi.h:142
long fid
Feature id.
Definition: dig_structs.h:497
int n_points
Number of points.
Definition: dig_structs.h:1692
#define GV_CENTROID
Definition: dig_defines.h:185
void G_free(void *)
Free allocated memory.
Definition: gis/alloc.c:149
#define GV_POINTS
Definition: dig_defines.h:191
int * lines_cats
List of line cats (used only for PostGIS Topology access)
Definition: dig_structs.h:481
struct Format_info fInfo
Format info for non-native formats.
Definition: dig_structs.h:1415
double E
East.
Definition: dig_structs.h:78
#define PRI_OFF_T
Definition: gis.h:69
char ** G_tokenize(const char *, const char *)
Tokenize string.
Definition: gis/token.c:48
char * table_name
Table name.
Definition: dig_structs.h:619
void Vect_line_box(const struct line_pnts *, struct bound_box *)
Get bounding box of line.
Definition: line.c:922
#define NULL
Definition: ccmath.h:32
char int isize
Definition: lz4.h:582
#define x
int type
Feature type constraint.
Definition: dig_structs.h:1374
int n_values
Number of items in the list.
Definition: dig_structs.h:1767
#define GV_POINT
Feature types used in memory on run time (may change)
Definition: dig_defines.h:182
struct bound_box * box
Array of bounding boxes.
Definition: dig_structs.h:1759
char * topogeom_column
TopoGeometry column (feature table)
Definition: dig_structs.h:695
int Vect__open_cursor_next_line_pg(struct Format_info_pg *pg_info, int fetch_all, int built_level)
Create select cursor for sequential access (internal use only)
Definition: read_pg.c:1234
int lines_num
Number of lines which forms current feature.
Definition: dig_structs.h:489
plus_t n_lines
Current number of lines.
Definition: dig_structs.h:947
Feature category info.
Definition: dig_structs.h:1702
void Vect__reallocate_cache(struct Format_info_cache *cache, int num, int incr)
Reallocate lines cache.
Definition: read_pg.c:1575
int V2_read_line_pg(struct Map_info *Map, struct line_pnts *line_p, struct line_cats *line_c, int line)
Read feature from PostGIS layer on topological level.
Definition: read_pg.c:319
#define GV_LINE
Definition: dig_defines.h:183
double N
North.
Definition: dig_structs.h:70
char type
Line type.
Definition: dig_structs.h:1586
int Vect__execute_get_value_pg(PGconn *conn, const char *stmt)
Execute SQL statement and get value.
Definition: read_pg.c:1548
int Vect_select_lines_by_box(struct Map_info *, const struct bound_box *, int, struct boxlist *)
Select lines with bounding boxes by box.
Definition: sindex.c:34
Feature geometry info - coordinates.
Definition: dig_structs.h:1675
Centroid topology.
Definition: dig_structs.h:1532
#define ENDIAN_BIG
Definition: gis.h:392
plus_t next_line
Feature id for sequential access.
Definition: dig_structs.h:1353
int ctype
Cache type.
Definition: dig_structs.h:507
struct Map_info::@11 constraint
Constraints for sequential feature access.
Non-native format info (PostGIS)
Definition: dig_structs.h:602
char * geom_column
Geometry column (simple feature access)
Definition: dig_structs.h:631
struct Format_info_cache cache
Lines cache for reading feature.
Definition: dig_structs.h:683
#define FALSE
Definition: gis.h:63
struct line_pnts * Vect_new_line_struct(void)
Creates and initializes a line_pnts structure.
Definition: line.c:45
int Vect__close_cursor_pg(struct Format_info_pg *pg_info)
Close select cursor.
Definition: read_pg.c:1408
void * topo
Topology info.
Definition: dig_structs.h:1599
int lines_alloc
Number of allocated lines in cache.
Definition: dig_structs.h:485
char * cursor_name
Open cursor.
Definition: dig_structs.h:672
struct Format_info_pg pg
PostGIS info.
Definition: dig_structs.h:726
int Vect_get_constraint_box(const struct Map_info *, struct bound_box *)
Get constraint box.
Definition: constraint.c:79
int G_number_of_tokens(char **)
Return number of tokens.
Definition: gis/token.c:185
int Vect_append_point(struct line_pnts *, double, double, double)
Appends one point to the end of a line.
Definition: line.c:149
#define GV_BOUNDARY
Definition: dig_defines.h:184
int Vect__select_line_pg(struct Format_info_pg *pg_info, int fid, int type)
Select feature (internal use only)
Definition: read_pg.c:1441
struct Plus_head plus
Plus info (topology, version, ...)
Definition: dig_structs.h:1286
int next_line
Next line to be read.
Definition: dig_structs.h:678
SF_FeatureType get_feature(struct Map_info *, int, int)
Read feature geometry.
Definition: read_pg.c:564
Lines cache for reading feature (non-native formats)
Definition: dig_structs.h:461
int Vect_reset_cats(struct line_cats *)
Reset category structure to make sure cats structure is clean to be re-used.
int dig_init_boxlist(struct boxlist *, int)
#define WITH_Z
Definition: dig_defines.h:171
int type_flag
Non-zero value to enable feature type constraint.
Definition: dig_structs.h:1370
struct dig_head head
Header info.
Definition: dig_structs.h:1403
int array_num
Number of items in offset list.
Definition: dig_structs.h:450
int V2_read_next_line_pg(struct Map_info *Map, struct line_pnts *line_p, struct line_cats *line_c)
Read next feature from PostGIS layer on topological level (simple feature access).
Definition: read_pg.c:112
int * lines_types
List of line types (GV_POINT, GV_LINE, ...)
Definition: dig_structs.h:477
Vector map info.
Definition: dig_structs.h:1259
struct Format_info_offset offset
Offset list used for building pseudo-topology (simple features access)
Definition: dig_structs.h:689
int Vect__execute_pg(PGconn *conn, const char *stmt)
Execute SQL statement.
Definition: read_pg.c:1514
SF_FeatureType Vect__cache_feature_pg(const char *data, int skip_polygon, int force_type, struct Format_info_cache *cache, struct feat_parts *fparts)
Read geometry from HEX data.
Definition: read_pg.c:757
List of bounding boxes with id.
Definition: dig_structs.h:1750
void G_free_tokens(char **)
Free memory allocated to tokens.
Definition: gis/token.c:204
void G_warning(const char *,...) __attribute__((format(printf
int lines_next
Next line to be read from cache.
Definition: dig_structs.h:493
PGresult * res
Definition: dig_structs.h:664
struct line_pnts ** lines
Lines array.
Definition: dig_structs.h:473
#define G_realloc(p, n)
Definition: defs/gis.h:114
#define _(str)
Definition: glocale.h:10
off_t last_offset
Offset of last read line.
Definition: dig_structs.h:366
#define GV_BUILD_CENTROIDS
Topology levels - assign centroids to areas.
Definition: dig_defines.h:131
#define GV_LINES
Definition: dig_defines.h:192
SF_FeatureType sf_type
Simple feature type (currently used only by PG format)
Definition: dig_structs.h:501
int G_asprintf(char **, const char *,...) __attribute__((format(printf
int Vect_box_overlap(const struct bound_box *, const struct bound_box *)
Tests for overlap of two boxes.
int V1_read_line_pg(struct Map_info *Map, struct line_pnts *line_p, struct line_cats *line_c, off_t offset)
Read feature from PostGIS layer at given offset (level 1 without topology)
Definition: read_pg.c:237
char * schema_name
Schema name.
Definition: dig_structs.h:615
int G_debug(int, const char *,...) __attribute__((format(printf
char * where
SQL where statement (fo filter features)
Definition: dig_structs.h:623
char * fid_column
FID column.
Definition: dig_structs.h:627
void Vect_reset_line(struct line_pnts *)
Reset line.
Definition: line.c:130
int Vect_cat_set(struct line_cats *, int, int)
Add new field/cat to category structure if doesn&#39;t exist yet.
SF_FeatureType
Simple feature types.
Definition: dig_defines.h:234