GRASS GIS 8 Programmer's Manual  8.5.0dev(2025)-299a28a7d0
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 
28 struct 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 
38 static char *get_key_column(struct Format_info_pg *);
39 static SF_FeatureType ftype_from_string(const char *);
40 static int drop_table(struct Format_info_pg *);
41 static void connect_db(struct Format_info_pg *);
42 static int check_topo(struct Format_info_pg *, struct Plus_head *);
43 static int parse_bbox(const char *, struct bound_box *);
44 static struct P_node *read_p_node(struct Plus_head *, int, int, const char *,
45  const char *, const char *,
46  struct Format_info_pg *, int);
47 static struct P_line *read_p_line(struct Plus_head *, int,
48  const struct line_data *, int,
49  struct Format_info_cache *);
50 static struct P_area *read_p_area(struct Plus_head *, int, const char *, int,
51  const char *);
52 static struct P_isle *read_p_isle(struct Plus_head *, int, const char *, int);
53 static void notice_processor(void *, const char *);
54 static char **scan_array(const char *);
55 static int remap_node(const struct Format_info_offset *, int);
56 static 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  */
73 int V1_open_old_pg(struct Map_info *Map NOPG_UNUSED, int update NOPG_UNUSED)
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);
113  if (!res || PQresultStatus(res) != PGRES_TUPLES_OK)
114  G_fatal_error("%s\n%s", _("No feature tables found in database."),
115  PQresultErrorMessage(res));
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);
186  if (!res || PQresultStatus(res) != PGRES_TUPLES_OK) {
187  G_warning("%s\n%s", _("Topology schema not found."),
188  PQresultErrorMessage(res));
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>"),
201  Vect_get_full_name(Map));
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)
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);
275  if (!res || PQresultStatus(res) != PGRES_TUPLES_OK)
276  G_fatal_error("%s\n%s", _("No feature tables found in database."),
277  PQresultErrorMessage(res));
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)
354  Vect__clean_grass_db_topo(pg_info);
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  */
374 char *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  }
406  key_column = G_store(PQgetvalue(res, 0, 0));
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  */
420 SF_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)
437  return SF_GEOMETRYCOLLECTION;
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  */
454 int 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')",
483  topo_schema);
484  G_debug(2, "SQL: %s", stmt);
485 
486  result_drop = PQexec(pg_info->conn, stmt);
487  if (!result_drop || PQresultStatus(result_drop) != PGRES_TUPLES_OK)
488  G_warning(_("Execution failed: %s"),
489  PQerrorMessage(pg_info->conn));
490 
491  G_verbose_message(_("PostGIS topology schema <%s> dropped"),
492  topo_schema);
493  PQclear(result_drop);
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  */
517 void 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=");
556  strcat(conninfo, passwd);
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  */
619 int 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);
659  if (Vect__execute_get_value_pg(pg_info->conn, stmt) != TOPO_TABLE_NUM)
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  */
685 int 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) {
711  G_free_tokens(tokens);
712  return -1;
713  }
714 
715  /* parse bbox LL corner */
716  tokens_coord = G_tokenize(tokens[0], " ");
717  if (G_number_of_tokens(tokens_coord) != 3) {
718  G_free_tokens(tokens);
719  G_free_tokens(tokens_coord);
720  }
721  bbox->W = atof(tokens_coord[0]);
722  bbox->S = atof(tokens_coord[1]);
723  bbox->B = atof(tokens_coord[2]);
724 
725  G_free_tokens(tokens_coord);
726 
727  /* parse bbox UR corner */
728  tokens_coord = G_tokenize(tokens[1], " ");
729  if (G_number_of_tokens(tokens_coord) != 3) {
730  G_free_tokens(tokens);
731  G_free_tokens(tokens_coord);
732  }
733  bbox->E = atof(tokens_coord[0]);
734  bbox->N = atof(tokens_coord[1]);
735  bbox->T = atof(tokens_coord[2]);
736 
737  G_free_tokens(tokens_coord);
738  G_free_tokens(tokens);
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  */
760 struct 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  */
906 struct 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  */
1019 struct 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);
1032  nisles = G_number_of_tokens(isles);
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);
1045  dig_area_alloc_isle(area, nisles);
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  */
1081 struct 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",
1145  TOPO_BBOX, TOPO_SCHEMA, TOPO_TABLE, TOPO_ID,
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);
1190  n_nodes = Vect__execute_get_value_pg(pg_info->conn, stmt);
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);
1217  n_areas = Vect__execute_get_value_pg(pg_info->conn, stmt);
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);
1240  n_isles = Vect__execute_get_value_pg(pg_info->conn, stmt);
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  */
1308 int 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);
1362  Vect_build_line_area(Map, line, 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  }
1452  plus->built = GV_BUILD_ATTACH_ISLES;
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 
1478  Vect_destroy_line_struct(Points);
1479  Vect_destroy_list(List);
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  */
1492 int Vect__load_map_nodes_pg(struct Map_info *Map, int geom_only)
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,
1522  TOPO_TABLE_NODE);
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,
1550  pg_info, geom_only);
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  */
1762 void 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  */
1781 char **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  */
1809 int 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  */
1834 int 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
#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:150
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:94
void void G_verbose_message(const char *,...) __attribute__((format(printf
int G_get_overwrite(void)
Get overwrite value.
Definition: parser.c:960
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:198
int G_number_of_tokens(char **)
Return number of tokens.
Definition: gis/token.c:179
int int G_strcasecmp(const char *, const char *)
String compare ignoring case (upper or lower)
Definition: strings.c:47
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
char * G_store(const char *)
Copy string to allocated memory.
Definition: strings.c:87
char ** G_tokenize(const char *, const char *)
Tokenize string.
Definition: gis/token.c:47
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:256
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:79
const char * Vect_get_full_name(struct Map_info *)
Get fully qualified name of vector map.
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.
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
Definition: dig_defines.h:186
SF_FeatureType
Simple feature types.
Definition: dig_defines.h:239
@ SF_POLYGON
Definition: dig_defines.h:244
@ SF_MULTIPOLYGON
Definition: dig_defines.h:249
@ SF_MULTILINESTRING
Definition: dig_defines.h:248
@ SF_LINESTRING
Definition: dig_defines.h:242
@ SF_GEOMETRY
Definition: dig_defines.h:240
@ SF_GEOMETRYCOLLECTION
Definition: dig_defines.h:250
@ SF_MULTIPOINT
Definition: dig_defines.h:247
@ SF_POINT
Definition: dig_defines.h:241
#define VECT_OPEN_CODE
Vector map open code.
Definition: dig_defines.h:111
#define GV_LINE
Definition: dig_defines.h:184
#define GV_POINT
Feature types used in memory on run time (may change)
Definition: dig_defines.h:183
#define GV_LINES
Definition: dig_defines.h:193
#define GV_BOUNDARY
Definition: dig_defines.h:185
#define GV_BUILD_ATTACH_ISLES
Topology levels - attach islands to areas.
Definition: dig_defines.h:129
#define GV_BUILD_BASE
Topology levels - basic level (without areas and isles)
Definition: dig_defines.h:125
#define GV_BUILD_ALL
Topology levels - build everything (currently same as GV_BUILD_CENTROIDS)
Definition: dig_defines.h:134
#define WITH_Z
Definition: dig_defines.h:172
#define GV_BUILD_AREAS
Topology levels - build areas.
Definition: dig_defines.h:127
#define GV_BUILD_CENTROIDS
Topology levels - assign centroids to areas.
Definition: dig_defines.h:131
#define GV_RIGHT
Definition: dig_defines.h:176
#define GV_PG_GEOMETRY_COLUMN
GRASS-PostGIS data provider - default geometry column.
Definition: dig_defines.h:273
#define GV_AREA
Definition: dig_defines.h:189
#define GV_LEFT
Boundary side indicator left/right.
Definition: dig_defines.h:175
#define GV_PG_FID_COLUMN
GRASS-PostGIS data provider - default fid column.
Definition: dig_defines.h:271
int dig_spidx_add_isle(struct Plus_head *, int, const struct bound_box *)
Add new island to spatial index.
Definition: spindex.c:395
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.
Definition: struct_alloc.c:105
struct P_isle * dig_alloc_isle(void)
Allocate new isle structure.
Definition: struct_alloc.c:301
int dig_cidx_add_cat(struct Plus_head *, int, int, int, int)
Definition: diglib/cindex.c:72
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.
Definition: struct_alloc.c:218
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.
Definition: struct_alloc.c:243
int dig_spidx_add_node(struct Plus_head *, int, double, double, double)
Add new node to spatial index.
Definition: spindex.c:291
int dig_area_alloc_isle(struct P_area *, int)
Allocate space in P_area for add new isles.
Definition: struct_alloc.c:440
int dig_node_alloc_line(struct P_node *, int)
Allocate space in P_node struct.
Definition: struct_alloc.c:69
void * dig_alloc_topo(char)
Allocate new topo struct.
Definition: struct_alloc.c:145
int dig_alloc_lines(struct Plus_head *, int)
Reallocate array of pointers to lines.
Definition: struct_alloc.c:193
struct P_area * dig_alloc_area(void)
Allocate new area structure.
Definition: struct_alloc.c:266
void dig_line_add_updated(struct Plus_head *, int, off_t)
Add new line to updated.
struct P_line * dig_alloc_line(void)
Allocate new line structure.
Definition: struct_alloc.c:127
int dig_isle_alloc_line(struct P_isle *, int)
Allocate space in P_isle for add new lines.
Definition: struct_alloc.c:466
int dig_area_alloc_line(struct P_area *, int)
allocate space in P_area for add new lines
Definition: struct_alloc.c:414
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 *)
struct P_node * dig_alloc_node(void)
Allocate new node structure.
Definition: struct_alloc.c:30
#define TRUE
Definition: gis.h:78
#define FALSE
Definition: gis.h:82
#define GNAME_MAX
Definition: gis.h:190
#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 NOPG_UNUSED)
Open vector map - PostGIS feature table on topological level.
Definition: open_pg.c:166
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__open_topo_pg(struct Map_info *Map NOPG_UNUSED, int head_only NOPG_UNUSED, int update NOPG_UNUSED)
Read full-topology for PostGIS links.
Definition: open_pg.c:327
void notice_processor(void *arg UNUSED, const char *message)
Definition: open_pg.c:1762
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 V1_open_old_pg(struct Map_info *Map NOPG_UNUSED, int update NOPG_UNUSED)
Open vector map - PostGIS feature table on non-topological level.
Definition: open_pg.c:73
int V1_open_new_pg(struct Map_info *Map NOPG_UNUSED, const char *name NOPG_UNUSED, int with_z NOPG_UNUSED)
Prepare PostGIS database for creating new feature table (level 1)
Definition: open_pg.c:226
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
if(!(yy_init))
Definition: sqlp.yy.c:775
Lines cache for reading feature (non-native formats)
Definition: dig_structs.h:450
int ctype
Cache type.
Definition: dig_structs.h:496
SF_FeatureType sf_type
Simple feature type (currently used only by PG format)
Definition: dig_structs.h:490
long fid
Feature id.
Definition: dig_structs.h:486
struct line_pnts ** lines
Lines array.
Definition: dig_structs.h:462
int * lines_cats
List of line cats (used only for PostGIS Topology access)
Definition: dig_structs.h:470
int lines_num
Number of lines which forms current feature.
Definition: dig_structs.h:478
Data structure used for building pseudo-topology.
Definition: dig_structs.h:388
int * array
Offset list.
Definition: dig_structs.h:436
int array_alloc
Space allocated for offset list.
Definition: dig_structs.h:444
int array_num
Number of items in offset list.
Definition: dig_structs.h:440
Non-native format info (PostGIS)
Definition: dig_structs.h:590
char * db_name
Database name (derived from conninfo)
Definition: dig_structs.h:598
char * fid_column
FID column.
Definition: dig_structs.h:614
char * schema_name
Schema name.
Definition: dig_structs.h:602
PGconn * conn
PGconn object (generated by PQconnectdb)
Definition: dig_structs.h:650
char * toposchema_name
Topology schema name and id.
Definition: dig_structs.h:686
int coor_dim
Coordinates dimension (2D or 3D)
Definition: dig_structs.h:626
char * conninfo
Connection string.
Definition: dig_structs.h:594
PGresult * res
Definition: dig_structs.h:651
struct Format_info_cache cache
Lines cache for reading feature.
Definition: dig_structs.h:670
char * topogeom_column
TopoGeometry column (feature table)
Definition: dig_structs.h:682
int srid
Spatial reference system id (see spatial_ref_sys table)
Definition: dig_structs.h:631
struct Format_info_offset offset
Offset list used for building pseudo-topology (simple features access)
Definition: dig_structs.h:676
char * geom_column
Geometry column (simple feature access)
Definition: dig_structs.h:618
int topo_geo_only
Topology format.
Definition: dig_structs.h:694
char * table_name
Table name.
Definition: dig_structs.h:606
SF_FeatureType feature_type
Feature type (simple feature access)
Definition: dig_structs.h:622
struct Format_info_pg pg
PostGIS info.
Definition: dig_structs.h:712
Vector map info.
Definition: dig_structs.h:1243
struct Format_info fInfo
Format info for non-native formats.
Definition: dig_structs.h:1400
struct Plus_head plus
Plus info (topology, version, ...)
Definition: dig_structs.h:1270
Area (topology) info.
Definition: dig_structs.h:1583
plus_t n_isles
Number of islands inside.
Definition: dig_structs.h:1609
plus_t * isles
1st generation interior islands
Definition: dig_structs.h:1617
plus_t n_lines
Number of boundary lines.
Definition: dig_structs.h:1587
plus_t * lines
List of boundary lines.
Definition: dig_structs.h:1598
plus_t centroid
Number of first centroid within area.
Definition: dig_structs.h:1605
Isle (topology) info.
Definition: dig_structs.h:1623
plus_t * lines
List of boundary lines.
Definition: dig_structs.h:1638
plus_t n_lines
Number of boundary lines.
Definition: dig_structs.h:1627
plus_t area
Area it exists w/in, if any.
Definition: dig_structs.h:1645
Vector geometry.
Definition: dig_structs.h:1553
char type
Line type.
Definition: dig_structs.h:1564
off_t offset
Offset in coor file for line.
Definition: dig_structs.h:1571
void * topo
Topology info.
Definition: dig_structs.h:1577
Topological feature - node.
Definition: dig_structs.h:1433
double x
X coordinate.
Definition: dig_structs.h:1437
plus_t n_lines
Number of attached lines (size of lines, angle)
Definition: dig_structs.h:1456
float * angles
List of angles of connected lines.
Definition: dig_structs.h:1472
plus_t * lines
List of connected lines.
Definition: dig_structs.h:1463
double z
Z coordinate (used only for 3D data)
Definition: dig_structs.h:1445
double y
Y coordinate.
Definition: dig_structs.h:1441
Boundary topology.
Definition: dig_structs.h:1492
plus_t left
Area number to the left, negative for isle.
Definition: dig_structs.h:1504
plus_t N1
Start node.
Definition: dig_structs.h:1496
plus_t N2
End node.
Definition: dig_structs.h:1500
plus_t right
Area number to the right, negative for isle.
Definition: dig_structs.h:1508
Centroid topology.
Definition: dig_structs.h:1514
plus_t area
Area number, negative for duplicate centroid.
Definition: dig_structs.h:1518
Line topology.
Definition: dig_structs.h:1478
plus_t N1
Start node.
Definition: dig_structs.h:1482
plus_t N2
End node.
Definition: dig_structs.h:1486
Basic topology-related info.
Definition: dig_structs.h:769
int do_uplist
Indicates if the list of updated features is maintained.
Definition: dig_structs.h:1161
int with_z
2D/3D vector data
Definition: dig_structs.h:786
struct P_line ** Line
Array of vector geometries.
Definition: dig_structs.h:871
plus_t n_lines
Current number of lines.
Definition: dig_structs.h:931
int Spidx_new
Build new spatial index.
Definition: dig_structs.h:1043
plus_t n_plines
Current number of points.
Definition: dig_structs.h:886
int off_t_size
Offset size.
Definition: dig_structs.h:801
plus_t n_nodes
Current number of topological features derived from vector geometries.
Definition: dig_structs.h:923
plus_t n_blines
Current number of boundaries.
Definition: dig_structs.h:894
struct P_area ** Area
Array of areas.
Definition: dig_structs.h:875
plus_t n_clines
Current number of centroids.
Definition: dig_structs.h:898
int cidx_up_to_date
Category index to be updated.
Definition: dig_structs.h:1140
int update_cidx
Update category index if vector is modified.
Definition: dig_structs.h:1120
plus_t n_isles
Current number of isles.
Definition: dig_structs.h:939
struct Plus_head::@10 uplist
List of updated lines/nodes.
struct bound_box box
Bounding box of features.
Definition: dig_structs.h:861
struct P_isle ** Isle
Array of isles.
Definition: dig_structs.h:879
struct P_node ** Node
Array of nodes.
Definition: dig_structs.h:867
plus_t n_areas
Current number of areas.
Definition: dig_structs.h:935
int built
Highest level of topology currently available.
Definition: dig_structs.h:857
plus_t n_llines
Current number of lines.
Definition: dig_structs.h:890
Bounding box.
Definition: dig_structs.h:64
double W
West.
Definition: dig_structs.h:80
double T
Top.
Definition: dig_structs.h:84
double S
South.
Definition: dig_structs.h:72
double N
North.
Definition: dig_structs.h:68
double E
East.
Definition: dig_structs.h:76
double B
Bottom.
Definition: dig_structs.h:88
List of integers.
Definition: gis.h:709
Feature geometry info - coordinates.
Definition: dig_structs.h:1651
double * y
Array of Y coordinates.
Definition: dig_structs.h:1659
double * x
Array of X coordinates.
Definition: dig_structs.h:1655
double * z
Array of Z coordinates.
Definition: dig_structs.h:1663
void Vect__free_cache(struct Format_info_cache *cache)