1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027 |
- /*!
- \file lib/vector/Vlib/build_pg.c
- \brief Vector library - Building topology for PostGIS layers
- Higher level functions for reading/writing/manipulating vectors.
- Line offset (simple features only) is
- - centroids : FID
- - other types : index of the first record (which is FID) in offset array.
- (C) 2012-2013 by the GRASS Development Team
- This program is free software under the GNU General Public License
- (>=v2). Read the file COPYING that comes with GRASS for details.
- \author Martin Landa <landa.martin gmail.com>
- */
- #include <grass/vector.h>
- #include <grass/glocale.h>
- #include "local_proto.h"
- #ifdef HAVE_POSTGRES
- #include "pg_local_proto.h"
- static int build_topo(struct Map_info *, int);
- static int build_topogeom_stmt(const struct Format_info_pg *, int, int, int, char *);
- static int save_map_bbox(const struct Format_info_pg *, const struct bound_box*);
- static int create_topo_grass(const struct Format_info_pg *);
- static int has_topo_grass(const struct Format_info_pg *);
- static int write_nodes(const struct Plus_head *, const struct Format_info_pg *);
- static int write_lines(const struct Plus_head *, const struct Format_info_pg *);
- static int write_areas(const struct Plus_head *, const struct Format_info_pg *);
- static int write_isles(const struct Plus_head *, const struct Format_info_pg *);
- static void build_stmt_id(const void *, int, int, const struct Plus_head *, char **, size_t *);
- static int create_simple_feature_from_topo(struct Map_info *);
- #endif
- /*!
- \brief Build topology for PostGIS layer
- Build levels:
- - GV_BUILD_NONE
- - GV_BUILD_BASE
- - GV_BUILD_ATTACH_ISLES
- - GV_BUILD_CENTROIDS
- - GV_BUILD_ALL
- \param Map pointer to Map_info structure
- \param build build level
- \return 1 on success
- \return 0 on error
- */
- int Vect_build_pg(struct Map_info *Map, int build)
- {
- #ifdef HAVE_POSTGRES
- struct Plus_head *plus;
- struct Format_info_pg *pg_info;
- plus = &(Map->plus);
- pg_info = &(Map->fInfo.pg);
- G_debug(1, "Vect_build_pg(): db='%s' table='%s', build=%d",
- pg_info->db_name, pg_info->table_name, build);
- /* commit transaction block (update mode only) */
- if (pg_info->inTransaction && Vect__execute_pg(pg_info->conn, "COMMIT") == -1)
- return 0;
- pg_info->inTransaction = FALSE;
-
- if (pg_info->feature_type == SF_GEOMETRY)
- return 1;
-
- if (build == plus->built)
- return 1; /* do nothing */
- /* TODO move this init to better place (Vect_open_ ?), because in
- theory build may be reused on level2 */
- if (!pg_info->toposchema_name && build >= plus->built && build > GV_BUILD_BASE) {
- G_free(pg_info->offset.array);
- G_zero(&(pg_info->offset), sizeof(struct Format_info_offset));
- }
- if (!pg_info->conn) {
- G_warning(_("No DB connection"));
- return 0;
- }
- if (!pg_info->fid_column && !pg_info->toposchema_name) {
- G_warning(_("Feature table <%s> has no primary key defined"),
- pg_info->table_name);
- G_warning(_("Random read is not supported for this layer. "
- "Unable to build topology."));
- return 0;
- }
- if (build > GV_BUILD_NONE) {
- G_message(_("Using external data format '%s' (feature type '%s')"),
- Vect_get_finfo_format_info(Map),
- Vect_get_finfo_geometry_type(Map));
- if (!pg_info->toposchema_name)
- G_message(_("Building pseudo-topology over simple features..."));
- else
- G_message(_("Building topology from PostGIS topology schema <%s>..."),
- pg_info->toposchema_name);
- }
- if (!pg_info->toposchema_name) /* pseudo-topology for simple features */
- return Vect__build_sfa(Map, build);
-
- /* PostGIS Topology */
- return build_topo(Map, build);
- #else
- G_fatal_error(_("GRASS is not compiled with PostgreSQL support"));
- return 0;
- #endif
- }
- #ifdef HAVE_POSTGRES
- /*!
- \brief Build from PostGIS topology schema
- \todo Attach isles
- \param Map pointer to Map_info struct
- \param build build level
- \return 1 on success
- \return 0 on error
- */
- int build_topo(struct Map_info *Map, int build)
- {
- int line, type, s, n_nodes;
- int area, nareas, isle, nisles;
- int face[2];
- char stmt[DB_SQL_MAX];
- char *def_file;
-
- struct Plus_head *plus;
- struct Format_info_pg *pg_info;
- struct P_line *Line;
- struct P_area *Area;
- struct P_topo_b *topo_b;
- struct P_isle *Isle;
-
- plus = &(Map->plus);
- pg_info = &(Map->fInfo.pg);
-
- /* check if upgrade or downgrade */
- if (build < plus->built) {
- /* -> downgrade */
- Vect__build_downgrade(Map, build);
- return 1;
- }
- /* -> upgrade */
-
- if (build < GV_BUILD_BASE)
- return 1; /* nothing to print */
-
- /* cache features to speed-up random access (when attaching isles
- to areas) */
- if (build >= GV_BUILD_BASE) {
- /* clean-up GRASS topology tables in DB */
- if (!pg_info->topo_geo_only)
- Vect__clean_grass_db_topo(&(Map->fInfo.pg));
-
- if (Map->mode == GV_MODE_RW &&
- pg_info->cache.lines_num > 0) {
- /* read line cache from scratch when map is open in update
- * mode, before building native topology read nodes from
- * PostGIS Topology */
- /* clean-up spatial a category indices */
- dig_free_plus(&(Map->plus));
- dig_init_plus(&(Map->plus));
- plus->Spidx_new = TRUE;
- plus->update_cidx = TRUE;
- /* reset cache for reading features */
- Vect__free_cache(&(pg_info->cache));
- }
- }
- if (plus->built >= GV_BUILD_BASE &&
- pg_info->cache.lines_num < 1) {
- /* features are not cached, build from scratch */
- Vect_build_partial(Map, GV_BUILD_NONE);
- }
- if (plus->built < GV_BUILD_BASE) {
- /* force loading nodes from DB to get up-to-date node
- * offsets, see write_nodes() for details */
- Vect__free_offset(&(pg_info->offset));
- pg_info->cache.ctype = CACHE_FEATURE; /* do not cache nodes */
- n_nodes = Map->plus.n_nodes = Vect__load_map_nodes_pg(Map, TRUE);
- Vect__free_cache(&(pg_info->cache));
- }
- if (build > GV_BUILD_BASE)
- pg_info->cache.ctype = CACHE_MAP; /* cache all features */
- /* update TopoGeometry based on GRASS-like topology */
- Vect_build_nat(Map, build);
-
- if (n_nodes != Map->plus.n_nodes)
- G_warning(_("Inconsistency in topology: number of nodes %d (should be %d)"),
- Map->plus.n_nodes, n_nodes);
- /* store map boundig box in DB */
- save_map_bbox(pg_info, &(plus->box));
-
- /* begin transaction */
- if (Vect__execute_pg(pg_info->conn, "BEGIN"))
- return 0;
-
- Vect__execute_pg(pg_info->conn, "SET CONSTRAINTS ALL DEFERRED");
-
- /* write full node topo info to DB if requested */
- if (!pg_info->topo_geo_only) {
- write_nodes(plus, pg_info);
- write_lines(plus, pg_info);
- }
- /* update faces from GRASS Topology */
- if (build >= GV_BUILD_AREAS) {
- /* do clean up (1-3)
- insert new faces (4)
- update edges (5)
- */
- G_message(_("Cleaning-up topology schema..."));
- /* 1) reset centroids to '0' (universal face) */
- sprintf(stmt, "UPDATE \"%s\".node SET containing_face = 0 WHERE "
- "containing_face IS NOT NULL", pg_info->toposchema_name);
- G_debug(2, "SQL: %s", stmt);
- if(Vect__execute_pg(pg_info->conn, stmt) == -1) {
- Vect__execute_pg(pg_info->conn, "ROLLBACK");
- return 0;
- }
- /* 2) reset left|right edges */
- sprintf(stmt, "UPDATE \"%s\".edge_data SET left_face = 0, right_face = 0",
- pg_info->toposchema_name);
- G_debug(2, "SQL: %s", stmt);
- if(Vect__execute_pg(pg_info->conn, stmt) == -1) {
- Vect__execute_pg(pg_info->conn, "ROLLBACK");
- return 0;
- }
- /* 3) delete faces (areas/isles) */
- sprintf(stmt, "DELETE FROM \"%s\".face WHERE "
- "face_id != 0", pg_info->toposchema_name);
- G_debug(2, "SQL: %s", stmt);
- if(Vect__execute_pg(pg_info->conn, stmt) == -1) {
- Vect__execute_pg(pg_info->conn, "ROLLBACK");
- return 0;
- }
- if (!pg_info->topo_geo_only) {
- sprintf(stmt, "DELETE FROM \"%s\".%s", pg_info->toposchema_name, TOPO_TABLE_AREA);
- G_debug(2, "SQL: %s", stmt);
- if(Vect__execute_pg(pg_info->conn, stmt) == -1) {
- Vect__execute_pg(pg_info->conn, "ROLLBACK");
- return 0;
- }
-
- sprintf(stmt, "DELETE FROM \"%s\".%s", pg_info->toposchema_name, TOPO_TABLE_ISLE);
- G_debug(2, "SQL: %s", stmt);
- if(Vect__execute_pg(pg_info->conn, stmt) == -1) {
- Vect__execute_pg(pg_info->conn, "ROLLBACK");
- return 0;
- }
- }
- /* 4) insert faces & update nodes (containing_face) based on
- * GRASS topology */
- G_message(_("Updating faces..."));
- nareas = Vect_get_num_areas(Map);
- for (area = 1; area <= nareas; area++) {
- G_percent(area, nareas, 5);
- if (0 == Vect__insert_face_pg(Map, area)) {
- Vect__execute_pg(pg_info->conn, "ROLLBACK");
- return 0;
- }
-
- if (build < GV_BUILD_CENTROIDS)
- continue;
-
- /* update centroids (node -> containing_face) */
- Area = plus->Area[area];
- if (Area->centroid < 1) {
- G_debug(3, "Area %d without centroid, skipped", area);
- continue;
- }
-
- Line = plus->Line[Area->centroid];
- sprintf(stmt, "UPDATE \"%s\".node SET "
- "containing_face = %d WHERE node_id = %d",
- pg_info->toposchema_name, area, (int)Line->offset);
- G_debug(2, "SQL: %s", stmt);
-
- if(Vect__execute_pg(pg_info->conn, stmt) == -1) {
- Vect__execute_pg(pg_info->conn, "ROLLBACK");
- return 0;
- }
- }
- /* 5) update edges (left and right face) */
- G_message(_("Updating edges..."));
- for (line = 1; line <= plus->n_lines; line++) {
- G_percent(line, plus->n_lines, 5);
- type = Vect_read_line(Map, NULL, NULL, line);
- if (type != GV_BOUNDARY)
- continue;
-
- Line = Map->plus.Line[line];
- if (!Line) {
- G_warning(_("Inconsistency in topology detected. "
- "Dead line found."));
- return 0;
- }
-
- topo_b = (struct P_topo_b *) Line->topo;
-
- for (s = 0; s < 2; s++) { /* for both sides */
- face[s] = s == 0 ? topo_b->left : topo_b->right;
- if (face[s] < 0) {
- /* isle */
- Isle = plus->Isle[abs(face[s])];
- face[s] = Isle->area;
- }
- }
- G_debug(3, "update edge %d: left_face = %d, right_face = %d",
- (int)Line->offset, face[0], face[1]);
-
- sprintf(stmt, "UPDATE \"%s\".edge_data SET "
- "left_face = %d, right_face = %d "
- "WHERE edge_id = %d", pg_info->toposchema_name,
- face[0], face[1], (int) Line->offset);
-
- if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
- Vect__execute_pg(pg_info->conn, "ROLLBACK");
- return 0;
- }
- }
- /* write full area topo info to DB if requested */
- if (!pg_info->topo_geo_only) {
- write_areas(plus, pg_info);
- }
- } /* build >= GV_BUILD_AREAS */
-
- if (build >= GV_BUILD_ATTACH_ISLES) {
- /* insert isles as faces with negative face_id */
- nisles = Vect_get_num_islands(Map);
- for(isle = 1; isle <= nisles; isle++) {
- Isle = plus->Isle[isle];
- Vect__insert_face_pg(Map, -isle);
- }
- /* write full isles topo info to DB if requested */
- if (!pg_info->topo_geo_only) {
- write_isles(plus, pg_info);
- }
- } /* build >= GV_BUILD_ISLES */
- if (pg_info->feature_type == SF_POLYGON) {
- int centroid;
-
- struct P_line *Line;
-
- G_message(_("Updating TopoGeometry data..."));
- for (area = 1; area <= plus->n_areas; area++) {
- G_percent(area, plus->n_areas, 5);
- centroid = Vect_get_area_centroid(Map, area);
- if (centroid < 1)
- continue;
-
- Line = plus->Line[centroid];
- if (!Line)
- continue;
- /* update topogeometry object: centroid -> face */
- if (build_topogeom_stmt(pg_info, GV_CENTROID, area, (int) Line->offset, stmt) &&
- Vect__execute_pg(pg_info->conn, stmt) == -1) {
- Vect__execute_pg(pg_info->conn, "ROLLBACK");
- return 0;
- }
- }
- }
- if (Vect__execute_pg(pg_info->conn, "COMMIT") == -1)
- return 0;
- /* check if we want to create simple features from topogeometry
- data */
- def_file = getenv("GRASS_VECTOR_PGFILE");
-
- if (G_find_file2("", def_file ? def_file : "PG", G_mapset())) {
- FILE *fp;
- const char *p;
-
- struct Key_Value *key_val;
- fp = G_fopen_old("", def_file ? def_file : "PG", G_mapset());
- if (!fp) {
- G_fatal_error(_("Unable to open PG file"));
- }
- key_val = G_fread_key_value(fp);
- fclose(fp);
-
- /* build simple features from topogeometry data */
- p = G_find_key_value("simple_feature", key_val);
- if (p && G_strcasecmp(p, "yes") == 0) {
- if (build > GV_BUILD_BASE)
- Map->level = LEVEL_2; /* force level to avoid errors */
-
- if (create_simple_feature_from_topo(Map) != 0)
- return 0;
- }
- G_free_key_value(key_val);
- }
-
- return 1;
- }
- /*!
- \brief Build UPDATE statement for topo geometry element stored in
- feature table
- \param pg_info so pointer to Format_info_pg
- \param type feature type (GV_POINT, ...)
- \param topo_id topology element id
- \param fid feature id
- \param[out] stmt string buffer
-
- \return 1 on success
- \return 0 on failure
- */
- int build_topogeom_stmt(const struct Format_info_pg *pg_info,
- int type, int topo_id, int fid, char *stmt)
- {
- int topogeom_type;
-
- switch(type) {
- case GV_POINT:
- topogeom_type = 1;
- break;
- case GV_LINE:
- case GV_BOUNDARY:
- topogeom_type = 2;
- break;
- case GV_CENTROID:
- topogeom_type = 3;
- break;
- default:
- G_warning(_("Unsupported topo geometry type %d"), type);
- return 0;
- }
-
- sprintf(stmt, "UPDATE \"%s\".\"%s\" SET %s = "
- "'(%d, 1, %d, %d)'::topology.TopoGeometry "
- "WHERE (%s).id = %d",
- pg_info->schema_name, pg_info->table_name,
- pg_info->topogeom_column, pg_info->toposchema_id,
- topo_id, topogeom_type, pg_info->topogeom_column, fid);
-
- return 1;
- }
- /*!
- \brief Store map bounding box in DB head table
- \param pg_info pointer to Format_info_pg struct
- \param box pointer to bounding box
- \return 1 on success
- \return 0 on failure
- */
- int save_map_bbox(const struct Format_info_pg *pg_info, const struct bound_box *box)
- {
- char stmt[DB_SQL_MAX];
-
- /* create if not exists */
- if (create_topo_grass(pg_info) == -1) {
- G_warning(_("Unable to create <%s.%s>"), TOPO_SCHEMA, TOPO_TABLE);
- return 0;
- }
-
- /* update bbox */
- if (has_topo_grass(pg_info)) {
- /* -> update */
- sprintf(stmt, "UPDATE \"%s\".\"%s\" SET %s = "
- "'BOX3D(%.12f %.12f %.12f, %.12f %.12f %.12f)'::box3d WHERE %s = %d",
- TOPO_SCHEMA, TOPO_TABLE, TOPO_BBOX,
- box->W, box->S, box->B, box->E, box->N, box->T,
- TOPO_ID, pg_info->toposchema_id);
- }
- else {
- /* -> insert */
- sprintf(stmt, "INSERT INTO \"%s\".\"%s\" (%s, %s) "
- "VALUES(%d, 'BOX3D(%.12f %.12f %.12f, %.12f %.12f %.12f)'::box3d)",
- TOPO_SCHEMA, TOPO_TABLE, TOPO_ID, TOPO_BBOX, pg_info->toposchema_id,
- box->W, box->S, box->B, box->E, box->N, box->T);
- }
-
- if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
- return -1;
- }
-
- return 1;
- }
- /*!
- \brief Creates 'topology.grass' table if not exists
- \return 0 table already exists
- \return 1 table successfully added
- \return -1 on error
- */
- int create_topo_grass(const struct Format_info_pg *pg_info)
- {
- char stmt[DB_SQL_MAX];
- PGresult *result;
-
- /* check if table exists */
- sprintf(stmt, "SELECT COUNT(*) FROM information_schema.tables "
- "WHERE table_schema = '%s' AND table_name = '%s'",
- TOPO_SCHEMA, TOPO_TABLE);
- result = PQexec(pg_info->conn, stmt);
- if (!result || PQresultStatus(result) != PGRES_TUPLES_OK) {
- PQclear(result);
- return -1;
- }
-
- if (atoi(PQgetvalue(result, 0, 0)) == 1) {
- /* table already exists */
- PQclear(result);
- return 1;
- }
- PQclear(result);
-
- G_debug(1, "<%s.%s> created", TOPO_SCHEMA, TOPO_TABLE);
-
- /* create table */
- sprintf(stmt, "CREATE TABLE \"%s\".\"%s\" (%s INTEGER, %s box3d)",
- TOPO_SCHEMA, TOPO_TABLE, TOPO_ID, TOPO_BBOX);
- if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
- return -1;
- }
- /* add primary key */
- sprintf(stmt, "ALTER TABLE \"%s\".\"%s\" ADD PRIMARY KEY (%s)",
- TOPO_SCHEMA, TOPO_TABLE, TOPO_ID);
- if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
- return -1;
- }
- /* add constraint */
- sprintf(stmt, "ALTER TABLE \"%s\".\"%s\" ADD CONSTRAINT \"%s_%s_fkey\" "
- "FOREIGN KEY (%s) REFERENCES topology.topology(id) ON DELETE CASCADE",
- TOPO_SCHEMA, TOPO_TABLE, TOPO_TABLE, TOPO_ID, TOPO_ID);
- if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
- return -1;
- }
-
- return 1;
- }
- /*!
- \brief Check if 'topology_id' exists in 'topology.grass'
- \param pg_info pointer to Format_info_pg struct
- \return TRUE if exists
- \return FALSE otherwise
- \return -1 on error
- */
- int has_topo_grass(const struct Format_info_pg *pg_info)
- {
- int has_topo;
- char stmt[DB_SQL_MAX];
-
- PGresult *result;
-
- sprintf(stmt, "SELECT COUNT(*) FROM \"%s\".\"%s\" "
- "WHERE %s = %d",
- TOPO_SCHEMA, TOPO_TABLE, TOPO_ID, pg_info->toposchema_id);
- result = PQexec(pg_info->conn, stmt);
- if (!result || PQresultStatus(result) != PGRES_TUPLES_OK) {
- PQclear(result);
- return -1;
- }
-
- has_topo = FALSE;
- if (atoi(PQgetvalue(result, 0, 0)) == 1) {
- /* table already exists */
- has_topo = TRUE;
- }
- PQclear(result);
- return has_topo;
- }
- /*!
- \brief Insert node into 'node_grass' table
-
- Writes (see P_node struct):
- - lines
- - angles
- Already stored in Topo-Geo:
- - x,y,z (geom)
-
- \param plus pointer to Plus_head struct
- \param pg_info pointer to Format_info_pg struct
- \return 0 on success
- \return -1 on error
- */
- int write_nodes(const struct Plus_head *plus,
- const struct Format_info_pg *pg_info)
- {
- int i, node_id;
- size_t stmt_lines_size, stmt_angles_size, stmt_size;
- char *stmt_lines, *stmt_angles, *stmt;
-
- const struct P_node *Node;
- const struct Format_info_offset *offset;
- offset = &(pg_info->offset);
-
- if (offset->array_num < 1) /* nothing to write */
- return 0;
-
- if (plus->n_nodes != offset->array_num) {
- G_warning(_("Unable to write nodes, offset array mismatch"));
- return -1;
- }
-
- stmt_size = 2 * DB_SQL_MAX + 512;
- stmt = (char *) G_malloc(stmt_size);
-
- stmt_lines = stmt_angles = NULL;
- for (i = 1; i <= plus->n_nodes; i++) {
- Node = plus->Node[i];
- if (!Node)
- continue; /* should not happen */
-
- node_id = offset->array[i-1];
- /* 'lines' array */
- build_stmt_id(Node->lines, Node->n_lines, TRUE, plus, &stmt_lines, &stmt_lines_size);
- /* 'angle' array */
- build_stmt_id(Node->angles, Node->n_lines, FALSE, NULL, &stmt_angles, &stmt_angles_size);
-
- /* build SQL statement to add new node into 'node_grass' */
- if (stmt_lines_size + stmt_angles_size + 512 > stmt_size) {
- stmt_size = stmt_lines_size + stmt_angles_size + 512;
- stmt = (char *) G_realloc(stmt, stmt_size);
- }
- sprintf(stmt, "INSERT INTO \"%s\".%s VALUES ("
- "%d, '{%s}', '{%s}')", pg_info->toposchema_name, TOPO_TABLE_NODE,
- node_id, stmt_lines, stmt_angles);
- if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
- G_warning(_("Unable to write nodes"));
- return -1;
- }
- }
- G_free(stmt_lines);
- G_free(stmt_angles);
- G_free(stmt);
-
- return 0;
- }
- /*!
- \brief Insert lines into 'line_grass' table
-
- Writes (see P_line struct) - only for boundaries:
- - left, right area
- Already stored in Topo-Geo:
- - edge_id, left_face, right_face
-
- \param plus pointer to Plus_head struct
- \param pg_info pointer to Format_info_pg struct
- \return 0 on success
- \return -1 on error
- */
- int write_lines(const struct Plus_head *plus,
- const struct Format_info_pg *pg_info)
- {
- int i, row, offset;
- char stmt[DB_SQL_MAX];
- const struct P_line *Line;
- const struct P_topo_b *topo;
-
- PGresult *res;
-
- sprintf(stmt, "SELECT edge_id FROM \"%s\".edge_data WHERE "
- "left_face != 0 OR right_face != 0 ORDER BY edge_id",
- pg_info->toposchema_name);
- G_debug(2, "SQL: %s", stmt);
- res = PQexec(pg_info->conn, stmt);
- if (!res || PQresultStatus(res) != PGRES_TUPLES_OK ||
- (PQntuples(res) > 0 && PQntuples(res) != plus->n_blines)) {
- G_warning(_("Inconsistency in topology: number of "
- "boundaries %d (should be %d)"),
- PQntuples(res), plus->n_blines);
- if (res)
- PQclear(res);
- return -1;
- }
-
- for (row = 0, i = 1; i <= plus->n_lines; i++) {
- Line = plus->Line[i];
- if (!Line || Line->type != GV_BOUNDARY)
- continue;
- if (Line->offset == 0L)
- offset = atoi(PQgetvalue(res, row++, 0));
- else
- offset = (int)Line->offset;
-
- topo = (struct P_topo_b *)Line->topo;
- sprintf(stmt, "INSERT INTO \"%s\".%s VALUES ("
- "%d, %d, %d)", pg_info->toposchema_name, TOPO_TABLE_LINE,
- offset, topo->left, topo->right);
- if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
- G_warning(_("Unable to write lines"));
- return -1;
- }
- }
- return 0;
- }
- /*!
- \brief Insert area into 'area_grass' table
- Writes (see P_area struct):
- - lines
- - centroid
- - isles
- \param plus pointer to Plus_head struct
- \param pg_info pointer to Format_info_pg struct
- \return 0 on success
- \return -1 on error
- */
- int write_areas(const struct Plus_head *plus,
- const struct Format_info_pg *pg_info)
- {
- int area, centroid;
- size_t stmt_lines_size, stmt_isles_size, stmt_size;
- char *stmt_lines, *stmt_isles, *stmt;
-
- const struct P_line *Line;
- const struct P_area *Area;
- stmt_size = 2 * DB_SQL_MAX + 512;
- stmt = (char *) G_malloc(stmt_size);
-
- stmt_lines = stmt_isles = NULL;
- for (area = 1; area <= plus->n_areas; area++) {
- Area = plus->Area[area];
- if (!Area) {
- G_debug(3, "Area %d skipped (dead)", area);
- continue; /* should not happen */
- }
-
- /* 'lines' array */
- build_stmt_id(Area->lines, Area->n_lines, TRUE, NULL, &stmt_lines, &stmt_lines_size);
- /* 'isles' array */
- build_stmt_id(Area->isles, Area->n_isles, TRUE, NULL, &stmt_isles, &stmt_isles_size);
-
- if (Area->centroid != 0) {
- Line = plus->Line[Area->centroid];
- if (!Line) {
- G_warning(_("Topology for centroid %d not available. Area %d skipped"),
- Area->centroid, area);
- continue;
- }
- centroid = (int) Line->offset;
- }
- else {
- centroid = 0;
- }
-
- /* build SQL statement to add new node into 'node_grass' */
- if (stmt_lines_size + stmt_isles_size + 512 > stmt_size) {
- stmt_size = stmt_lines_size + stmt_isles_size + 512;
- stmt = (char *) G_realloc(stmt, stmt_size);
- }
- sprintf(stmt, "INSERT INTO \"%s\".%s VALUES ("
- "%d, '{%s}', %d, '{%s}')", pg_info->toposchema_name, TOPO_TABLE_AREA,
- area, stmt_lines, centroid, stmt_isles);
- if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
- return -1;
- }
- }
- G_free(stmt_lines);
- G_free(stmt_isles);
- G_free(stmt);
-
- return 0;
- }
- /*!
- \brief Insert isle into 'isle_grass' table
- Writes (see P_isle struct):
- - lines
- - area
- \param plus pointer to Plus_head struct
- \param pg_info pointer to Format_info_pg struct
- \return 0 on success
- \return -1 on error
- */
- int write_isles(const struct Plus_head *plus,
- const struct Format_info_pg *pg_info)
- {
- int isle;
- size_t stmt_lines_size, stmt_size;
- char *stmt_lines, *stmt;
-
- const struct P_isle *Isle;
- stmt_size = DB_SQL_MAX + 512;
- stmt = (char *) G_malloc(stmt_size);
- stmt_lines = NULL;
- for (isle = 1; isle <= plus->n_isles; isle++) {
- Isle = plus->Isle[isle];
- if (!Isle)
- continue; /* should not happen */
-
- /* 'lines' array */
- build_stmt_id(Isle->lines, Isle->n_lines, TRUE, NULL, &stmt_lines, &stmt_lines_size);
-
- /* build SQL statement to add new node into 'node_grass' */
- if (stmt_lines_size + 512 > stmt_size) {
- stmt_size = stmt_lines_size + 512;
- stmt = (char *) G_realloc(stmt, stmt_size);
- }
- sprintf(stmt, "INSERT INTO \"%s\".%s VALUES ("
- "%d, '{%s}', %d)", pg_info->toposchema_name, TOPO_TABLE_ISLE,
- isle, stmt_lines, Isle->area);
- if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
- return -1;
- }
- }
- G_free(stmt_lines);
- G_free(stmt);
- return 0;
- }
- /*!
- \brief Create PG-like array for int/float array
- \param array array of items
- \param nitems number of items in the array
- \param is_int TRUE for array of integers otherwise floats
- \param plus pointer to Plus_head struct
- \param[in,out] output buffer (re-used)
- \param[in,out] buffer size
- */
- void build_stmt_id(const void *array, int nitems, int is_int, const struct Plus_head *plus,
- char **stmt, size_t *stmt_size)
- {
- int i, ivalue;
- int *iarray;
- float *farray;
-
- size_t stmt_id_size;
- char *stmt_id, buf_id[128];
- struct P_line *Line;
-
- if (is_int)
- iarray = (int *) array;
- else
- farray = (float *) array;
-
- if (!(*stmt)) {
- stmt_id_size = DB_SQL_MAX;
- stmt_id = (char *) G_malloc(stmt_id_size);
- }
- else {
- stmt_id_size = *stmt_size;
- stmt_id = *stmt;
- }
- /* reset array */
- stmt_id[0] = '\0';
-
- for (i = 0; i < nitems; i++) {
- /* realloc array if needed */
- if (strlen(stmt_id) + 100 > stmt_id_size) {
- stmt_id_size = strlen(stmt_id) + DB_SQL_MAX;
- stmt_id = (char *) G_realloc(stmt_id, stmt_id_size);
- }
-
- if (is_int) {
- if (plus) {
- Line = plus->Line[abs(iarray[i])];
- ivalue = (int) Line->offset;
- if (iarray[i] < 0)
- ivalue *= -1;
- }
- else {
- ivalue = iarray[i];
- }
- sprintf(buf_id, "%d", ivalue);
- }
- else {
- sprintf(buf_id, "%f", farray[i]);
- }
- if (i > 0)
- strcat(stmt_id, ",");
- strcat(stmt_id, buf_id);
- }
- *stmt = stmt_id;
- *stmt_size = stmt_id_size;
- }
- /*!
- \brief Clean-up GRASS Topology tables
- \param pg_info pointer to Format_info_pg pg_info
- \return 0 on success
- \return -1 on error
- */
- int Vect__clean_grass_db_topo(struct Format_info_pg *pg_info)
- {
- char stmt[DB_SQL_MAX];
- sprintf(stmt, "DELETE FROM \"%s\".\"%s\"",
- pg_info->toposchema_name, TOPO_TABLE_NODE);
- if (-1 == Vect__execute_pg(pg_info->conn, stmt))
- return -1;
-
- sprintf(stmt, "DELETE FROM \"%s\".\"%s\"",
- pg_info->toposchema_name, TOPO_TABLE_LINE);
- if (-1 == Vect__execute_pg(pg_info->conn, stmt))
- return -1;
-
- sprintf(stmt, "DELETE FROM \"%s\".\"%s\"",
- pg_info->toposchema_name, TOPO_TABLE_AREA);
- if (-1 == Vect__execute_pg(pg_info->conn, stmt))
- return -1;
-
- sprintf(stmt, "DELETE FROM \"%s\".\"%s\"",
- pg_info->toposchema_name, TOPO_TABLE_ISLE);
- if (-1 == Vect__execute_pg(pg_info->conn, stmt))
- return -1;
- return 0;
- }
- /*!
- \brief Create simple features geometry from topogeometry data
- \param Map pointer to Map_info struct
- \return 0 on success
- \return -1 on error
- */
- int create_simple_feature_from_topo(struct Map_info *Map)
- {
- char stmt[DB_SQL_MAX];
- struct Format_info_pg *pg_info;
- pg_info = &(Map->fInfo.pg);
- G_debug(1, "build_simple_feature_from_topo(): %d", pg_info->feature_type);
-
- G_message(_("Create simple features topology from topogeometry data..."));
- Vect__execute_pg(pg_info->conn, "BEGIN");
- if (pg_info->feature_type == SF_POINT ||
- pg_info->feature_type == SF_LINESTRING) {
- sprintf(stmt, "UPDATE \"%s\".\"%s\" SET %s = (SELECT geom FROM \"%s\".node "
- "WHERE node_id = (%s).id)", pg_info->schema_name, pg_info->table_name,
- pg_info->geom_column, pg_info->toposchema_name, pg_info->topogeom_column);
- if(Vect__execute_pg(pg_info->conn, stmt) == -1) {
- Vect__execute_pg(pg_info->conn, "ROLLBACK");
- return -1;
- }
- }
- else if (pg_info->feature_type == SF_POLYGON) {
- Vect__copy_areas(Map, 1, Map);
- }
- else {
- G_warning(_("Unable to build simple features from topogeometry data. "
- "Unsupported type %d."), pg_info->feature_type);
- }
-
- Vect__execute_pg(pg_info->conn, "COMMIT");
- return 0;
- }
- #endif
|