浏览代码

vlib/pg: fix building PostGIS Topology in update mode (line deleted)
add 'line' topo DB table into topology schema (full mode only)
various related fixes and improvements
(work in progress)


git-svn-id: https://svn.osgeo.org/grass/grass/trunk@58324 15284696-431f-4ddb-bdfa-cd5b030d7da7

Martin Landa 11 年之前
父节点
当前提交
548b1c9df3

+ 0 - 1
include/defs/vector.h

@@ -477,7 +477,6 @@ int Vect_coor_info(const struct Map_info *, struct Coor_info *);
 const char *Vect_maptype_info(const struct Map_info *);
 const char *Vect_maptype_info(const struct Map_info *);
 int Vect_maptype(const struct Map_info *);
 int Vect_maptype(const struct Map_info *);
 int Vect_open_topo(struct Map_info *, int);
 int Vect_open_topo(struct Map_info *, int);
-int Vect_open_topo_pg(struct Map_info *, int);
 int Vect_save_topo(struct Map_info *);
 int Vect_save_topo(struct Map_info *);
 int Vect_open_sidx(struct Map_info *, int);
 int Vect_open_sidx(struct Map_info *, int);
 int Vect_save_sidx(struct Map_info *);
 int Vect_save_sidx(struct Map_info *);

+ 89 - 1
lib/vector/Vlib/build_pg.c

@@ -31,6 +31,7 @@ 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 create_topo_grass(const struct Format_info_pg *);
 static int has_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_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_areas(const struct Plus_head *, const struct Format_info_pg *);
 static int write_isles(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 void build_stmt_id(const void *, int, int, const struct Plus_head *, char **, size_t *);
@@ -160,8 +161,30 @@ int build_topo(struct Map_info *Map, int build)
     
     
     /* cache features to speed-up random access (when attaching isles
     /* cache features to speed-up random access (when attaching isles
        to areas) */
        to areas) */
-    if (build >= GV_BUILD_BASE)
+    if (build >= GV_BUILD_BASE) {
         pg_info->cache.ctype = CACHE_MAP;
         pg_info->cache.ctype = CACHE_MAP;
+        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 indeces */
+            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));
+
+            /* force loading nodes from DB to get up-to-date node
+             * offsets */
+            Vect__free_offset(&(pg_info->offset));
+            Map->plus.n_nodes = Vect__load_map_nodes_pg(Map, TRUE);
+        }
+    }
     /* update TopoGeometry based on GRASS-like topology */
     /* update TopoGeometry based on GRASS-like topology */
     Vect_build_nat(Map, build);
     Vect_build_nat(Map, build);
     
     
@@ -177,6 +200,7 @@ int build_topo(struct Map_info *Map, int build)
     /* write full node topo info to DB if requested */
     /* write full node topo info to DB if requested */
     if (!pg_info->topo_geo_only) {
     if (!pg_info->topo_geo_only) {
         write_nodes(plus, pg_info);
         write_nodes(plus, pg_info);
+        write_lines(plus, pg_info);
     }
     }
 
 
     /* update faces from GRASS Topology */
     /* update faces from GRASS Topology */
@@ -604,6 +628,70 @@ int write_nodes(const struct Plus_head *plus,
 }
 }
 
 
 /*!
 /*!
+  \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
   \brief Insert area into 'area_grass' table
 
 
   Writes (see P_area struct):
   Writes (see P_area struct):

+ 12 - 0
lib/vector/Vlib/close.c

@@ -305,8 +305,20 @@ void Vect__free_cache(struct Format_info_cache *cache) {
     G_free(cache->lines);
     G_free(cache->lines);
     G_free(cache->lines_types);
     G_free(cache->lines_types);
     G_free(cache->lines_cats);
     G_free(cache->lines_cats);
+
+    G_zero(cache, sizeof(struct Format_info_cache));
 }
 }
 
 
+/*! Free memory of offset array
+
+  \param cache pointer to offset array to be freed
+*/
+void Vect__free_offset(struct Format_info_offset *offset)
+{
+    G_free(offset->array);
+    G_zero(offset, sizeof(struct Format_info_offset));
+}   
+
 void unlink_file(const struct Map_info *Map, const char *name)
 void unlink_file(const struct Map_info *Map, const char *name)
 {
 {
     char *path;
     char *path;

+ 1 - 1
lib/vector/Vlib/close_ogr.c

@@ -104,7 +104,7 @@ int V2_close_ogr(struct Map_info *Map)
 	G_warning(_("Unable to save feature index file for vector map <%s>"),
 	G_warning(_("Unable to save feature index file for vector map <%s>"),
 		  Map->name);
 		  Map->name);
     
     
-    G_free(ogr_info->offset.array);
+    Vect__free_offset(&(ogr_info->offset));
     
     
     return 0;
     return 0;
 #else
 #else

+ 1 - 1
lib/vector/Vlib/close_pg.c

@@ -137,7 +137,7 @@ int V2_close_pg(struct Map_info *Map)
         G_warning(_("Unable to save feature index file for vector map <%s>"),
         G_warning(_("Unable to save feature index file for vector map <%s>"),
                   Map->name);
                   Map->name);
 
 
-    G_free(Map->fInfo.pg.offset.array);
+    Vect__free_offset(&(Map->fInfo.pg.offset));
 
 
     return 0;
     return 0;
 #else
 #else

+ 1 - 0
lib/vector/Vlib/local_proto.h

@@ -16,6 +16,7 @@ int Vect__get_area_points_nat(const struct Map_info *, const plus_t *, int, stru
 
 
 /* close.c */
 /* close.c */
 void Vect__free_cache(struct Format_info_cache *);
 void Vect__free_cache(struct Format_info_cache *);
+void Vect__free_offset(struct Format_info_offset *);
 
 
 /* map.c */
 /* map.c */
 int Vect__delete(const char *, int);
 int Vect__delete(const char *, int);

+ 3 - 2
lib/vector/Vlib/open.c

@@ -322,7 +322,7 @@ int Vect__open_old(struct Map_info *Map, const char *name, const char *mapset,
         ret = -1;
         ret = -1;
         if (Map->format == GV_FORMAT_POSTGIS)
         if (Map->format == GV_FORMAT_POSTGIS)
             /* try to read full-topology for PostGIS links */
             /* try to read full-topology for PostGIS links */
-            ret = Vect_open_topo_pg(Map, head_only);
+          ret = Vect__open_topo_pg(Map, head_only, update);
         
         
         if (ret != 0) {
         if (ret != 0) {
             /* read topology for native format
             /* read topology for native format
@@ -361,6 +361,7 @@ int Vect__open_old(struct Map_info *Map, const char *name, const char *mapset,
                     level = 1;
                     level = 1;
                 }
                 }
         }
         }
+
         /* open category index */
         /* open category index */
         if (level >= 2) {
         if (level >= 2) {
             ret = Vect_cidx_open(Map, head_only);
             ret = Vect_cidx_open(Map, head_only);
@@ -1268,7 +1269,7 @@ int Vect_open_sidx(struct Map_info *Map, int mode)
     }
     }
 
 
     Plus->Spidx_built = TRUE;
     Plus->Spidx_built = TRUE;
-
+    
     return 0;
     return 0;
 }
 }
 
 

+ 349 - 251
lib/vector/Vlib/open_pg.c

@@ -43,20 +43,18 @@ static int check_topo(struct Format_info_pg *, struct Plus_head *);
 static int parse_bbox(const char *, struct bound_box *);
 static int parse_bbox(const char *, struct bound_box *);
 static struct P_node *read_p_node(struct Plus_head *, int, int,
 static struct P_node *read_p_node(struct Plus_head *, int, int,
                                   const char *, const char *, const char *,
                                   const char *, const char *, const char *,
-                                  struct Format_info_pg *);
+                                  struct Format_info_pg *, int);
 static struct P_line *read_p_line(struct Plus_head *, int,
 static struct P_line *read_p_line(struct Plus_head *, int,
-                                  const struct line_data *,
+                                  const struct line_data *, int,
                                   struct Format_info_cache *);
                                   struct Format_info_cache *);
 static struct P_area *read_p_area(struct Plus_head *, int,
 static struct P_area *read_p_area(struct Plus_head *, int,
                                   const char *, int, const char *);
                                   const char *, int, const char *);
 static struct P_isle *read_p_isle(struct Plus_head *, int,
 static struct P_isle *read_p_isle(struct Plus_head *, int,
                                   const char *, int);
                                   const char *, int);
-static int load_plus_head(struct Format_info_pg *, struct Plus_head *);
 static void notice_processor(void *, const char *);
 static void notice_processor(void *, const char *);
 static char **scan_array(const char *);
 static char **scan_array(const char *);
 static int remap_node(const struct Format_info_offset *, int);
 static int remap_node(const struct Format_info_offset *, int);
 static int remap_line(const struct Plus_head*, off_t, int);
 static int remap_line(const struct Plus_head*, off_t, int);
-static void reset_cache(struct Format_info_cache *);
 #endif
 #endif
 
 
 /*!
 /*!
@@ -135,8 +133,8 @@ int V1_open_old_pg(struct Map_info *Map, int update)
     }
     }
     PQclear(res);
     PQclear(res);
 
 
-    /* no feature in cache */
-    pg_info->cache.fid = -1;
+    /* fid -1 for empty / -2 for full cache */
+    pg_info->cache.fid = pg_info->cache.ctype != CACHE_MAP ? -1 : -2;
 
 
     if (!found) {
     if (!found) {
         G_warning(_("Feature table <%s> not found in 'geometry_columns'"),
         G_warning(_("Feature table <%s> not found in 'geometry_columns'"),
@@ -324,14 +322,16 @@ int V1_open_new_pg(struct Map_info *Map, const char *name, int with_z)
   
   
   \param[in,out] Map pointer to Map_info structure
   \param[in,out] Map pointer to Map_info structure
   \param head_only TRUE to read only header
   \param head_only TRUE to read only header
-  
+  \param update TRUE for update mode (reloading topology)
+
   \return 0 on success
   \return 0 on success
   \return 1 topology layer does not exist
   \return 1 topology layer does not exist
   \return -1 on error
   \return -1 on error
 */
 */
-int Vect_open_topo_pg(struct Map_info *Map, int head_only)
+int Vect__open_topo_pg(struct Map_info *Map, int head_only, int update)
 {
 {
 #ifdef HAVE_POSTGRES
 #ifdef HAVE_POSTGRES
+    int ret;
     struct Plus_head *plus;
     struct Plus_head *plus;
     struct Format_info_pg *pg_info;
     struct Format_info_pg *pg_info;
     
     
@@ -344,12 +344,36 @@ int Vect_open_topo_pg(struct Map_info *Map, int head_only)
     if (check_topo(pg_info, plus) != 0)
     if (check_topo(pg_info, plus) != 0)
         return 1;
         return 1;
     
     
-    /* free and init plus structure */
+    /* free and init plus structure, update spatial and category
+     * indeces  */
     dig_init_plus(plus);
     dig_init_plus(plus);
-    plus->Spidx_new = TRUE;   /* force to create new spatial index */
-    plus->update_cidx = TRUE; /* force to update category index */
+    plus->Spidx_new = TRUE;   
+    plus->update_cidx = TRUE; 
+    
+    ret = Vect__load_plus_pg(Map, head_only);
+
+    /* clean-up GRASS-like topology tables on update mode */
+    if (update && !pg_info->topo_geo_only && ret == 0) {
+        char stmt[DB_SQL_MAX];
+
+        sprintf(stmt, "DELETE FROM \"%s\".\"%s\"",
+                pg_info->toposchema_name, TOPO_TABLE_NODE);
+        Vect__execute_pg(pg_info->conn, stmt);
+
+        sprintf(stmt, "DELETE FROM \"%s\".\"%s\"",
+                pg_info->toposchema_name, TOPO_TABLE_LINE);
+        Vect__execute_pg(pg_info->conn, stmt);
+
+        sprintf(stmt, "DELETE FROM \"%s\".\"%s\"",
+                pg_info->toposchema_name, TOPO_TABLE_AREA);
+        Vect__execute_pg(pg_info->conn, stmt);
 
 
-    return Vect__load_plus_pg(Map, head_only);
+        sprintf(stmt, "DELETE FROM \"%s\".\"%s\"",
+                pg_info->toposchema_name, TOPO_TABLE_ISLE);
+        Vect__execute_pg(pg_info->conn, stmt);
+    }
+
+    return 0;
 #else
 #else
     G_fatal_error(_("GRASS is not compiled with PostgreSQL support"));
     G_fatal_error(_("GRASS is not compiled with PostgreSQL support"));
     return -1;
     return -1;
@@ -671,13 +695,15 @@ int parse_bbox(const char *value, struct bound_box *bbox)
   \param lines_data lines array or NULL
   \param lines_data lines array or NULL
   \param angles_data angles array or NULL
   \param angles_data angles array or NULL
   \param pg_info pointer to Format_info_pg sttucture
   \param pg_info pointer to Format_info_pg sttucture
+  \param geom_only TRUE to read node's geometry 
 
 
   \return pointer to new P_node struct
   \return pointer to new P_node struct
   \return NULL on error
   \return NULL on error
 */
 */
 struct P_node *read_p_node(struct Plus_head *plus, int n,
 struct P_node *read_p_node(struct Plus_head *plus, int n,
                            int id, const char *wkb_data, const char *lines_data,
                            int id, const char *wkb_data, const char *lines_data,
-                           const char *angles_data, struct Format_info_pg *pg_info)
+                           const char *angles_data, struct Format_info_pg *pg_info,
+                           int geom_only)
 {
 {
     int i, cnt;
     int i, cnt;
     char **lines, **angles;
     char **lines, **angles;
@@ -688,81 +714,86 @@ struct P_node *read_p_node(struct Plus_head *plus, int n,
     PGresult *res;
     PGresult *res;
     
     
     /* get lines connected to the node */
     /* get lines connected to the node */
+    cnt = 0;
     lines = angles = NULL;
     lines = angles = NULL;
-    if (!lines_data && !angles_data) { /* pg_info->topo_geo_only == TRUE */
-        char stmt[DB_SQL_MAX];
+    if (!geom_only) {
+        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);
+        }
+        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 */
+        }
         
         
-        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);
+        if (cnt == 0) { /* dead */
+            plus->Node[n] = NULL;
             return NULL;
             return NULL;
         }
         }
-        cnt = PQntuples(res);
-    }
-    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;
-    }
-
+    
     node = dig_alloc_node();
     node = dig_alloc_node();
     node->n_lines = cnt;
     node->n_lines = cnt;
     G_debug(4, "read_p_node(): id = %d, n_lines = %d", id, cnt);
     G_debug(4, "read_p_node(): id = %d, n_lines = %d", id, cnt);
     
     
-    if (dig_node_alloc_line(node, node->n_lines) == -1)
-        return NULL;
-
-    /* lines / angles */
-    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]);
-        }
+    if (!geom_only) {
+        if (dig_node_alloc_line(node, node->n_lines) == -1)
+            return NULL;
         
         
-        G_free_tokens(lines);
-        G_free_tokens(angles);
-    }
-    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;
+        /* lines / angles */
+        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);
+        }
+        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);
         }
         }
-        PQclear(res);
     }
     }
     
     
     /* get node coordinates */
     /* get node coordinates */
@@ -805,13 +836,14 @@ struct P_node *read_p_node(struct Plus_head *plus, int n,
   \param plus pointer to Plus_head structure
   \param plus pointer to Plus_head structure
   \param n index (starts at 1)
   \param n index (starts at 1)
   \param data edge data (id, start/end node, left/right face, ...)
   \param data edge data (id, start/end node, left/right face, ...)
-  \param pg_info pointer to Format_info_pg sttucture
+  \param topo_geo_only TRUE for topo-geo-only mode
+  \param cache pointer to Format_info_cache structure
 
 
   \return pointer to P_line struct
   \return pointer to P_line struct
   \return NULL on error
   \return NULL on error
 */
 */
 struct P_line *read_p_line(struct Plus_head *plus, int n,
 struct P_line *read_p_line(struct Plus_head *plus, int n,
-                           const struct line_data *data,
+                           const struct line_data *data, int topo_geo_only,
                            struct Format_info_cache *cache)
                            struct Format_info_cache *cache)
 {
 {
     int tp, cat;
     int tp, cat;
@@ -866,9 +898,13 @@ struct P_line *read_p_line(struct Plus_head *plus, int n,
             topo->N1    = data->start_node;
             topo->N1    = data->start_node;
             topo->N2    = data->end_node;
             topo->N2    = data->end_node;
 
 
-            /* skip left/right area - will be detected when building
-               areas/isles */
-            topo->left = topo->right = 0;
+            if (topo_geo_only) {
+                topo->left = topo->right = 0;
+            }
+            else {
+                topo->left  = data->left_face;
+                topo->right = data->right_face;
+            }
         }
         }
         /* centroids */
         /* centroids */
         else if (line->type == GV_CENTROID) {
         else if (line->type == GV_CENTROID) {
@@ -1025,12 +1061,18 @@ struct P_isle *read_p_isle(struct Plus_head *plus, int n,
   \return 0 on success
   \return 0 on success
   \return -1 on error
   \return -1 on error
 */
 */
-int load_plus_head(struct Format_info_pg *pg_info, struct Plus_head *plus)
+int Vect__load_plus_head(struct Map_info *Map)
 {
 {
     char stmt[DB_SQL_MAX];
     char stmt[DB_SQL_MAX];
     
     
+    struct Format_info_pg *pg_info;
+    struct Plus_head *plus;
+
     PGresult *res;
     PGresult *res;
     
     
+    plus = &(Map->plus);
+    pg_info = &(Map->fInfo.pg);
+    
     plus->off_t_size = -1;
     plus->off_t_size = -1;
     
     
     /* get map bounding box
     /* get map bounding box
@@ -1201,12 +1243,10 @@ int load_plus_head(struct Format_info_pg *pg_info, struct Plus_head *plus)
 */
 */
 int Vect__load_plus_pg(struct Map_info *Map, int head_only)
 int Vect__load_plus_pg(struct Map_info *Map, int head_only)
 {
 {
-    int i, side, line, id, ntuples;
+    int i, side, line;
     char stmt[DB_SQL_MAX];
     char stmt[DB_SQL_MAX];
-    struct line_data line_data;
     
     
     struct Format_info_pg *pg_info;
     struct Format_info_pg *pg_info;
-    struct Format_info_offset *offset;
     struct Plus_head *plus;
     struct Plus_head *plus;
     struct P_line *Line;
     struct P_line *Line;
     struct line_pnts *Points;
     struct line_pnts *Points;
@@ -1217,9 +1257,8 @@ int Vect__load_plus_pg(struct Map_info *Map, int head_only)
   
   
     pg_info = &(Map->fInfo.pg);
     pg_info = &(Map->fInfo.pg);
     plus = &(Map->plus);
     plus = &(Map->plus);
-    offset = &(pg_info->offset);
 
 
-    if (load_plus_head(pg_info, plus) != 0)
+    if (Vect__load_plus_head(Map) != 0)
         return -1;
         return -1;
 
 
     if (head_only)
     if (head_only)
@@ -1231,7 +1270,171 @@ int Vect__load_plus_pg(struct Map_info *Map, int head_only)
     /* read nodes (GRASS Topo)
     /* read nodes (GRASS Topo)
        note: standalone nodes (ie. points/centroids) are ignored
        note: standalone nodes (ie. points/centroids) are ignored
     */
     */
-    if (pg_info->topo_geo_only) 
+    Vect__load_map_nodes_pg(Map, FALSE);
+
+    /* read lines (GRASS Topo)
+       - standalone nodes -> points|centroids
+       - edges -> lines/boundaries
+    */
+    Vect__free_cache(&(pg_info->cache));
+    pg_info->cache.ctype = CACHE_MAP;
+
+    Vect__load_map_lines_pg(Map);
+    
+    /* 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",
+                        i, side);
+                Vect_build_line_area(Map, line, side);
+            }
+        }
+    }
+    else {
+        int cat;
+        
+        /* 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;
+        }
+
+        
+        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));
+            
+            if (plus->Spidx_new) {
+                /* update spatial index */
+                Vect_get_area_points(Map, i+1, Points);
+                dig_line_box(Points, &box);
+                dig_spidx_add_area(&(Map->plus), i+1, &box);
+            }
+
+            if (plus->update_cidx) {
+                /* update category index */
+                cat = pg_info->cache.lines_cats[plus->Area[i+1]->centroid-1];
+                dig_cidx_add_cat(plus, cat > 0 ? 1 : 0, cat > 0 ? cat : 0, i+1, GV_AREA);
+            }
+        }
+        PQclear(res);
+    }
+    plus->built = GV_BUILD_AREAS;
+
+    /* attach isles */
+    if (pg_info->topo_geo_only) {
+        plus->n_isles = 0; /* reset isles */
+        G_warning(_("To be implemented: 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)));
+
+            if (plus->Spidx_new) {
+                /* update spatial index */
+                Vect_get_isle_points(Map, i+1, Points);
+                dig_line_box(Points, &box);
+                dig_spidx_add_isle(&(Map->plus), i+1, &box);
+            }
+        }
+        PQclear(res);
+    }
+    plus->built = GV_BUILD_ATTACH_ISLES;
+    
+    /* attach centroids */
+    if (pg_info->topo_geo_only && plus->n_areas > 0) {
+        int area;
+        struct P_area   *Area;
+        struct P_topo_c *topo;
+        
+        for (line = 1; line <= plus->n_lines; line++) {
+            Line = plus->Line[line];
+            if (Line->type != GV_CENTROID)
+                continue;
+            
+            Vect_read_line(Map, Points, NULL, line);
+            area = Vect_find_area(Map, Points->x[0], Points->y[0]);
+            topo = (struct P_topo_c *)Line->topo;
+            topo->area = area;
+            Area = plus->Area[topo->area];
+            Area->centroid = Line->offset;
+        }
+    }
+    plus->built = GV_BUILD_CENTROIDS;
+    
+    /* done */
+    plus->built = GV_BUILD_ALL;
+    
+    Vect_destroy_line_struct(Points);
+    Vect_destroy_list(List);
+    
+    return 0;
+}
+
+/*!
+  \brief Read nodes from DB 
+  
+  \param Map pointer to Map_info struct
+  \param geom_only read only node's geometry
+
+  \return number of nodes
+*/
+int Vect__load_map_nodes_pg(struct Map_info *Map, int geom_only)
+{
+    int i, id, n_nodes;
+    char stmt[DB_SQL_MAX];
+    struct Plus_head *plus;
+    struct Format_info_pg *pg_info;
+    struct Format_info_offset *offset;
+
+    PGresult *res;
+
+    plus = &(Map->plus);
+    pg_info = &(Map->fInfo.pg);
+    offset = &(pg_info->offset);
+
+    if (pg_info->topo_geo_only || geom_only) 
         sprintf(stmt,
         sprintf(stmt,
                 "SELECT node_id,geom FROM \"%s\".node WHERE node_id IN "
                 "SELECT node_id,geom FROM \"%s\".node WHERE node_id IN "
                 "(SELECT node FROM (SELECT start_node AS node FROM \"%s\".edge "
                 "(SELECT node FROM (SELECT start_node AS node FROM \"%s\".edge "
@@ -1247,7 +1450,7 @@ int Vect__load_plus_pg(struct Map_info *Map, int head_only)
     G_debug(2, "SQL: %s", stmt);
     G_debug(2, "SQL: %s", stmt);
     res = PQexec(pg_info->conn, stmt);
     res = PQexec(pg_info->conn, stmt);
     if (!res || PQresultStatus(res) != PGRES_TUPLES_OK ||
     if (!res || PQresultStatus(res) != PGRES_TUPLES_OK ||
-        PQntuples(res) != plus->n_nodes) {
+        (!geom_only && PQntuples(res) != plus->n_nodes)) {
         G_warning(_("Inconsistency in topology: number of "
         G_warning(_("Inconsistency in topology: number of "
                     "nodes %d (should be %d)"),
                     "nodes %d (should be %d)"),
                   PQntuples(res), plus->n_nodes);
                   PQntuples(res), plus->n_nodes);
@@ -1256,31 +1459,54 @@ int Vect__load_plus_pg(struct Map_info *Map, int head_only)
         return -1;
         return -1;
     }
     }
 
 
-    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++) {
+    n_nodes = PQntuples(res);
+    G_debug(3, "load_plus(): n_nodes = %d", n_nodes);
+    dig_alloc_nodes(plus, n_nodes);
+    offset->array = (int *) G_malloc (sizeof(int) * n_nodes);
+    offset->array_num = offset->array_alloc = n_nodes;
+    for (i = 0; i < n_nodes; i++) {
         G_debug(5, "node: %d", i);
         G_debug(5, "node: %d", i);
         id = atoi(PQgetvalue(res, i, 0));
         id = atoi(PQgetvalue(res, i, 0));
         read_p_node(plus, i + 1, /* node index starts at 1 */
         read_p_node(plus, i + 1, /* node index starts at 1 */
                     id, (const char *) PQgetvalue(res, i, 1),
                     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, 2) : NULL,
                     !pg_info->topo_geo_only ? (const char *) PQgetvalue(res, i, 3) : NULL,
                     !pg_info->topo_geo_only ? (const char *) PQgetvalue(res, i, 3) : NULL,
-                    pg_info);
+                    pg_info, geom_only);
         /* update offset */
         /* update offset */
         offset->array[i] = id;
         offset->array[i] = id;
     }
     }
     PQclear(res);
     PQclear(res);
 
 
-    /* read lines (GRASS Topo)
-       - standalone nodes -> points|centroids
-       - edges -> lines/boundaries
-    */
-    G_debug(3, "load_plus(): n_lines = %d", plus->n_lines);
+    return n_nodes;
+}
+
+/*!
+  \brief Read features from DB 
+  
+  \param Map pointer to Map_info struct
+
+  \return number of features
+*/
+int Vect__load_map_lines_pg(struct Map_info *Map)
+{
+    int i, id, ntuples;
+    
+    char stmt[DB_SQL_MAX];
+    
+    struct Plus_head *plus;
+    struct Format_info_pg *pg_info;
+    struct line_data line_data;
+    struct Format_info_offset *offset;
+    
+    PGresult *res;
+    
+    plus = &(Map->plus);
+    pg_info = &(Map->fInfo.pg);
+    offset = &(pg_info->offset);
+ 
     dig_alloc_lines(plus, plus->n_lines); 
     dig_alloc_lines(plus, plus->n_lines); 
     G_zero(plus->Line, sizeof(struct P_line *) * (plus->n_lines + 1)); /* index starts at 1 */
     G_zero(plus->Line, sizeof(struct P_line *) * (plus->n_lines + 1)); /* index starts at 1 */
-    
+
     /* read PostGIS Topo standalone nodes (containing_face is null)
     /* read PostGIS Topo standalone nodes (containing_face is null)
        -> points
        -> points
     */
     */
@@ -1316,10 +1542,7 @@ int Vect__load_plus_pg(struct Map_info *Map, int head_only)
             PQclear(res);
             PQclear(res);
         return -1;
         return -1;
     }
     }
-    
-    reset_cache(&(pg_info->cache));
-    pg_info->cache.ctype = CACHE_MAP; /* experimental - cache all features */
-    
+
     ntuples = PQntuples(res); /* plus->n_plines */
     ntuples = PQntuples(res); /* plus->n_plines */
     G_zero(&line_data, sizeof(struct line_data));
     G_zero(&line_data, sizeof(struct line_data));
     for (i = 0; i < ntuples; i++) {
     for (i = 0; i < ntuples; i++) {
@@ -1328,7 +1551,7 @@ int Vect__load_plus_pg(struct Map_info *Map, int head_only)
         line_data.wkb_geom = (char *) PQgetvalue(res, i, 1);
         line_data.wkb_geom = (char *) PQgetvalue(res, i, 1);
         line_data.fid = atoi(PQgetvalue(res, i, 2)); /* feature id */
         line_data.fid = atoi(PQgetvalue(res, i, 2)); /* feature id */
 
 
-        read_p_line(plus, i + 1, &line_data, &(pg_info->cache));
+        read_p_line(plus, i + 1, &line_data, pg_info->topo_geo_only, &(pg_info->cache));
     }
     }
     PQclear(res);
     PQclear(res);
     
     
@@ -1336,12 +1559,22 @@ int Vect__load_plus_pg(struct Map_info *Map, int head_only)
        -> lines
        -> lines
        -> boundaries
        -> boundaries
     */
     */
-    sprintf(stmt, /* fix fid column */
-            "SELECT edge_id,start_node,end_node,left_face,right_face,tt.geom,ft.%s "
-            "FROM \"%s\".edge AS tt LEFT JOIN \"%s\" AS ft ON (%s).type = 2 AND "
-            "(%s).id = edge_id ORDER BY edge_id",
-            "fid", pg_info->toposchema_name, pg_info->table_name,
-            pg_info->topogeom_column, pg_info->topogeom_column);
+    if (pg_info->topo_geo_only)
+        sprintf(stmt, /* TODO: fix fid column! */
+                "SELECT edge_id,start_node,end_node,left_face,right_face AS right_area,tt.geom,ft.%s "
+                "FROM \"%s\".edge AS tt LEFT JOIN \"%s\" AS ft ON (%s).type = 2 AND "
+                "(%s).id = edge_id ORDER BY edge_id",
+                "fid", pg_info->toposchema_name, pg_info->table_name,
+                pg_info->topogeom_column, pg_info->topogeom_column);
+    else
+        sprintf(stmt, /* TODO: fix fid column! */
+                "SELECT edge_id,start_node,end_node,left_area,right_area,tt.geom,ft.%s "
+                "FROM \"%s\".edge AS tt LEFT JOIN \"%s\".%s ON "
+                "edge_id = line_id LEFT JOIN \"%s\" AS ft ON (%s).type = 2 AND "
+                "(%s).id = edge_id ORDER BY edge_id",
+                "fid", pg_info->toposchema_name, pg_info->toposchema_name, TOPO_TABLE_LINE,
+                pg_info->table_name, pg_info->topogeom_column, pg_info->topogeom_column);
+        
     G_debug(2, "SQL: %s", stmt);
     G_debug(2, "SQL: %s", stmt);
     res = PQexec(pg_info->conn, stmt);
     res = PQexec(pg_info->conn, stmt);
     if (!res || PQresultStatus(res) != PGRES_TUPLES_OK ||
     if (!res || PQresultStatus(res) != PGRES_TUPLES_OK ||
@@ -1366,7 +1599,7 @@ int Vect__load_plus_pg(struct Map_info *Map, int head_only)
         line_data.fid        = atoi(PQgetvalue(res, i, 6)); /* feature id */
         line_data.fid        = atoi(PQgetvalue(res, i, 6)); /* feature id */
 
 
         id = plus->n_plines + i + 1; /* points already registered */
         id = plus->n_plines + i + 1; /* points already registered */
-        read_p_line(plus, id, &line_data, &(pg_info->cache));
+        read_p_line(plus, id, &line_data, pg_info->topo_geo_only, &(pg_info->cache));
     }
     }
     PQclear(res);
     PQclear(res);
 
 
@@ -1417,140 +1650,13 @@ int Vect__load_plus_pg(struct Map_info *Map, int head_only)
         line_data.fid = atoi(PQgetvalue(res, i, 3)); /* feature id */
         line_data.fid = atoi(PQgetvalue(res, i, 3)); /* feature id */
         /* area id and face id can be different */
         /* area id and face id can be different */
         
         
-        read_p_line(plus, id + i, &line_data, &(pg_info->cache));
+        read_p_line(plus, id + i, &line_data, pg_info->topo_geo_only, &(pg_info->cache));
     }
     }
     PQclear(res);
     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 {
-        int cat;
-        
-        /* 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;
-        }
 
 
-        
-        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));
-            
-            if (plus->Spidx_new) {
-                /* update spatial index */
-                Vect_get_area_points(Map, i+1, Points);
-                dig_line_box(Points, &box);
-                dig_spidx_add_area(&(Map->plus), i+1, &box);
-            }
-
-            if (plus->update_cidx) {
-                /* update category index */
-                cat = pg_info->cache.lines_cats[plus->Area[i+1]->centroid-1];
-                dig_cidx_add_cat(plus, cat > 0 ? 1 : 0, cat > 0 ? cat : 0, i+1, GV_AREA);
-            }
-        }
-        PQclear(res);
-    }
-    plus->built = GV_BUILD_AREAS;
-
-    /* attach isles */
-    if (pg_info->topo_geo_only) {
-        plus->n_isles = 0; /* reset isles */
-        G_warning(_("To be implemented: 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)));
-
-            if (plus->Spidx_new) {
-                /* update spatial index */
-                Vect_get_isle_points(Map, i+1, Points);
-                dig_line_box(Points, &box);
-                dig_spidx_add_isle(&(Map->plus), i+1, &box);
-            }
-        }
-        PQclear(res);
-    }
-    plus->built = GV_BUILD_ATTACH_ISLES;
-    
-    /* attach centroids */
-    if (pg_info->topo_geo_only && plus->n_areas > 0) {
-        int area;
-        struct P_area   *Area;
-        struct P_topo_c *topo;
-        
-        for (line = 1; line <= plus->n_lines; line++) {
-            Line = plus->Line[line];
-            if (Line->type != GV_CENTROID)
-                continue;
-            
-            Vect_read_line(Map, Points, NULL, line);
-            area = Vect_find_area(Map, Points->x[0], Points->y[0]);
-            topo = (struct P_topo_c *)Line->topo;
-            topo->area = area;
-            Area = plus->Area[topo->area];
-            Area->centroid = Line->offset;
-        }
-    }
-    plus->built = GV_BUILD_CENTROIDS;
-    
-    /* done */
-    plus->built = GV_BUILD_ALL;
-    
-    Vect_destroy_line_struct(Points);
-    Vect_destroy_list(List);
+    plus->built = GV_BUILD_BASE;
     
     
-    return 0;
+    return plus->n_lines;
 }
 }
 
 
 /*
 /*
@@ -1648,12 +1754,4 @@ int remap_line(const struct Plus_head* plus, off_t offset, int type)
 
 
     return -1;
     return -1;
 }
 }
-
-/*! Reset lines cache */
-void reset_cache(struct Format_info_cache *cache)
-{
-    Vect__free_cache(cache);
-    G_zero(cache, sizeof(struct Format_info_cache));
-    cache->fid = -1;
-}
 #endif
 #endif

+ 6 - 1
lib/vector/Vlib/pg_local_proto.h

@@ -13,8 +13,9 @@
 #define TOPO_ID     "topology_id"
 #define TOPO_ID     "topology_id"
 #define TOPO_TABLE  "grass"
 #define TOPO_TABLE  "grass"
 #define TOPO_BBOX   "bbox"
 #define TOPO_BBOX   "bbox"
-#define TOPO_TABLE_NUM  3
+#define TOPO_TABLE_NUM  4
 #define TOPO_TABLE_NODE "node_grass"
 #define TOPO_TABLE_NODE "node_grass"
+#define TOPO_TABLE_LINE "line_grass"
 #define TOPO_TABLE_AREA "area_grass"
 #define TOPO_TABLE_AREA "area_grass"
 #define TOPO_TABLE_ISLE "isle_grass"
 #define TOPO_TABLE_ISLE "isle_grass"
 
 
@@ -78,8 +79,12 @@ off_t V2__write_area_pg(struct Map_info *, const struct line_pnts **, int,
 int Vect__insert_face_pg(struct Map_info *, int);
 int Vect__insert_face_pg(struct Map_info *, int);
 
 
 /* open_pg.c */
 /* open_pg.c */
+int Vect__load_plus_head(struct Map_info *);
 int Vect__load_plus_pg(struct Map_info *, int);
 int Vect__load_plus_pg(struct Map_info *, int);
 int Vect__open_new_pg(struct Map_info *, int);
 int Vect__open_new_pg(struct Map_info *, int);
+int Vect__open_topo_pg(struct Map_info *, int, int);
+int Vect__load_map_nodes_pg(struct Map_info *, int);
+int Vect__load_map_lines_pg(struct Map_info *);
 
 
 #endif /* HAVE_POSTGRES */
 #endif /* HAVE_POSTGRES */
 
 

+ 20 - 9
lib/vector/Vlib/read_pg.c

@@ -459,6 +459,12 @@ int read_next_line_pg(struct Map_info *Map,
 
 
         /* read feature to cache if necessary */
         /* read feature to cache if necessary */
         while (pg_info->cache.lines_next == pg_info->cache.lines_num) {
         while (pg_info->cache.lines_next == pg_info->cache.lines_num) {
+            if (pg_info->cache.ctype == CACHE_MAP &&
+                pg_info->cache.fid == -2) {
+                /* stop reading - last cached line */
+                return -2;
+            }
+            
             /* cache feature -> line_p & line_c */
             /* cache feature -> line_p & line_c */
             sf_type = get_feature(pg_info, -1, -1);
             sf_type = get_feature(pg_info, -1, -1);
             
             
@@ -467,8 +473,11 @@ int read_next_line_pg(struct Map_info *Map,
                 return -1;
                 return -1;
             }
             }
 
 
-            if ((int)sf_type < 0)       /* -1 || - 2 */
+            if ((int)sf_type < 0) {      /* -1 || - 2 */
+                if (pg_info->cache.ctype == CACHE_MAP)
+                    pg_info->cache.fid = -2; /* last line cached */
                 return (int)sf_type;
                 return (int)sf_type;
+            }
 
 
             if (sf_type == SF_UNKNOWN || sf_type == SF_NONE) {
             if (sf_type == SF_UNKNOWN || sf_type == SF_NONE) {
                 G_warning(_("Feature without geometry. Skipped."));
                 G_warning(_("Feature without geometry. Skipped."));
@@ -481,9 +490,13 @@ int read_next_line_pg(struct Map_info *Map,
             Map->head.last_offset = pg_info->cache.fid;
             Map->head.last_offset = pg_info->cache.fid;
         }
         }
 
 
-        /* get data from cache */
-        itype = pg_info->cache.lines_types[pg_info->cache.lines_next];
-        iline = pg_info->cache.lines[pg_info->cache.lines_next];
+        /* get data from cache, skip dead lines (NULL) */
+        do {
+            itype = pg_info->cache.lines_types[pg_info->cache.lines_next];
+            iline = pg_info->cache.lines[pg_info->cache.lines_next];
+
+            pg_info->cache.lines_next++; /* read next line from cache */
+        } while (iline == NULL);
 
 
         G_debug(4, "read next cached line %d (type = %d)",
         G_debug(4, "read next cached line %d (type = %d)",
                 pg_info->cache.lines_next, itype);
                 pg_info->cache.lines_next, itype);
@@ -514,15 +527,15 @@ int read_next_line_pg(struct Map_info *Map,
                 cat = (int)pg_info->cache.fid;
                 cat = (int)pg_info->cache.fid;
             }
             }
             else {                           /* PostGIS Topology (cats are cached) */
             else {                           /* PostGIS Topology (cats are cached) */
-                cat = pg_info->cache.lines_cats[pg_info->cache.lines_next];
+                cat = pg_info->cache.lines_cats[pg_info->cache.lines_next-1];
                 if (cat == 0) { /* not cached yet */
                 if (cat == 0) { /* not cached yet */
                     int col_idx;
                     int col_idx;
                     
                     
                     col_idx = itype & GV_POINTS ? 2 : 3;
                     col_idx = itype & GV_POINTS ? 2 : 3;
                 
                 
-                    if (!PQgetisnull(pg_info->res, pg_info->cache.lines_next, col_idx))
+                    if (!PQgetisnull(pg_info->res, pg_info->cache.lines_next-1, col_idx))
                         cat = pg_info->cache.lines_cats[Map->next_line-1] =
                         cat = pg_info->cache.lines_cats[Map->next_line-1] =
-                        atoi(PQgetvalue(pg_info->res, pg_info->cache.lines_next, col_idx)); 
+                        atoi(PQgetvalue(pg_info->res, pg_info->cache.lines_next-1, col_idx)); 
                     else
                     else
                         pg_info->cache.lines_cats[Map->next_line-1] = -1; /* no cat */ 
                         pg_info->cache.lines_cats[Map->next_line-1] = -1; /* no cat */ 
                 }
                 }
@@ -531,8 +544,6 @@ int read_next_line_pg(struct Map_info *Map,
                 Vect_cat_set(line_c, 1, cat);
                 Vect_cat_set(line_c, 1, cat);
         }
         }
 
 
-        pg_info->cache.lines_next++; /* read next line from cache */
-
         return itype;
         return itype;
     }
     }
 
 

+ 3 - 2
lib/vector/Vlib/rewind_pg.c

@@ -44,10 +44,11 @@ int V1_rewind_pg(struct Map_info *Map)
     pg_info->next_line = 0;
     pg_info->next_line = 0;
 
 
     /* reset cache */
     /* reset cache */
-    if (pg_info->cache.ctype != CACHE_MAP)
+    if (pg_info->cache.ctype != CACHE_MAP) {
         pg_info->cache.lines_num = 0;
         pg_info->cache.lines_num = 0;
+        pg_info->cache.fid = -1;
+    }
     pg_info->cache.lines_next = 0;
     pg_info->cache.lines_next = 0;
-    pg_info->cache.fid = -1;
 
 
     /* close DB cursor if necessary */
     /* close DB cursor if necessary */
     return Vect__close_cursor_pg(pg_info);
     return Vect__close_cursor_pg(pg_info);

+ 1 - 1
lib/vector/Vlib/write_nat.c

@@ -516,7 +516,7 @@ void V2__delete_area_cats_from_cidx_nat(struct Map_info *Map, int area)
     if (!Cats)
     if (!Cats)
 	Cats = Vect_new_cats_struct();
 	Cats = Vect_new_cats_struct();
 
 
-    V2_read_line_nat(Map, NULL, Cats, Area->centroid);
+    Vect_read_line(Map, NULL, Cats, Area->centroid);
 
 
     for (i = 0; i < Cats->n_cats; i++) {
     for (i = 0; i < Cats->n_cats; i++) {
 	dig_cidx_del_cat(&(Map->plus), Cats->field[i], Cats->cat[i], area,
 	dig_cidx_del_cat(&(Map->plus), Cats->field[i], Cats->cat[i], area,

+ 71 - 41
lib/vector/Vlib/write_pg.c

@@ -366,6 +366,7 @@ int V1_delete_line_pg(struct Map_info *Map, off_t offset)
 int V2_delete_line_pg(struct Map_info *Map, int line)
 int V2_delete_line_pg(struct Map_info *Map, int line)
 {
 {
 #ifdef HAVE_POSTGRES
 #ifdef HAVE_POSTGRES
+    int ret;
     struct Format_info_pg *pg_info;
     struct Format_info_pg *pg_info;
 
 
     pg_info = &(Map->fInfo.pg);
     pg_info = &(Map->fInfo.pg);
@@ -395,7 +396,9 @@ int V2_delete_line_pg(struct Map_info *Map, int line)
             G_warning(_("Attempt to access dead feature %d"), line);
             G_warning(_("Attempt to access dead feature %d"), line);
             return -1;
             return -1;
         }
         }
-            
+         
+        Vect__execute_pg(pg_info->conn, "BEGIN");
+   
         if (Line->type & GV_POINTS) {
         if (Line->type & GV_POINTS) {
             table_name = keycolumn = "node";
             table_name = keycolumn = "node";
         }
         }
@@ -441,9 +444,23 @@ int V2_delete_line_pg(struct Map_info *Map, int line)
             Vect__execute_pg(pg_info->conn, "ROLLBACK");
             Vect__execute_pg(pg_info->conn, "ROLLBACK");
             return -1;
             return -1;
         }
         }
+
+        if (pg_info->cache.ctype == CACHE_MAP) {
+            /* delete from cache */
+            
+            Vect_destroy_line_struct(pg_info->cache.lines[line-1]);
+            pg_info->cache.lines[line-1] = NULL;
+            pg_info->cache.lines_types[line-1] = 0;
+            pg_info->cache.lines_cats[line-1] = 0;
+        }
         
         
         /* update topology */
         /* update topology */
-        return delete_line_from_topo_pg(Map, line, type, Points);
+        ret = delete_line_from_topo_pg(Map, line, type, Points);
+
+        if (ret == 0)
+            Vect__execute_pg(pg_info->conn, "COMMIT");
+        
+        return ret;
     }
     }
 #else
 #else
     G_fatal_error(_("GRASS is not compiled with PostgreSQL support"));
     G_fatal_error(_("GRASS is not compiled with PostgreSQL support"));
@@ -781,8 +798,6 @@ int create_topo_schema(struct Format_info_pg *pg_info, int with_z)
     char stmt[DB_SQL_MAX];
     char stmt[DB_SQL_MAX];
     char *def_file;
     char *def_file;
     
     
-    PGresult *result;
-    
     def_file = getenv("GRASS_VECTOR_PGFILE");
     def_file = getenv("GRASS_VECTOR_PGFILE");
     
     
     /* read default values from PG file*/
     /* read default values from PG file*/
@@ -899,7 +914,27 @@ int create_topo_schema(struct Format_info_pg *pg_info, int with_z)
             return -1;
             return -1;
         }
         }
 
 
-        /* (2) create 'area_grass' (see P_area struct)
+        /* (2) create 'line_grass' (see P_line struct)
+           
+        */
+        sprintf(stmt, "CREATE TABLE \"%s\".%s (line_id SERIAL PRIMARY KEY, "
+                "left_area integer, right_area integer)",
+                pg_info->toposchema_name, TOPO_TABLE_LINE);
+        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 line_exists "
+                "FOREIGN KEY (line_id) REFERENCES \"%s\".edge_data (edge_id) "
+                "DEFERRABLE INITIALLY DEFERRED",
+                pg_info->toposchema_name, TOPO_TABLE_LINE, pg_info->toposchema_name);
+        if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
+            Vect__execute_pg(pg_info->conn, "ROLLBACK");
+            return -1;
+        }
+
+        /* (3) create 'area_grass' (see P_area struct)
            
            
            todo: add constraints for lines, centtroid and isles
            todo: add constraints for lines, centtroid and isles
         */
         */
@@ -911,7 +946,7 @@ int create_topo_schema(struct Format_info_pg *pg_info, int with_z)
             return -1;
             return -1;
         }
         }
 
 
-        /* (3) create 'isle_grass' (see P_isle struct)
+        /* (4) create 'isle_grass' (see P_isle struct)
            
            
            todo: add constraints for lines and area
            todo: add constraints for lines and area
         */
         */
@@ -2675,8 +2710,6 @@ int add_line_to_topo_pg(struct Map_info *Map, off_t offset, int type,
 /*!
 /*!
   \brief Delete line from native and PostGIS topology
   \brief Delete line from native and PostGIS topology
 
 
-  \todo Implement deleting nodes on request
-  
   \param Map vector map
   \param Map vector map
   \param line feature id to remove from topo
   \param line feature id to remove from topo
   \param type feature type
   \param type feature type
@@ -2688,46 +2721,43 @@ int add_line_to_topo_pg(struct Map_info *Map, off_t offset, int type,
 int delete_line_from_topo_pg(struct Map_info *Map, int line, int type,
 int delete_line_from_topo_pg(struct Map_info *Map, int line, int type,
                              const struct line_pnts *Points)
                              const struct line_pnts *Points)
 {
 {
-    Vect_reset_updated(Map);
-    if (0 != V2__delete_line_from_topo_nat(Map, line, type, Points, NULL))
-        return -1;
-    
-    /* disabled: remove isolated nodes when closing the map
-       note: node id cannot be used as node_id
-       
-       delete nodes from 'node' table
-
-    int n_nodes;
+    int N1, N2, node_id;
     char stmt[DB_SQL_MAX];
     char stmt[DB_SQL_MAX];
-
+    
     struct Format_info_pg *pg_info;
     struct Format_info_pg *pg_info;
-    struct Plus_head *plus;
+    struct P_node *Node;
 
 
     pg_info = &(Map->fInfo.pg);
     pg_info = &(Map->fInfo.pg);
-    plus    = &(Map->plus);
     
     
-    n_nodes = Vect_get_num_updated_nodes(Map);
-    if (n_nodes > 0) {
-        int i, node;
-        
-        for (i = 0; i < n_nodes; i++) {
-            node = Vect_get_updated_node(Map, i);
-            if (node > 0 || plus->Node[abs(node)] != NULL)
-                continue; 
-            
-            node = abs(node);
-            G_debug(3, "delete node %d from 'node' table", node);
-            
-            sprintf(stmt, "DELETE FROM \"%s\".\"node\" WHERE node_id = %d",
-                    pg_info->toposchema_name, node);
-            if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
-                G_warning(_("Unable to delete node %d"), node);
-                Vect__execute_pg(pg_info->conn, "ROLLBACK");
-                return -1;
-            }
+    Vect_reset_updated(Map);
+
+    Vect_get_line_nodes(Map, line, &N1, &N2);
+    if (0 != V2__delete_line_from_topo_nat(Map, line, type, Points, NULL))
+        return -1;
+    
+    Node = Map->plus.Node[N1];
+    if (!Node || Node->n_lines == 0) {
+        node_id = pg_info->offset.array[N1-1];
+        sprintf(stmt, "DELETE FROM \"%s\".\"node\" WHERE node_id = %d",
+                pg_info->toposchema_name, node_id);
+        if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
+            G_warning(_("Unable to delete node %d"), node_id);
+            Vect__execute_pg(pg_info->conn, "ROLLBACK");
+            return -1;
+        }
+    }
+
+    Node = Map->plus.Node[N2];
+    if (!Node || Node->n_lines == 0) {
+        node_id = pg_info->offset.array[N2-1];
+        sprintf(stmt, "DELETE FROM \"%s\".\"node\" WHERE node_id = %d",
+                pg_info->toposchema_name, node_id);
+        if (Vect__execute_pg(pg_info->conn, stmt) == -1) {
+            G_warning(_("Unable to delete node %d"), node_id);
+            Vect__execute_pg(pg_info->conn, "ROLLBACK");
+            return -1;
         }
         }
     }
     }
-    */
 
 
     return 0;
     return 0;
 }
 }