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