GRASS 8 Programmer's Manual 8.6.0dev(2026)-5f4f7ad06c
Loading...
Searching...
No Matches
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
40static unsigned char *wkb_data;
41static unsigned int wkb_data_length;
42
43static int read_next_line_pg(struct Map_info *, struct line_pnts *,
44 struct line_cats *, int);
45SF_FeatureType get_feature(struct Map_info *, int, int);
46static unsigned char *hex_to_wkb(const char *, int *);
47static int point_from_wkb(const unsigned char *, int, int, int,
48 struct line_pnts *);
49static int linestring_from_wkb(const unsigned char *, int, int, int,
50 struct line_pnts *, int);
51static int polygon_from_wkb(const unsigned char *, int, int, int,
52 struct Format_info_cache *, int *);
53static int geometry_collection_from_wkb(const unsigned char *, int, int, int,
54 struct Format_info_cache *,
55 struct feat_parts *);
56static int error_corrupted_data(const char *);
57static void add_fpart(struct feat_parts *, SF_FeatureType, int, int);
58static int get_centroid(struct Map_info *, int, struct line_pnts *,
59 struct line_cats *);
60static 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 */
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 */
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)
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) {
184 list.box[found].N, 0.0);
185 }
186 }
187 if (line_c != NULL) {
188 /* cat = FID and offset = FID for centroid */
190 Vect_cat_set(line_c, 1, (int)Line->offset);
191 }
192
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 */
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 */
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)
266 if (line_c != NULL)
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)
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 */
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)
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
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)
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 */
447int read_next_line_pg(struct Map_info *Map, struct line_pnts *line_p,
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)
461
462 while (TRUE) {
463 /* reset data structures */
464 if (line_p != NULL)
466 if (line_c != NULL)
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 */
523
524 if (!Vect_box_overlap(&lbox, &mbox))
525 continue;
526 }
527
528 /* skip feature by field ignored */
529
530 if (line_p)
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] =
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 */
580SF_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)
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) {
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))
663 else if (seq_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 */
734unsigned 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 */
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;
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)
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) {
879 cache->lines_types[cache->lines_num - 1] =
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) {
886 cache->lines_types[cache->lines_num - 1] =
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 ||
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 */
933int 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) {
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 */
985int 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;
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)
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 */
1061int 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 }
1080
1081 /* reallocate space for islands if needed */
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 */
1134int 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{
1140 unsigned char *wkb_subdata;
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 }
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 */
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) {
1176 }
1177 else {
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 }
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 */
1231int 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);
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 */
1380int 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 */
1487int 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 */
1565int 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 */
1599int 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 */
1626void 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
1662void 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 */
1699int 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) {
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
1739void 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"),
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
Main header of GRASS DataBase Management Interface.
#define DB_SQL_MAX
Definition dbmi.h:142
void G_free(void *)
Free allocated memory.
Definition gis/alloc.c:147
#define G_realloc(p, n)
Definition defs/gis.h:141
void void void void G_fatal_error(const char *,...) __attribute__((format(printf
void G_warning(const char *,...) __attribute__((format(printf
char ** G_tokenize(const char *, const char *)
Tokenize string.
Definition gis/token.c:47
void G_free_tokens(char **)
Free memory allocated to tokens.
Definition gis/token.c:197
int G_asprintf(char **, const char *,...) __attribute__((format(printf
int G_number_of_tokens(char **)
Return number of tokens.
Definition gis/token.c:178
int G_debug(int, const char *,...) __attribute__((format(printf
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.
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
struct line_pnts * Vect_new_line_struct(void)
Creates and initializes a line_pnts structure.
Definition line.c:45
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
SF_FeatureType
Simple feature types.
@ SF_POLYGON
@ SF_NONE
@ SF_MULTIPOLYGON
@ SF_MULTILINESTRING
@ SF_LINESTRING
@ SF_GEOMETRY
@ SF_GEOMETRYCOLLECTION
@ SF_MULTIPOINT
@ SF_POINT
#define GV_LINE
#define GV_POINT
Feature types used in memory on run time (may change)
#define GV_LINES
#define GV_BOUNDARY
#define WITH_Z
#define GV_BUILD_CENTROIDS
Topology levels - assign centroids to areas.
#define GV_FORWARD
Line direction indicator forward/backward.
#define GV_POINTS
int dig_init_boxlist(struct boxlist *, int)
#define ENDIAN_LITTLE
Endian check.
Definition gis.h:415
#define TRUE
Definition gis.h:78
#define FALSE
Definition gis.h:82
#define ENDIAN_BIG
Definition gis.h:416
#define _(str)
Definition glocale.h:10
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
int V2_read_next_line_pg(struct Map_info *Map, struct line_pnts *line_p, struct line_cats *line_c)
Read next feature from PostGIS layer on topological level (simple feature access).
Definition read_pg.c:119
#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
int V2_read_line_pg(struct Map_info *Map, struct line_pnts *line_p, struct line_cats *line_c, int line)
Read feature from PostGIS layer on topological level.
Definition read_pg.c:328
int V1_read_next_line_pg(struct Map_info *Map, struct line_pnts *line_p, struct line_cats *line_c)
Read next feature from PostGIS layer. Skip empty features (level 1 without topology)....
Definition read_pg.c:88
int V1_read_line_pg(struct Map_info *Map, struct line_pnts *line_p, struct line_cats *line_c, off_t offset)
Read feature from PostGIS layer at given offset (level 1 without topology)
Definition read_pg.c:245
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 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 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 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
Lines cache for reading feature (non-native formats)
int lines_next
Next line to be read from cache.
int * lines_types
List of line types (GV_POINT, GV_LINE, ...)
struct line_pnts ** lines
Lines array.
int lines_alloc
Number of allocated lines in cache.
int * lines_cats
List of line cats (used only for PostGIS Topology access)
int lines_num
Number of lines which forms current feature.
Non-native format info (PostGIS)
struct Format_info_cache cache
Lines cache for reading feature.
struct Format_info_offset offset
Offset list used for building pseudo-topology (simple features access)
Vector map info.
Vector geometry.
char type
Line type.
off_t offset
Offset in coor file for line.
void * topo
Topology info.
Centroid topology.
plus_t area
Area number, negative for duplicate centroid.
Bounding box.
Definition dig_structs.h:62
List of bounding boxes with id.
Feature category info.
Feature geometry info - coordinates.
Definition manage.h:4
#define x