GRASS 8 Programmer's Manual 8.6.0dev(2026)-1d1e47ad9d
Loading...
Searching...
No Matches
open_pg.c
Go to the documentation of this file.
1/*!
2 \file lib/vector/Vlib/open_pg.c
3
4 \brief Vector library - Open PostGIS layer as vector map layer
5
6 Higher level functions for reading/writing/manipulating vectors.
7
8 (C) 2011-2013 by the GRASS Development Team
9
10 This program is free software under the GNU General Public License
11 (>=v2). Read the file COPYING that comes with GRASS for details.
12
13 \author Martin Landa <landa.martin gmail.com>
14 */
15
16#include <string.h>
17#include <stdlib.h>
18
19#include <grass/vector.h>
20#include <grass/dbmi.h>
21#include <grass/glocale.h>
22
23#include "local_proto.h"
24
25#ifdef HAVE_POSTGRES
26#include "pg_local_proto.h"
27
28struct line_data {
29 int id;
30 int fid;
31 int start_node;
32 int end_node;
33 int left_face;
34 int right_face;
35 char *wkb_geom;
36};
37
38static char *get_key_column(struct Format_info_pg *);
39static SF_FeatureType ftype_from_string(const char *);
40static int drop_table(struct Format_info_pg *);
41static void connect_db(struct Format_info_pg *);
42static int check_topo(struct Format_info_pg *, struct Plus_head *);
43static int parse_bbox(const char *, struct bound_box *);
44static struct P_node *read_p_node(struct Plus_head *, int, int, const char *,
45 const char *, const char *,
46 struct Format_info_pg *, int);
47static struct P_line *read_p_line(struct Plus_head *, int,
48 const struct line_data *, int,
49 struct Format_info_cache *);
50static struct P_area *read_p_area(struct Plus_head *, int, const char *, int,
51 const char *);
52static struct P_isle *read_p_isle(struct Plus_head *, int, const char *, int);
53static void notice_processor(void *, const char *);
54static char **scan_array(const char *);
55static int remap_node(const struct Format_info_offset *, int);
56static int remap_line(const struct Plus_head *, off_t, int);
57
58#define NOPG_UNUSED
59#else
60#define NOPG_UNUSED UNUSED
61#endif
62
63/*!
64 \brief Open vector map - PostGIS feature table on non-topological
65 level
66
67 \param[in,out] Map pointer to Map_info structure
68 \param update TRUE for write mode, otherwise read-only
69
70 \return 0 success
71 \return -1 error
72 */
74{
75#ifdef HAVE_POSTGRES
76 int found;
77
78 char stmt[DB_SQL_MAX];
79
80 PGresult *res;
81
82 struct Format_info_pg *pg_info;
83
84 G_debug(2, "V1_open_old_pg(): update = %d", update);
85
86 pg_info = &(Map->fInfo.pg);
87 if (!pg_info->conninfo) {
88 G_warning(_("Connection string not defined"));
89 return -1;
90 }
91
92 if (!pg_info->table_name) {
93 G_warning(_("PostGIS feature table not defined"));
94 return -1;
95 }
96
97 G_debug(1, "V1_open_old_pg(): conninfo='%s' table='%s'", pg_info->conninfo,
98 pg_info->table_name);
99
100 /* connect database */
101 if (!pg_info->conn)
102 connect_db(pg_info);
103
104 /* get fid and geometry column */
105 snprintf(stmt, sizeof(stmt),
106 "SELECT f_geometry_column, coord_dimension, srid, type "
107 "FROM geometry_columns WHERE f_table_schema = '%s' AND "
108 "f_table_name = '%s'",
109 pg_info->schema_name, pg_info->table_name);
110 G_debug(2, "SQL: %s", stmt);
111
112 res = PQexec(pg_info->conn, stmt);
114 G_fatal_error("%s\n%s", _("No feature tables found in database."),
116
117 found = PQntuples(res) > 0 ? TRUE : FALSE;
118 if (found) {
119 /* geometry column */
120 pg_info->geom_column = G_store(PQgetvalue(res, 0, 0));
121 G_debug(3, "\t-> table = %s column = %s", pg_info->table_name,
122 pg_info->geom_column);
123 /* fid column */
124 pg_info->fid_column = get_key_column(pg_info);
125 /* coordinates dimension */
126 pg_info->coor_dim = atoi(PQgetvalue(res, 0, 1));
127 /* SRS ID */
128 pg_info->srid = atoi(PQgetvalue(res, 0, 2));
129 /* feature type */
130 pg_info->feature_type = ftype_from_string(PQgetvalue(res, 0, 3));
131 }
132 PQclear(res);
133
134 /* fid -1 for empty / -2 for full cache */
135 pg_info->cache.fid = pg_info->cache.ctype != CACHE_MAP ? -1 : -2;
136
137 if (!found) {
138 G_warning(_("Feature table <%s> not found in 'geometry_columns'"),
139 pg_info->table_name);
140 return 0; /* avoid calling G_fatal_error() */
141 }
142
143 /* check for topo schema */
144 check_topo(pg_info, &(Map->plus));
145
146 return 0;
147#else
148 G_fatal_error(_("GRASS is not compiled with PostgreSQL support"));
149 return -1;
150#endif
151}
152
153/*!
154 \brief Open vector map - PostGIS feature table on topological level
155
156 Simple feature access:
157 - open feature index file
158 PostGIS Topology:
159 - check if topological schema exists
160
161 \param[in,out] Map pointer to Map_info structure
162
163 \return 0 success
164 \return -1 error
165 */
167{
168#ifdef HAVE_POSTGRES
169 struct Format_info_pg *pg_info;
170
171 PGresult *res;
172
173 G_debug(3, "V2_open_old_pg(): name = %s mapset = %s", Map->name,
174 Map->mapset);
175
176 pg_info = &(Map->fInfo.pg);
177
178 if (pg_info->toposchema_name) {
179 char stmt[DB_SQL_MAX];
180
181 /* get topo schema id */
182 snprintf(stmt, sizeof(stmt),
183 "SELECT id FROM topology.topology WHERE name = '%s'",
184 pg_info->toposchema_name);
185 res = PQexec(pg_info->conn, stmt);
187 G_warning("%s\n%s", _("Topology schema not found."),
189 if (res)
190 PQclear(res);
191 return -1;
192 }
193 pg_info->toposchema_id = atoi(PQgetvalue(res, 0, 0));
194 PQclear(res);
195 }
196 else {
197 /* fidx file needed only for simple features access */
198 if (Vect_open_fidx(Map, &(pg_info->offset)) != 0) {
199 G_warning(
200 _("Unable to open feature index file for vector map <%s>"),
202 G_zero(&(pg_info->offset), sizeof(struct Format_info_offset));
203 }
204 }
205 return 0;
206#else
207 G_fatal_error(_("GRASS is not compiled with PostgreSQL support"));
208 return -1;
209#endif
210}
211
212/*!
213 \brief Prepare PostGIS database for creating new feature table
214 (level 1)
215
216 New PostGIS table is created when writing features by
217 Vect_wrile_line().
218
219 \param[out] Map pointer to Map_info structure
220 \param name name of PostGIS feature table to create
221 \param with_z WITH_Z for 3D vector data otherwise WITHOUT_Z
222
223 \return 0 success
224 \return -1 error
225 */
227 const char *name NOPG_UNUSED, int with_z NOPG_UNUSED)
228{
229#ifdef HAVE_POSTGRES
230 char stmt[DB_SQL_MAX];
231
232 struct Format_info_pg *pg_info;
233
234 PGresult *res;
235
236 G_debug(2, "V1_open_new_pg(): name = %s with_z = %d", name, with_z);
237
238 pg_info = &(Map->fInfo.pg);
239 if (!pg_info->conninfo) {
240 G_warning(_("Connection string not defined"));
241 return -1;
242 }
243
244 if (!pg_info->table_name) {
245 G_warning(_("PostGIS feature table not defined"));
246 return -1;
247 }
248
249 G_debug(1, "V1_open_new_pg(): conninfo='%s' table='%s'", pg_info->conninfo,
250 pg_info->table_name);
251
252 /* connect database & get DB name */
253 connect_db(pg_info);
254
255 /* if schema not defined, use 'public' */
256 if (!pg_info->schema_name)
257 pg_info->schema_name = G_store("public");
258
259 /* if fid_column not defined, use 'fid' */
260 if (!pg_info->fid_column)
261 pg_info->fid_column = G_store(GV_PG_FID_COLUMN);
262
263 /* if geom_column not defined, use 'geom' */
264 if (!pg_info->geom_column)
265 pg_info->geom_column = G_store(GV_PG_GEOMETRY_COLUMN);
266
267 /* check if feature table already exists */
268 snprintf(stmt, sizeof(stmt),
269 "SELECT * FROM pg_tables "
270 "WHERE schemaname = '%s' AND tablename = '%s'",
271 pg_info->schema_name, pg_info->table_name);
272 G_debug(2, "SQL: %s", stmt);
273
274 res = PQexec(pg_info->conn, stmt);
276 G_fatal_error("%s\n%s", _("No feature tables found in database."),
278
279 if (PQntuples(res) > 0) {
280 /* table found */
281 if (G_get_overwrite()) {
282 G_warning(_("PostGIS layer <%s.%s> already exists and will be "
283 "overwritten"),
284 pg_info->schema_name, pg_info->table_name);
285 if (drop_table(pg_info) == -1) {
286 G_warning(_("Unable to delete PostGIS layer <%s>"),
287 pg_info->table_name);
288 return -1;
289 }
290 }
291 else {
292 G_warning(
293 _("PostGIS layer <%s.%s> already exists in database '%s'"),
294 pg_info->schema_name, pg_info->table_name, pg_info->db_name);
295 return -1;
296 }
297 }
298
299 /* no feature in cache */
300 pg_info->cache.fid = -1;
301
302 /* unknown feature type */
303 pg_info->feature_type = SF_GEOMETRY;
304
305 PQclear(res);
306
307 return 0;
308#else
309 G_fatal_error(_("GRASS is not compiled with PostgreSQL support"));
310 return -1;
311#endif
312}
313
314/*!
315 \brief Read full-topology for PostGIS links
316
317 Note: Only 2D topological primitives are currently supported
318
319 \param[in,out] Map pointer to Map_info structure
320 \param head_only TRUE to read only header
321 \param update TRUE to clean GRASS topology in update mode
322
323 \return 0 on success
324 \return 1 topology layer does not exist
325 \return -1 on error
326 */
328 int head_only NOPG_UNUSED, int update NOPG_UNUSED)
329{
330#ifdef HAVE_POSTGRES
331 int ret;
332 struct Plus_head *plus;
333 struct Format_info_pg *pg_info;
334
335 Map->open = VECT_OPEN_CODE; /* needed by load_plus */
336
337 plus = &(Map->plus);
338 pg_info = &(Map->fInfo.pg);
339
340 /* check for topo schema */
341 if (check_topo(pg_info, plus) != 0)
342 return 1;
343
344 /* free and init plus structure, update spatial and category
345 * indices */
346 dig_init_plus(plus);
347 plus->Spidx_new = TRUE; /* force building spatial and category indices */
348 plus->update_cidx = TRUE;
349 Map->support_updated = FALSE; /* don't write support files */
350
351 ret = Vect__load_plus_pg(Map, head_only);
352
353 if (update)
355
356 plus->cidx_up_to_date = TRUE; /* category index built from topo */
357
358 return ret;
359#else
360 G_fatal_error(_("GRASS is not compiled with PostgreSQL support"));
361 return -1;
362#endif
363}
364
365#ifdef HAVE_POSTGRES
366/*!
367 \brief Get key column for feature table
368
369 \param pg_info pointer to Format_info_pg
370
371 \return string buffer with key column name
372 \return NULL on missing key column
373 */
374char *get_key_column(struct Format_info_pg *pg_info)
375{
376 char *key_column;
377 char stmt[DB_SQL_MAX];
378
379 PGresult *res;
380
381 snprintf(stmt, sizeof(stmt),
382 "SELECT kcu.column_name "
383 "FROM INFORMATION_SCHEMA.TABLES t "
384 "LEFT JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS tc "
385 "ON tc.table_catalog = t.table_catalog "
386 "AND tc.table_schema = t.table_schema "
387 "AND tc.table_name = t.table_name "
388 "AND tc.constraint_type = 'PRIMARY KEY' "
389 "LEFT JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE kcu "
390 "ON kcu.table_catalog = tc.table_catalog "
391 "AND kcu.table_schema = tc.table_schema "
392 "AND kcu.table_name = tc.table_name "
393 "AND kcu.constraint_name = tc.constraint_name "
394 "WHERE t.table_schema = '%s' AND t.table_name = '%s'",
395 pg_info->schema_name, pg_info->table_name);
396 G_debug(2, "SQL: %s", stmt);
397
398 res = PQexec(pg_info->conn, stmt);
399 if (!res || PQresultStatus(res) != PGRES_TUPLES_OK || PQntuples(res) != 1 ||
400 strlen(PQgetvalue(res, 0, 0)) < 1) {
401 G_warning(_("No key column detected."));
402 if (res)
403 PQclear(res);
404 return NULL;
405 }
407
408 PQclear(res);
409
410 return key_column;
411}
412
413/*!
414 \brief Get simple feature type from string
415
416 \param type string
417
418 \return SF type
419 */
420SF_FeatureType ftype_from_string(const char *type)
421{
422 SF_FeatureType sf_type;
423
424 if (G_strcasecmp(type, "POINT") == 0)
425 return SF_POINT;
426 else if (G_strcasecmp(type, "LINESTRING") == 0)
427 return SF_LINESTRING;
428 else if (G_strcasecmp(type, "POLYGON") == 0)
429 return SF_POLYGON;
430 else if (G_strcasecmp(type, "MULTIPOINT") == 0)
431 return SF_MULTIPOINT;
432 else if (G_strcasecmp(type, "MULTILINESTRING") == 0)
433 return SF_MULTILINESTRING;
434 else if (G_strcasecmp(type, "MULTIPOLYGON") == 0)
435 return SF_MULTIPOLYGON;
436 else if (G_strcasecmp(type, "GEOMETRYCOLLECTION") == 0)
438 else
439 return SF_GEOMETRY;
440
441 G_debug(3, "ftype_from_string(): type='%s' -> %d", type, sf_type);
442
443 return sf_type;
444}
445
446/*!
447 \brief Drop feature table and topology schema if exists
448
449 \param pg_info pointer to Format_info_pg
450
451 \return -1 on error
452 \return 0 on success
453 */
454int drop_table(struct Format_info_pg *pg_info)
455{
456 int i;
457 char stmt[DB_SQL_MAX];
458 char *topo_schema;
459
460 PGresult *result, *result_drop;
461
462 /* check if topology schema exists */
463 snprintf(stmt, sizeof(stmt),
464 "SELECT COUNT(*) FROM pg_tables WHERE schemaname = 'topology'");
465 if (Vect__execute_get_value_pg(pg_info->conn, stmt) != 0) {
466 /* drop topology schema(s) related to the feature table */
467 snprintf(stmt, sizeof(stmt),
468 "SELECT t.name FROM topology.layer AS l JOIN "
469 "topology.topology AS t ON l.topology_id = t.id "
470 "WHERE l.table_name = '%s'",
471 pg_info->table_name);
472 G_debug(2, "SQL: %s", stmt);
473
474 result = PQexec(pg_info->conn, stmt);
475 if (!result || PQresultStatus(result) != PGRES_TUPLES_OK) {
476 G_warning(_("Execution failed: %s"), PQerrorMessage(pg_info->conn));
477 PQclear(result);
478 return -1;
479 }
480 for (i = 0; i < PQntuples(result); i++) {
481 topo_schema = PQgetvalue(result, i, 0);
482 snprintf(stmt, sizeof(stmt), "SELECT topology.DropTopology('%s')",
484 G_debug(2, "SQL: %s", stmt);
485
486 result_drop = PQexec(pg_info->conn, stmt);
488 G_warning(_("Execution failed: %s"),
489 PQerrorMessage(pg_info->conn));
490
491 G_verbose_message(_("PostGIS topology schema <%s> dropped"),
494 }
495 PQclear(result);
496 }
497
498 /* drop feature table */
499 snprintf(stmt, sizeof(stmt), "DROP TABLE \"%s\".\"%s\"",
500 pg_info->schema_name, pg_info->table_name);
501 G_debug(2, "SQL: %s", stmt);
502
503 if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
504 return -1;
505 }
506
507 return 0;
508}
509
510/*!
511 \brief Establish PG connection (pg_info->conninfo)
512
513 Check if DB is spatial as defined by PostGIS.
514
515 \param pg_info pointer to Format_info_pg
516 */
517void connect_db(struct Format_info_pg *pg_info)
518{
519 char stmt[DB_SQL_MAX];
520
521 /* check if connection string already contains user/passwd */
522 if (!strstr(pg_info->conninfo, "user")) {
523 char dbname[GNAME_MAX];
524 char *p;
525 const char *user, *passwd, *host, *port;
526
527 dbname[0] = '\0';
528 p = strstr(pg_info->conninfo, "dbname");
529 if (p) {
530 int i;
531
532 p += strlen("dbname") + 1; /* dbname= */
533
534 for (i = 0; *p && *p != ' '; i++, p++)
535 dbname[i] = *p;
536 }
537
538 /* try connection settings for given database first, then try
539 * any settings defined for pg driver */
540 db_get_login("pg", dbname, &user, &passwd, &host, &port);
541 /* any settings defined for pg driver disabled - can cause
542 problems when running multiple local/remote db clusters
543 if (strlen(dbname) > 0 && !user && !passwd)
544 db_get_login("pg", NULL, &user, &passwd, &host, &port);
545 */
546 if (user || passwd || host || port) {
547 char conninfo[DB_SQL_MAX];
548
549 snprintf(conninfo, sizeof(conninfo), "%s", pg_info->conninfo);
550 if (user) {
551 strcat(conninfo, " user=");
552 strcat(conninfo, user);
553 }
554 if (passwd) {
555 strcat(conninfo, " password=");
557 }
558 if (host) {
559 strcat(conninfo, " host=");
560 strcat(conninfo, host);
561 }
562 if (port) {
563 strcat(conninfo, " port=");
564 strcat(conninfo, port);
565 }
566 G_free(pg_info->conninfo);
567 pg_info->conninfo = G_store(conninfo);
568 }
569 }
570
571 pg_info->conn = PQconnectdb(pg_info->conninfo);
572 G_debug(1, " PQconnectdb(): %s", pg_info->conninfo);
573 if (PQstatus(pg_info->conn) == CONNECTION_BAD)
574 G_fatal_error("%s\n%s",
575 _("Connection to PostgreSQL database failed. "
576 "Try to set up username/password by db.login."),
577 PQerrorMessage(pg_info->conn));
578
579 /* get DB name */
580 pg_info->db_name = G_store(PQdb(pg_info->conn));
581 if (!pg_info->db_name)
582 G_warning(_("Unable to get database name"));
583
584 snprintf(
585 stmt, sizeof(stmt),
586 "SELECT COUNT(*) FROM pg_tables WHERE tablename = 'spatial_ref_sys'");
587 if (Vect__execute_get_value_pg(pg_info->conn, stmt) != 1) {
588 PQfinish(pg_info->conn);
589 G_fatal_error(_("<%s> is not PostGIS database. DB table "
590 "'spatial_ref_sys' not found."),
591 pg_info->db_name ? pg_info->db_name : pg_info->conninfo);
592 }
593
594 if (pg_info->toposchema_name) {
595 /* check if topology schema exists */
596 snprintf(
597 stmt, sizeof(stmt),
598 "SELECT COUNT(*) FROM pg_tables WHERE schemaname = 'topology'");
599 if (Vect__execute_get_value_pg(pg_info->conn, stmt) == 0) {
600 PQfinish(pg_info->conn);
602 _("PostGIS Topology extension not found in the database <%s>"),
603 pg_info->db_name);
604 }
605 }
606
607 /* print notice messages only on verbose level */
608 PQsetNoticeProcessor(pg_info->conn, notice_processor, NULL);
609}
610
611/*!
612 \brief Check for topology schema (pg_info->toposchema_name)
613
614 \param pg_info pointer to Format_info_pg
615
616 \return 0 schema exists
617 \return 1 schema doesn't exists
618 */
619int check_topo(struct Format_info_pg *pg_info, struct Plus_head *plus)
620{
621 char stmt[DB_SQL_MAX];
622
623 PGresult *res;
624
625 /* connect database */
626 if (!pg_info->conn)
627 connect_db(pg_info);
628
629 if (pg_info->toposchema_name)
630 return 0;
631
632 /* check if topology layer/schema exists */
633 snprintf(stmt, sizeof(stmt),
634 "SELECT t.id,t.name,t.hasz,l.feature_column FROM topology.layer "
635 "AS l JOIN topology.topology AS t ON l.topology_id = t.id "
636 "WHERE schema_name = '%s' AND table_name = '%s'",
637 pg_info->schema_name, pg_info->table_name);
638 G_debug(2, "SQL: %s", stmt);
639
640 res = PQexec(pg_info->conn, stmt);
641 if (!res || PQresultStatus(res) != PGRES_TUPLES_OK || PQntuples(res) != 1) {
642 G_debug(1, "Topology layers for '%s.%s' not found (%s)",
643 pg_info->schema_name, pg_info->table_name,
644 PQerrorMessage(pg_info->conn));
645 if (res)
646 PQclear(res);
647 return 1;
648 }
649
650 pg_info->toposchema_id = atoi(PQgetvalue(res, 0, 0));
651 pg_info->toposchema_name = G_store(PQgetvalue(res, 0, 1));
652 pg_info->topogeom_column = G_store(PQgetvalue(res, 0, 3));
653
654 /* check extra GRASS tables */
655 snprintf(stmt, sizeof(stmt),
656 "SELECT COUNT(*) FROM pg_tables WHERE schemaname = '%s' "
657 "AND tablename LIKE '%%_grass'",
658 pg_info->toposchema_name);
660 pg_info->topo_geo_only = TRUE;
661
662 G_debug(
663 1,
664 "PostGIS topology detected: schema = %s column = %s topo_geo_only = %d",
665 pg_info->toposchema_name, pg_info->topogeom_column,
666 pg_info->topo_geo_only);
667
668 /* check for 3D */
669 if (strcmp(PQgetvalue(res, 0, 2), "t") == 0)
670 plus->with_z = WITH_Z;
671 PQclear(res);
672
673 return 0;
674}
675
676/*!
677 \brief Parse BBOX string
678
679 \param value string buffer
680 \param[out] bbox pointer to output bound_box struct
681
682 \return 0 on success
683 \return -1 on error
684 */
685int parse_bbox(const char *value, struct bound_box *bbox)
686{
687 unsigned int i;
688 size_t length, prefix_length;
689 char **tokens, **tokens_coord, *coord;
690
691 if (strlen(value) < 1) {
692 G_warning(_("Empty bounding box"));
693 return -1;
694 }
695
696 prefix_length = strlen("box3d(");
697 if (G_strncasecmp(value, "box3d(", prefix_length) != 0)
698 return -1;
699
700 /* strip off "bbox3d(...)" */
701 length = strlen(value);
702 coord = G_malloc(length - prefix_length);
703 for (i = prefix_length; i < length; i++)
704 coord[i - prefix_length] = value[i];
705 coord[length - prefix_length - 1] = '\0';
706
707 tokens = G_tokenize(coord, ",");
708 G_free(coord);
709
710 if (G_number_of_tokens(tokens) != 2) {
712 return -1;
713 }
714
715 /* parse bbox LL corner */
716 tokens_coord = G_tokenize(tokens[0], " ");
720 }
721 bbox->W = atof(tokens_coord[0]);
722 bbox->S = atof(tokens_coord[1]);
723 bbox->B = atof(tokens_coord[2]);
724
726
727 /* parse bbox UR corner */
728 tokens_coord = G_tokenize(tokens[1], " ");
732 }
733 bbox->E = atof(tokens_coord[0]);
734 bbox->N = atof(tokens_coord[1]);
735 bbox->T = atof(tokens_coord[2]);
736
739
740 return 0;
741}
742
743/*!
744 \brief Read P_node structure
745
746 See dig_Rd_P_node() for reference.
747
748 \param plus pointer to Plus_head structure
749 \param n index (starts at 1)
750 \param id node id (table "node")
751 \param wkb_data geometry data (wkb)
752 \param lines_data lines array or NULL
753 \param angles_data angles array or NULL
754 \param pg_info pointer to Format_info_pg sttucture
755 \param geom_only TRUE to read node's geometry
756
757 \return pointer to new P_node struct
758 \return NULL on error
759 */
760struct P_node *read_p_node(struct Plus_head *plus, int n, int id,
761 const char *wkb_data, const char *lines_data,
762 const char *angles_data,
763 struct Format_info_pg *pg_info, int geom_only)
764{
765 int i, cnt;
766 char **lines, **angles;
767
768 struct P_node *node;
769 struct line_pnts *points;
770
771 PGresult *res;
772
773 /* get lines connected to the node */
774 cnt = 0;
775 lines = angles = NULL;
776 if (!geom_only) {
777 if (!lines_data && !angles_data) { /* pg_info->topo_geo_only == TRUE */
778 char stmt[DB_SQL_MAX];
779
780 snprintf(
781 stmt, sizeof(stmt),
782 "SELECT edge_id,'s' as node,"
783 "ST_Azimuth(ST_StartPoint(geom), ST_PointN(geom, 2)) AS angle"
784 " FROM \"%s\".edge WHERE start_node = %d UNION ALL "
785 "SELECT edge_id,'e' as node,"
786 "ST_Azimuth(ST_EndPoint(geom), ST_PointN(geom, "
787 "ST_NumPoints(geom) - 1)) AS angle"
788 " FROM \"%s\".edge WHERE end_node = %d"
789 " ORDER BY angle DESC",
790 pg_info->toposchema_name, id, pg_info->toposchema_name, id);
791 G_debug(2, "SQL: %s", stmt);
792 res = PQexec(pg_info->conn, stmt);
793 if (!res || PQresultStatus(res) != PGRES_TUPLES_OK) {
794 G_warning(
795 _("Inconsistency in topology: unable to read node %d"), id);
796 if (res)
797 PQclear(res);
798 return NULL;
799 }
800 cnt = PQntuples(res);
801 }
802 else { /* pg_info->topo_geo_only != TRUE */
803 lines = scan_array(lines_data);
804 angles = scan_array(angles_data);
805
806 cnt = G_number_of_tokens(lines);
807 if (cnt != G_number_of_tokens(angles))
808 return NULL; /* 'lines' and 'angles' array must have the same
809 size */
810 }
811
812 if (cnt == 0) { /* dead */
813 plus->Node[n] = NULL;
814 return NULL;
815 }
816 }
817
818 node = dig_alloc_node();
819 node->n_lines = cnt;
820 G_debug(4, "read_p_node(): id = %d, n_lines = %d", id, cnt);
821
822 if (!geom_only) {
823 if (dig_node_alloc_line(node, node->n_lines) == -1)
824 return NULL;
825
826 /* lines / angles */
827 if (lines) {
828 for (i = 0; i < node->n_lines; i++) {
829 node->lines[i] = atoi(lines[i]);
830 node->angles[i] = atof(angles[i]);
831
832 G_debug(5, "\tline = %d angle = %f", node->lines[i],
833 node->angles[i]);
834 }
835
836 G_free_tokens(lines);
837 G_free_tokens(angles);
838 }
839 else {
840 for (i = 0; i < node->n_lines; i++) {
841 node->lines[i] = atoi(PQgetvalue(res, i, 0));
842 if (strcmp(PQgetvalue(res, i, 1), "s") != 0) {
843 /* end node */
844 node->lines[i] *= -1;
845 }
846 node->angles[i] = M_PI / 2 - atof(PQgetvalue(res, i, 2));
847 /* angles range <-PI; PI> */
848 if (node->angles[i] > M_PI)
849 node->angles[i] = node->angles[i] - 2 * M_PI;
850 if (node->angles[i] < -1.0 * M_PI)
851 node->angles[i] = node->angles[i] + 2 * M_PI;
852 G_debug(5, "\tline = %d angle = %f", node->lines[i],
853 node->angles[i]);
854 }
855 PQclear(res);
856 }
857 }
858
859 /* get node coordinates */
860 if (SF_POINT !=
861 Vect__cache_feature_pg(wkb_data, FALSE, FALSE, &(pg_info->cache), NULL))
862 G_warning(_("Inconsistency in topology: node %d - unexpected feature "
863 "type %d"),
864 n, pg_info->cache.sf_type);
865
866 points = pg_info->cache.lines[0];
867 node->x = points->x[0];
868 node->y = points->y[0];
869 if (plus->with_z)
870 node->z = points->z[0];
871 else
872 node->z = 0.0;
873
874 /* update spatial index */
875 if (plus->Spidx_new)
876 dig_spidx_add_node(plus, n, node->x, node->y, node->z);
877
878 if (plus->uplist.do_uplist)
879 /* collect updated nodes if requested */
880 dig_node_add_updated(plus, n);
881
882 plus->Node[n] = node;
883
884 return node;
885}
886
887/*!
888 \brief Read P_line structure
889
890 See dig_Rd_P_line() for reference.
891
892 Supported feature types:
893 - GV_POINT
894 - GV_LINE
895 - GV_BOUNDARY
896
897 \param plus pointer to Plus_head structure
898 \param n index (starts at 1)
899 \param data edge data (id, start/end node, left/right face, ...)
900 \param topo_geo_only TRUE for topo-geo-only mode
901 \param cache pointer to Format_info_cache structure
902
903 \return pointer to P_line struct
904 \return NULL on error
905 */
906struct P_line *read_p_line(struct Plus_head *plus, int n,
907 const struct line_data *data, int topo_geo_only,
908 struct Format_info_cache *cache)
909{
910 int tp, cat;
911 struct P_line *line;
912
913 if (data->start_node == 0 && data->end_node == 0) {
914 if (data->left_face == 0)
915 tp = GV_POINT;
916 else
917 tp = GV_CENTROID;
918 }
919 else if (data->left_face == 0 && data->right_face == 0) {
920 tp = GV_LINE;
921 }
922 else {
923 tp = GV_BOUNDARY;
924 }
925
926 if (tp == 0) { /* dead ??? */
927 plus->Line[n] = NULL;
928 return NULL;
929 }
930
931 line = dig_alloc_line();
932
933 /* type & offset ( = id) */
934 line->type = tp;
935 line->offset = data->id;
936 G_debug(4, "read_p_line(): id/offset = %d type = %d", data->id, line->type);
937
938 /* topo */
939 if (line->type == GV_POINT) {
940 line->topo = NULL;
941 }
942 else {
943 line->topo = dig_alloc_topo(line->type);
944
945 if ((line->type & GV_LINES) &
946 (data->start_node < 0 || data->end_node < 0))
947 return NULL;
948
949 /* lines */
950 if (line->type == GV_LINE) {
951 struct P_topo_l *topo = (struct P_topo_l *)line->topo;
952
953 topo->N1 = data->start_node;
954 topo->N2 = data->end_node;
955 }
956 /* boundaries */
957 else if (line->type == GV_BOUNDARY) {
958 struct P_topo_b *topo = (struct P_topo_b *)line->topo;
959
960 topo->N1 = data->start_node;
961 topo->N2 = data->end_node;
962
963 if (topo_geo_only) {
964 topo->left = topo->right = 0;
965 }
966 else {
967 topo->left = data->left_face;
968 topo->right = data->right_face;
969 }
970 }
971 /* centroids */
972 else if (line->type == GV_CENTROID) {
973 struct P_topo_c *topo = (struct P_topo_c *)line->topo;
974
975 topo->area = data->left_face;
976 }
977 }
978
979 Vect__cache_feature_pg(data->wkb_geom, FALSE, tp, cache, NULL);
980 cat = cache->lines_cats[cache->lines_num - 1] =
981 data->fid > 0 ? data->fid : -1;
982
983 /* update spatial index */
984 if (plus->Spidx_new) {
985 struct line_pnts *points;
986 struct bound_box box;
987
988 points = cache->lines[cache->lines_num - 1];
989 dig_line_box(points, &box);
990 dig_spidx_add_line(plus, n, &box);
991 }
992
993 /* update category index */
994 if (plus->update_cidx)
995 dig_cidx_add_cat(plus, cat > 0 ? 1 : 0, cat > 0 ? cat : 0, n, tp);
996
997 if (plus->uplist.do_uplist) {
998 /* collect updated lines if requested */
999 dig_line_add_updated(plus, n, line->offset);
1000 }
1001
1002 plus->Line[n] = line;
1003
1004 return line;
1005}
1006
1007/*!
1008 \brief Read P_area structure
1009
1010 \param plus pointer to Plus_head structure
1011 \param n index (starts at 1)
1012 \param lines_data lines array (see P_area struct)
1013 \param centroid centroid id (see P_area struct)
1014 \param isles_data lines array (see P_area struct)
1015
1016 \return pointer to P_area struct
1017 \return NULL on error
1018 */
1019struct P_area *read_p_area(struct Plus_head *plus, int n,
1020 const char *lines_data, int centroid,
1021 const char *isles_data)
1022{
1023 int i;
1024 int nlines, nisles;
1025 char **lines, **isles;
1026
1027 struct P_area *area;
1028
1029 lines = scan_array(lines_data);
1030 nlines = G_number_of_tokens(lines);
1031 isles = scan_array(isles_data);
1033
1034 if (nlines < 1) {
1035 G_warning(_("Area %d without boundary detected"), n);
1036 return NULL;
1037 }
1038
1039 G_debug(3, "read_p_area(): n = %d nlines = %d nisles = %d", n, nlines,
1040 nisles);
1041
1042 /* allocate area */
1043 area = dig_alloc_area();
1044 dig_area_alloc_line(area, nlines);
1046
1047 /* set lines */
1048 area->n_lines = nlines;
1049 for (i = 0; i < nlines; i++) {
1050 area->lines[i] = atoi(lines[i]);
1051 }
1052
1053 /* set isles */
1054 area->n_isles = nisles;
1055 for (i = 0; i < nisles; i++) {
1056 area->isles[i] = atoi(isles[i]);
1057 }
1058
1059 /* set centroid */
1060 area->centroid = remap_line(plus, centroid, GV_CENTROID);
1061
1064
1065 plus->Area[n] = area;
1066
1067 return area;
1068}
1069
1070/*!
1071 \brief Read P_isle structure
1072
1073 \param plus pointer to Plus_head structure
1074 \param n index (starts at 1)
1075 \param lines_data lines array (see P_isle struct)
1076 \param area area id (see P_isle struct)
1077
1078 \return pointer to P_isle struct
1079 \return NULL on error
1080 */
1081struct P_isle *read_p_isle(struct Plus_head *plus, int n,
1082 const char *lines_data, int area)
1083{
1084 int i;
1085 int nlines;
1086 char **lines;
1087
1088 struct P_isle *isle;
1089
1090 lines = scan_array(lines_data);
1091 nlines = G_number_of_tokens(lines);
1092
1093 if (nlines < 1) {
1094 G_warning(_("Isle %d without boundary detected"), n);
1095 return NULL;
1096 }
1097
1098 G_debug(3, "read_p_isle(): n = %d nlines = %d", n, nlines);
1099
1100 /* allocate isle */
1101 isle = dig_alloc_isle();
1102 dig_isle_alloc_line(isle, nlines);
1103
1104 /* set lines */
1105 isle->n_lines = nlines;
1106 for (i = 0; i < nlines; i++) {
1107 isle->lines[i] = atoi(lines[i]);
1108 }
1109
1110 /* set area */
1111 isle->area = area;
1112
1114
1115 plus->Isle[n] = isle;
1116
1117 return isle;
1118}
1119
1120/*!
1121 \brief Read topo from PostGIS topology schema -- header info only
1122
1123 \param[in,out] plus pointer to Plus_head struct
1124
1125 \return 0 on success
1126 \return -1 on error
1127 */
1129{
1130 char stmt[DB_SQL_MAX];
1131
1132 struct Format_info_pg *pg_info;
1133 struct Plus_head *plus;
1134
1135 PGresult *res;
1136
1137 plus = &(Map->plus);
1138 pg_info = &(Map->fInfo.pg);
1139
1140 plus->off_t_size = -1;
1141
1142 /* get map bounding box
1143 first try to get info from 'topology.grass' table */
1144 snprintf(stmt, sizeof(stmt), "SELECT %s FROM \"%s\".\"%s\" WHERE %s = %d",
1146 pg_info->toposchema_id);
1147 G_debug(2, "SQL: %s", stmt);
1148 res = PQexec(pg_info->conn, stmt);
1149 if (!res || PQresultStatus(res) != PGRES_TUPLES_OK || PQntuples(res) != 1) {
1150 PQclear(res);
1151
1152 /* otherwise try to calculate bbox from TopoGeometry elements */
1153 snprintf(stmt, sizeof(stmt),
1154 "SELECT ST_3DExtent(%s) FROM \"%s\".\"%s\"",
1155 pg_info->topogeom_column, pg_info->schema_name,
1156 pg_info->table_name);
1157 G_debug(2, "SQL: %s", stmt);
1158 res = PQexec(pg_info->conn, stmt);
1159 if (!res || PQresultStatus(res) != PGRES_TUPLES_OK ||
1160 PQntuples(res) != 1 || strlen(PQgetvalue(res, 0, 0)) < 1) {
1161 G_warning(_("Unable to get map bounding box from topology"));
1162 PQclear(res);
1163 return -1;
1164 }
1165 }
1166
1167 if (parse_bbox(PQgetvalue(res, 0, 0), &(plus->box)) != 0) {
1168 G_warning(_("Unable to parse map bounding box:\n%s"),
1169 PQgetvalue(res, 0, 0));
1170 return -1;
1171 }
1172 PQclear(res);
1173
1174 /* get number of topological elements */
1175
1176 /* nodes
1177 note: isolated nodes are registered in GRASS Topology model */
1178 snprintf(stmt, sizeof(stmt),
1179 "SELECT COUNT(DISTINCT node) FROM (SELECT start_node AS node "
1180 "FROM \"%s\".edge GROUP BY start_node UNION ALL SELECT end_node "
1181 "AS node FROM \"%s\".edge GROUP BY end_node) AS foo",
1182 pg_info->toposchema_name, pg_info->toposchema_name);
1183 plus->n_nodes = Vect__execute_get_value_pg(pg_info->conn, stmt);
1184 if (!pg_info->topo_geo_only) {
1185 int n_nodes;
1186
1187 /* check nodes consistency */
1188 snprintf(stmt, sizeof(stmt), "SELECT COUNT(*) FROM \"%s\".%s",
1189 pg_info->toposchema_name, TOPO_TABLE_NODE);
1191 if (n_nodes != plus->n_nodes) {
1192 G_warning(_("Different number of nodes detected (%d, %d)"),
1193 plus->n_nodes, n_nodes);
1194 return -1;
1195 }
1196 }
1197 G_debug(3, "Vect_open_topo_pg(): n_nodes=%d", plus->n_nodes);
1198
1199 /* lines (edges in PostGIS Topology model) */
1200 snprintf(stmt, sizeof(stmt), "SELECT COUNT(*) FROM \"%s\".edge",
1201 pg_info->toposchema_name);
1202 /* + isolated nodes as points
1203 + centroids */
1204 plus->n_lines = Vect__execute_get_value_pg(pg_info->conn, stmt);
1205
1206 /* areas (faces with face_id > 0 in PostGIS Topology model) */
1207 snprintf(stmt, sizeof(stmt),
1208 "SELECT COUNT(*) FROM \"%s\".face WHERE face_id > 0",
1209 pg_info->toposchema_name);
1210 plus->n_areas = Vect__execute_get_value_pg(pg_info->conn, stmt);
1211 if (!pg_info->topo_geo_only) {
1212 int n_areas;
1213
1214 /* check areas consistency */
1215 snprintf(stmt, sizeof(stmt), "SELECT COUNT(*) FROM \"%s\".%s",
1216 pg_info->toposchema_name, TOPO_TABLE_AREA);
1218 if (n_areas != plus->n_areas) {
1219 G_warning(_("Different number of areas detected (%d, %d)"),
1220 plus->n_areas, n_areas);
1221 return -1;
1222 }
1223 }
1224 G_debug(3, "Vect_open_topo_pg(): n_areas=%d", plus->n_areas);
1225
1226 /* isles (faces with face_id <=0 in PostGIS Topology model)
1227 note: universal face is represented in GRASS Topology model as isle
1228 (area=0)
1229 */
1230 snprintf(stmt, sizeof(stmt),
1231 "SELECT COUNT(*) FROM \"%s\".face WHERE face_id < 0",
1232 pg_info->toposchema_name);
1233 plus->n_isles = Vect__execute_get_value_pg(pg_info->conn, stmt);
1234 if (!pg_info->topo_geo_only) {
1235 int n_isles;
1236
1237 /* check areas consistency */
1238 snprintf(stmt, sizeof(stmt), "SELECT COUNT(*) FROM \"%s\".%s",
1239 pg_info->toposchema_name, TOPO_TABLE_ISLE);
1241 if (n_isles != plus->n_isles) {
1242 G_warning(_("Different number of areas detected (%d, %d)"),
1243 plus->n_isles, n_isles);
1244 return -1;
1245 }
1246 }
1247 G_debug(3, "Vect_open_topo_pg(): n_isles=%d", plus->n_isles);
1248
1249 /* number of features according the type */
1250
1251 /* points */
1252 snprintf(stmt, sizeof(stmt),
1253 "SELECT COUNT(*) FROM \"%s\".node WHERE containing_face "
1254 "IS NULL AND node_id NOT IN "
1255 "(SELECT node FROM (SELECT start_node AS node FROM \"%s\".edge "
1256 "GROUP BY start_node UNION ALL SELECT end_node AS node FROM "
1257 "\"%s\".edge GROUP BY end_node) AS foo)",
1258 pg_info->toposchema_name, pg_info->toposchema_name,
1259 pg_info->toposchema_name);
1260 plus->n_plines = Vect__execute_get_value_pg(pg_info->conn, stmt);
1261 G_debug(3, "Vect_open_topo_pg(): n_plines=%d", plus->n_plines);
1262
1263 /* lines */
1264 snprintf(stmt, sizeof(stmt),
1265 "SELECT COUNT(*) FROM \"%s\".edge WHERE "
1266 "left_face = 0 AND right_face = 0",
1267 pg_info->toposchema_name);
1268 plus->n_llines = Vect__execute_get_value_pg(pg_info->conn, stmt);
1269 G_debug(3, "Vect_open_topo_pg(): n_llines=%d", plus->n_llines);
1270
1271 /* boundaries */
1272 snprintf(stmt, sizeof(stmt),
1273 "SELECT COUNT(*) FROM \"%s\".edge WHERE "
1274 "left_face != 0 OR right_face != 0",
1275 pg_info->toposchema_name);
1276 plus->n_blines = Vect__execute_get_value_pg(pg_info->conn, stmt);
1277 G_debug(3, "Vect_open_topo_pg(): n_blines=%d", plus->n_blines);
1278
1279 /* centroids */
1280 snprintf(stmt, sizeof(stmt),
1281 "SELECT COUNT(*) FROM \"%s\".node WHERE containing_face "
1282 "IS NOT NULL AND node_id NOT IN "
1283 "(SELECT node FROM (SELECT start_node AS node FROM \"%s\".edge "
1284 "GROUP BY start_node UNION ALL SELECT end_node AS node FROM "
1285 "\"%s\".edge GROUP BY end_node) AS foo)",
1286 pg_info->toposchema_name, pg_info->toposchema_name,
1287 pg_info->toposchema_name);
1288 plus->n_clines = Vect__execute_get_value_pg(pg_info->conn, stmt);
1289 G_debug(3, "Vect_open_topo_pg(): n_clines=%d", plus->n_clines);
1290
1291 /* update number of lines - add points and centroids */
1292 plus->n_lines += plus->n_plines + plus->n_clines;
1293 G_debug(3, "Vect_open_topo_pg(): n_lines=%d", plus->n_lines);
1294
1295 return 0;
1296}
1297
1298/*!
1299 \brief Read topo info from PostGIS topology schema
1300
1301 \param pg_info pointer to Format_info_pg
1302 \param[in,out] plus pointer to Plus_head struct
1303 \param head_only TRUE to read only header info
1304
1305 \return 0 on success
1306 \return -1 on error
1307 */
1308int Vect__load_plus_pg(struct Map_info *Map, int head_only)
1309{
1310 int i, side, line;
1311 char stmt[DB_SQL_MAX];
1312
1313 struct Format_info_pg *pg_info;
1314 struct Plus_head *plus;
1315 struct P_line *Line;
1316 struct line_pnts *Points;
1317 struct ilist *List;
1318 struct bound_box box;
1319
1320 PGresult *res;
1321
1322 pg_info = &(Map->fInfo.pg);
1323 plus = &(Map->plus);
1324
1325 if (Vect__load_plus_head(Map) != 0)
1326 return -1;
1327
1328 if (head_only)
1329 return 0;
1330
1331 Points = Vect_new_line_struct();
1332 List = Vect_new_list();
1333
1334 /* read nodes (GRASS Topo)
1335 note: standalone nodes (ie. points/centroids) are ignored
1336 */
1338
1339 /* read lines (GRASS Topo)
1340 - standalone nodes -> points|centroids
1341 - edges -> lines/boundaries
1342 */
1343 Vect__free_cache(&(pg_info->cache));
1344 pg_info->cache.ctype = CACHE_MAP;
1345
1347
1348 /* build areas */
1349 if (pg_info->topo_geo_only) {
1350 /* build areas for boundaries
1351 reset values -> build from scratch */
1352 plus->n_areas = plus->n_isles = 0;
1353 for (line = 1; line <= plus->n_lines; line++) {
1354 Line = plus->Line[line]; /* centroids: Line is NULL */
1355 if (!Line || Line->type != GV_BOUNDARY)
1356 continue;
1357
1358 for (i = 0; i < 2; i++) { /* for both sides build an area/isle */
1359 side = i == 0 ? GV_LEFT : GV_RIGHT;
1360
1361 G_debug(3, "Build area for line = %d, side = %d", i, side);
1363 }
1364 }
1365 }
1366 else {
1367 int cat;
1368
1369 /* read areas from 'area_grass' table */
1370 snprintf(stmt, sizeof(stmt),
1371 "SELECT area_id,lines,centroid,isles FROM \"%s\".%s ORDER BY "
1372 "area_id",
1373 pg_info->toposchema_name, TOPO_TABLE_AREA);
1374 G_debug(2, "SQL: %s", stmt);
1375
1376 res = PQexec(pg_info->conn, stmt);
1377 if (!res || PQresultStatus(res) != PGRES_TUPLES_OK ||
1378 plus->n_areas != PQntuples(res)) {
1379 if (res)
1380 PQclear(res);
1381 return -1;
1382 }
1383
1384 dig_alloc_areas(plus, plus->n_areas);
1385 G_zero(plus->Area, sizeof(struct P_area *) *
1386 (plus->n_areas + 1)); /* index starts at 1 */
1387 G_debug(3, "Vect_open_topo_pg(): n_areas=%d", plus->n_areas);
1388
1389 for (i = 0; i < plus->n_areas; i++) {
1390 read_p_area(plus, i + 1, (char *)PQgetvalue(res, i, 1),
1391 atoi(PQgetvalue(res, i, 2)),
1392 (char *)PQgetvalue(res, i, 3));
1393
1394 if (plus->Spidx_new) {
1395 /* update spatial index */
1396 Vect_get_area_points(Map, i + 1, Points);
1397 dig_line_box(Points, &box);
1398 dig_spidx_add_area(&(Map->plus), i + 1, &box);
1399 }
1400
1401 if (plus->update_cidx) {
1402 /* update category index */
1403 cat =
1404 pg_info->cache.lines_cats[plus->Area[i + 1]->centroid - 1];
1405 dig_cidx_add_cat(plus, cat > 0 ? 1 : 0, cat > 0 ? cat : 0,
1406 i + 1, GV_AREA);
1407 }
1408 }
1409 PQclear(res);
1410 }
1411 plus->built = GV_BUILD_AREAS;
1412
1413 /* attach isles */
1414 if (pg_info->topo_geo_only) {
1415 plus->n_isles = 0; /* reset isles */
1416 G_warning(
1417 _("To be implemented: isles not attached in Topo-Geo-only mode"));
1418 }
1419 else {
1420 /* read isles from 'isle_grass' table */
1421 snprintf(stmt, sizeof(stmt),
1422 "SELECT isle_id,lines,area FROM \"%s\".%s ORDER BY isle_id",
1423 pg_info->toposchema_name, TOPO_TABLE_ISLE);
1424 G_debug(2, "SQL: %s", stmt);
1425
1426 res = PQexec(pg_info->conn, stmt);
1427 if (!res || PQresultStatus(res) != PGRES_TUPLES_OK ||
1428 plus->n_isles != PQntuples(res)) {
1429 if (res)
1430 PQclear(res);
1431 return -1;
1432 }
1433
1434 dig_alloc_isles(plus, plus->n_isles);
1435 G_zero(plus->Isle, sizeof(struct P_isle *) *
1436 (plus->n_isles + 1)); /* index starts at 1 */
1437 G_debug(3, "Vect_open_topo_pg(): n_isles=%d", plus->n_isles);
1438
1439 for (i = 0; i < plus->n_isles; i++) {
1440 read_p_isle(plus, i + 1, (char *)PQgetvalue(res, i, 1),
1441 atoi(PQgetvalue(res, i, 2)));
1442
1443 if (plus->Spidx_new) {
1444 /* update spatial index */
1445 Vect_get_isle_points(Map, i + 1, Points);
1446 dig_line_box(Points, &box);
1447 dig_spidx_add_isle(&(Map->plus), i + 1, &box);
1448 }
1449 }
1450 PQclear(res);
1451 }
1453
1454 /* attach centroids */
1455 if (pg_info->topo_geo_only && plus->n_areas > 0) {
1456 int area;
1457 struct P_area *Area;
1458 struct P_topo_c *topo;
1459
1460 for (line = 1; line <= plus->n_lines; line++) {
1461 Line = plus->Line[line];
1462 if (Line->type != GV_CENTROID)
1463 continue;
1464
1465 Vect_read_line(Map, Points, NULL, line);
1466 area = Vect_find_area(Map, Points->x[0], Points->y[0]);
1467 topo = (struct P_topo_c *)Line->topo;
1468 topo->area = area;
1469 Area = plus->Area[topo->area];
1470 Area->centroid = Line->offset;
1471 }
1472 }
1473 plus->built = GV_BUILD_CENTROIDS;
1474
1475 /* done */
1476 plus->built = GV_BUILD_ALL;
1477
1480
1481 return 0;
1482}
1483
1484/*!
1485 \brief Read nodes from DB
1486
1487 \param Map pointer to Map_info struct
1488 \param geom_only read only node's geometry
1489
1490 \return number of nodes
1491 */
1493{
1494 int i, id, n_nodes;
1495 char stmt[DB_SQL_MAX];
1496 struct Plus_head *plus;
1497 struct Format_info_pg *pg_info;
1498 struct Format_info_offset *offset;
1499
1500 PGresult *res;
1501
1502 plus = &(Map->plus);
1503 pg_info = &(Map->fInfo.pg);
1504 offset = &(pg_info->offset);
1505
1506 if (pg_info->topo_geo_only || geom_only)
1507 snprintf(
1508 stmt, sizeof(stmt),
1509 "SELECT node_id,geom FROM \"%s\".node WHERE node_id IN "
1510 "(SELECT node FROM (SELECT start_node AS node FROM \"%s\".edge "
1511 "GROUP BY start_node UNION ALL SELECT end_node AS node FROM "
1512 "\"%s\".edge GROUP BY end_node) AS foo) ORDER BY node_id",
1513 pg_info->toposchema_name, pg_info->toposchema_name,
1514 pg_info->toposchema_name);
1515 else
1516 snprintf(
1517 stmt, sizeof(stmt),
1518 "SELECT node.node_id,geom,lines,angles FROM \"%s\".node AS node "
1519 "JOIN \"%s\".%s AS node_grass ON node.node_id = node_grass.node_id "
1520 "ORDER BY node_id",
1521 pg_info->toposchema_name, pg_info->toposchema_name,
1523 G_debug(2, "SQL: %s", stmt);
1524 res = PQexec(pg_info->conn, stmt);
1525 if (!res || PQresultStatus(res) != PGRES_TUPLES_OK ||
1526 (!geom_only && PQntuples(res) != plus->n_nodes)) {
1527 G_warning(_("Inconsistency in topology: number of "
1528 "nodes %d (should be %d)"),
1529 PQntuples(res), plus->n_nodes);
1530 if (res)
1531 PQclear(res);
1532 return -1;
1533 }
1534
1535 n_nodes = PQntuples(res);
1536 G_debug(3, "load_plus(): n_nodes = %d", n_nodes);
1537 dig_alloc_nodes(plus, n_nodes);
1538 offset->array = (int *)G_malloc(sizeof(int) * n_nodes);
1539 offset->array_num = offset->array_alloc = n_nodes;
1540 for (i = 0; i < n_nodes; i++) {
1541 G_debug(5, "node: %d", i);
1542 id = atoi(PQgetvalue(res, i, 0));
1543 read_p_node(
1544 plus, i + 1, /* node index starts at 1 */
1545 id, (const char *)PQgetvalue(res, i, 1),
1546 !pg_info->topo_geo_only ? (const char *)PQgetvalue(res, i, 2)
1547 : NULL,
1548 !pg_info->topo_geo_only ? (const char *)PQgetvalue(res, i, 3)
1549 : NULL,
1551 /* update offset */
1552 offset->array[i] = id;
1553 }
1554 PQclear(res);
1555
1556 return n_nodes;
1557}
1558
1559/*!
1560 \brief Read features from DB
1561
1562 \param Map pointer to Map_info struct
1563
1564 \return number of features
1565 */
1567{
1568 int i, id, ntuples;
1569
1570 char stmt[DB_SQL_MAX];
1571
1572 struct Plus_head *plus;
1573 struct Format_info_pg *pg_info;
1574 struct line_data line_data;
1575 struct Format_info_offset *offset;
1576
1577 PGresult *res;
1578
1579 plus = &(Map->plus);
1580 pg_info = &(Map->fInfo.pg);
1581 offset = &(pg_info->offset);
1582
1583 dig_alloc_lines(plus, plus->n_lines);
1584 G_zero(plus->Line, sizeof(struct P_line *) *
1585 (plus->n_lines + 1)); /* index starts at 1 */
1586
1587 /* read PostGIS Topo standalone nodes (containing_face is null)
1588 -> points
1589 */
1590 if (pg_info->topo_geo_only)
1591 snprintf(
1592 stmt, sizeof(stmt),
1593 "SELECT tt.node_id,tt.geom,ft.%s FROM \"%s\".node AS tt "
1594 "LEFT JOIN \"%s\".\"%s\" AS ft ON "
1595 "(%s).type = 1 AND (%s).id = node_id WHERE containing_face "
1596 "IS NULL AND node_id NOT IN "
1597 "(SELECT node FROM (SELECT start_node AS node FROM \"%s\".edge "
1598 "GROUP BY start_node UNION ALL SELECT end_node AS node FROM "
1599 "\"%s\".edge GROUP BY end_node) AS foo) ORDER BY node_id",
1600 pg_info->fid_column, pg_info->toposchema_name, pg_info->schema_name,
1601 pg_info->table_name, pg_info->topogeom_column,
1602 pg_info->topogeom_column, pg_info->toposchema_name,
1603 pg_info->toposchema_name);
1604 else
1605 snprintf(stmt, sizeof(stmt),
1606 "SELECT tt.node_id,tt.geom,ft.%s "
1607 "FROM \"%s\".node AS tt LEFT JOIN \"%s\".\"%s\" AS ft ON "
1608 "(%s).type = 1 AND (%s).id = node_id WHERE node_id NOT IN "
1609 "(SELECT node_id FROM \"%s\".%s) AND containing_face IS NULL "
1610 "ORDER BY node_id",
1611 pg_info->fid_column, pg_info->toposchema_name,
1612 pg_info->schema_name, pg_info->table_name,
1613 pg_info->topogeom_column, pg_info->topogeom_column,
1614 pg_info->toposchema_name, TOPO_TABLE_NODE);
1615 G_debug(2, "SQL: %s", stmt);
1616 res = PQexec(pg_info->conn, stmt);
1617 if (!res || PQresultStatus(res) != PGRES_TUPLES_OK ||
1618 PQntuples(res) > plus->n_plines) {
1619 G_warning(_("Inconsistency in topology: number of "
1620 "points %d (should be %d)"),
1621 PQntuples(res), plus->n_plines);
1622 if (res)
1623 PQclear(res);
1624 return -1;
1625 }
1626
1627 ntuples = PQntuples(res); /* plus->n_plines */
1628 G_zero(&line_data, sizeof(struct line_data));
1629 for (i = 0; i < ntuples; i++) {
1630 /* process standalone nodes (PostGIS Topo) */
1631 line_data.id = atoi(PQgetvalue(res, i, 0));
1632 line_data.wkb_geom = (char *)PQgetvalue(res, i, 1);
1633 line_data.fid = atoi(PQgetvalue(res, i, 2)); /* feature id */
1634
1635 read_p_line(plus, i + 1, &line_data, pg_info->topo_geo_only,
1636 &(pg_info->cache));
1637 }
1638 PQclear(res);
1639
1640 /* read PostGIS Topo edges
1641 -> lines
1642 -> boundaries
1643 */
1644 if (pg_info->topo_geo_only)
1645 snprintf(stmt, sizeof(stmt),
1646 "SELECT edge_id,start_node,end_node,left_face,right_face AS "
1647 "right_area,tt.geom,ft.%s "
1648 "FROM \"%s\".edge AS tt LEFT JOIN \"%s\".\"%s\" AS ft ON "
1649 "(%s).type = 2 AND "
1650 "(%s).id = edge_id ORDER BY edge_id",
1651 pg_info->fid_column, pg_info->toposchema_name,
1652 pg_info->schema_name, pg_info->table_name,
1653 pg_info->topogeom_column, pg_info->topogeom_column);
1654 else
1655 snprintf(
1656 stmt, sizeof(stmt),
1657 "SELECT "
1658 "edge_id,start_node,end_node,left_area,right_area,tt.geom,ft.%s "
1659 "FROM \"%s\".edge AS tt LEFT JOIN \"%s\".\"%s\" ON "
1660 "edge_id = line_id LEFT JOIN \"%s\".\"%s\" AS ft ON (%s).type = 2 "
1661 "AND "
1662 "(%s).id = edge_id ORDER BY edge_id",
1663 pg_info->fid_column, pg_info->toposchema_name,
1664 pg_info->toposchema_name, TOPO_TABLE_LINE, pg_info->schema_name,
1665 pg_info->table_name, pg_info->topogeom_column,
1666 pg_info->topogeom_column);
1667
1668 G_debug(2, "SQL: %s", stmt);
1669 res = PQexec(pg_info->conn, stmt);
1670 if (!res || PQresultStatus(res) != PGRES_TUPLES_OK ||
1671 PQntuples(res) > plus->n_lines) {
1672 G_warning(_("Inconsistency in topology: number of "
1673 "lines %d (should be %d)"),
1674 PQntuples(res), plus->n_lines);
1675 if (res)
1676 PQclear(res);
1677 return -1;
1678 }
1679
1680 /* process edges (PostGIS Topo) */
1681 ntuples = PQntuples(res);
1682 for (i = 0; i < ntuples; i++) {
1683 line_data.id = atoi(PQgetvalue(res, i, 0));
1684 line_data.start_node = remap_node(offset, atoi(PQgetvalue(res, i, 1)));
1685 line_data.end_node = remap_node(offset, atoi(PQgetvalue(res, i, 2)));
1686 line_data.left_face = atoi(PQgetvalue(res, i, 3));
1687 line_data.right_face = atoi(PQgetvalue(res, i, 4));
1688 line_data.wkb_geom = (char *)PQgetvalue(res, i, 5);
1689 line_data.fid = atoi(PQgetvalue(res, i, 6)); /* feature id */
1690
1691 id = plus->n_plines + i + 1; /* points already registered */
1692 read_p_line(plus, id, &line_data, pg_info->topo_geo_only,
1693 &(pg_info->cache));
1694 }
1695 PQclear(res);
1696
1697 /* read PostGIS Topo standalone nodes (containing_face is not null)
1698 -> centroids
1699 */
1700 if (pg_info->topo_geo_only)
1701 snprintf(
1702 stmt, sizeof(stmt),
1703 "SELECT node_id,tt.geom,containing_face,ft.%s FROM "
1704 "\"%s\".node AS tt LEFT JOIN \"%s\".\"%s\" AS ft ON "
1705 "(%s).type = 3 AND (%s).id = containing_face WHERE containing_face "
1706 "IS NOT NULL AND node_id NOT IN "
1707 "(SELECT node FROM (SELECT start_node AS node FROM \"%s\".edge "
1708 "GROUP BY start_node UNION ALL SELECT end_node AS node FROM "
1709 "\"%s\".edge GROUP BY end_node) AS foo) ORDER BY node_id",
1710 pg_info->fid_column, pg_info->toposchema_name, pg_info->schema_name,
1711 pg_info->table_name, pg_info->topogeom_column,
1712 pg_info->topogeom_column, pg_info->toposchema_name,
1713 pg_info->toposchema_name);
1714 else
1715 snprintf(stmt, sizeof(stmt),
1716 "SELECT tt.node_id,tt.geom,containing_face,ft.%s FROM "
1717 "\"%s\".node AS tt LEFT JOIN \"%s\".\"%s\" AS ft ON "
1718 "(%s).type = 3 AND (%s).id = containing_face WHERE "
1719 "node_id NOT IN (SELECT node_id FROM \"%s\".%s) AND "
1720 "containing_face "
1721 "IS NOT NULL ORDER BY node_id",
1722 pg_info->fid_column, pg_info->toposchema_name,
1723 pg_info->schema_name, pg_info->table_name,
1724 pg_info->topogeom_column, pg_info->topogeom_column,
1725 pg_info->toposchema_name, TOPO_TABLE_NODE);
1726 G_debug(2, "SQL: %s", stmt);
1727 res = PQexec(pg_info->conn, stmt);
1728 if (!res || PQresultStatus(res) != PGRES_TUPLES_OK ||
1729 PQntuples(res) != plus->n_clines) {
1730 G_warning(_("Inconsistency in topology: number of "
1731 "centroids %d (should be %d)"),
1732 PQntuples(res), plus->n_clines);
1733 if (res)
1734 PQclear(res);
1735 return -1;
1736 }
1737
1738 G_zero(&line_data, sizeof(struct line_data));
1739 id = plus->n_plines + plus->n_llines + plus->n_blines + 1;
1740 for (i = 0; i < plus->n_clines; i++) {
1741 line_data.id = atoi(PQgetvalue(res, i, 0));
1742 line_data.wkb_geom = (char *)PQgetvalue(res, i, 1);
1743 line_data.left_face = atoi(PQgetvalue(res, i, 2)); /* face id */
1744 line_data.fid = atoi(PQgetvalue(res, i, 3)); /* feature id */
1745 /* area id and face id can be different */
1746
1747 read_p_line(plus, id + i, &line_data, pg_info->topo_geo_only,
1748 &(pg_info->cache));
1749 }
1750 PQclear(res);
1751
1752 plus->built = GV_BUILD_BASE;
1753
1754 return plus->n_lines;
1755}
1756
1757/*
1758 \brief PostgreSQL notice processor
1759
1760 Print out NOTICE message only on verbose level
1761 */
1762void notice_processor(void *arg UNUSED, const char *message)
1763{
1764 if (G_verbose() > G_verbose_std()) {
1765 fprintf(stderr, "%s", message);
1766 }
1767}
1768
1769/*!
1770 \brief Scan string array
1771
1772 Creates tokens based on string array, eg. '{1, 2, 3}' become
1773 [1,2,3].
1774
1775 Allocated tokes should be freed by G_free_tokens().
1776
1777 \param sArray string array
1778
1779 \return tokens
1780 */
1781char **scan_array(const char *sarray)
1782{
1783 char *buf, **tokens;
1784 int i, len;
1785
1786 /* remove '{}' */
1787 len = strlen(sarray) - 1; /* skip '}' */
1788 buf = (char *)G_malloc(len);
1789
1790 for (i = 1; i < len; i++)
1791 buf[i - 1] = sarray[i];
1792 buf[len - 1] = '\0';
1793
1794 tokens = G_tokenize(buf, ",");
1795 G_free(buf);
1796
1797 return tokens;
1798}
1799
1800/*!
1801 \brief Get node id from offset
1802
1803 \param offset pointer to Format_info_offset struct
1804 \param node node to find
1805
1806 \return node id
1807 \return -1 not found
1808 */
1809int remap_node(const struct Format_info_offset *offset, int node)
1810{
1811 /* probably not needed
1812 int i;
1813 for (i = node-1; i < offset->array_num; i++) {
1814 if (offset->array[i] == node)
1815 return i + 1;
1816 }
1817
1818 return -1;
1819 */
1820
1821 return offset->array[node - 1];
1822}
1823
1824/*!
1825 \brief Get line id from offset
1826
1827 \param plus pointer to Plus_head struct
1828 \param offset line offset
1829 \param type line type
1830
1831 \return line id
1832 \return -1 not found
1833 */
1834int remap_line(const struct Plus_head *plus, off_t offset, int type)
1835{
1836 int i;
1837
1838 struct P_line *Line;
1839
1840 for (i = (int)offset; i <= plus->n_lines; i++) {
1841 Line = plus->Line[i];
1842
1843 if (!Line || Line->type != type)
1844 continue;
1845
1846 if ((int)Line->offset == offset)
1847 return i;
1848 }
1849
1850 return -1;
1851}
1852#endif
int Vect__clean_grass_db_topo(struct Format_info_pg *pg_info)
Clean-up GRASS Topology tables.
Definition build_pg.c:1001
#define NULL
Definition ccmath.h:32
AMI_err name(char **stream_name)
Definition ami_stream.h:426
Main header of GRASS DataBase Management Interface.
#define DB_SQL_MAX
Definition dbmi.h:142
int db_get_login(const char *, const char *, const char **, const char **, const char **, const char **)
Get login parameters for driver/database.
Definition login.c:382
void G_zero(void *, int)
Zero out a buffer, buf, of length i.
Definition gis/zero.c:23
void G_free(void *)
Free allocated memory.
Definition gis/alloc.c:147
void void void void G_fatal_error(const char *,...) __attribute__((format(printf
void G_warning(const char *,...) __attribute__((format(printf
#define G_malloc(n)
Definition defs/gis.h:139
void void G_verbose_message(const char *,...) __attribute__((format(printf
int G_get_overwrite(void)
Get overwrite value.
Definition parser.c:959
char ** G_tokenize(const char *, const char *)
Tokenize string.
Definition gis/token.c:47
int G_verbose(void)
Get current verbosity level.
Definition verbose.c:60
void G_free_tokens(char **)
Free memory allocated to tokens.
Definition gis/token.c:197
int G_number_of_tokens(char **)
Return number of tokens.
Definition gis/token.c:178
int int G_strcasecmp(const char *, const char *)
String compare ignoring case (upper or lower)
Definition strings.c:47
char * G_store(const char *)
Copy string to allocated memory.
Definition strings.c:87
int G_strncasecmp(const char *, const char *, int)
String compare ignoring case (upper or lower) - limited number of characters.
Definition strings.c:69
int G_debug(int, const char *,...) __attribute__((format(printf
int G_verbose_std(void)
Get standard verbosity level.
Definition verbose.c:91
void Vect_destroy_line_struct(struct line_pnts *)
Frees all memory associated with a line_pnts structure, including the structure itself.
Definition line.c:77
int Vect_open_fidx(struct Map_info *, struct Format_info_offset *)
Open feature index file.
Definition open_ogr.c:238
int Vect_build_line_area(struct Map_info *, int, int)
Build area on given side of line (GV_LEFT or GV_RIGHT)
Definition build.c:72
int Vect_get_isle_points(struct Map_info *, int, struct line_pnts *)
Returns polygon array of points for given isle.
int Vect_get_area_points(struct Map_info *, int, struct line_pnts *)
Returns polygon array of points (outer ring) of given area.
void Vect_destroy_list(struct ilist *)
Frees all memory associated with a struct ilist, including the struct itself.
int Vect_read_line(struct Map_info *, struct line_pnts *, struct line_cats *, int)
Read vector feature (topological level required)
struct ilist * Vect_new_list(void)
Creates and initializes a struct ilist.
const char * Vect_get_full_name(struct Map_info *)
Get fully qualified name of vector map.
struct line_pnts * Vect_new_line_struct(void)
Creates and initializes a line_pnts structure.
Definition line.c:45
int Vect_find_area(struct Map_info *, double, double)
Find the nearest area.
#define GV_CENTROID
SF_FeatureType
Simple feature types.
@ SF_POLYGON
@ SF_MULTIPOLYGON
@ SF_MULTILINESTRING
@ SF_LINESTRING
@ SF_GEOMETRY
@ SF_GEOMETRYCOLLECTION
@ SF_MULTIPOINT
@ SF_POINT
#define VECT_OPEN_CODE
Vector map open code.
#define GV_LINE
#define GV_POINT
Feature types used in memory on run time (may change)
#define GV_LINES
#define GV_BOUNDARY
#define GV_BUILD_ATTACH_ISLES
Topology levels - attach islands to areas.
#define GV_BUILD_BASE
Topology levels - basic level (without areas and isles)
#define GV_BUILD_ALL
Topology levels - build everything (currently same as GV_BUILD_CENTROIDS)
#define WITH_Z
#define GV_BUILD_AREAS
Topology levels - build areas.
#define GV_BUILD_CENTROIDS
Topology levels - assign centroids to areas.
#define GV_RIGHT
#define GV_PG_GEOMETRY_COLUMN
GRASS-PostGIS data provider - default geometry column.
#define GV_AREA
#define GV_LEFT
Boundary side indicator left/right.
#define GV_PG_FID_COLUMN
GRASS-PostGIS data provider - default fid column.
int dig_spidx_add_isle(struct Plus_head *, int, const struct bound_box *)
Add new island to spatial index.
Definition spindex.c:394
void dig_node_add_updated(struct Plus_head *, int)
Add node to updated.
int dig_alloc_nodes(struct Plus_head *, int)
Reallocate array of pointers to nodes.
int dig_cidx_add_cat(struct Plus_head *, int, int, int, int)
int dig_spidx_add_area(struct Plus_head *, int, const struct bound_box *)
Add new area to spatial index.
Definition spindex.c:360
int dig_alloc_areas(struct Plus_head *, int)
Reallocate array of pointers to areas.
struct P_node * dig_alloc_node(void)
Allocate new node structure.
int dig_init_plus(struct Plus_head *)
Initialize Plus_head structure.
Definition plus.c:30
int dig_alloc_isles(struct Plus_head *, int)
Reallocate array of pointers to isles.
int dig_spidx_add_node(struct Plus_head *, int, double, double, double)
Add new node to spatial index.
Definition spindex.c:291
void * dig_alloc_topo(char)
Allocate new topo struct.
struct P_line * dig_alloc_line(void)
Allocate new line structure.
int dig_area_alloc_isle(struct P_area *, int)
Allocate space in P_area for add new isles.
int dig_node_alloc_line(struct P_node *, int)
Allocate space in P_node struct.
int dig_alloc_lines(struct Plus_head *, int)
Reallocate array of pointers to lines.
void dig_line_add_updated(struct Plus_head *, int, off_t)
Add new line to updated.
struct P_isle * dig_alloc_isle(void)
Allocate new isle structure.
struct P_area * dig_alloc_area(void)
Allocate new area structure.
int dig_isle_alloc_line(struct P_isle *, int)
Allocate space in P_isle for add new lines.
int dig_area_alloc_line(struct P_area *, int)
allocate space in P_area for add new lines
int dig_spidx_add_line(struct Plus_head *, int, const struct bound_box *)
Add new line to spatial index.
Definition spindex.c:326
int dig_line_box(const struct line_pnts *, struct bound_box *)
#define TRUE
Definition gis.h:78
#define FALSE
Definition gis.h:82
#define GNAME_MAX
Definition gis.h:196
#define UNUSED
A macro for an attribute, if attached to a variable, indicating that the variable is not used.
Definition gis.h:46
#define M_PI
Definition gis.h:157
#define _(str)
Definition glocale.h:10
const char * name
Definition named_colr.c:6
int V2_open_old_pg(struct Map_info *Map)
Open vector map - PostGIS feature table on topological level.
Definition open_pg.c:166
int V1_open_new_pg(struct Map_info *Map, const char *name, int with_z)
Prepare PostGIS database for creating new feature table (level 1)
Definition open_pg.c:226
int Vect__load_plus_head(struct Map_info *Map)
Read topo from PostGIS topology schema – header info only.
Definition open_pg.c:1128
#define NOPG_UNUSED
Definition open_pg.c:58
int Vect__load_map_nodes_pg(struct Map_info *Map, int geom_only)
Read nodes from DB.
Definition open_pg.c:1492
int Vect__load_map_lines_pg(struct Map_info *Map)
Read features from DB.
Definition open_pg.c:1566
int Vect__load_plus_pg(struct Map_info *Map, int head_only)
Read topo info from PostGIS topology schema.
Definition open_pg.c:1308
int Vect__open_topo_pg(struct Map_info *Map, int head_only, int update)
Read full-topology for PostGIS links.
Definition open_pg.c:327
int V1_open_old_pg(struct Map_info *Map, int update)
Open vector map - PostGIS feature table on non-topological level.
Definition open_pg.c:73
int Vect__execute_get_value_pg(PGconn *conn, const char *stmt)
Execute SQL statement and get value.
Definition read_pg.c:1599
int Vect__execute_pg(PGconn *conn, const char *stmt)
Execute SQL statement.
Definition read_pg.c:1565
SF_FeatureType Vect__cache_feature_pg(const char *data, int skip_polygon, int force_type, struct Format_info_cache *cache, struct feat_parts *fparts)
Read geometry from HEX data.
Definition read_pg.c:779
Lines cache for reading feature (non-native formats)
struct line_pnts ** lines
Lines array.
int * lines_cats
List of line cats (used only for PostGIS Topology access)
int lines_num
Number of lines which forms current feature.
Data structure used for building pseudo-topology.
int * array
Offset list.
int array_alloc
Space allocated for offset list.
int array_num
Number of items in offset list.
Non-native format info (PostGIS)
char * conninfo
Connection string.
PGresult * res
Vector map info.
Area (topology) info.
plus_t n_isles
Number of islands inside.
plus_t * isles
1st generation interior islands
plus_t n_lines
Number of boundary lines.
plus_t * lines
List of boundary lines.
plus_t centroid
Number of first centroid within area.
Isle (topology) info.
plus_t * lines
List of boundary lines.
plus_t area
Area it exists w/in, if any.
Vector geometry.
char type
Line type.
off_t offset
Offset in coor file for line.
void * topo
Topology info.
Topological feature - node.
double x
X coordinate.
plus_t n_lines
Number of attached lines (size of lines, angle)
float * angles
List of angles of connected lines.
plus_t * lines
List of connected lines.
double z
Z coordinate (used only for 3D data)
double y
Y coordinate.
Boundary topology.
Centroid topology.
plus_t area
Area number, negative for duplicate centroid.
Line topology.
Basic topology-related info.
int with_z
2D/3D vector data
struct P_line ** Line
Array of vector geometries.
plus_t n_lines
Current number of lines.
int Spidx_new
Build new spatial index.
plus_t n_plines
Current number of points.
int off_t_size
Offset size.
plus_t n_nodes
Current number of topological features derived from vector geometries.
plus_t n_blines
Current number of boundaries.
struct P_area ** Area
Array of areas.
plus_t n_clines
Current number of centroids.
int cidx_up_to_date
Category index to be updated.
int update_cidx
Update category index if vector is modified.
plus_t n_isles
Current number of isles.
struct Plus_head::@10 uplist
List of updated lines/nodes.
struct bound_box box
Bounding box of features.
struct P_isle ** Isle
Array of isles.
struct P_node ** Node
Array of nodes.
plus_t n_areas
Current number of areas.
int built
Highest level of topology currently available.
plus_t n_llines
Current number of lines.
Bounding box.
Definition dig_structs.h:62
List of integers.
Definition gis.h:715
Feature geometry info - coordinates.
double * y
Array of Y coordinates.
double * x
Array of X coordinates.
double * z
Array of Z coordinates.
void Vect__free_cache(struct Format_info_cache *cache)