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