[GRASS-SVN] r52059 - grass/trunk/lib/vector/Vlib

svn_grass at osgeo.org svn_grass at osgeo.org
Wed Jun 13 04:30:37 PDT 2012


Author: martinl
Date: 2012-06-13 04:30:35 -0700 (Wed, 13 Jun 2012)
New Revision: 52059

Modified:
   grass/trunk/lib/vector/Vlib/open_pg.c
   grass/trunk/lib/vector/Vlib/write_pg.c
Log:
vlib/pg: initial support for writing PostGIS topology
         Vect_write_line() - points only (nodes in PostGIS topology)


Modified: grass/trunk/lib/vector/Vlib/open_pg.c
===================================================================
--- grass/trunk/lib/vector/Vlib/open_pg.c	2012-06-13 07:16:58 UTC (rev 52058)
+++ grass/trunk/lib/vector/Vlib/open_pg.c	2012-06-13 11:30:35 UTC (rev 52059)
@@ -23,6 +23,8 @@
 #ifdef HAVE_POSTGRES
 #include "pg_local_proto.h"
 
+#define TOPOGEOM_COLUMN "topo"
+
 struct edge_data {
     int id;
     int start_node;
@@ -37,6 +39,7 @@
 static int drop_table(struct Format_info_pg *);
 static int check_schema(const struct Format_info_pg *);
 static int create_table(struct Format_info_pg *, const struct field_info *);
+static int create_topo_schema(struct Format_info_pg *, int);
 static void connect_db(struct Format_info_pg *);
 static int check_topo(struct Format_info_pg *, struct Plus_head *);
 static int parse_bbox(const char *, struct bound_box *);
@@ -48,6 +51,7 @@
                                   struct Format_info_cache *);
 static int load_plus_head(struct Format_info_pg *, struct Plus_head *);
 static void notice_processor(void *, const char *);
+static char *get_sftype(SF_FeatureType);
 #endif
 
 /*!
@@ -371,11 +375,20 @@
         }
     }
 
+    /* create new feature table */
     if (create_table(pg_info, Fi) == -1) {
-        G_warning(_("Unable to create new PostGIS table"));
+        G_warning(_("Unable to create new PostGIS feature table"));
         return -1;
     }
-
+    
+    /* create new topology schema (if PostGIS topology support is enabled) */
+    if(pg_info->toposchema_name) {
+        if (create_topo_schema(pg_info, Vect_is_3d(Map)) == -1) {
+            G_warning(_("Unable to create new PostGIS topology schema"));
+            return -1;
+        }
+    }
+    
     if (Fi)
         G_free(Fi);
 
@@ -505,7 +518,7 @@
 }
 
 /*!
-  \brief Drop feature table
+  \brief Drop feature table and topology schema if exists
 
   \param pg_info pointer to Format_info_pg
 
@@ -514,15 +527,49 @@
 */
 int drop_table(struct Format_info_pg *pg_info)
 {
+    int i;
     char stmt[DB_SQL_MAX];
-
+    char *topo_schema;
+    
+    PGresult *result, *result_drop;
+    
+    /* drop topology schema(s) related to the feature table */
+    sprintf(stmt, "SELECT t.name FROM topology.layer AS l JOIN "
+            "topology.topology AS t ON l.topology_id = t.id "
+            "WHERE l.table_name = '%s'", pg_info->table_name);
+    G_debug(2, "SQL: %s", stmt);
+    
+    result = PQexec(pg_info->conn, stmt);
+    if (!result || PQresultStatus(result) != PGRES_TUPLES_OK) {
+        G_warning(_("Execution failed: %s"), PQerrorMessage(pg_info->conn));
+        PQclear(result);
+        return -1;
+    }
+    for (i = 0; i < PQntuples(result); i++) {
+        topo_schema = PQgetvalue(result, i, 0);
+        sprintf(stmt, "SELECT topology.DropTopology('%s')",
+                topo_schema);
+        G_debug(2, "SQL: %s", stmt);
+        
+        result_drop = PQexec(pg_info->conn, stmt);
+        if (!result_drop || PQresultStatus(result_drop) != PGRES_TUPLES_OK)
+            G_warning(_("Execution failed: %s"), PQerrorMessage(pg_info->conn));
+        
+        G_verbose_message(_("PostGIS topology schema <%s> dropped"),
+                          topo_schema);
+        PQclear(result_drop);
+    }
+    PQclear(result);
+    
+    /* drop feature table */
     sprintf(stmt, "DROP TABLE \"%s\".\"%s\"",
             pg_info->schema_name, pg_info->table_name);
     G_debug(2, "SQL: %s", stmt);
-
+    
     if (execute(pg_info->conn, stmt) == -1) {
         return -1;
     }
+
     return 0;
 }
 
@@ -586,8 +633,9 @@
 int create_table(struct Format_info_pg *pg_info, const struct field_info *Fi)
 {
     int spatial_index, primary_key;
-    char stmt[DB_SQL_MAX], *geom_type;
-
+    char stmt[DB_SQL_MAX];
+    char *geom_type;
+    
     PGresult *result;
 
     /* by default create spatial index & add primary key */
@@ -609,10 +657,21 @@
         p = G_find_key_value("spatial_index", key_val);
         if (p && G_strcasecmp(p, "off") == 0)
             spatial_index = FALSE;
+        
         /* disable primary key ? */
         p = G_find_key_value("primary_key", key_val);
         if (p && G_strcasecmp(p, "off") == 0)
             primary_key = FALSE;
+
+        /* PostGIS topology enabled ? */
+        p = G_find_key_value("topology", key_val);
+        if (p && G_strcasecmp(p, "on") == 0) {
+            /* define topology name
+               this should be configurable by the user
+            */
+            G_asprintf(&(pg_info->toposchema_name), "topo_%s",
+                       pg_info->table_name);
+        }
     }
 
     /* create schema if not exists */
@@ -746,7 +805,7 @@
         execute(pg_info->conn, "ROLLBACK");
         return -1;
     }
-
+    
     /* add geometry column */
     sprintf(stmt, "SELECT AddGeometryColumn('%s', '%s', "
             "'%s', %d, '%s', %d)",
@@ -755,13 +814,13 @@
             geom_type, pg_info->coor_dim);
     G_debug(2, "SQL: %s", stmt);
     result = PQexec(pg_info->conn, stmt);
-
+    
     if (!result || PQresultStatus(result) != PGRES_TUPLES_OK) {
         PQclear(result);
         execute(pg_info->conn, "ROLLBACK");
         return -1;
     }
-
+    
     /* create index ? */
     if (spatial_index) {
         G_verbose_message(_("Building spatial index on <%s>..."),
@@ -772,7 +831,7 @@
                 pg_info->schema_name, pg_info->table_name,
                 pg_info->geom_column);
         G_debug(2, "SQL: %s", stmt);
-
+        
         if (execute(pg_info->conn, stmt) == -1) {
             execute(pg_info->conn, "ROLLBACK");
             return -1;
@@ -788,6 +847,97 @@
 }
 
 /*!
+  \brief Create new PostGIS topology schema
+
+  - create topology schema
+  - add topology column to the feature table
+  
+  \param pg_info pointer to Format_info_pg
+
+  \return 0 on success
+  \return 1 topology disable, nothing to do
+  \return -1 on failure
+*/
+int create_topo_schema(struct Format_info_pg *pg_info, int with_z)
+{
+    double tolerance;
+    char stmt[DB_SQL_MAX];
+    
+    PGresult *result;
+    
+    /* read default values from PG file*/
+    tolerance = 0.;
+    if (G_find_file2("", "PG", G_mapset())) {
+        FILE *fp;
+        const char *p;
+
+        struct Key_Value *key_val;
+
+        fp = G_fopen_old("", "PG", G_mapset());
+        if (!fp) {
+            G_fatal_error(_("Unable to open PG file"));
+        }
+        key_val = G_fread_key_value(fp);
+        fclose(fp);
+
+        /* tolerance */
+        p = G_find_key_value("tolerance", key_val);
+        if (p)
+            tolerance = atof(p);
+
+        /* topogeom column */
+        p = G_find_key_value("topogeom_column", key_val);
+        if (p)
+            pg_info->topogeom_column = G_store(p);
+        else
+            pg_info->topogeom_column = G_store(TOPOGEOM_COLUMN);
+    }
+
+    /* begin transaction (create topo schema) */
+    if (execute(pg_info->conn, "BEGIN") == -1) {
+        return -1;
+    }
+
+    /* create topology schema */
+    G_message(_("Creating topology schema <%s>..."), pg_info->toposchema_name);
+    sprintf(stmt, "SELECT topology.createtopology('%s', "
+            "find_srid('%s', '%s', '%s'), %f, '%s')",
+            pg_info->toposchema_name, pg_info->schema_name,
+            pg_info->table_name, pg_info->geom_column, tolerance,
+            with_z == WITH_Z ? "t" : "f");
+    G_debug(2, "SQL: %s", stmt);
+
+    result = PQexec(pg_info->conn, stmt);
+    if (!result || PQresultStatus(result) != PGRES_TUPLES_OK) {
+        G_warning(_("Execution failed: %s"), PQerrorMessage(pg_info->conn));
+        execute(pg_info->conn, "ROLLBACK");
+        return -1;
+    }
+    
+    /* add topo column to the feature table */
+    G_message(_("Adding new topology column <%s>..."), pg_info->topogeom_column);
+    sprintf(stmt, "SELECT topology.AddTopoGeometryColumn('%s', '%s', '%s', "
+            "'%s', '%s')", pg_info->toposchema_name, pg_info->schema_name,
+            pg_info->table_name, pg_info->topogeom_column,
+            get_sftype(pg_info->feature_type));
+    G_debug(2, "SQL: %s", stmt);
+
+    result = PQexec(pg_info->conn, stmt);
+    if (!result || PQresultStatus(result) != PGRES_TUPLES_OK) {
+        G_warning(_("Execution failed: %s"), PQerrorMessage(pg_info->conn));
+        execute(pg_info->conn, "ROLLBACK");
+        return -1;
+    }
+
+    /* close transaction (create topo schema) */
+    if (execute(pg_info->conn, "COMMIT") == -1) {
+        return -1;
+    }
+
+    return 0;
+}
+
+/*!
   \brief Establish PG connection (pg_info->conninfo)
 
   \param pg_info pointer to Format_info_pg
@@ -875,6 +1025,11 @@
     size_t length, prefix_length;
     char **tokens, **tokens_coord, *coord;
     
+    if (strlen(value) < 1) {
+        G_warning(_("Empty bounding box"));
+        return -1;
+    }
+    
     prefix_length = strlen("box3d(");
     if (G_strncasecmp(value, "box3d(", prefix_length) != 0)
         return -1;
@@ -1463,4 +1618,31 @@
         fprintf(stderr, "%s", message);
     }
 }
+
+/*!
+  \brief Get simple feature type as a string
+
+  Used for AddTopoGeometryColumn().
+
+  Valid types:
+   - SF_POINT
+   - SF_LINESTRING
+   - SF_POLYGON
+
+  \return string with feature type
+  \return empty string
+*/
+char *get_sftype(SF_FeatureType sftype)
+{
+    if (sftype == SF_POINT)
+        return "POINT";
+    else if (sftype == SF_LINESTRING)
+        return "LINE";
+    else if (sftype == SF_POLYGON)
+        return "POLYGON";
+    else
+        G_warning(_("Unsupported feature type %d"), sftype);
+    
+    return "";
+}
 #endif

Modified: grass/trunk/lib/vector/Vlib/write_pg.c
===================================================================
--- grass/trunk/lib/vector/Vlib/write_pg.c	2012-06-13 07:16:58 UTC (rev 52058)
+++ grass/trunk/lib/vector/Vlib/write_pg.c	2012-06-13 11:30:35 UTC (rev 52059)
@@ -42,6 +42,9 @@
                          int, const struct field_info *);
 static char *build_insert_stmt(const struct Format_info_pg *, const char *,
                                int, const struct field_info *);
+static char *build_topo_stmt(const struct Format_info_pg *, int, const char *);
+static char *build_topogeom_stmt(const struct Format_info_pg *, int, int, int);
+static int execute_topo(PGconn *, const char *);
 #endif
 
 /*!
@@ -206,7 +209,7 @@
                          const struct line_pnts * points,
                          const struct line_cats * cats)
 {
-    G_debug(3, "V1_rewrite_line_pg(): line=%d type=%d offset=%llu",
+    G_debug(3, "V1_rewrite_line_pg(): line=%d type=%d offset=%lu",
             line, type, offset);
 #ifdef HAVE_POSTGRES
     if (type != V1_read_line_pg(Map, NULL, NULL, offset)) {
@@ -575,7 +578,7 @@
         return -1;
     }
 
-    byte_order = ENDIAN_LITTLE; /* TODO: get endianness for system from dig__byte_order_out()? */
+    byte_order = dig__byte_order_out();
 
     /* get wkb data */
     nbytes = -1;
@@ -641,7 +644,9 @@
     strcpy(text_data_p, hex_data);
     G_free(hex_data);
 
-    /* build INSERT statement */
+    /* build INSERT statement
+       simple feature geometry + attributes
+    */
     stmt = build_insert_stmt(pg_info, text_data, cat, Fi);
     G_debug(2, "SQL: %s", stmt);
 
@@ -652,16 +657,45 @@
             return -1;
     }
 
-    if (execute(pg_info->conn, stmt) == -1) {
+    /* stmt can NULL when writing PostGIS topology with no attributes
+     * attached */
+    if (stmt && execute(pg_info->conn, stmt) == -1) {
         /* rollback transaction */
         execute(pg_info->conn, "ROLLBACK");
         return -1;
     }
+    G_free(stmt);
+    
+    /* write feature in PostGIS topology schema if enabled */
+    if (pg_info->toposchema_name) {
+        int id, do_update;
 
+        do_update = stmt ? TRUE : FALSE; /* update or insert new record 
+                                            to the feature table */
+        
+        /* insert feature into topology schema (node or edge) */
+        stmt = build_topo_stmt(pg_info, type, text_data);
+        id = execute_topo(pg_info->conn, stmt);
+        if (id == -1) {
+            /* rollback transaction */
+            execute(pg_info->conn, "ROLLBACK");
+            return -1;
+        }
+        G_free(stmt);
+
+        /* insert topoelement into feature table) */
+        stmt = build_topogeom_stmt(pg_info, id, type, do_update);
+        if (execute(pg_info->conn, stmt) == -1) {
+            /* rollback transaction */
+            execute(pg_info->conn, "ROLLBACK");
+            return -1;
+        }
+        G_free(stmt);
+    }
+
     G_free(wkb_data);
     G_free(text_data);
-    G_free(stmt);
-
+    
     return 0;
 }
 
@@ -705,10 +739,9 @@
         db_set_string(&dbstmt, buf);
 
         /* prepare INSERT statement */
-        sprintf(buf, "INSERT INTO \"%s\".\"%s\" (%s",
-                pg_info->schema_name, pg_info->table_name,
-                pg_info->geom_column);
-
+        sprintf(buf, "INSERT INTO \"%s\".\"%s\" (",
+                pg_info->schema_name, pg_info->table_name);
+        
         /* select data */
         if (db_open_select_cursor(pg_info->dbdriver, &dbstmt,
                                   &cursor, DB_SEQUENTIAL) != DB_OK) {
@@ -737,9 +770,11 @@
                         continue;
 
                     /* -> columns */
-                    sprintf(buf_tmp, ",%s", colname);
+                    sprintf(buf_tmp, "%s", colname);
                     strcat(buf, buf_tmp);
-
+                    if (col < ncol - 1)
+                        strcat(buf, ",");
+                    
                     /* -> values */
                     value = db_get_column_value(column);
                     /* for debug only */
@@ -754,40 +789,53 @@
                     if (!db_test_value_isnull(value)) {
                         switch (ctype) {
                         case DB_C_TYPE_INT:
-                            sprintf(buf_tmp, ",%d", db_get_value_int(value));
+                            sprintf(buf_tmp, "%d", db_get_value_int(value));
                             break;
                         case DB_C_TYPE_DOUBLE:
-                            sprintf(buf_tmp, ",%.14f",
+                            sprintf(buf_tmp, "%.14f",
                                     db_get_value_double(value));
                             break;
                         case DB_C_TYPE_STRING:
                             str_val = G_store(db_get_value_string(value));
                             G_str_to_sql(str_val);
-                            sprintf(buf_tmp, ",'%s'", str_val);
+                            sprintf(buf_tmp, "'%s'", str_val);
                             G_free(str_val);
                             break;
                         case DB_C_TYPE_DATETIME:
                             db_convert_column_value_to_string(column,
                                                               &dbstmt);
-                            sprintf(buf_tmp, ",%s", db_get_string(&dbstmt));
+                            sprintf(buf_tmp, "%s", db_get_string(&dbstmt));
                             break;
                         default:
                             G_warning(_("Unsupported column type %d"), ctype);
-                            sprintf(buf_tmp, ",NULL");
+                            sprintf(buf_tmp, "NULL");
                             break;
                         }
                     }
                     else {
-                        sprintf(buf_tmp, ",NULL");
+                        sprintf(buf_tmp, "NULL");
                     }
                     strcat(buf_val, buf_tmp);
+                    if (col < ncol - 1)
+                        strcat(buf_val, ",");
                 }
-
-                G_asprintf(&stmt, "%s) VALUES ('%s'::GEOMETRY%s)",
-                           buf, geom_data, buf_val);
+                
+                if (!pg_info->toposchema_name) {
+                    /* simple feature access */
+                    G_asprintf(&stmt, "%s,%s) VALUES (%s,'%s'::GEOMETRY)",
+                               buf, pg_info->geom_column, buf_val, geom_data);
+                }
+                else {
+                    /* PostGIS topology access, write geometry in
+                     * topology schema, skip geometry at this point */
+                    G_asprintf(&stmt, "%s) VALUES (%s)",
+                               buf, buf_val);
+                }
             }
         }
     }
+    else if (pg_info->toposchema_name)
+        return NULL; /* don't write simple feature element */
 
     if (!stmt) {
         /* no attributes */
@@ -799,4 +847,113 @@
 
     return stmt;
 }
+
+/*!
+  \brief Build SELECT statement to insert new element into PostGIS
+  topology schema
+
+  \param pg_info so pointer to Format_info_pg
+  \param type feature type (GV_POINT, ...)
+  \param geom_data geometry in wkb
+
+  \return pointer to allocated string buffer with SQL statement
+  \return NULL on error
+*/
+char *build_topo_stmt(const struct Format_info_pg *pg_info,
+                      int type, const char *geom_data)
+{
+    char *stmt;
+    
+    stmt = NULL;
+    if (type == GV_POINT) {
+        G_asprintf(&stmt, "SELECT AddNode('%s', '%s'::GEOMETRY)",
+                pg_info->toposchema_name, geom_data);
+    }
+    
+    return stmt;
+}
+
+/*!
+  \brief Build INSERT / UPDATE statement to insert topo geometry
+  object into feature table
+
+  Allocated string should be freed by G_free()
+  
+  \param pg_info so pointer to Format_info_pg
+  \param type feature type (GV_POINT, ...)
+  \param id topology element id
+  \param do_update TRUE for UPDATE otherwise build SELECT statement
+  
+  \return pointer to allocated string buffer with SQL statement
+  \return NULL on error
+*/
+char *build_topogeom_stmt(const struct Format_info_pg *pg_info,
+                          int id, int type, int do_update)
+{
+    int topogeom_type;
+    char *stmt;
+    
+    stmt = NULL;
+
+    if (type == GV_POINT)
+        topogeom_type = 1;
+    else if (type == GV_LINES)
+        topogeom_type = 2;
+    else {
+        G_warning(_("Unsupported topo geometry type %d"), type);
+        return NULL;
+    }
+    
+    if (!do_update)
+        G_asprintf(&stmt, "INSERT INTO \"%s\".\"%s\" (%s) VALUES "
+                   "(topology.CreateTopoGeom('%s', 1, %d, "
+                   "'{{%d, %d}}'::topology.topoelementarray))",
+                   pg_info->schema_name, pg_info->table_name,
+                   pg_info->topogeom_column, pg_info->toposchema_name,
+                   topogeom_type, id, topogeom_type);
+    else
+        G_asprintf(&stmt, "UPDATE \"%s\".\"%s\" SET %s = "
+                   "topology.CreateTopoGeom('%s', 1, %d, "
+                   "'{{%d, %d}}'::topology.topoelementarray) "
+                   "WHERE %s = %d",
+                   pg_info->schema_name, pg_info->table_name,
+                   pg_info->topogeom_column, pg_info->toposchema_name,
+                   topogeom_type, id, topogeom_type,
+                   pg_info->fid_column, id);
+    
+    return stmt;
+}
+
+/*!
+   \brief Execute SQL topo select statement
+
+   \param conn pointer to PGconn
+   \param stmt query
+
+   \return value on success
+   \return -1 on error
+ */
+int execute_topo(PGconn *conn, const char *stmt)
+{
+    int ret;
+    PGresult *result;
+
+    result = NULL;
+
+    G_debug(3, "execute_topo(): %s", stmt);
+    result = PQexec(conn, stmt);
+    if (!result || PQresultStatus(result) != PGRES_TUPLES_OK ||
+        PQntuples(result) != 1) {
+        PQclear(result);
+
+        G_warning(_("Execution failed: %s"), PQerrorMessage(conn));
+        return -1;
+    }
+
+    ret = atoi(PQgetvalue(result, 0, 0));
+    PQclear(result);
+    
+    return ret;
+}
+   
 #endif



More information about the grass-commit mailing list