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