GRASS GIS 8 Programmer's Manual  8.4.0dev(2024)-112dd97adf
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  sprintf(stmt, "FETCH %d in %s", CURSOR_PAGE, pg_info->cursor_name);
635  G_debug(3, "SQL: %s", stmt);
636  pg_info->res = PQexec(pg_info->conn, stmt);
637  if (!pg_info->res || PQresultStatus(pg_info->res) != PGRES_TUPLES_OK) {
638  error_tuples(pg_info);
639  return -1;
640  }
641  pg_info->next_line = 0;
642  }
643 
644  G_debug(3, "get_feature(): next_line = %d", pg_info->next_line);
645 
646  /* out of results ? */
647  if (PQntuples(pg_info->res) == pg_info->next_line) {
648  if (Vect__close_cursor_pg(pg_info) != 0)
649  return -1; /* failure */
650  else
651  return -2; /* nothing to read */
652  }
653 
654  force_type = -1;
655  if (pg_info->toposchema_name) {
656  if (fid < 0) {
657  /* sequatial access */
658  seq_type = atoi(PQgetvalue(pg_info->res, pg_info->next_line, 2));
659  if (seq_type == GV_BOUNDARY ||
660  (seq_type == GV_LINE && pg_info->feature_type == SF_POLYGON))
661  force_type = GV_BOUNDARY;
662  else if (seq_type == GV_CENTROID)
663  force_type = GV_CENTROID;
664  }
665  else {
666  /* random access: check topological element type consistency */
667  if (type & GV_POINTS) {
668  if (type == GV_POINT &&
669  strlen(PQgetvalue(pg_info->res, pg_info->next_line, 1)) !=
670  0)
671  G_warning(_("Inconsistency in topology: detected centroid "
672  "(should be point)"));
673  }
674  else {
675  int left_face, right_face;
676 
677  left_face =
678  atoi(PQgetvalue(pg_info->res, pg_info->next_line, 1));
679  right_face =
680  atoi(PQgetvalue(pg_info->res, pg_info->next_line, 2));
681 
682  if (type == GV_LINE && (left_face != 0 || right_face != 0))
683  G_warning(_("Inconsistency in topology: detected boundary "
684  "(should be line)"));
685  }
686  }
687  }
688 
689  /* get geometry data */
690  data = (char *)PQgetvalue(pg_info->res, pg_info->next_line, 0);
691 
692  /* load feature to the cache */
693  pg_info->cache.sf_type = Vect__cache_feature_pg(data, FALSE, force_type,
694  &(pg_info->cache), NULL);
695 
696  /* cache also categories (only for PostGIS Topology) */
697  if (pg_info->toposchema_name) {
698  int cat, col_idx;
699 
700  col_idx =
701  fid < 0 ? 3 : 2; /* TODO: determine col_idx for random access */
702 
703  if (!PQgetisnull(pg_info->res, pg_info->next_line, col_idx))
704  cat = atoi(PQgetvalue(pg_info->res, pg_info->next_line, col_idx));
705  else
706  cat = -1; /* no cat */
707  pg_info->cache.lines_cats[pg_info->cache.lines_next] = cat;
708  G_debug(3, "line=%d, type=%d -> cat=%d", pg_info->cache.lines_next + 1,
709  pg_info->cache.lines_types[pg_info->cache.lines_next], cat);
710  }
711 
712  /* set feature id */
713  if (fid < 0) {
714  pg_info->cache.fid =
715  atoi(PQgetvalue(pg_info->res, pg_info->next_line, 1));
716  pg_info->next_line++;
717  }
718  else {
719  pg_info->cache.fid = fid;
720  }
721 
722  return pg_info->cache.sf_type;
723 }
724 
725 /*!
726  \brief Convert HEX to WKB data
727 
728  \param hex_data HEX data
729  \param[out] nbytes number of bytes in output buffer
730 
731  \return pointer to WKB data buffer
732  */
733 unsigned char *hex_to_wkb(const char *hex_data, int *nbytes)
734 {
735  unsigned int length;
736  int i;
737 
738  length = strlen(hex_data) / 2 + 1;
739  if (length > wkb_data_length) {
740  wkb_data_length = length;
741  wkb_data = G_realloc(wkb_data, wkb_data_length);
742  }
743 
744  *nbytes = length - 1;
745  for (i = 0; i < (*nbytes); i++) {
746  wkb_data[i] =
747  (unsigned char)((hex_data[2 * i] > 'F' ? hex_data[2 * i] - 0x57
748  : hex_data[2 * i] > '9' ? hex_data[2 * i] - 0x37
749  : hex_data[2 * i] - 0x30)
750  << 4);
751  wkb_data[i] |= (unsigned char)(hex_data[2 * i + 1] > 'F'
752  ? hex_data[2 * i + 1] - 0x57
753  : hex_data[2 * i + 1] > '9'
754  ? hex_data[2 * i + 1] - 0x37
755  : hex_data[2 * i + 1] - 0x30);
756  }
757 
758  wkb_data[(*nbytes)] = 0;
759 
760  return wkb_data;
761 }
762 
763 /*!
764  \brief Read geometry from HEX data
765 
766  This code is inspired by OGRGeometryFactory::createFromWkb() from
767  GDAL/OGR library.
768 
769  \param data HEX data
770  \param skip_polygon skip polygons (level 1)
771  \param force_type force GV_BOUNDARY or GV_CENTROID (used for PostGIS topology
772  only) \param[out] cache lines cache \param[out] fparts used for building
773  pseudo-topology (or NULL)
774 
775  \return simple feature type
776  \return SF_GEOMETRY on error
777  */
778 SF_FeatureType Vect__cache_feature_pg(const char *data, int skip_polygon,
779  int force_type,
780  struct Format_info_cache *cache,
781  struct feat_parts *fparts)
782 {
783  int ret, byte_order, nbytes, is3D;
784  unsigned char *wkb_data;
785  unsigned int wkb_flags;
786  SF_FeatureType ftype;
787 
788  /* reset cache */
789  if (cache->ctype == CACHE_MAP)
790  cache->lines_num++;
791  else {
792  /* next to be read from cache */
793  cache->lines_next = 0;
794  cache->lines_num = 1;
795  }
796  cache->fid = -1;
797 
798  if (fparts)
799  fparts->n_parts = 0;
800 
801  wkb_flags = 0;
802  wkb_data = hex_to_wkb(data, &nbytes);
803 
804  if (nbytes < 5) {
805  /* G_free(wkb_data); */
806  if (nbytes > 0) {
807  G_debug(3, "Vect__cache_feature_pg(): invalid geometry");
808  G_warning(_("Invalid WKB content: %d bytes"), nbytes);
809  return SF_GEOMETRY;
810  }
811  else {
812  G_debug(3, "Vect__cache_feature_pg(): no geometry");
813  return SF_NONE;
814  }
815  }
816 
817  /* parsing M coordinate not supported */
818  memcpy(&wkb_flags, wkb_data + 1, 4);
819  byte_order = (wkb_data[0] == 0 ? ENDIAN_BIG : ENDIAN_LITTLE);
820  if (byte_order == ENDIAN_BIG)
821  wkb_flags = SWAP32(wkb_flags);
822 
823  if (wkb_flags & 0x40000000) {
824  G_warning(_("Reading EWKB with 4-dimensional coordinates (XYZM) "
825  "is not supported"));
826  /* G_free(wkb_data); */
827  return SF_GEOMETRY;
828  }
829 
830  /* PostGIS EWKB format includes an SRID, but this won't be
831  understood by OGR, so if the SRID flag is set, we remove the
832  SRID (bytes at offset 5 to 8).
833  */
834  if (nbytes > 9 && ((byte_order == ENDIAN_BIG && (wkb_data[1] & 0x20)) ||
835  (byte_order == ENDIAN_LITTLE && (wkb_data[4] & 0x20)))) {
836  memmove(wkb_data + 5, wkb_data + 9, nbytes - 9);
837  nbytes -= 4;
838  if (byte_order == ENDIAN_BIG)
839  wkb_data[1] &= (~0x20);
840  else
841  wkb_data[4] &= (~0x20);
842  }
843 
844  if (nbytes < 9 && nbytes != -1) {
845  /* G_free(wkb_data); */
846  return SF_GEOMETRY;
847  }
848 
849  /* Get the geometry feature type. For now we assume that geometry
850  type is between 0 and 255 so we only have to fetch one byte.
851  */
852  if (byte_order == ENDIAN_LITTLE) {
853  ftype = (SF_FeatureType)wkb_data[1];
854  is3D = wkb_data[4] & 0x80 || wkb_data[2] & 0x80;
855  }
856  else {
857  ftype = (SF_FeatureType)wkb_data[4];
858  is3D = wkb_data[1] & 0x80 || wkb_data[3] & 0x80;
859  }
860  G_debug(3, "Vect__cache_feature_pg(): sf_type = %d", ftype);
861 
862  /* allocate space in lines cache - be minimalistic
863 
864  more lines require eg. polygon with more rings, multi-features
865  or geometry collections
866  */
867  if (cache->ctype == CACHE_MAP) {
869  }
870  else {
871  if (!cache->lines) {
873  }
874  }
875 
876  ret = -1;
877  if (ftype == SF_POINT) {
879  force_type == GV_CENTROID ? force_type : GV_POINT;
880  ret = point_from_wkb(wkb_data, nbytes, byte_order, is3D,
881  cache->lines[cache->lines_num - 1]);
882  add_fpart(fparts, ftype, 0, 1);
883  }
884  else if (ftype == SF_LINESTRING) {
886  force_type == GV_BOUNDARY ? force_type : GV_LINE;
887  ret = linestring_from_wkb(wkb_data, nbytes, byte_order, is3D,
888  cache->lines[cache->lines_num - 1], FALSE);
889  add_fpart(fparts, ftype, 0, 1);
890  }
891  else if (ftype == SF_POLYGON && !skip_polygon) {
892  int nrings;
893 
894  cache->lines_num = 0; /* reset before reading rings */
895  ret = polygon_from_wkb(wkb_data, nbytes, byte_order, is3D, cache,
896  &nrings);
897  add_fpart(fparts, ftype, 0, nrings);
898  }
899  else if (ftype == SF_MULTIPOINT || ftype == SF_MULTILINESTRING ||
900  ftype == SF_MULTIPOLYGON || ftype == SF_GEOMETRYCOLLECTION) {
901  ret = geometry_collection_from_wkb(wkb_data, nbytes, byte_order, is3D,
902  cache, fparts);
903  }
904  else {
905  G_warning(_("Unsupported feature type %d"), ftype);
906  }
907 
908  if (cache->ctype != CACHE_MAP) {
909  /* read next feature from cache */
910  cache->lines_next = 0;
911  }
912 
913  /* G_free(wkb_data); */
914 
915  return ret > 0 ? ftype : SF_GEOMETRY;
916 }
917 
918 /*!
919  \brief Read point for WKB data
920 
921  See OGRPoint::importFromWkb() from GDAL/OGR library
922 
923  \param wkb_data WKB data
924  \param nbytes number of bytes (WKB data buffer)
925  \param byte_order byte order (ENDIAN_LITTLE, ENDIAN_BIG)
926  \param with_z WITH_Z for 3D data
927  \param[out] line_p point geometry (pointer to line_pnts struct)
928 
929  \return wkb size
930  \return -1 on error
931  */
932 int point_from_wkb(const unsigned char *wkb_data, int nbytes, int byte_order,
933  int with_z, struct line_pnts *line_p)
934 {
935  double x, y, z;
936 
937  if (nbytes < 21 && nbytes != -1)
938  return -1;
939 
940  /* get vertex */
941  memcpy(&x, wkb_data + 5, 8);
942  memcpy(&y, wkb_data + 5 + 8, 8);
943 
944  if (byte_order == ENDIAN_BIG) {
945  SWAPDOUBLE(&x);
946  SWAPDOUBLE(&y);
947  }
948 
949  if (with_z) {
950  if (nbytes < 29 && nbytes != -1)
951  return -1;
952 
953  memcpy(&z, wkb_data + 5 + 16, 8);
954  if (byte_order == ENDIAN_BIG) {
955  SWAPDOUBLE(&z);
956  }
957  }
958  else {
959  z = 0.0;
960  }
961 
962  if (line_p) {
963  Vect_reset_line(line_p);
964  Vect_append_point(line_p, x, y, z);
965  }
966 
967  return 5 + 8 * (with_z == WITH_Z ? 3 : 2);
968 }
969 
970 /*!
971  \brief Read line for WKB data
972 
973  See OGRLineString::importFromWkb() from GDAL/OGR library
974 
975  \param wkb_data WKB data
976  \param nbytes number of bytes (WKB data buffer)
977  \param byte_order byte order (ENDIAN_LITTLE, ENDIAN_BIG)
978  \param with_z WITH_Z for 3D data
979  \param[out] line_p line geometry (pointer to line_pnts struct)
980 
981  \return wkb size
982  \return -1 on error
983  */
984 int linestring_from_wkb(const unsigned char *wkb_data, int nbytes,
985  int byte_order, int with_z, struct line_pnts *line_p,
986  int is_ring)
987 {
988  int npoints, point_size, buff_min_size, offset;
989  int i;
990  double x, y, z;
991 
992  if (is_ring)
993  offset = 5;
994  else
995  offset = 0;
996 
997  if (is_ring && nbytes < 4 && nbytes != -1)
998  return error_corrupted_data(NULL);
999 
1000  /* get the vertex count */
1001  memcpy(&npoints, wkb_data + (5 - offset), 4);
1002 
1003  if (byte_order == ENDIAN_BIG) {
1004  npoints = SWAP32(npoints);
1005  }
1006 
1007  /* check if the wkb stream buffer is big enough to store fetched
1008  number of points. 16 or 24 - size of point structure
1009  */
1010  point_size = with_z ? 24 : 16;
1011  if (npoints < 0 || npoints > INT_MAX / point_size)
1012  return error_corrupted_data(NULL);
1013 
1014  buff_min_size = point_size * npoints;
1015 
1016  if (nbytes != -1 && buff_min_size > nbytes - (9 - offset))
1017  return error_corrupted_data(_("Length of input WKB is too small"));
1018 
1019  if (line_p)
1020  Vect_reset_line(line_p);
1021 
1022  /* get the vertex */
1023  for (i = 0; i < npoints; i++) {
1024  memcpy(&x, wkb_data + (9 - offset) + i * point_size, 8);
1025  memcpy(&y, wkb_data + (9 - offset) + 8 + i * point_size, 8);
1026  if (with_z)
1027  memcpy(&z, wkb_data + (9 - offset) + 16 + i * point_size, 8);
1028  else
1029  z = 0.0;
1030 
1031  if (byte_order == ENDIAN_BIG) {
1032  SWAPDOUBLE(&x);
1033  SWAPDOUBLE(&y);
1034  if (with_z)
1035  SWAPDOUBLE(&z);
1036  }
1037 
1038  if (line_p)
1039  Vect_append_point(line_p, x, y, z);
1040  }
1041 
1042  return (9 - offset) + (with_z == WITH_Z ? 3 : 2) * 8 * line_p->n_points;
1043 }
1044 
1045 /*!
1046  \brief Read polygon for WKB data
1047 
1048  See OGRPolygon::importFromWkb() from GDAL/OGR library
1049 
1050  \param wkb_data WKB data
1051  \param nbytes number of bytes (WKB data buffer)
1052  \param byte_order byte order (ENDIAN_LITTLE, ENDIAN_BIG)
1053  \param with_z WITH_Z for 3D data
1054  \param[out] line_p array of rings (pointer to line_pnts struct)
1055  \param[out] nrings number of rings
1056 
1057  \return wkb size
1058  \return -1 on error
1059  */
1060 int polygon_from_wkb(const unsigned char *wkb_data, int nbytes, int byte_order,
1061  int with_z, struct Format_info_cache *cache, int *nrings)
1062 {
1063  int data_offset, i, nsize, isize;
1064  int num_of_rings;
1065  struct line_pnts *line_i;
1066 
1067  if (nbytes < 9 && nbytes != -1)
1068  return -1;
1069 
1070  /* get the ring count */
1071  memcpy(nrings, wkb_data + 5, 4);
1072  if (byte_order == ENDIAN_BIG) {
1073  *nrings = SWAP32(*nrings);
1074  }
1075  if (*nrings < 0) {
1076  return -1;
1077  }
1078  num_of_rings = *nrings;
1079 
1080  /* reallocate space for islands if needed */
1081  Vect__reallocate_cache(cache, num_of_rings, FALSE);
1082  cache->lines_num += num_of_rings;
1083 
1084  /* each ring has a minimum of 4 bytes (point count) */
1085  if (nbytes != -1 && nbytes - 9 < num_of_rings * 4) {
1086  return error_corrupted_data(_("Length of input WKB is too small"));
1087  }
1088 
1089  data_offset = 9;
1090  if (nbytes != -1)
1091  nbytes -= data_offset;
1092 
1093  /* get the rings */
1094  nsize = 9;
1095  for (i = 0; i < num_of_rings; i++) {
1096  if (cache->lines_next >= cache->lines_num)
1097  G_fatal_error(_("Invalid cache index %d (max: %d)"),
1098  cache->lines_next, cache->lines_num);
1099  line_i = cache->lines[cache->lines_next];
1100  cache->lines_types[cache->lines_next++] = GV_BOUNDARY;
1101 
1102  linestring_from_wkb(wkb_data + data_offset, nbytes, byte_order, with_z,
1103  line_i, TRUE);
1104 
1105  if (nbytes != -1) {
1106  isize = 4 + 8 * (with_z == WITH_Z ? 3 : 2) * line_i->n_points;
1107  nbytes -= isize;
1108  }
1109 
1110  nsize += isize;
1111  data_offset += isize;
1112  }
1113 
1114  return nsize;
1115 }
1116 
1117 /*!
1118  \brief Read geometry collection for WKB data
1119 
1120  See OGRGeometryCollection::importFromWkbInternal() from GDAL/OGR library
1121 
1122  \param wkb_data WKB data
1123  \param nbytes number of bytes (WKB data buffer)
1124  \param byte_order byte order (ENDIAN_LITTLE, ENDIAN_BIG)
1125  \param with_z WITH_Z for 3D data
1126  \param ipart part to cache (starts at 0)
1127  \param[out] cache lines cache
1128  \param[in,out] fparts feature parts (required for building pseudo-topology)
1129 
1130  \return number of parts
1131  \return -1 on error
1132  */
1133 int geometry_collection_from_wkb(const unsigned char *wkb_data, int nbytes,
1134  int byte_order, int with_z,
1135  struct Format_info_cache *cache,
1136  struct feat_parts *fparts)
1137 {
1138  int ipart, nparts, data_offset, nsize;
1139  unsigned char *wkb_subdata;
1140  SF_FeatureType ftype;
1141 
1142  if (nbytes < 9 && nbytes != -1)
1143  return error_corrupted_data(NULL);
1144 
1145  /* get the geometry count */
1146  memcpy(&nparts, wkb_data + 5, 4);
1147  if (byte_order == ENDIAN_BIG) {
1148  nparts = SWAP32(nparts);
1149  }
1150  if (nparts < 0 || nparts > INT_MAX / 9) {
1151  return error_corrupted_data(NULL);
1152  }
1153  G_debug(5, "\t(geometry collections) parts: %d", nparts);
1154 
1155  /* each geometry has a minimum of 9 bytes */
1156  if (nbytes != -1 && nbytes - 9 < nparts * 9) {
1157  return error_corrupted_data(_("Length of input WKB is too small"));
1158  }
1159 
1160  data_offset = 9;
1161  if (nbytes != -1)
1162  nbytes -= data_offset;
1163 
1164  /* reallocate space for parts if needed */
1165  Vect__reallocate_cache(cache, nparts, FALSE);
1166 
1167  /* get parts */
1168  for (ipart = 0; ipart < nparts; ipart++) {
1169  wkb_subdata = (unsigned char *)wkb_data + data_offset;
1170  if (nbytes < 9 && nbytes != -1)
1171  return error_corrupted_data(NULL);
1172 
1173  if (byte_order == ENDIAN_LITTLE) {
1174  ftype = (SF_FeatureType)wkb_subdata[1];
1175  }
1176  else {
1177  ftype = (SF_FeatureType)wkb_subdata[4];
1178  }
1179 
1180  if (ftype == SF_POINT) {
1181  cache->lines_types[cache->lines_next] = GV_POINT;
1182  nsize = point_from_wkb(wkb_subdata, nbytes, byte_order, with_z,
1183  cache->lines[cache->lines_next]);
1184  cache->lines_num++;
1185  add_fpart(fparts, ftype, cache->lines_next, 1);
1186  cache->lines_next++;
1187  }
1188  else if (ftype == SF_LINESTRING) {
1189  cache->lines_types[cache->lines_next] = GV_LINE;
1190  nsize = linestring_from_wkb(wkb_subdata, nbytes, byte_order, with_z,
1191  cache->lines[cache->lines_next], FALSE);
1192  cache->lines_num++;
1193  add_fpart(fparts, ftype, cache->lines_next, 1);
1194  cache->lines_next++;
1195  }
1196  else if (ftype == SF_POLYGON) {
1197  int idx, nrings;
1198 
1199  idx = cache->lines_next;
1200  nsize = polygon_from_wkb(wkb_subdata, nbytes, byte_order, with_z,
1201  cache, &nrings);
1202  add_fpart(fparts, ftype, idx, nrings);
1203  }
1204  else if (ftype == SF_GEOMETRYCOLLECTION || ftype == SF_MULTIPOLYGON ||
1205  ftype == SF_MULTILINESTRING || ftype == SF_MULTIPOLYGON) {
1206  geometry_collection_from_wkb(wkb_subdata, nbytes, byte_order,
1207  with_z, cache, fparts);
1208  }
1209  else {
1210  G_warning(_("Unsupported feature type %d"), ftype);
1211  }
1212 
1213  if (nbytes != -1) {
1214  nbytes -= nsize;
1215  }
1216 
1217  data_offset += nsize;
1218  }
1219 
1220  return nparts;
1221 }
1222 
1223 /*!
1224  \brief Report error message
1225 
1226  \param msg message (NULL)
1227 
1228  \return -1
1229  */
1230 int error_corrupted_data(const char *msg)
1231 {
1232  if (msg)
1233  G_warning(_("Corrupted data. %s."), msg);
1234  else
1235  G_warning(_("Corrupted data"));
1236 
1237  return -1;
1238 }
1239 
1240 /*!
1241  \brief Create select cursor for sequential access (internal use only)
1242 
1243  Allocated cursor name should be freed by G_free().
1244 
1245  \param pg_info pointer to Format_info_pg struct
1246  \param fetch_all TRUE to fetch all records
1247  \param[out] cursor name
1248 
1249  \return 0 on success
1250  \return -1 on failure
1251  */
1253  int fetch_all, int built_level)
1254 {
1255  char stmt[DB_SQL_MAX];
1256 
1257  if (Vect__execute_pg(pg_info->conn, "BEGIN") == -1)
1258  return -1;
1259 
1260  /* set cursor name */
1261  G_asprintf(&(pg_info->cursor_name), "%s_%s_%p", pg_info->schema_name,
1262  pg_info->table_name, (void *)pg_info->conn);
1263 
1264  if (!pg_info->toposchema_name) {
1265  /* simple feature access (geom, fid) */
1266  /* TODO: start_fid */
1267  if (pg_info->where) {
1268  /* set attribute filter if where sql statement defined */
1269  char **tokens = G_tokenize(pg_info->where, "=");
1270 
1271  if (G_number_of_tokens(tokens) != 2) {
1272  G_warning(_("Unable to parse '%s'"), pg_info->where);
1273  return -1;
1274  }
1275  sprintf(stmt,
1276  "DECLARE %s CURSOR FOR SELECT \"%s\",\"%s\" FROM "
1277  "\"%s\".\"%s\" WHERE \"%s\"=%s ORDER BY \"%s\"",
1278  pg_info->cursor_name, pg_info->geom_column,
1279  pg_info->fid_column, pg_info->schema_name,
1280  pg_info->table_name, tokens[0], tokens[1],
1281  pg_info->fid_column);
1282  G_free_tokens(tokens);
1283  }
1284  else {
1285  sprintf(stmt,
1286  "DECLARE %s CURSOR FOR SELECT \"%s\",\"%s\" FROM "
1287  "\"%s\".\"%s\" ORDER BY \"%s\"",
1288  pg_info->cursor_name, pg_info->geom_column,
1289  pg_info->fid_column, pg_info->schema_name,
1290  pg_info->table_name, pg_info->fid_column);
1291  }
1292  }
1293  else {
1294  /* topology access (geom,id,fid,type) */
1295  /* TODO: optimize SQL statement (for points/centroids) */
1296  sprintf(
1297  stmt,
1298  "DECLARE %s CURSOR FOR "
1299  "SELECT geom,id,type,fid FROM ("
1300  "SELECT tt.node_id AS id,tt.geom, %d AS type, ft.%s AS fid FROM "
1301  "\"%s\".node AS tt "
1302  "LEFT JOIN \"%s\".\"%s\" AS ft ON (%s).type = 1 AND (%s).id = "
1303  "node_id "
1304  "WHERE containing_face IS NULL AND node_id NOT IN "
1305  "(SELECT node FROM (SELECT start_node AS node FROM \"%s\".edge "
1306  "GROUP BY start_node UNION ALL "
1307  "SELECT end_node AS node FROM \"%s\".edge GROUP BY end_node) AS "
1308  "foo) UNION ALL "
1309  "SELECT tt.node_id AS id,tt.geom, %d AS type, ft.%s AS fid FROM "
1310  "\"%s\".node AS tt "
1311  "LEFT JOIN \"%s\".\"%s\" AS ft ON (%s).type = 3 AND (%s).id = %s "
1312  "WHERE containing_face IS NOT NULL AND node_id NOT IN "
1313  "(SELECT node FROM (SELECT start_node AS node FROM \"%s\".edge "
1314  "GROUP BY start_node UNION ALL "
1315  "SELECT end_node AS node FROM \"%s\".edge GROUP BY end_node) AS "
1316  "foo) UNION ALL "
1317  "SELECT tt.edge_id AS id, tt.geom, %d AS type, ft.%s AS fid FROM "
1318  "\"%s\".edge AS tt "
1319  "LEFT JOIN \"%s\".\"%s\" AS ft ON (%s).type = 2 AND (%s).id = "
1320  "edge_id "
1321  "WHERE left_face = 0 AND right_face = 0 UNION ALL "
1322  "SELECT tt.edge_id AS id, tt.geom, %d AS type, ft.%s AS fid FROM "
1323  "\"%s\".edge AS tt "
1324  "LEFT JOIN \"%s\".\"%s\" AS ft ON (%s).type = 2 AND (%s).id = "
1325  "edge_id "
1326  "WHERE left_face != 0 OR right_face != 0 ) AS foo ORDER BY type,id",
1327  pg_info->cursor_name, GV_POINT, pg_info->fid_column,
1328  pg_info->toposchema_name, pg_info->schema_name, pg_info->table_name,
1329  pg_info->topogeom_column, pg_info->topogeom_column,
1330  pg_info->toposchema_name, pg_info->toposchema_name, GV_CENTROID,
1331  pg_info->fid_column, pg_info->toposchema_name, pg_info->schema_name,
1332  pg_info->table_name, pg_info->topogeom_column,
1333  pg_info->topogeom_column,
1334  built_level >= GV_BUILD_CENTROIDS ? "containing_face" : "node_id",
1335  pg_info->toposchema_name, pg_info->toposchema_name, GV_LINE,
1336  pg_info->fid_column, pg_info->toposchema_name, pg_info->schema_name,
1337  pg_info->table_name, pg_info->topogeom_column,
1338  pg_info->topogeom_column, GV_BOUNDARY, pg_info->fid_column,
1339  pg_info->toposchema_name, pg_info->schema_name, pg_info->table_name,
1340  pg_info->topogeom_column, pg_info->topogeom_column);
1341  }
1342  if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
1343  Vect__execute_pg(pg_info->conn, "ROLLBACK");
1344  return -1;
1345  }
1346 
1347  if (fetch_all)
1348  sprintf(stmt, "FETCH ALL in %s", pg_info->cursor_name);
1349  else
1350  sprintf(stmt, "FETCH %d in %s", CURSOR_PAGE, pg_info->cursor_name);
1351  G_debug(3, "SQL: %s", stmt);
1352  pg_info->res =
1353  PQexec(pg_info->conn, stmt); /* fetch records from select cursor */
1354  if (!pg_info->res || PQresultStatus(pg_info->res) != PGRES_TUPLES_OK) {
1355  error_tuples(pg_info);
1356  return -1;
1357  }
1358  pg_info->next_line = 0;
1359 
1360  return 0;
1361 }
1362 
1363 /*!
1364  \brief Open select cursor for random access (internal use only)
1365 
1366  Fetch number of feature (given by CURSOR_PAGE) starting with
1367  <em>fid</em>.
1368 
1369  Allocated cursor name should be freed by G_free().
1370 
1371  \param pg_info pointer to Format_info_pg struct
1372  \param fid feature id to get
1373  \param type feature type
1374 
1375  \return 0 on success
1376  \return -1 on failure
1377  */
1378 int Vect__open_cursor_line_pg(struct Format_info_pg *pg_info, int fid, int type)
1379 {
1380  char stmt[DB_SQL_MAX];
1381 
1382  G_debug(3, "Vect__open_cursor_line_pg(): fid range = %d-%d, type = %d", fid,
1383  fid + CURSOR_PAGE, type);
1384 
1385  if (Vect__execute_pg(pg_info->conn, "BEGIN") == -1)
1386  return -1;
1387 
1388  pg_info->cursor_fid = fid;
1389  G_asprintf(&(pg_info->cursor_name), "%s_%s_%d_%p", pg_info->schema_name,
1390  pg_info->table_name, fid, (void *)pg_info->conn);
1391 
1392  if (!pg_info->toposchema_name) {
1393  /* simple feature access (geom) */
1394  sprintf(stmt,
1395  "DECLARE %s CURSOR FOR SELECT %s FROM \"%s\".\"%s\" "
1396  "WHERE %s BETWEEN %d AND %d ORDER BY %s",
1397  pg_info->cursor_name, pg_info->geom_column,
1398  pg_info->schema_name, pg_info->table_name, pg_info->fid_column,
1399  fid, fid + CURSOR_PAGE, pg_info->fid_column);
1400  }
1401  else {
1402  /* topological access */
1403  if (!(type & (GV_POINTS | GV_LINES))) {
1404  G_warning(_("Unsupported feature type %d"), type);
1405  Vect__execute_pg(pg_info->conn, "ROLLBACK");
1406  return -1;
1407  }
1408 
1409  if (type & GV_POINTS) {
1410  /* points (geom,containing_face) */
1411  sprintf(stmt,
1412  "DECLARE %s CURSOR FOR SELECT geom,containing_face "
1413  " FROM \"%s\".node WHERE node_id BETWEEN %d AND %d ORDER "
1414  "BY node_id",
1415  pg_info->cursor_name, pg_info->toposchema_name, fid,
1416  fid + CURSOR_PAGE);
1417  }
1418  else {
1419  /* edges (geom,left_face,right_face) */
1420  sprintf(stmt,
1421  "DECLARE %s CURSOR FOR SELECT geom,left_face,right_face "
1422  " FROM \"%s\".edge WHERE edge_id BETWEEN %d AND %d ORDER "
1423  "BY edge_id",
1424  pg_info->cursor_name, pg_info->toposchema_name, fid,
1425  fid + CURSOR_PAGE);
1426  }
1427  }
1428  if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
1429  Vect__execute_pg(pg_info->conn, "ROLLBACK");
1430  return -1;
1431  }
1432  pg_info->next_line = 0;
1433 
1434  sprintf(stmt, "FETCH ALL in %s", pg_info->cursor_name);
1435  pg_info->res = PQexec(pg_info->conn, stmt);
1436  if (!pg_info->res || PQresultStatus(pg_info->res) != PGRES_TUPLES_OK) {
1437  error_tuples(pg_info);
1438  return -1;
1439  }
1440 
1441  return 0;
1442 }
1443 
1444 /*!
1445  \brief Close select cursor
1446 
1447  \param pg_info pointer to Format_info_pg struct
1448 
1449  \return 0 on success
1450  \return -1 on failure
1451  */
1453 {
1454  if (pg_info->res) {
1455  PQclear(pg_info->res);
1456  pg_info->res = NULL;
1457  }
1458 
1459  if (pg_info->cursor_name) {
1460  char stmt[DB_SQL_MAX];
1461 
1462  sprintf(stmt, "CLOSE %s", pg_info->cursor_name);
1463  if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
1464  G_warning(_("Unable to close cursor %s"), pg_info->cursor_name);
1465  return -1;
1466  }
1467  Vect__execute_pg(pg_info->conn, "COMMIT");
1468  G_free(pg_info->cursor_name);
1469  pg_info->cursor_name = NULL;
1470  }
1471 
1472  return 0;
1473 }
1474 
1475 /*!
1476  \brief Select feature (internal use only)
1477 
1478  \param pg_info pointer to Format_info_pg struct
1479  \param fid feature id to get
1480  \param type feature type
1481 
1482  \return 0 on success
1483  \return -1 on failure
1484  */
1485 int Vect__select_line_pg(struct Format_info_pg *pg_info, int fid, int type)
1486 {
1487  char stmt[DB_SQL_MAX];
1488 
1489  if (!pg_info->toposchema_name) {
1490  /* simple feature access */
1491  sprintf(stmt, "SELECT %s FROM \"%s\".\"%s\" WHERE %s = %d",
1492  pg_info->geom_column, pg_info->schema_name, pg_info->table_name,
1493  pg_info->fid_column, fid);
1494  }
1495  else {
1496  /* topological access */
1497  if (!(type & (GV_POINTS | GV_LINES))) {
1498  G_warning(_("Unsupported feature type %d"), type);
1499  return -1;
1500  }
1501 
1502  if (type & GV_POINTS) {
1503  int topotype;
1504  char *nodeid;
1505 
1506  if (type == GV_POINT) {
1507  topotype = 1;
1508  nodeid = pg_info->fid_column;
1509  }
1510  else { /* assuming GV_CENTROID */
1511  topotype = 3;
1512  nodeid = "containing_face";
1513  }
1514 
1515  sprintf(stmt,
1516  "SELECT tt.geom,tt.containing_face,ft.%s FROM \"%s\".node "
1517  "AS tt "
1518  "LEFT JOIN \"%s\".\"%s\" AS ft ON (%s).type = %d and "
1519  "(%s).id = %s "
1520  "WHERE node_id = %d",
1521  pg_info->fid_column, pg_info->toposchema_name,
1522  pg_info->schema_name, pg_info->table_name,
1523  pg_info->topogeom_column, topotype,
1524  pg_info->topogeom_column, nodeid, fid);
1525  }
1526  else {
1527  sprintf(stmt,
1528  "SELECT tt.geom,tt.left_face,tt.right_face,ft.%s FROM "
1529  "\"%s\".edge AS tt "
1530  "LEFT JOIN \"%s\".\"%s\" AS ft ON (%s).type = 2 and "
1531  "(%s).id = edge_id "
1532  "WHERE edge_id = %d",
1533  pg_info->fid_column, pg_info->toposchema_name,
1534  pg_info->schema_name, pg_info->table_name,
1535  pg_info->topogeom_column, pg_info->topogeom_column, fid);
1536  }
1537  }
1538  G_debug(3, "SQL: %s", stmt);
1539 
1540  pg_info->next_line = 0;
1541 
1542  pg_info->res = PQexec(pg_info->conn, stmt);
1543  if (!pg_info->res || PQresultStatus(pg_info->res) != PGRES_TUPLES_OK) {
1544  error_tuples(pg_info);
1545  return -1;
1546  }
1547 
1548  return 0;
1549 }
1550 
1551 /*!
1552  \brief Execute SQL statement
1553 
1554  See pg_local_proto.h
1555 
1556  \param conn pointer to PGconn
1557  \param stmt query
1558 
1559  \return 0 on success
1560  \return -1 on error
1561  */
1562 int Vect__execute_pg(PGconn *conn, const char *stmt)
1563 {
1564  PGresult *result;
1565 
1566  result = NULL;
1567 
1568  G_debug(3, "Vect__execute_pg(): %s", stmt);
1569  result = PQexec(conn, stmt);
1570  if (!result || PQresultStatus(result) != PGRES_COMMAND_OK) {
1571  size_t stmt_len;
1572  char stmt_prt[512];
1573 
1574  PQclear(result);
1575  stmt_len = strlen(stmt);
1576  strncpy(stmt_prt, stmt, stmt_len > 511 ? 511 : stmt_len);
1577  stmt_prt[stmt_len > 511 ? 511 : stmt_len] = '\0';
1578  G_warning(_("Execution failed: %s (...)\nReason: %s"), stmt_prt,
1579  PQerrorMessage(conn));
1580  return -1;
1581  }
1582 
1583  PQclear(result);
1584  return 0;
1585 }
1586 
1587 /*!
1588  \brief Execute SQL statement and get value.
1589 
1590  \param conn pointer to PGconn
1591  \param stmt query
1592 
1593  \return value on success
1594  \return -1 on error
1595  */
1596 int Vect__execute_get_value_pg(PGconn *conn, const char *stmt)
1597 {
1598  int ret;
1599  PGresult *result;
1600 
1601  result = NULL;
1602 
1603  G_debug(3, "Vect__execute_get_value_pg(): %s", stmt);
1604  result = PQexec(conn, stmt);
1605  if (!result || PQresultStatus(result) != PGRES_TUPLES_OK ||
1606  PQntuples(result) != 1) {
1607  PQclear(result);
1608 
1609  G_warning(_("Execution failed: %s\nReason: %s"), stmt,
1610  PQerrorMessage(conn));
1611  return -1;
1612  }
1613 
1614  ret = atoi(PQgetvalue(result, 0, 0));
1615  PQclear(result);
1616 
1617  return ret;
1618 }
1619 
1620 /*!
1621  \brief Reallocate lines cache
1622  */
1623 void Vect__reallocate_cache(struct Format_info_cache *cache, int num, int incr)
1624 {
1625  int i;
1626 
1627  if (!incr && cache->lines_alloc >= num)
1628  return;
1629 
1630  if (!incr && !cache->lines) {
1631  /* most of features requires only one line cache */
1632  cache->lines_alloc = 1;
1633  }
1634  else {
1635  cache->lines_alloc += num;
1636  }
1637 
1638  cache->lines = (struct line_pnts **)G_realloc(
1639  cache->lines, cache->lines_alloc * sizeof(struct line_pnts *));
1640  cache->lines_types =
1641  (int *)G_realloc(cache->lines_types, cache->lines_alloc * sizeof(int));
1642  cache->lines_cats =
1643  (int *)G_realloc(cache->lines_cats, cache->lines_alloc * sizeof(int));
1644 
1645  if (cache->lines_alloc > 1) {
1646  for (i = cache->lines_alloc - num; i < cache->lines_alloc; i++) {
1647  cache->lines[i] = Vect_new_line_struct();
1648  cache->lines_types[i] = -1;
1649  cache->lines_cats[i] = -1;
1650  }
1651  }
1652  else {
1653  cache->lines[0] = Vect_new_line_struct();
1654  cache->lines_types[0] = -1;
1655  cache->lines_cats[0] = -1;
1656  }
1657 }
1658 
1659 void add_fpart(struct feat_parts *fparts, SF_FeatureType ftype, int idx,
1660  int nlines)
1661 {
1662  if (!fparts)
1663  return;
1664 
1665  if (fparts->a_parts == 0 || fparts->n_parts >= fparts->a_parts) {
1666  if (fparts->a_parts == 0)
1667  fparts->a_parts = 1;
1668  else
1669  fparts->a_parts += fparts->n_parts;
1670 
1671  fparts->ftype = (SF_FeatureType *)G_realloc(
1672  fparts->ftype, fparts->a_parts * sizeof(SF_FeatureType));
1673  fparts->nlines =
1674  (int *)G_realloc(fparts->nlines, fparts->a_parts * sizeof(int));
1675  fparts->idx =
1676  (int *)G_realloc(fparts->idx, fparts->a_parts * sizeof(int));
1677  }
1678 
1679  fparts->ftype[fparts->n_parts] = ftype;
1680  fparts->idx[fparts->n_parts] = idx;
1681  fparts->nlines[fparts->n_parts] = nlines;
1682 
1683  fparts->n_parts++;
1684 }
1685 
1686 /*
1687  \brief Get centroid
1688 
1689  \param pg_info pointer to Format_info_pg
1690  \param centroid centroid id
1691  \param[out] line_p output geometry
1692 
1693  \return GV_CENTROID on success
1694  \return -1 on error
1695  */
1696 int get_centroid(struct Map_info *Map, int centroid, struct line_pnts *line_p,
1697  struct line_cats *line_c)
1698 {
1699  int i, found;
1700  struct bound_box box;
1701  struct boxlist list;
1702  struct P_line *Line;
1703  struct P_topo_c *topo;
1704 
1705  Line = Map->plus.Line[centroid];
1706  topo = (struct P_topo_c *)Line->topo;
1707 
1708  /* get area bbox */
1709  Vect_get_area_box(Map, topo->area, &box);
1710  /* search in spatial index for centroid with area bbox */
1712  Vect_select_lines_by_box(Map, &box, Line->type, &list);
1713 
1714  found = -1;
1715  for (i = 0; i < list.n_values; i++) {
1716  if (list.id[i] == centroid) {
1717  found = i;
1718  break;
1719  }
1720  }
1721 
1722  if (found == -1)
1723  return -1;
1724 
1725  if (line_p) {
1726  Vect_reset_line(line_p);
1727  Vect_append_point(line_p, list.box[found].E, list.box[found].N, 0.0);
1728  }
1729  if (line_c) {
1730  Vect_cat_set(line_c, 1, Line->offset);
1731  }
1732 
1733  return GV_CENTROID;
1734 }
1735 
1736 void error_tuples(struct Format_info_pg *pg_info)
1737 {
1738  Vect__execute_pg(pg_info->conn, "ROLLBACK");
1739  G_warning(_("Unable to read features. Reason:\n%s"),
1740  PQresultErrorMessage(pg_info->res));
1741 
1742  if (pg_info->res) {
1743  PQclear(pg_info->res);
1744  pg_info->res = NULL;
1745  }
1746 }
1747 #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:406
#define TRUE
Definition: gis.h:79
#define FALSE
Definition: gis.h:83
#define ENDIAN_BIG
Definition: gis.h:407
#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:1452
int Vect__execute_get_value_pg(PGconn *conn, const char *stmt)
Execute SQL statement and get value.
Definition: read_pg.c:1596
#define NOPG_UNUSED
Definition: read_pg.c:62
int Vect__execute_pg(PGconn *conn, const char *stmt)
Execute SQL statement.
Definition: read_pg.c:1562
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:778
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:1378
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:1485
void Vect__reallocate_cache(struct Format_info_cache *cache, int num, int incr)
Reallocate lines cache.
Definition: read_pg.c:1623
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:1252
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