[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