[GRASS-SVN] r56437 - in grass/trunk: include/vect lib/vector/Vlib
svn_grass at osgeo.org
svn_grass at osgeo.org
Mon May 27 10:46:52 PDT 2013
Author: martinl
Date: 2013-05-27 10:46:52 -0700 (Mon, 27 May 2013)
New Revision: 56437
Modified:
grass/trunk/include/vect/dig_defines.h
grass/trunk/include/vect/dig_externs.h
grass/trunk/include/vect/dig_structs.h
grass/trunk/lib/vector/Vlib/build_pg.c
grass/trunk/lib/vector/Vlib/copy.c
grass/trunk/lib/vector/Vlib/open.c
grass/trunk/lib/vector/Vlib/open_pg.c
grass/trunk/lib/vector/Vlib/pg_local_proto.h
grass/trunk/lib/vector/Vlib/write_pg.c
Log:
vlib(pg): store GRASS topology in DB (can be optionally disabled by the user)
various fixes reading and writing topological elements from DB
use offset to map nodes
doxygen cosmetics
Modified: grass/trunk/include/vect/dig_defines.h
===================================================================
--- grass/trunk/include/vect/dig_defines.h 2013-05-27 14:17:29 UTC (rev 56436)
+++ grass/trunk/include/vect/dig_defines.h 2013-05-27 17:46:52 UTC (rev 56437)
@@ -213,9 +213,11 @@
/*! \brief Maximum category value */
#define GV_CAT_MAX PORT_INT_MAX
-/*! \brief GRASS ASCII vector format */
+/*! \brief GRASS ASCII vector format - point format */
#define GV_ASCII_FORMAT_POINT 0
+/*! \brief GRASS ASCII vector format - standard format */
#define GV_ASCII_FORMAT_STD 1
+/*! \brief GRASS ASCII vector format - well-known-text format */
#define GV_ASCII_FORMAT_WKT 2
/*! \brief Simple feature types
@@ -250,6 +252,6 @@
#define HEADSTR 50
/*! \brief GRASS-PostGIS data provider - default fid column */
-#define GV_PG_FID_COLUMN "fid"
+#define GV_PG_FID_COLUMN "fid"
/*! \brief GRASS-PostGIS data provider - default geometry column */
#define GV_PG_GEOMETRY_COLUMN "geom"
Modified: grass/trunk/include/vect/dig_externs.h
===================================================================
--- grass/trunk/include/vect/dig_externs.h 2013-05-27 14:17:29 UTC (rev 56436)
+++ grass/trunk/include/vect/dig_externs.h 2013-05-27 17:46:52 UTC (rev 56437)
@@ -223,7 +223,7 @@
SearchHitCallback , void *, struct Plus_head *);
/* struct_alloc.c */
-int dig_node_alloc_line(struct P_node *, int add);
+int dig_node_alloc_line(struct P_node *, int);
int dig_alloc_nodes(struct Plus_head *, int);
int dig_alloc_lines(struct Plus_head *, int);
int dig_alloc_areas(struct Plus_head *, int);
Modified: grass/trunk/include/vect/dig_structs.h
===================================================================
--- grass/trunk/include/vect/dig_structs.h 2013-05-27 14:17:29 UTC (rev 56436)
+++ grass/trunk/include/vect/dig_structs.h 2013-05-27 17:46:52 UTC (rev 56437)
@@ -433,7 +433,7 @@
5 2 third part (polygon)
6 0 first ring of polygon
- Topology:
+ GRASS topology:
line idx
-----------------
@@ -441,6 +441,9 @@
2 2 line
3 4 boundary
4 1 centroid read from topo (idx == FID)
+
+ In PostGIS Topology mode the offset array is used for mapping
+ nodes.
\endverbatim
*/
int *array;
@@ -674,6 +677,13 @@
*/
char *toposchema_name;
int toposchema_id;
+ /*!
+ \brief Topology format
+
+ TRUE to store only Topo-Geo data in DB otherwise GRASS-like
+ topology is also maintained in DB
+ */
+ int topo_geo_only;
};
/*!
Modified: grass/trunk/lib/vector/Vlib/build_pg.c
===================================================================
--- grass/trunk/lib/vector/Vlib/build_pg.c 2013-05-27 14:17:29 UTC (rev 56436)
+++ grass/trunk/lib/vector/Vlib/build_pg.c 2013-05-27 17:46:52 UTC (rev 56437)
@@ -28,6 +28,10 @@
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_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 *);
#endif
/*!
@@ -71,8 +75,8 @@
/* TODO move this init to better place (Vect_open_ ?), because in
theory build may be reused on level2 */
- if (build >= plus->built && build > GV_BUILD_BASE) {
- G_free((void *)pg_info->offset.array);
+ 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));
}
@@ -162,6 +166,11 @@
if (Vect__execute_pg(pg_info->conn, "BEGIN"))
return 0;
+ /* write full node topo info to DB if requested */
+ if (!pg_info->topo_geo_only) {
+ write_nodes(plus, pg_info);
+ }
+
/* update faces from GRASS Topology */
if (build >= GV_BUILD_AREAS) {
/* do clean up (1-3)
@@ -186,7 +195,7 @@
return 0;
}
- /* 3) delete faces */
+ /* 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);
@@ -194,7 +203,22 @@
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..."));
@@ -266,6 +290,11 @@
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) {
@@ -275,6 +304,11 @@
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) {
@@ -479,4 +513,237 @@
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;
+ char *stmt_lines, *stmt_angles;
+ char stmt[DB_SQL_MAX];
+
+ const struct P_node *Node;
+ const struct Format_info_offset *offset;
+
+ offset = &(pg_info->offset);
+ if (plus->n_nodes != offset->array_num)
+ return -1;
+
+ 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' */
+ 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) {
+ return -1;
+ }
+ }
+
+ G_free(stmt_lines);
+ G_free(stmt_angles);
+
+ 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;
+ char *stmt_lines, *stmt_isles;
+ char stmt[DB_SQL_MAX];
+
+ const struct P_line *Line;
+ const struct P_area *Area;
+
+ stmt_lines = stmt_isles = NULL;
+ for (area = 1; area <= plus->n_areas; area++) {
+ Area = plus->Area[area];
+ if (!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);
+
+ Line = plus->Line[Area->centroid];
+ if (!Line)
+ return -1;
+ centroid = (int) Line->offset;
+
+ /* build SQL statement to add new node into 'node_grass' */
+ 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);
+
+ 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;
+ char *stmt_lines;
+ char stmt[DB_SQL_MAX];
+
+ const struct P_isle *Isle;
+
+ 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' */
+ 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);
+
+ 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;
+}
+
#endif
Modified: grass/trunk/lib/vector/Vlib/copy.c
===================================================================
--- grass/trunk/lib/vector/Vlib/copy.c 2013-05-27 14:17:29 UTC (rev 56436)
+++ grass/trunk/lib/vector/Vlib/copy.c 2013-05-27 17:46:52 UTC (rev 56437)
@@ -37,7 +37,11 @@
static int copy_lines_1(struct Map_info *, int, struct Map_info *);
static int copy_lines_2(struct Map_info *, int, int, struct Map_info *);
+#if 0
static int copy_nodes(const struct Map_info *, struct Map_info *);
+#endif
+static int copy_line_nodes(const struct Map_info *, int, int, struct line_pnts *,
+ struct Map_info *);
static int is_isle(const struct Map_info *, int);
static int copy_areas(const struct Map_info *, int, struct Map_info *);
@@ -109,11 +113,12 @@
ret = 0;
if (Vect_level(In) >= 2) {
/* -> copy features on level 2 */
+#if 0
if (topo == TOPO_POSTGIS) {
/* PostGIS topology - copy also nodes */
copy_nodes(In, Out);
}
-
+#endif
/* copy features */
ret += copy_lines_2(In, field, topo, Out);
@@ -199,17 +204,20 @@
*/
int copy_lines_2(struct Map_info *In, int field, int topo, struct Map_info *Out)
{
- int i, type, nlines, nskipped;
- int ret, left, rite, centroid;
+ int i, type, nlines, nskipped;
+ int ret, left, rite, centroid, with_z;
- struct line_pnts *Points, *CPoints;
+ struct line_pnts *Points, *CPoints, *NPoints;
struct line_cats *Cats, *CCats;
Points = Vect_new_line_struct();
CPoints = Vect_new_line_struct();
+ NPoints = Vect_new_line_struct();
Cats = Vect_new_cats_struct();
CCats = Vect_new_cats_struct();
+ with_z = Vect_is_3d(In);
+
ret = 0;
nlines = Vect_get_num_lines(In);
if (topo == TOPO_NONE) {
@@ -225,6 +233,7 @@
else
G_message(_("Copying features..."));
+ Vect_append_point(NPoints, 0., 0., 0.);
nskipped = 0;
for (i = 1; i <= nlines; i++) {
if (!Vect_line_alive(In, i))
@@ -288,6 +297,39 @@
}
}
+ /* copy also nodes connected to the line (PostGIS Topology
+ * mode only) */
+ if (topo == TOPO_POSTGIS && (type & GV_LINES)) {
+ int n1, n2;
+
+ struct P_line *Line;
+ struct Format_info_offset *offset;
+
+ offset = &(Out->fInfo.pg.offset);
+
+ n1 = n2 = -1;
+ Line = In->plus.Line[i];
+ if (Line) {
+ if (type == GV_LINE) {
+ struct P_topo_l *topo = (struct P_topo_l *)Line->topo;
+
+ n1 = topo->N1;
+ n2 = topo->N2;
+ }
+ else if (type == GV_BOUNDARY) {
+ struct P_topo_b *topo = (struct P_topo_b *)Line->topo;
+
+ n1 = topo->N1;
+ n2 = topo->N2;
+ }
+ }
+
+ if (n1 > 0 && (n1 > offset->array_num || offset->array[n1-1] == 0))
+ copy_line_nodes(In, n1, with_z, NPoints, Out);
+ if (n2 > 0 && (n2 > offset->array_num || offset->array[n2-1] == 0))
+ copy_line_nodes(In, n2, with_z, NPoints, Out);
+ }
+
if (-1 == Vect_write_line(Out, type, Points, Cats)) {
G_warning(_("Writing new feature failed"));
return 1;
@@ -299,12 +341,14 @@
Vect_destroy_line_struct(Points);
Vect_destroy_line_struct(CPoints);
+ Vect_destroy_line_struct(NPoints);
Vect_destroy_cats_struct(Cats);
Vect_destroy_cats_struct(CCats);
return ret;
}
+#if 0
/*!
\brief Copy nodes as points (PostGIS Topology only)
@@ -317,40 +361,50 @@
int copy_nodes(const struct Map_info *In, struct Map_info *Out)
{
int nnodes, node, with_z;
- double x, y, z;
+
struct line_pnts *Points;
+
+ Points = Vect_new_line_struct();
- Points = Vect_new_line_struct();
-
with_z = Vect_is_3d(In);
nnodes = Vect_get_num_nodes(In);
- if (nnodes > 0)
- G_message(_("Exporting nodes..."));
+ G_message(_("Exporting nodes..."));
Vect_append_point(Points, 0., 0., 0.);
for (node = 1; node <= nnodes; node++) {
G_debug(3, "Exporting GRASS node %d", node);
G_percent(node, nnodes, 5);
- Vect_get_node_coor(In, node, &x, &y, &z);
- Points->x[0] = x;
- Points->y[0] = y;
- if (with_z)
- Points->z[0] = z;
-
+ copy_line_nodes(In, node, with_z, Points, Out);
+ }
+
+ Vect_destroy_line_struct(Points);
+
+ return 0;
+}
+#endif
+
+int copy_line_nodes(const struct Map_info *In, int node, int with_z,
+ struct line_pnts *Points, struct Map_info *Out)
+{
+ double x, y, z;
+
+ Vect_get_node_coor(In, node, &x, &y, &z);
+ Points->x[0] = x;
+ Points->y[0] = y;
+ if (with_z)
+ Points->z[0] = z;
+
#ifdef HAVE_POSTGRES
- if (-1 == V2__write_node_pg(Out, Points)) {
- G_warning(_("Writing node %d failed"), node);
- return 1;
- }
+ if (-1 == V2__write_node_pg(Out, Points)) {
+ G_warning(_("Writing node %d failed"), node);
+ return 1;
+ }
#else
- G_fatal_error(_("GRASS is not compiled with PostgreSQL support"));
- return 1;
+ G_fatal_error(_("GRASS is not compiled with PostgreSQL support"));
+ return 1;
#endif
- }
- Vect_destroy_line_struct(Points);
-
return 0;
}
Modified: grass/trunk/lib/vector/Vlib/open.c
===================================================================
--- grass/trunk/lib/vector/Vlib/open.c 2013-05-27 14:17:29 UTC (rev 56436)
+++ grass/trunk/lib/vector/Vlib/open.c 2013-05-27 17:46:52 UTC (rev 56437)
@@ -1316,12 +1316,18 @@
/* PostGIS topology enabled ? */
p = G_find_key_value("topology", key_val);
if (p && G_strcasecmp(p, "yes") == 0) {
- /* define topology name
- this should be configurable by the user
- */
- G_asprintf(&(pg_info->toposchema_name), "topo_%s",
- pg_info->table_name);
+ /* define topology name */
+ p = G_find_key_value("toposchema_name", key_val);
+ if (p)
+ pg_info->toposchema_name = G_store(p);
+ else
+ G_asprintf(&(pg_info->toposchema_name), "topo_%s",
+ pg_info->table_name);
+
+ G_debug(1, "PG: topology = yes, schema_name = %s",
+ pg_info->toposchema_name);
}
+ G_debug(1, "PG: topology = no");
if (getenv("GRASS_VECTOR_EXTERNAL_IMMEDIATE")) {
/* vector features are written directly to PostGIS layer */
Modified: grass/trunk/lib/vector/Vlib/open_pg.c
===================================================================
--- grass/trunk/lib/vector/Vlib/open_pg.c 2013-05-27 14:17:29 UTC (rev 56436)
+++ grass/trunk/lib/vector/Vlib/open_pg.c 2013-05-27 17:46:52 UTC (rev 56437)
@@ -39,12 +39,20 @@
static int check_topo(struct Format_info_pg *, struct Plus_head *);
static int parse_bbox(const char *, struct bound_box *);
static struct P_node *read_p_node(struct Plus_head *, int, int,
- const char *, struct Format_info_pg *);
+ const char *, const char *, const char *,
+ struct Format_info_pg *);
static struct P_line *read_p_line(struct Plus_head *, int,
const struct edge_data *,
struct Format_info_cache *);
+static struct P_area *read_p_area(struct Plus_head *, int,
+ const char *, int, const char *);
+static struct P_isle *read_p_isle(struct Plus_head *, int,
+ const char *, int);
static int load_plus_head(struct Format_info_pg *, struct Plus_head *);
static void notice_processor(void *, const char *);
+static char **scan_array(const char *);
+static int remap_node(const struct Format_info_offset *, int);
+static int remap_line(const struct Plus_head*, int);
#endif
/*!
@@ -543,9 +551,15 @@
pg_info->toposchema_name = G_store(PQgetvalue(res, 0, 1));
pg_info->topogeom_column = G_store(PQgetvalue(res, 0, 3));
- G_debug(1, "PostGIS topology detected: schema = %s column = %s",
- pg_info->toposchema_name, pg_info->topogeom_column);
+ /* check extra GRASS tables */
+ sprintf(stmt, "SELECT COUNT(*) FROM pg_tables WHERE schemaname = '%s' "
+ "AND tablename LIKE '%%_grass'", pg_info->toposchema_name);
+ if (Vect__execute_get_value_pg(pg_info->conn, stmt) != TOPO_TABLE_NUM)
+ pg_info->topo_geo_only = TRUE;
+ G_debug(1, "PostGIS topology detected: schema = %s column = %s topo_geo_only = %d",
+ pg_info->toposchema_name, pg_info->topogeom_column, pg_info->topo_geo_only);
+
/* check for 3D */
if (strcmp(PQgetvalue(res, 0, 2), "t") == 0)
plus->with_z = WITH_Z;
@@ -630,45 +644,60 @@
\param n index (starts at 1)
\param id node id (table "node")
\param wkb_data geometry data (wkb)
+ \param lines_data lines array or NULL
+ \param angles_data angles array or NULL
\param pg_info pointer to Format_info_pg sttucture
\return pointer to new P_node struct
\return NULL on error
*/
struct P_node *read_p_node(struct Plus_head *plus, int n,
- int id, const char *wkb_data,
- struct Format_info_pg *pg_info)
+ int id, const char *wkb_data, const char *lines_data,
+ const char *angles_data, struct Format_info_pg *pg_info)
{
int i, cnt;
- char stmt[DB_SQL_MAX];
-
+ char **lines, **angles;
+
struct P_node *node;
struct line_pnts *points;
PGresult *res;
/* get lines connected to the node */
- sprintf(stmt,
- "SELECT edge_id,'s' as node,"
- "ST_Azimuth(ST_StartPoint(geom), ST_PointN(geom, 2)) AS angle"
- " FROM \"%s\".edge WHERE start_node = %d UNION ALL "
- "SELECT edge_id,'e' as node,"
- "ST_Azimuth(ST_EndPoint(geom), ST_PointN(geom, ST_NumPoints(geom) - 1)) AS angle"
- " FROM \"%s\".edge WHERE end_node = %d"
- " ORDER BY angle DESC",
- pg_info->toposchema_name, id,
- pg_info->toposchema_name, id);
- G_debug(2, "SQL: %s", stmt);
- res = PQexec(pg_info->conn, stmt);
- if (!res || PQresultStatus(res) != PGRES_TUPLES_OK) {
- G_warning(_("Inconsistency in topology: unable to read node %d"), id);
- if (res)
- PQclear(res);
- return NULL;
+ lines = angles = NULL;
+ if (!lines_data && !angles_data) { /* pg_info->topo_geo_only == TRUE */
+ char stmt[DB_SQL_MAX];
+
+ sprintf(stmt,
+ "SELECT edge_id,'s' as node,"
+ "ST_Azimuth(ST_StartPoint(geom), ST_PointN(geom, 2)) AS angle"
+ " FROM \"%s\".edge WHERE start_node = %d UNION ALL "
+ "SELECT edge_id,'e' as node,"
+ "ST_Azimuth(ST_EndPoint(geom), ST_PointN(geom, ST_NumPoints(geom) - 1)) AS angle"
+ " FROM \"%s\".edge WHERE end_node = %d"
+ " ORDER BY angle DESC",
+ pg_info->toposchema_name, id,
+ pg_info->toposchema_name, id);
+ G_debug(2, "SQL: %s", stmt);
+ res = PQexec(pg_info->conn, stmt);
+ if (!res || PQresultStatus(res) != PGRES_TUPLES_OK) {
+ G_warning(_("Inconsistency in topology: unable to read node %d"), id);
+ if (res)
+ PQclear(res);
+ return NULL;
+ }
+ cnt = PQntuples(res);
}
- cnt = PQntuples(res);
-
- if (cnt == 0) { /* dead ??? */
+ else { /* pg_info->topo_geo_only != TRUE */
+ lines = scan_array(lines_data);
+ angles = scan_array(angles_data);
+
+ cnt = G_number_of_tokens(lines);
+ if (cnt != G_number_of_tokens(angles))
+ return NULL; /* 'lines' and 'angles' array must have the same size */
+ }
+
+ if (cnt == 0) { /* dead */
plus->Node[n] = NULL;
return NULL;
}
@@ -681,22 +710,36 @@
return NULL;
/* lines / angles */
- for (i = 0; i < node->n_lines; i++) {
- node->lines[i] = atoi(PQgetvalue(res, i, 0));
- if (strcmp(PQgetvalue(res, i, 1), "s") != 0) {
- /* end node */
- node->lines[i] *= -1;
+ if (lines) {
+ for (i = 0; i < node->n_lines; i++) {
+ node->lines[i] = atoi(lines[i]);
+ node->angles[i] = atof(angles[i]);
+
+ G_debug(5, "\tline = %d angle = %f", node->lines[i],
+ node->angles[i]);
}
- node->angles[i] = M_PI / 2 - atof(PQgetvalue(res, i, 2));
- /* angles range <-PI; PI> */
- if (node->angles[i] > M_PI)
- node->angles[i] = node->angles[i] - 2 * M_PI;
- if (node->angles[i] < -1.0 * M_PI)
- node->angles[i] = node->angles[i] + 2 * M_PI;
- G_debug(5, "\tline = %d angle = %f", node->lines[i],
- node->angles[i]);
+
+ G_free_tokens(lines);
+ G_free_tokens(angles);
}
- PQclear(res);
+ else {
+ for (i = 0; i < node->n_lines; i++) {
+ node->lines[i] = atoi(PQgetvalue(res, i, 0));
+ if (strcmp(PQgetvalue(res, i, 1), "s") != 0) {
+ /* end node */
+ node->lines[i] *= -1;
+ }
+ node->angles[i] = M_PI / 2 - atof(PQgetvalue(res, i, 2));
+ /* angles range <-PI; PI> */
+ if (node->angles[i] > M_PI)
+ node->angles[i] = node->angles[i] - 2 * M_PI;
+ if (node->angles[i] < -1.0 * M_PI)
+ node->angles[i] = node->angles[i] + 2 * M_PI;
+ G_debug(5, "\tline = %d angle = %f", node->lines[i],
+ node->angles[i]);
+ }
+ PQclear(res);
+ }
/* get node coordinates */
if (SF_POINT != Vect__cache_feature_pg(wkb_data, FALSE, FALSE,
@@ -784,6 +827,9 @@
else {
line->topo = dig_alloc_topo(line->type);
+ if ((line->type & GV_LINES) & (data->start_node < 0 || data->end_node < 0))
+ return NULL;
+
/* lines */
if (line->type == GV_LINE) {
struct P_topo_l *topo = (struct P_topo_l *)line->topo;
@@ -833,6 +879,117 @@
}
/*!
+ \brief Read P_area structure
+
+ \param plus pointer to Plus_head structure
+ \param n index (starts at 1)
+ \param lines_data lines array (see P_area struct)
+ \param centroid centroid id (see P_area struct)
+ \param isles_data lines array (see P_area struct)
+
+ \return pointer to P_area struct
+ \return NULL on error
+*/
+struct P_area *read_p_area(struct Plus_head *plus, int n,
+ const char *lines_data, int centroid, const char *isles_data)
+{
+ int i;
+ int nlines, nisles;
+ char **lines, **isles;
+
+ struct P_area *area;
+
+ lines = scan_array(lines_data);
+ nlines = G_number_of_tokens(lines);
+ isles = scan_array(isles_data);
+ nisles = G_number_of_tokens(isles);
+
+ if (nlines < 1) {
+ G_warning(_("Area %d without boundary detected"), n);
+ return NULL;
+ }
+
+ G_debug(3, "read_p_area(): n = %d nlines = %d nisles = %d", n, nlines, nisles);
+
+ /* allocate area */
+ area = dig_alloc_area();
+ dig_area_alloc_line(area, nlines);
+ dig_area_alloc_isle(area, nisles);
+
+ /* set lines */
+ area->n_lines = nlines;
+ for (i = 0; i < nlines; i++) {
+ area->lines[i] = atoi(lines[i]);
+ }
+
+ /* set isles */
+ area->n_isles = nisles;
+ for (i = 0; i < nisles; i++) {
+ area->isles[i] = atoi(isles[i]);
+ }
+
+ /* set centroid */
+ area->centroid = remap_line(plus, centroid);
+
+ G_free_tokens(lines);
+ G_free_tokens(isles);
+
+ plus->Area[n] = area;
+
+ return area;
+}
+
+/*!
+ \brief Read P_isle structure
+
+ \param plus pointer to Plus_head structure
+ \param n index (starts at 1)
+ \param lines_data lines array (see P_isle struct)
+ \param area area id (see P_isle struct)
+
+ \return pointer to P_isle struct
+ \return NULL on error
+*/
+struct P_isle *read_p_isle(struct Plus_head *plus, int n,
+ const char *lines_data, int area)
+{
+ int i;
+ int nlines;
+ char **lines;
+
+ struct P_isle *isle;
+
+ lines = scan_array(lines_data);
+ nlines = G_number_of_tokens(lines);
+
+ if (nlines < 1) {
+ G_warning(_("Isle %d without boundary detected"), n);
+ return NULL;
+ }
+
+ G_debug(3, "read_p_isle(): n = %d nlines = %d", n, nlines);
+
+ /* allocate isle */
+ isle = dig_alloc_isle();
+ dig_isle_alloc_line(isle, nlines);
+
+ /* set lines */
+ isle->n_lines = nlines;
+ for (i = 0; i < nlines; i++) {
+ isle->lines[i] = atoi(lines[i]);
+ }
+
+ /* set area */
+ isle->area = area;
+
+ G_free_tokens(lines);
+
+ plus->Isle[n] = isle;
+
+ return isle;
+}
+
+/*!
\brief Read topo from PostGIS topology schema -- header info only
\param[in,out] plus pointer to Plus_head struct
@@ -890,6 +1047,19 @@
"AS node FROM \"%s\".edge GROUP BY end_node) AS foo",
pg_info->toposchema_name, pg_info->toposchema_name);
plus->n_nodes = Vect__execute_get_value_pg(pg_info->conn, stmt);
+ if (!pg_info->topo_geo_only) {
+ int n_nodes;
+
+ /* check nodes consistency */
+ sprintf(stmt, "SELECT COUNT(*) FROM \"%s\".%s",
+ pg_info->toposchema_name, TOPO_TABLE_NODE);
+ n_nodes = Vect__execute_get_value_pg(pg_info->conn, stmt);
+ if (n_nodes != plus->n_nodes) {
+ G_warning(_("Different number of nodes detected (%d, %d)"),
+ plus->n_nodes, n_nodes);
+ return -1;
+ }
+ }
G_debug(3, "Vect_open_topo_pg(): n_nodes=%d", plus->n_nodes);
/* lines (edges in PostGIS Topology model) */
@@ -905,6 +1075,19 @@
"SELECT COUNT(*) FROM \"%s\".face WHERE face_id > 0",
pg_info->toposchema_name);
plus->n_areas = Vect__execute_get_value_pg(pg_info->conn, stmt);
+ if (!pg_info->topo_geo_only) {
+ int n_areas;
+
+ /* check areas consistency */
+ sprintf(stmt, "SELECT COUNT(*) FROM \"%s\".%s",
+ pg_info->toposchema_name, TOPO_TABLE_AREA);
+ n_areas = Vect__execute_get_value_pg(pg_info->conn, stmt);
+ if (n_areas != plus->n_areas) {
+ G_warning(_("Different number of areas detected (%d, %d)"),
+ plus->n_areas, n_areas);
+ return -1;
+ }
+ }
G_debug(3, "Vect_open_topo_pg(): n_areas=%d", plus->n_areas);
/* isles (faces with face_id <=0 in PostGIS Topology model)
@@ -914,6 +1097,19 @@
"SELECT COUNT(*) FROM \"%s\".face WHERE face_id < 0",
pg_info->toposchema_name);
plus->n_isles = Vect__execute_get_value_pg(pg_info->conn, stmt);
+ if (!pg_info->topo_geo_only) {
+ int n_isles;
+
+ /* check areas consistency */
+ sprintf(stmt, "SELECT COUNT(*) FROM \"%s\".%s",
+ pg_info->toposchema_name, TOPO_TABLE_ISLE);
+ n_isles = Vect__execute_get_value_pg(pg_info->conn, stmt);
+ if (n_isles != plus->n_isles) {
+ G_warning(_("Different number of areas detected (%d, %d)"),
+ plus->n_isles, n_isles);
+ return -1;
+ }
+ }
G_debug(3, "Vect_open_topo_pg(): n_isles=%d", plus->n_isles);
/* number of features according the type */
@@ -982,6 +1178,7 @@
struct edge_data line_data;
struct Format_info_pg *pg_info;
+ struct Format_info_offset *offset;
struct Plus_head *plus;
struct P_line *Line;
struct P_area *Area;
@@ -992,6 +1189,7 @@
pg_info = &(Map->fInfo.pg);
plus = &(Map->plus);
+ offset = &(pg_info->offset);
if (load_plus_head(pg_info, plus) != 0)
return -1;
@@ -1005,13 +1203,19 @@
/* read nodes (GRASS Topo)
note: standalone nodes (ie. points/centroids) are ignored
*/
- sprintf(stmt,
- "SELECT node_id,geom FROM \"%s\".node WHERE node_id IN "
- "(SELECT node FROM (SELECT start_node AS node FROM \"%s\".edge "
- "GROUP BY start_node UNION ALL SELECT end_node AS node FROM "
- "\"%s\".edge GROUP BY end_node) AS foo) ORDER BY node_id",
- pg_info->toposchema_name, pg_info->toposchema_name,
- pg_info->toposchema_name);
+ if (pg_info->topo_geo_only)
+ sprintf(stmt,
+ "SELECT node_id,geom FROM \"%s\".node WHERE node_id IN "
+ "(SELECT node FROM (SELECT start_node AS node FROM \"%s\".edge "
+ "GROUP BY start_node UNION ALL SELECT end_node AS node FROM "
+ "\"%s\".edge GROUP BY end_node) AS foo) ORDER BY node_id",
+ pg_info->toposchema_name, pg_info->toposchema_name,
+ pg_info->toposchema_name);
+ else
+ sprintf(stmt, "SELECT node.node_id,geom,lines,angles FROM \"%s\".node AS node "
+ "join \"%s\".%s AS node_grass ON node.node_id = node_grass.node_id "
+ "ORDER BY node_id", pg_info->toposchema_name, pg_info->toposchema_name,
+ TOPO_TABLE_NODE);
G_debug(2, "SQL: %s", stmt);
res = PQexec(pg_info->conn, stmt);
if (!res || PQresultStatus(res) != PGRES_TUPLES_OK ||
@@ -1026,11 +1230,18 @@
G_debug(3, "load_plus(): n_nodes = %d", plus->n_nodes);
dig_alloc_nodes(plus, plus->n_nodes);
+ offset->array = (int *) G_malloc (sizeof(int) * plus->n_nodes);
+ offset->array_num = offset->array_alloc = plus->n_nodes;
for (i = 0; i < plus->n_nodes; i++) {
G_debug(5, "node: %d", i);
id = atoi(PQgetvalue(res, i, 0));
read_p_node(plus, i + 1, /* node index starts at 1 */
- id, (const char *) PQgetvalue(res, i, 1), pg_info);
+ id, (const char *) PQgetvalue(res, i, 1),
+ !pg_info->topo_geo_only ? (const char *) PQgetvalue(res, i, 2) : NULL,
+ !pg_info->topo_geo_only ? (const char *) PQgetvalue(res, i, 3) : NULL,
+ pg_info);
+ /* update offset */
+ offset->array[i] = id;
}
PQclear(res);
@@ -1045,14 +1256,20 @@
/* read PostGIS Topo standalone nodes (containing_face is null)
-> points
*/
- sprintf(stmt,
- "SELECT node_id,geom FROM \"%s\".node WHERE containing_face "
- "IS NULL AND node_id NOT IN "
- "(SELECT node FROM (SELECT start_node AS node FROM \"%s\".edge "
- "GROUP BY start_node UNION ALL SELECT end_node AS node FROM "
- "\"%s\".edge GROUP BY end_node) AS foo) ORDER BY node_id",
- pg_info->toposchema_name, pg_info->toposchema_name,
- pg_info->toposchema_name);
+ if (pg_info->topo_geo_only)
+ sprintf(stmt,
+ "SELECT node_id,geom FROM \"%s\".node WHERE containing_face "
+ "IS NULL AND node_id NOT IN "
+ "(SELECT node FROM (SELECT start_node AS node FROM \"%s\".edge "
+ "GROUP BY start_node UNION ALL SELECT end_node AS node FROM "
+ "\"%s\".edge GROUP BY end_node) AS foo) ORDER BY node_id",
+ pg_info->toposchema_name, pg_info->toposchema_name,
+ pg_info->toposchema_name);
+ else
+ sprintf(stmt,
+ "SELECT node.node_id,geom FROM \"%s\".node AS node WHERE node_id NOT IN "
+ "(SELECT node_id FROM \"%s\".%s) AND containing_face IS NULL ORDER BY node_id",
+ pg_info->toposchema_name, pg_info->toposchema_name, TOPO_TABLE_NODE);
G_debug(2, "SQL: %s", stmt);
res = PQexec(pg_info->conn, stmt);
if (!res || PQresultStatus(res) != PGRES_TUPLES_OK ||
@@ -1099,8 +1316,8 @@
ntuples = PQntuples(res);
for (i = 0; i < ntuples; i++) {
line_data.id = atoi(PQgetvalue(res, i, 0));
- line_data.start_node = atoi(PQgetvalue(res, i, 1));
- line_data.end_node = atoi(PQgetvalue(res, i, 2));
+ line_data.start_node = remap_node(offset, atoi(PQgetvalue(res, i, 1)));
+ line_data.end_node = remap_node(offset, atoi(PQgetvalue(res, i, 2)));
line_data.left_face = atoi(PQgetvalue(res, i, 3));
line_data.right_face = atoi(PQgetvalue(res, i, 4));
line_data.wkb_geom = (char *) PQgetvalue(res, i, 5);
@@ -1114,14 +1331,21 @@
/* read PostGIS Topo standalone nodes (containing_face is not null)
-> centroids
*/
- sprintf(stmt,
- "SELECT node_id,geom,containing_face FROM \"%s\".node WHERE containing_face "
- "IS NOT NULL AND node_id NOT IN "
- "(SELECT node FROM (SELECT start_node AS node FROM \"%s\".edge "
- "GROUP BY start_node UNION ALL SELECT end_node AS node FROM "
- "\"%s\".edge GROUP BY end_node) AS foo) ORDER BY node_id",
- pg_info->toposchema_name, pg_info->toposchema_name,
- pg_info->toposchema_name);
+ if (pg_info->topo_geo_only)
+ sprintf(stmt,
+ "SELECT node_id,geom,containing_face FROM \"%s\".node WHERE containing_face "
+ "IS NOT NULL AND node_id NOT IN "
+ "(SELECT node FROM (SELECT start_node AS node FROM \"%s\".edge "
+ "GROUP BY start_node UNION ALL SELECT end_node AS node FROM "
+ "\"%s\".edge GROUP BY end_node) AS foo) ORDER BY node_id",
+ pg_info->toposchema_name, pg_info->toposchema_name,
+ pg_info->toposchema_name);
+ else
+ sprintf(stmt,
+ "SELECT node.node_id,geom,containing_face FROM \"%s\".node AS node WHERE "
+ "node_id NOT IN (SELECT node_id FROM \"%s\".%s) AND containing_face "
+ "IS NOT NULL ORDER BY node_id",
+ pg_info->toposchema_name, pg_info->toposchema_name, TOPO_TABLE_NODE);
G_debug(2, "SQL: %s", stmt);
res = PQexec(pg_info->conn, stmt);
if (!res || PQresultStatus(res) != PGRES_TUPLES_OK ||
@@ -1145,29 +1369,89 @@
read_p_line(plus, id + i, &line_data, &(pg_info->cache));
}
PQclear(res);
+ plus->built = GV_BUILD_BASE;
+
+ /* build areas */
+ if (pg_info->topo_geo_only) {
+ /* build areas for boundaries
+ reset values -> build from scratch */
+ plus->n_areas = plus->n_isles = 0;
+ for (line = 1; line <= plus->n_lines; line++) {
+ Line = plus->Line[line]; /* centroids: Line is NULL */
+ if (!Line || Line->type != GV_BOUNDARY)
+ continue;
+
+ for (i = 0; i < 2; i++) { /* for both sides build an area/isle */
+ side = i == 0 ? GV_LEFT : GV_RIGHT;
+
+ G_debug(3, "Build area for line = %d, side = %d",
+ id, side);
+ Vect_build_line_area(Map, line, side);
+ }
+ }
+ }
+ else {
+ /* read areas from 'area_grass' table */
+ sprintf(stmt,
+ "SELECT area_id,lines,centroid,isles FROM \"%s\".%s ORDER BY area_id",
+ pg_info->toposchema_name, TOPO_TABLE_AREA);
+ G_debug(2, "SQL: %s", stmt);
+
+ res = PQexec(pg_info->conn, stmt);
+ if (!res || PQresultStatus(res) != PGRES_TUPLES_OK |
+ plus->n_areas != PQntuples(res)) {
+ if (res)
+ PQclear(res);
+ return -1;
+ }
- /* build areas for boundaries
- reset values -> build from scratch */
- plus->n_areas = plus->n_isles = 0;
- for (line = 1; line <= plus->n_lines; line++) {
- Line = plus->Line[line]; /* centroids: Line is NULL */
- if (!Line || Line->type != GV_BOUNDARY)
- continue;
- for (i = 0; i < 2; i++) { /* for both sides build an area/isle */
- side = i == 0 ? GV_LEFT : GV_RIGHT;
-
- G_debug(3, "Build area for line = %d, side = %d",
- id, side);
- Vect_build_line_area(Map, line, side);
+ dig_alloc_areas(plus, plus->n_areas);
+ G_zero(plus->Area, sizeof(struct P_area *) * (plus->n_areas + 1)); /* index starts at 1 */
+ G_debug(3, "Vect_open_topo_pg(): n_areas=%d", plus->n_areas);
+
+ for (i = 0; i < plus->n_areas; i++) {
+ read_p_area(plus, i + 1, (char *)PQgetvalue(res, i, 1),
+ atoi(PQgetvalue(res, i, 2)), (char *)PQgetvalue(res, i, 3));
}
+ PQclear(res);
}
plus->built = GV_BUILD_AREAS;
- /* TODO: attach isles */
+ /* attach isles */
+ if (pg_info->topo_geo_only) {
+ plus->n_isles = 0; /* reset isles */
+ G_warning(_("To be implented: isles not attached in Topo-Geo-only mode"));
+ }
+ else {
+ /* read isles from 'isle_grass' table */
+ sprintf(stmt,
+ "SELECT isle_id,lines,area FROM \"%s\".%s ORDER BY isle_id",
+ pg_info->toposchema_name, TOPO_TABLE_ISLE);
+ G_debug(2, "SQL: %s", stmt);
+
+ res = PQexec(pg_info->conn, stmt);
+ if (!res || PQresultStatus(res) != PGRES_TUPLES_OK ||
+ plus->n_isles != PQntuples(res)) {
+ if (res)
+ PQclear(res);
+ return -1;
+ }
+
+ dig_alloc_isles(plus, plus->n_isles);
+ G_zero(plus->Isle, sizeof(struct P_isle *) * (plus->n_isles + 1)); /* index starts at 1 */
+ G_debug(3, "Vect_open_topo_pg(): n_isles=%d", plus->n_isles);
+
+ for (i = 0; i < plus->n_isles; i++) {
+ read_p_isle(plus, i + 1, (char *)PQgetvalue(res, i, 1),
+ atoi(PQgetvalue(res, i, 2)));
+ }
+ PQclear(res);
+ }
plus->built = GV_BUILD_ATTACH_ISLES;
- /* attach centroids */
+ /* attach centroids (disabled: centroids are already attached) */
+#if 0
if (plus->n_areas > 0) {
struct P_topo_c *topo;
@@ -1184,6 +1468,7 @@
Area->centroid = Line->offset;
}
}
+#endif
plus->built = GV_BUILD_CENTROIDS;
/* done */
@@ -1206,4 +1491,87 @@
fprintf(stderr, "%s", message);
}
}
+
+/*!
+ \brief Scan string array
+
+ Creates tokens based on string array, eg. '{1, 2, 3}' become
+ [1,2,3].
+
+ Allocated tokes should be freed by G_free_tokens().
+
+ \param sArray string array
+
+ \return tokens
+*/
+char **scan_array(const char *sarray)
+{
+ char *buf, **tokens;
+ int i, len;
+
+ /* remove '{}' */
+ buf = (char *)G_malloc(sizeof(sarray) - 2);
+ len = strlen(sarray) - 1;
+ for (i = 1; i < len; i++)
+ buf[i-1] = sarray[i];
+ buf[len-1] = '\0';
+
+ tokens = G_tokenize(buf, ",");
+ G_free(buf);
+
+ return tokens;
+}
+
+/*!
+ \brief Get node id from offset
+
+ \todo speed up
+
+ \param offset pointer to Format_info_offset struct
+ \param node node to find
+
+ \return node id
+ \return -1 not found
+*/
+int remap_node(const struct Format_info_offset *offset, int node)
+{
+ int i;
+
+ for (i = 0; i < offset->array_num; i++) {
+ if (offset->array[i] == node)
+ return i + 1; /* node id starts at 1 */
+ }
+
+ return -1;
+}
+
+/*!
+ \brief Get line id from offset
+
+ \todo speed up
+
+ \param plus pointer to Plus_head struct
+ \param line line to find
+
+ \return line id
+ \return -1 not found
+*/
+int remap_line(const struct Plus_head* plus, int line)
+{
+ int i;
+
+ struct P_line *Line;
+
+ for (i = 1; i <= plus->n_lines; i++) {
+ Line = plus->Line[i];
+
+ if (!Line)
+ continue;
+
+ if ((int) Line->offset == line)
+ return i;
+ }
+
+ return -1;
+}
#endif
Modified: grass/trunk/lib/vector/Vlib/pg_local_proto.h
===================================================================
--- grass/trunk/lib/vector/Vlib/pg_local_proto.h 2013-05-27 14:17:29 UTC (rev 56436)
+++ grass/trunk/lib/vector/Vlib/pg_local_proto.h 2013-05-27 17:46:52 UTC (rev 56437)
@@ -13,6 +13,10 @@
#define TOPO_ID "topology_id"
#define TOPO_TABLE "grass"
#define TOPO_BBOX "bbox"
+#define TOPO_TABLE_NUM 3
+#define TOPO_TABLE_NODE "node_grass"
+#define TOPO_TABLE_AREA "area_grass"
+#define TOPO_TABLE_ISLE "isle_grass"
#define SWAP32(x) \
((unsigned int)( \
Modified: grass/trunk/lib/vector/Vlib/write_pg.c
===================================================================
--- grass/trunk/lib/vector/Vlib/write_pg.c 2013-05-27 14:17:29 UTC (rev 56436)
+++ grass/trunk/lib/vector/Vlib/write_pg.c 2013-05-27 17:46:52 UTC (rev 56437)
@@ -458,6 +458,7 @@
GV_BUILD_BASE. PostGIS Topology schema must be defined.
\param Map pointer to Map_info structure
+ \param node node id (starts at 1)
\param points pointer to line_pnts structure
\return 0 on success
@@ -764,6 +765,8 @@
- create topology schema
- add topology column to the feature table
+ \todo Add constraints for grass-like tables
+
\param pg_info pointer to Format_info_pg
\return 0 on success
@@ -796,16 +799,24 @@
fclose(fp);
/* tolerance */
- p = G_find_key_value("tolerance", key_val);
+ p = G_find_key_value("topo_tolerance", key_val);
if (p)
tolerance = atof(p);
-
+ G_debug(1, "PG: tolerance: %f", tolerance);
+
/* topogeom column */
- p = G_find_key_value("topogeom_column", key_val);
+ p = G_find_key_value("topogeom_name", key_val);
if (p)
pg_info->topogeom_column = G_store(p);
else
pg_info->topogeom_column = G_store(TOPOGEOM_COLUMN);
+ G_debug(1, "PG: topogeom_column :%s", pg_info->topogeom_column);
+
+ /* topo-geo only (default: no) */
+ p = G_find_key_value("topo_geo_only", key_val);
+ if (p && G_strcasecmp(p, "yes") == 0)
+ pg_info->topo_geo_only = TRUE;
+ G_debug(1, "PG: topo_geo_only :%d", pg_info->topo_geo_only);
}
/* begin transaction (create topo schema) */
@@ -848,6 +859,53 @@
return -1;
}
+ /* create additional tables in topological schema to store
+ GRASS topology in DB */
+ if (!pg_info->topo_geo_only) {
+ /* (1) create 'node_grass' (see P_node struct)
+
+ todo: add constraints for lines and angles
+ */
+ sprintf(stmt, "CREATE TABLE \"%s\".%s (node_id SERIAL PRIMARY KEY, "
+ "lines integer[], angles float[])", pg_info->toposchema_name, TOPO_TABLE_NODE);
+ if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
+ Vect__execute_pg(pg_info->conn, "ROLLBACK");
+ return -1;
+ }
+
+ sprintf(stmt, "ALTER TABLE \"%s\".%s ADD CONSTRAINT node_exists "
+ "FOREIGN KEY (node_id) REFERENCES \"%s\".node (node_id)",
+ pg_info->toposchema_name, TOPO_TABLE_NODE, pg_info->toposchema_name);
+ if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
+ Vect__execute_pg(pg_info->conn, "ROLLBACK");
+ return -1;
+ }
+
+ /* (2) create 'area_grass' (see P_area struct)
+
+ todo: add constraints for lines, centtroid and isles
+ */
+ sprintf(stmt, "CREATE TABLE \"%s\".%s (area_id SERIAL PRIMARY KEY, "
+ "lines integer[], centroid integer, isles integer[])",
+ pg_info->toposchema_name, TOPO_TABLE_AREA);
+ if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
+ Vect__execute_pg(pg_info->conn, "ROLLBACK");
+ return -1;
+ }
+
+ /* (3) create 'isle_grass' (see P_isle struct)
+
+ todo: add constraints for lines and area
+ */
+ sprintf(stmt, "CREATE TABLE \"%s\".%s (isle_id SERIAL PRIMARY KEY, "
+ "lines integer[], area integer)",
+ pg_info->toposchema_name, TOPO_TABLE_ISLE);
+ if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
+ Vect__execute_pg(pg_info->conn, "ROLLBACK");
+ return -1;
+ }
+ }
+
/* close transaction (create topo schema) */
if (Vect__execute_pg(pg_info->conn, "COMMIT") == -1) {
return -1;
@@ -1148,13 +1206,14 @@
\param is_node TRUE for nodes (written as points)
\return feature id (build level >= GV_BUILD_BASE otherwise 0)
+ \return 0 for nodes
\return -1 on error
*/
off_t write_line_tp(struct Map_info *Map, int type, int is_node,
const struct line_pnts *points,
const struct line_cats *cats)
{
- int line, cat;
+ int line, cat, line_id;
struct field_info *Fi;
struct Format_info_pg *pg_info;
@@ -1242,11 +1301,30 @@
- feature table for simple features
- feature table and topo schema for topological access
*/
- if (-1 == write_feature(Map, line, type, &points, 1, cat, Fi)) {
+ line_id = write_feature(Map, line, type, &points, 1, cat, Fi);
+ if (line_id < 0) {
Vect__execute_pg(pg_info->conn, "ROLLBACK");
return -1;
}
+ /* update offset array for nodes */
+ if (is_node) {
+ int node;
+
+ struct Format_info_offset *offset;
+
+ offset = &(pg_info->offset);
+
+ node = abs(line);
+ if (node > offset->array_alloc) {
+ offset->array_alloc += 1000;
+ offset->array = (int *) G_realloc (offset->array, offset->array_alloc * sizeof(int));
+ }
+
+ offset->array_num = node;
+ offset->array[node-1] = (int) line_id; /* node id starts at 1 */
+ }
+
/* update PostGIS-line topo */
if (plus->built >= GV_BUILD_AREAS && type == GV_BOUNDARY)
update_topo_face(Map, line); /* TODO: avoid extra statements */
@@ -1638,8 +1716,9 @@
\param cat category number (-1 for no category)
\param Fi pointer to field_info (attributes to copy, NULL for no attributes)
+ \return topo_id for PostGIS Topology
+ \return 0 for simple features access
\return -1 on error
- \retirn 0 on success
*/
int write_feature(struct Map_info *Map, int line, int type,
const struct line_pnts **points, int nparts,
@@ -1708,7 +1787,7 @@
}
G_free(geom_data);
- return 0;
+ return pg_info->toposchema_name ? topo_id : 0;
}
/*!
@@ -1954,6 +2033,12 @@
return -1;
}
Line = Map->plus.Line[topo_id];
+
+ if (Line->type & GV_POINTS) {
+ /* set topo_id for points */
+ topo_id = Vect_get_num_primitives(Map, GV_POINTS) + Vect_get_num_nodes(Map);
+ }
+
}
else if (id < 0) { /* node */
topo_id = abs(id);
@@ -1965,6 +2050,10 @@
G_warning(_("Invalid node %d (%d)"), topo_id, Map->plus.n_nodes);
return -1;
}
+
+ /* increment topo_id - also points and centroids are
+ * stored in 'node' table */
+ topo_id += Vect_get_num_primitives(Map, GV_POINTS);
}
}
@@ -1997,7 +2086,11 @@
pg_info->toposchema_name, geom_data);
#else
int n1, n2, nle, nre;
-
+
+ struct Format_info_offset *offset;
+
+ offset = &(pg_info->offset);
+
if (id == 0) {
/* get edge_id */
sprintf(stmt_id, "SELECT nextval('\"%s\".edge_data_edge_id_seq')",
@@ -2044,12 +2137,15 @@
G_debug(3, "new edge: id=%d next_left_edge=%d next_right_edge=%d",
topo_id, nle, nre);
+ if (n1 > offset->array_num || n2 > offset->array_num) /* node id starts at 1 */
+ return -1;
+
/* build insert statement */
G_asprintf(&stmt, "INSERT INTO \"%s\".edge_data (edge_id, start_node, end_node, "
"next_left_edge, abs_next_left_edge, next_right_edge, abs_next_right_edge, "
"left_face, right_face, geom) VALUES "
"(%d, %d, %d, %d, %d, %d, %d, 0, 0, '%s'::GEOMETRY)",
- pg_info->toposchema_name, topo_id, n1, n2,
+ pg_info->toposchema_name, topo_id, offset->array[n1-1], offset->array[n2-1],
nle, abs(nle), nre, abs(nre), geom_data);
#endif
break;
More information about the grass-commit
mailing list