Quellcode durchsuchen

vlib(pg): implement V1_write_line_pg() and V2_write_line_sfa()

git-svn-id: https://svn.osgeo.org/grass/grass/trunk@51027 15284696-431f-4ddb-bdfa-cd5b030d7da7
Martin Landa vor 13 Jahren
Ursprung
Commit
8eb93cfd41

+ 3 - 1
include/defs/vector.h

@@ -523,7 +523,9 @@ off_t V2_write_line_nat(struct Map_info *, int, const struct line_pnts *,
 			const struct line_cats *);
 			const struct line_cats *);
 off_t V1_write_line_ogr(struct Map_info *, int, const struct line_pnts *,
 off_t V1_write_line_ogr(struct Map_info *, int, const struct line_pnts *,
 			const struct line_cats *);
 			const struct line_cats *);
-off_t V2_write_line_ogr(struct Map_info *, int, const struct line_pnts *,
+off_t V1_write_line_pg(struct Map_info *, int, const struct line_pnts *,
+		       const struct line_cats *);
+off_t V2_write_line_sfa(struct Map_info *, int, const struct line_pnts *,
 			const struct line_cats *);
 			const struct line_cats *);
 off_t V1_rewrite_line_nat(struct Map_info *, int, int, off_t,
 off_t V1_rewrite_line_nat(struct Map_info *, int, int, off_t,
 			  const struct line_pnts *, const struct line_cats *);
 			  const struct line_pnts *, const struct line_cats *);

+ 13 - 1
include/vect/dig_structs.h

@@ -604,7 +604,19 @@ struct Format_info_pg
     /*!
     /*!
       \brief Geometry column
       \brief Geometry column
     */
     */
-    char    *geom_column;       
+    char    *geom_column;
+    /*!
+      \brief Feature type
+    */
+    SF_FeatureType feature_type;
+    /*!
+      \brief Coordinates dimension
+    */
+    int      coor_dim;
+    /*!
+      \brief SRS ID
+    */
+    int      srid;
 #ifdef HAVE_POSTGRES
 #ifdef HAVE_POSTGRES
     /*!
     /*!
       \brief PGconn object (generated by PQconnectdb)
       \brief PGconn object (generated by PQconnectdb)

+ 5 - 10
lib/vector/Vlib/build.c

@@ -22,12 +22,6 @@
 
 
 #define SEP "-----------------------------------\n"
 #define SEP "-----------------------------------\n"
 
 
-static int build_dummy()
-{
-    G_warning(_("Not implemented"));
-    return -1;
-}
-
 #if !defined HAVE_OGR || !defined HAVE_POSTGRES
 #if !defined HAVE_OGR || !defined HAVE_POSTGRES
 static int format()
 static int format()
 {
 {
@@ -704,11 +698,12 @@ int Vect_build_partial(struct Map_info *Map, int build)
 
 
     G_debug(3, "Vect_build(): build = %d", build);
     G_debug(3, "Vect_build(): build = %d", build);
 
 
-    /* If topology is already build (map on level2), set level to 1 so that lines will
-     *  be read by V1_read_ (all lines) */
-    Map->level = 1;		/* may be not needed, because  V1_read is used directly by Vect_build_ */
+    /* If topology is already build (map on level2), set level to 1 so
+     * that lines will be read by V1_read_ (all lines) */
+    Map->level = 1; /* may be not needed, because V1_read is used
+		       directly by Vect_build_ */
     if (Map->format != GV_FORMAT_OGR_DIRECT)
     if (Map->format != GV_FORMAT_OGR_DIRECT)
-	Map->support_updated = 1;
+	Map->support_updated = TRUE;
 
 
     if (Map->plus.Spidx_built == FALSE)
     if (Map->plus.Spidx_built == FALSE)
 	Vect_open_sidx(Map, 2);
 	Vect_open_sidx(Map, 2);

+ 12 - 5
lib/vector/Vlib/open.c

@@ -368,7 +368,8 @@ int open_old(struct Map_info *Map, const char *name, const char *mapset,
     }
     }
 
 
     /* open level 1 files / sources (format specific) */
     /* open level 1 files / sources (format specific) */
-    if (!head_only || ogr_mapset) {		/* no need to open coordinates */
+    if (!head_only || ogr_mapset || format == GV_FORMAT_POSTGIS) {
+	/* no need to open coordinates */
 	if (0 != (*Open_old_array[format][1]) (Map, update)) {	/* cannot open */
 	if (0 != (*Open_old_array[format][1]) (Map, update)) {	/* cannot open */
 	    if (level == 2) {	/* support files opened */
 	    if (level == 2) {	/* support files opened */
 		dig_free_plus(&(Map->plus));
 		dig_free_plus(&(Map->plus));
@@ -470,21 +471,27 @@ int open_old(struct Map_info *Map, const char *name, const char *mapset,
     
     
     /* delete support files if native format was opened for update (not head_only) */
     /* delete support files if native format was opened for update (not head_only) */
     if (update && !head_only) {
     if (update && !head_only) {
-	char file_path[2000];
+	char file_path[GPATH_MAX];
 
 
 	sprintf(buf, "%s/%s", GV_DIRECTORY, name);
 	sprintf(buf, "%s/%s", GV_DIRECTORY, name);
 
 
 	G_file_name(file_path, buf, GV_TOPO_ELEMENT, G_mapset());
 	G_file_name(file_path, buf, GV_TOPO_ELEMENT, G_mapset());
-	if (access(file_path, F_OK) == 0)	/* file exists? */
+	if (access(file_path, F_OK) == 0)	/* topo file exists? */
 	    unlink(file_path);
 	    unlink(file_path);
 
 
 	G_file_name(file_path, buf, GV_SIDX_ELEMENT, G_mapset());
 	G_file_name(file_path, buf, GV_SIDX_ELEMENT, G_mapset());
-	if (access(file_path, F_OK) == 0)	/* file exists? */
+	if (access(file_path, F_OK) == 0)	/* sidx file exists? */
 	    unlink(file_path);
 	    unlink(file_path);
 
 
 	G_file_name(file_path, buf, GV_CIDX_ELEMENT, G_mapset());
 	G_file_name(file_path, buf, GV_CIDX_ELEMENT, G_mapset());
-	if (access(file_path, F_OK) == 0)	/* file exists? */
+	if (access(file_path, F_OK) == 0)	/* cidx file exists? */
 	    unlink(file_path);
 	    unlink(file_path);
+
+	if (format == GV_FORMAT_OGR || format == GV_FORMAT_POSTGIS) {
+	    G_file_name(file_path, buf, GV_FIDX_ELEMENT, G_mapset());
+	    if (access(file_path, F_OK) == 0)	/* fidx file exists? */
+		unlink(file_path);
+	}
     }
     }
 
 
     return level;
     return level;

+ 36 - 1
lib/vector/Vlib/open_pg.c

@@ -58,6 +58,32 @@ static char *get_key_column(struct Format_info_pg *pg_info)
 
 
     return key_column;
     return key_column;
 }
 }
+
+static SF_FeatureType ftype_from_string(const char *type)
+{
+    SF_FeatureType sf_type;
+
+    if (G_strcasecmp(type, "POINT") == 0)
+	return SF_POINT;
+    else if (G_strcasecmp(type, "LINESTRING") == 0)
+	return SF_LINESTRING;
+    else if (G_strcasecmp(type, "POLYGON") == 0)
+	return SF_POLYGON;
+    else if (G_strcasecmp(type, "MULTIPOINT") == 0)
+	return SF_MULTIPOINT;
+    else if (G_strcasecmp(type, "MULTILINESTRING") == 0)
+	return SF_MULTILINESTRING;
+    else if (G_strcasecmp(type, "MULTIPOLYGON") == 0)
+	return SF_MULTIPOLYGON;
+    else if (G_strcasecmp(type, "GEOMETRYCOLLECTION") == 0)
+	return SF_GEOMETRYCOLLECTION;
+    
+    return SF_UNKNOWN;
+    
+    G_debug(3, "ftype_from_string(): type='%s' -> %d", type, sf_type);
+    
+    return sf_type;
+}
 #endif
 #endif
 
 
 /*!
 /*!
@@ -113,7 +139,8 @@ int V1_open_old_pg(struct Map_info *Map, int update)
     }
     }
     
     
     /* get fid and geometry column */
     /* get fid and geometry column */
-    db_set_string(&stmt, "SELECT f_table_name, f_geometry_column "
+    db_set_string(&stmt, "SELECT f_table_name, f_geometry_column,"
+		  "coord_dimension,srid,type "
 		  "FROM geometry_columns");
 		  "FROM geometry_columns");
     G_debug(2, "SQL: %s", db_get_string(&stmt));
     G_debug(2, "SQL: %s", db_get_string(&stmt));
     
     
@@ -127,10 +154,18 @@ int V1_open_old_pg(struct Map_info *Map, int update)
     found = FALSE;
     found = FALSE;
     for (i = 0; i < ntables; i++) {
     for (i = 0; i < ntables; i++) {
 	if (strcmp(PQgetvalue(res, i, 0), pg_info->table_name) == 0) {
 	if (strcmp(PQgetvalue(res, i, 0), pg_info->table_name) == 0) {
+	    /* geometry column */
 	    pg_info->geom_column = G_store(PQgetvalue(res, i, 1));
 	    pg_info->geom_column = G_store(PQgetvalue(res, i, 1));
 	    G_debug(3, "\t-> table = %s column = %s", pg_info->table_name,
 	    G_debug(3, "\t-> table = %s column = %s", pg_info->table_name,
 		    pg_info->geom_column);
 		    pg_info->geom_column);
+	    /* fid column */
 	    pg_info->fid_column = get_key_column(pg_info);
 	    pg_info->fid_column = get_key_column(pg_info);
+	    /* coordinates dimension */
+	    pg_info->coor_dim = atoi(PQgetvalue(res, i, 2));
+	    /* SRS ID */
+	    pg_info->srid = atoi(PQgetvalue(res, i, 3));
+	    /* feature type */
+	    pg_info->feature_type = ftype_from_string(PQgetvalue(res, i, 4));
 	    found = TRUE;
 	    found = TRUE;
 	    break;
 	    break;
 	}
 	}

+ 32 - 0
lib/vector/Vlib/pg_local_proto.h

@@ -1,9 +1,41 @@
 #ifndef __PG_LOCAL_PROTO_H__
 #ifndef __PG_LOCAL_PROTO_H__
 #define __PG_LOCAL_PROTO_H__
 #define __PG_LOCAL_PROTO_H__
 
 
+#include <grass/vector.h>
+
 #ifdef HAVE_POSTGRES
 #ifdef HAVE_POSTGRES
 #include <libpq-fe.h>
 #include <libpq-fe.h>
 
 
+#define CURSOR_PAGE 500
+
+#define SWAP32(x) \
+        ((unsigned int)( \
+            (((unsigned int)(x) & (unsigned int)0x000000ffUL) << 24) | \
+            (((unsigned int)(x) & (unsigned int)0x0000ff00UL) <<  8) | \
+            (((unsigned int)(x) & (unsigned int)0x00ff0000UL) >>  8) | \
+            (((unsigned int)(x) & (unsigned int)0xff000000UL) >> 24) ))
+
+#define SWAPDOUBLE(x) \
+{                                                                 \
+    unsigned char temp, *data = (unsigned char *) (x);            \
+                                                                  \
+    temp = data[0];                                               \
+    data[0] = data[7];                                            \
+    data[7] = temp;                                               \
+    temp = data[1];                                               \
+    data[1] = data[6];                                            \
+    data[6] = temp;                                               \
+    temp = data[2];                                               \
+    data[2] = data[5];                                            \
+    data[5] = temp;                                               \
+    temp = data[3];                                               \
+    data[3] = data[4];                                            \
+    data[4] = temp;                                               \
+}                                                                    
+
+#define LSBWORD32(x)      (x)
+#define MSBWORD32(x)      SWAP32(x)
+
 /* used for building pseudo-topology (requires some extra information
 /* used for building pseudo-topology (requires some extra information
  * about lines in cache) */
  * about lines in cache) */
 struct feat_parts
 struct feat_parts

+ 2 - 0
lib/vector/Vlib/read.c

@@ -21,6 +21,8 @@
 
 
 static int read_dummy()
 static int read_dummy()
 {
 {
+    G_warning("Vect_read_line() %s",
+	      _("for this format/level not supported"));
     return -1;
     return -1;
 }
 }
 
 

+ 0 - 27
lib/vector/Vlib/read_pg.c

@@ -27,33 +27,6 @@
 #ifdef HAVE_POSTGRES
 #ifdef HAVE_POSTGRES
 #include "pg_local_proto.h"
 #include "pg_local_proto.h"
 
 
-#define CURSOR_PAGE 500
-
-#define SWAP32(x) \
-        ((unsigned int)( \
-            (((unsigned int)(x) & (unsigned int)0x000000ffUL) << 24) | \
-            (((unsigned int)(x) & (unsigned int)0x0000ff00UL) <<  8) | \
-            (((unsigned int)(x) & (unsigned int)0x00ff0000UL) >>  8) | \
-            (((unsigned int)(x) & (unsigned int)0xff000000UL) >> 24) ))
-
-#define SWAPDOUBLE(x) \
-{                                                                 \
-    unsigned char temp, *data = (unsigned char *) (x);            \
-                                                                  \
-    temp = data[0];                                               \
-    data[0] = data[7];                                            \
-    data[7] = temp;                                               \
-    temp = data[1];                                               \
-    data[1] = data[6];                                            \
-    data[6] = temp;                                               \
-    temp = data[2];                                               \
-    data[2] = data[5];                                            \
-    data[5] = temp;                                               \
-    temp = data[3];                                               \
-    data[3] = data[4];                                            \
-    data[4] = temp;                                               \
-}                                                                    
-
 static int read_next_line_pg(struct Map_info *,
 static int read_next_line_pg(struct Map_info *,
 		      struct line_pnts *, struct line_cats *, int);
 		      struct line_pnts *, struct line_cats *, int);
 SF_FeatureType get_feature(struct Format_info_pg *, int);
 SF_FeatureType get_feature(struct Format_info_pg *, int);

+ 65 - 31
lib/vector/Vlib/write.c

@@ -5,13 +5,20 @@
 
 
    Higher level functions for reading/writing/manipulating vectors.
    Higher level functions for reading/writing/manipulating vectors.
 
 
-   (C) 2001-2010 by the GRASS Development Team
+   Operations:
+    - Add feature
+    - Rewrite feature
+    - Delete feature
+    - Restore feature
+
+   (C) 2001-2010, 2012 by the GRASS Development Team
 
 
    This program is free software under the GNU General Public License
    This program is free software under the GNU General Public License
    (>=v2). Read the file COPYING that comes with GRASS for details.
    (>=v2). Read the file COPYING that comes with GRASS for details.
 
 
    \author Radim Blazek
    \author Radim Blazek
-   \author Updated by Martin Landa <landa.martin gmail.com> (restore lines, OGR support)
+   \author Updated by Martin Landa <landa.martin gmail.com>
+   (restore lines, OGR & PostGIS support)
  */
  */
 
 
 #include <sys/types.h>
 #include <sys/types.h>
@@ -44,7 +51,7 @@ static int restore_dummy()
     return -1;
     return -1;
 }
 }
 
 
-#ifndef HAVE_OGR
+#if !defined HAVE_OGR || !defined HAVE_POSTGRES
 static int format()
 static int format()
 {
 {
     G_fatal_error(_("Requested format is not compiled in this version"));
     G_fatal_error(_("Requested format is not compiled in this version"));
@@ -56,54 +63,74 @@ static off_t format_l()
     G_fatal_error(_("Requested format is not compiled in this version"));
     G_fatal_error(_("Requested format is not compiled in this version"));
     return 0;
     return 0;
 }
 }
-
 #endif
 #endif
 
 
 static off_t (*Vect_write_line_array[][3]) () = {
 static off_t (*Vect_write_line_array[][3]) () = {
     {
     {
-    write_dummy, V1_write_line_nat, V2_write_line_nat}
+	write_dummy, V1_write_line_nat, V2_write_line_nat}
 #ifdef HAVE_OGR
 #ifdef HAVE_OGR
     , {
     , {
-    write_dummy, V1_write_line_ogr, V2_write_line_ogr}
+	write_dummy, V1_write_line_ogr, V2_write_line_sfa}
     , {
     , {
-    write_dummy, V1_write_line_ogr, V2_write_line_ogr}
+	write_dummy, V1_write_line_ogr, V2_write_line_sfa}
 #else
 #else
     , {
     , {
-    write_dummy, format_l, format_l}
+	write_dummy, format_l, format_l}
     , {
     , {
-    write_dummy, format_l, format_l}
+	write_dummy, format_l, format_l}
+#endif
+#ifdef HAVE_POSTGRES
+    , {
+	write_dummy, V1_write_line_pg, V2_write_line_sfa}
+#else
+    , {
+	write_dummy, format_l, format_l}
 #endif
 #endif
 };
 };
 
 
 static off_t (*Vect_rewrite_line_array[][3]) () = {
 static off_t (*Vect_rewrite_line_array[][3]) () = {
     {
     {
-    rewrite_dummy, V1_rewrite_line_nat, V2_rewrite_line_nat}
+	rewrite_dummy, V1_rewrite_line_nat, V2_rewrite_line_nat}
 #ifdef HAVE_OGR
 #ifdef HAVE_OGR
     , {
     , {
-    rewrite_dummy, V1_rewrite_line_ogr, V2_rewrite_line_ogr}
+	rewrite_dummy, V1_rewrite_line_ogr, V2_rewrite_line_ogr}
     , {
     , {
-    rewrite_dummy, V1_rewrite_line_ogr, V2_rewrite_line_ogr}
+	rewrite_dummy, V1_rewrite_line_ogr, V2_rewrite_line_ogr}
 #else
 #else
     , {
     , {
-    rewrite_dummy, format, format}
+	rewrite_dummy, format_l, format_l}
+    , {
+	rewrite_dummy, format_l, format_l}
+#endif
+#ifdef HAVE_POSTGRES
+    , {
+	rewrite_dummy, rewrite_dummy, rewrite_dummy}
+#else
     , {
     , {
-    rewrite_dummy, format, format}
+	rewrite_dummy, format_l, format_l}
 #endif
 #endif
 };
 };
 
 
 static int (*Vect_delete_line_array[][3]) () = {
 static int (*Vect_delete_line_array[][3]) () = {
     {
     {
-    delete_dummy, V1_delete_line_nat, V2_delete_line_nat}
+	delete_dummy, V1_delete_line_nat, V2_delete_line_nat}
 #ifdef HAVE_OGR
 #ifdef HAVE_OGR
     , {
     , {
-    delete_dummy, V1_delete_line_ogr, V2_delete_line_ogr}
+	delete_dummy, V1_delete_line_ogr, V2_delete_line_ogr}
     , {
     , {
-    delete_dummy, V1_delete_line_ogr, V2_delete_line_ogr}
+	delete_dummy, V1_delete_line_ogr, V2_delete_line_ogr}
 #else
 #else
     , {
     , {
-    delete_dummy, format, format}
+	delete_dummy, format, format}
+    , {
+	delete_dummy, format, format}
+#endif
+#ifdef HAVE_POSTGRES
+    , {
+	delete_dummy, delete_dummy, delete_dummy}
+#else
     , {
     , {
-    delete_dummy, format, format}
+	delete_dummy, format, format}
 #endif
 #endif
 };
 };
 
 
@@ -112,14 +139,21 @@ static int (*Vect_restore_line_array[][3]) () = {
     restore_dummy, restore_dummy, V2_restore_line_nat}
     restore_dummy, restore_dummy, V2_restore_line_nat}
 #ifdef HAVE_OGR
 #ifdef HAVE_OGR
     , {
     , {
-    restore_dummy, restore_dummy, restore_dummy}
+	restore_dummy, restore_dummy, restore_dummy}
     , {
     , {
-    restore_dummy, restore_dummy, restore_dummy}
+	restore_dummy, restore_dummy, restore_dummy}
 #else
 #else
     , {
     , {
-    restore_dummy, format, format}
+	restore_dummy, format, format}
+    , {
+	restore_dummy, format, format}
+#endif
+#ifdef HAVE_POSTGRES
+    , {
+	restore_dummy, restore_dummy, restore_dummy}
+#else
     , {
     , {
-    restore_dummy, format, format}
+	restore_dummy, format, format}
 #endif
 #endif
 };
 };
 
 
@@ -130,8 +164,8 @@ static int (*Vect_restore_line_array[][3]) () = {
 
 
    The function calls G_fatal_error() on error.
    The function calls G_fatal_error() on error.
 
 
-   \param Map pointer to vector map
-   \param type feature type
+   \param Map pointer to Map_info structure
+   \param type feature type (GV_POINT, GV_LINE, ...)
    \param points feature geometry
    \param points feature geometry
    \param cats feature categories
    \param cats feature categories
 
 
@@ -153,9 +187,9 @@ off_t Vect_write_line(struct Map_info *Map, int type,
 	Map->plus.cidx_up_to_date = 0;
 	Map->plus.cidx_up_to_date = 0;
     }
     }
 
 
-    offset =
+    offset = 
 	(*Vect_write_line_array[Map->format][Map->level]) (Map, type, points,
 	(*Vect_write_line_array[Map->format][Map->level]) (Map, type, points,
-						      cats);
+							   cats);
 
 
     if (offset == -1)
     if (offset == -1)
 	G_fatal_error(_("Unable to write feature (negative offset)"));
 	G_fatal_error(_("Unable to write feature (negative offset)"));
@@ -174,9 +208,9 @@ off_t Vect_write_line(struct Map_info *Map, int type,
 
 
    This function calls G_fatal_error() on error.
    This function calls G_fatal_error() on error.
 
 
-   \param Map pointer to vector map
+   \param Map pointer to Map_info structure
    \param line feature id
    \param line feature id
-   \param type feature type
+   \param type feature type (GV_POINT, GV_LINE, ...)
    \param points feature geometry
    \param points feature geometry
    \param cats feature categories
    \param cats feature categories
 
 
@@ -216,7 +250,7 @@ off_t Vect_rewrite_line(struct Map_info *Map, int line, int type,
 
 
    This function calls G_fatal_error() on error.
    This function calls G_fatal_error() on error.
 
 
-   \param Map pointer to vector map
+   \param Map pointer to Map_info structure
    \param line feature id
    \param line feature id
 
 
    \return 0 on success
    \return 0 on success
@@ -260,7 +294,7 @@ int Vect_delete_line(struct Map_info *Map, int line)
 
 
    This function calls G_fatal_error() on error.
    This function calls G_fatal_error() on error.
 
 
-   \param Map pointer to vector map
+   \param Map pointer to Map_info structure
    \param line feature id to be restored
    \param line feature id to be restored
 
 
    \return 0 on success
    \return 0 on success

+ 18 - 21
lib/vector/Vlib/write_nat.c

@@ -5,13 +5,7 @@
 
 
    Higher level functions for reading/writing/manipulating vectors.
    Higher level functions for reading/writing/manipulating vectors.
 
 
-   Operations:
-    - Add feature
-    - Rewrite feature
-    - Delete feature
-    - Restore feature
-
-   (C) 2001-2010 by the GRASS Development Team
+   (C) 2001-2010, 2012 by the GRASS Development Team
 
 
    This program is free software under the GNU General Public License
    This program is free software under the GNU General Public License
    (>=v2). Read the file COPYING that comes with GRASS for details.
    (>=v2). Read the file COPYING that comes with GRASS for details.
@@ -29,13 +23,14 @@
 #include <grass/glocale.h>
 #include <grass/glocale.h>
 
 
 static off_t V1__rewrite_line_nat(struct Map_info *, off_t, int,
 static off_t V1__rewrite_line_nat(struct Map_info *, off_t, int,
-			      const struct line_pnts *, const struct line_cats *);
-
+				  const struct line_pnts *, const struct line_cats *);
 
 
 /*! 
 /*! 
   \brief Deletes area (i.e. centroid) categories from category
   \brief Deletes area (i.e. centroid) categories from category
   index (internal use only)
   index (internal use only)
 
 
+  Call G_fatal_error() when area do not exits.
+  
   \param Map pointer to Map_info structure
   \param Map pointer to Map_info structure
   \param area area id
   \param area area id
 */
 */
@@ -70,6 +65,8 @@ static void V2__delete_area_cats_from_cidx_nat(struct Map_info *Map, int area)
   \brief Adds area (i.e. centroid) categories from category index
   \brief Adds area (i.e. centroid) categories from category index
   (internal use only)
   (internal use only)
 
 
+  Call G_fatal_error() when area do not exits.
+  
   \param Map pointer to Map_info structure
   \param Map pointer to Map_info structure
   \param area area id
   \param area area id
 */
 */
@@ -101,7 +98,7 @@ static void V2__add_area_cats_to_cidx_nat(struct Map_info *Map, int area)
 }
 }
 
 
 /*!
 /*!
-  \brief Add line to topo file (internal use only)
+  \brief Add feature (line) to topo file (internal use only)
 
 
   Update areas. Areas are modified if: 
   Update areas. Areas are modified if: 
    
    
@@ -132,7 +129,7 @@ static void V2__add_area_cats_to_cidx_nat(struct Map_info *Map, int area)
     Note that 1) and 2) is done by the same code.
     Note that 1) and 2) is done by the same code.
 
 
     \param Map pointer to Map_info structure
     \param Map pointer to Map_info structure
-    \param line line id
+    \param line feature id
     \param points pointer to line_pnts structure (feature's geometry)
     \param points pointer to line_pnts structure (feature's geometry)
     \param cats pointer to line_cats structure (feature's categories)
     \param cats pointer to line_cats structure (feature's categories)
 */
 */
@@ -312,7 +309,7 @@ static void V2__add_line_to_topo_nat(struct Map_info *Map, int line,
   \brief Writes feature to 'coor' file (level 1)
   \brief Writes feature to 'coor' file (level 1)
   
   
   \param Map pointer to Map_info structure
   \param Map pointer to Map_info structure
-  \param type feature type
+  \param type feature type (GV_POINT, GV_LINE, ...)
   \param points feature geometry
   \param points feature geometry
   \param cats feature categories
   \param cats feature categories
   
   
@@ -338,7 +335,7 @@ off_t V1_write_line_nat(struct Map_info *Map,
   \brief Writes feature to 'coor' file (topology level) - internal use only
   \brief Writes feature to 'coor' file (topology level) - internal use only
   
   
   \param Map pointer to Map_info structure
   \param Map pointer to Map_info structure
-  \param type feature type
+  \param type feature type (GV_POINT, GV_LINE, ...)
   \param points feature geometry
   \param points feature geometry
   \param cats feature categories
   \param cats feature categories
   
   
@@ -346,7 +343,7 @@ off_t V1_write_line_nat(struct Map_info *Map,
   \return -1 on error
   \return -1 on error
 */
 */
 off_t V2_write_line_nat(struct Map_info *Map, int type,
 off_t V2_write_line_nat(struct Map_info *Map, int type,
-             const struct line_pnts *points, const struct line_cats *cats)
+			const struct line_pnts *points, const struct line_cats *cats)
 {
 {
     int line;
     int line;
     off_t offset;
     off_t offset;
@@ -396,7 +393,7 @@ off_t V2_write_line_nat(struct Map_info *Map, int type,
   
   
   \param Map pointer to Map_info structure
   \param Map pointer to Map_info structure
   \param offset feature offset
   \param offset feature offset
-  \param type feature type
+  \param type feature type (GV_POINT, GV_LINE, ...)
   \param points feature geometry
   \param points feature geometry
   \param cats feature categories
   \param cats feature categories
   
   
@@ -411,7 +408,8 @@ off_t V1_rewrite_line_nat(struct Map_info *Map, int line, int type, off_t offset
     struct line_cats *old_cats;
     struct line_cats *old_cats;
     off_t new_offset;
     off_t new_offset;
     
     
-    G_debug(3, "V1_rewrite_line_nat(), offset = %lu", (unsigned long) offset);
+    G_debug(3, "V1_rewrite_line_nat(): line = %d offset = %lu",
+	    line, (unsigned long) offset);
 
 
     /* TODO: enable points and cats == NULL  */
     /* TODO: enable points and cats == NULL  */
 
 
@@ -453,7 +451,7 @@ off_t V1_rewrite_line_nat(struct Map_info *Map, int line, int type, off_t offset
   \brief Rewrites feature to 'coor' file (topology level) - internal use only
   \brief Rewrites feature to 'coor' file (topology level) - internal use only
   
   
   \param Map pointer to Map_info structure
   \param Map pointer to Map_info structure
-  \param type feature type
+  \param type feature type  (GV_POINT, GV_LINE, ...)
   \param line feature id
   \param line feature id
   \param points feature geometry
   \param points feature geometry
   \param cats feature categories
   \param cats feature categories
@@ -510,7 +508,7 @@ off_t V2_rewrite_line_nat(struct Map_info *Map, int line, int type, off_t old_of
   
   
   \param Map pointer to Map_info structure
   \param Map pointer to Map_info structure
   \param offset feature offset
   \param offset feature offset
-  \param type feature type
+  \param type feature type  (GV_POINT, GV_LINE, ...)
   \param points feature geometry
   \param points feature geometry
   \param cats feature categories
   \param cats feature categories
   
   
@@ -518,9 +516,8 @@ off_t V2_rewrite_line_nat(struct Map_info *Map, int line, int type, off_t old_of
   \return -1 on error
   \return -1 on error
 */
 */
 off_t V1__rewrite_line_nat(struct Map_info *Map,
 off_t V1__rewrite_line_nat(struct Map_info *Map,
-		       off_t offset,
-		       int type,
-		       const struct line_pnts *points, const struct line_cats *cats)
+			   off_t offset, int type,
+			   const struct line_pnts *points, const struct line_cats *cats)
 {
 {
     int i, n_points;
     int i, n_points;
     char rhead, nc;
     char rhead, nc;

+ 33 - 190
lib/vector/Vlib/write_ogr.c

@@ -7,12 +7,7 @@
 
 
    Inspired by v.out.ogr's code.
    Inspired by v.out.ogr's code.
 
 
-   \todo OGR version of V2__delete_area_cats_from_cidx_nat()
-   \todo function to delete corresponding entry in fidx
-   \todo OGR version of V2__add_area_cats_to_cidx_nat
-   \todo OGR version of V2__add_line_to_topo_nat
-
-   (C) 2009-2011 by the GRASS Development Team
+   (C) 2009-2011, 2012 by Martin Landa, and the GRASS Development Team
 
 
    This program is free software under the GNU General Public License
    This program is free software under the GNU General Public License
    (>=v2). Read the file COPYING that comes with GRASS for details.
    (>=v2). Read the file COPYING that comes with GRASS for details.
@@ -30,77 +25,6 @@
 static int sqltype_to_ogrtype(int);
 static int sqltype_to_ogrtype(int);
 static int write_attributes(dbDriver *, int, const struct field_info *,
 static int write_attributes(dbDriver *, int, const struct field_info *,
 			    OGRLayerH, OGRFeatureH);
 			    OGRLayerH, OGRFeatureH);
-
-static void V2__add_line_to_topo_ogr(struct Map_info *Map, int line,
-				     const struct line_pnts *points,
-				     const struct line_cats *cats)
-{
-    int first, s, i;
-    int type, area, side;
-
-    struct Plus_head *plus;
-    struct P_line *Line;
-    
-    struct bound_box box, abox;
-    
-    G_debug(3, "V2__add_line_to_topo_ogr(): line = %d npoints = %d", line,
-	    points->n_points);
-
-    plus = &(Map->plus);
-    Line = plus->Line[line];
-    type = Line->type;
-
-    if (plus->built >= GV_BUILD_AREAS &&
-	type == GV_BOUNDARY) {	
-	struct P_topo_b *topo = (struct P_topo_b *)Line->topo;
-	
-	if (topo->N1 != topo->N2) {
-	    G_warning(_("Boundary is not closed. Skipping."));
-	    return;
-	}
-	
-	/* Build new areas/isles */
-	for (s = 0; s < 2; s++) {
-	    side = (s == 0 ? GV_LEFT : GV_RIGHT);
-	    area = Vect_build_line_area(Map, line, side);
-	    if (area > 0) {	/* area */
-		Vect_get_area_box(Map, area, &box);
-		if (first) {
-		    Vect_box_copy(&abox, &box);
-		    first = FALSE;
-		}
-		else
-		    Vect_box_extend(&abox, &box);
-	    }
-	    else if (area < 0) {
-		/* isle -> must be attached -> add to abox */
-		Vect_get_isle_box(Map, -area, &box);
-		if (first) {
-		    Vect_box_copy(&abox, &box);
-		    first = FALSE;
-		}
-		else
-		    Vect_box_extend(&abox, &box);
-	    }
-	    G_debug(4, "Vect_build_line_area(): -> area = %d", area);
-	}
-
-	/* Attach centroid/isle to the new area */
-	if (plus->built >= GV_BUILD_ATTACH_ISLES)
-	    Vect_attach_isles(Map, &abox);
-	if (plus->built >= GV_BUILD_CENTROIDS)
-	    Vect_attach_centroids(Map, &abox);
-    }
-    
-    /* Add category index */
-    for (i = 0; i < cats->n_cats; i++) {
-	dig_cidx_add_cat_sorted(plus, cats->field[i], cats->cat[i], line,
-				type);
-    }
-    
-    return;
-}
-
 #endif
 #endif
 
 
 /*!
 /*!
@@ -115,7 +39,7 @@ static void V2__add_line_to_topo_ogr(struct Map_info *Map, int line,
   \todo How to deal with OGRNullFID ?
   \todo How to deal with OGRNullFID ?
   
   
   \param Map pointer to Map_info structure
   \param Map pointer to Map_info structure
-  \param type feature type
+  \param type feature type (GV_POINT, GV_LINE, ...)
   \param points pointer to line_pnts structure (feature geometry) 
   \param points pointer to line_pnts structure (feature geometry) 
   \param cats pointer to line_cats structure (feature categories)
   \param cats pointer to line_cats structure (feature categories)
   
   
@@ -130,7 +54,8 @@ off_t V1_write_line_ogr(struct Map_info *Map, int type,
     int i, cat, ret;
     int i, cat, ret;
 
 
     struct field_info *Fi;
     struct field_info *Fi;
-    struct Format_info_ogr *fInfo;
+    struct Format_info_ogr *ogr_info;
+    struct Format_info_offset *offset_info;
     
     
     off_t offset;
     off_t offset;
     
     
@@ -139,7 +64,10 @@ off_t V1_write_line_ogr(struct Map_info *Map, int type,
     OGRFeatureDefnH    Ogr_featuredefn;
     OGRFeatureDefnH    Ogr_featuredefn;
     OGRwkbGeometryType Ogr_geom_type;
     OGRwkbGeometryType Ogr_geom_type;
 
 
-    if (!Map->fInfo.ogr.layer) {
+    ogr_info = &(Map->fInfo.ogr);
+    offset_info = &(ogr_info->offset);
+    
+    if (!ogr_info->layer) {
 	/* create OGR layer if doesn't exist */
 	/* create OGR layer if doesn't exist */
 	if (V2_open_new_ogr(Map, type) < 0)
 	if (V2_open_new_ogr(Map, type) < 0)
 	    return -1;
 	    return -1;
@@ -160,8 +88,7 @@ off_t V1_write_line_ogr(struct Map_info *Map, int type,
 	}
 	}
     }
     }
     
     
-    fInfo = &(Map->fInfo.ogr);
-    Ogr_featuredefn = OGR_L_GetLayerDefn(fInfo->layer);
+    Ogr_featuredefn = OGR_L_GetLayerDefn(ogr_info->layer);
     Ogr_geom_type = OGR_FD_GetGeomType(Ogr_featuredefn);
     Ogr_geom_type = OGR_FD_GetGeomType(Ogr_featuredefn);
     
     
     /* determine matching OGR feature geometry type */
     /* determine matching OGR feature geometry type */
@@ -237,28 +164,28 @@ off_t V1_write_line_ogr(struct Map_info *Map, int type,
     OGR_F_SetGeometry(Ogr_feature, Ogr_geometry);
     OGR_F_SetGeometry(Ogr_feature, Ogr_geometry);
 
 
     /* write attributes */
     /* write attributes */
-    if (cat > -1 && fInfo->dbdriver) {
-	write_attributes(fInfo->dbdriver,
-			 cat, Fi, fInfo->layer, Ogr_feature);
+    if (cat > -1 && ogr_info->dbdriver) {
+	write_attributes(ogr_info->dbdriver,
+			 cat, Fi, ogr_info->layer, Ogr_feature);
 	G_free(Fi);
 	G_free(Fi);
     }
     }
     /* write feature into layer */
     /* write feature into layer */
-    ret = OGR_L_CreateFeature(fInfo->layer, Ogr_feature);
+    ret = OGR_L_CreateFeature(ogr_info->layer, Ogr_feature);
 
 
     /* update offset array */
     /* update offset array */
-    if (fInfo->offset.array_num >= fInfo->offset.array_alloc) {
-	fInfo->offset.array_alloc += 1000;
-	fInfo->offset.array = (int *) G_realloc(fInfo->offset.array,
-						fInfo->offset.array_alloc *
+    if (offset_info->array_num >= offset_info->array_alloc) {
+	offset_info->array_alloc += 1000;
+	offset_info->array = (int *) G_realloc(offset_info->array,
+						offset_info->array_alloc *
 						sizeof(int));
 						sizeof(int));
     }
     }
 
 
-    offset = fInfo->offset.array_num;
+    offset = offset_info->array_num;
     
     
-    fInfo->offset.array[fInfo->offset.array_num++] = (int) OGR_F_GetFID(Ogr_feature);
+    offset_info->array[offset_info->array_num++] = (int) OGR_F_GetFID(Ogr_feature);
     if (Ogr_geom_type == wkbPolygon || Ogr_geom_type == wkbPolygon25D) {
     if (Ogr_geom_type == wkbPolygon || Ogr_geom_type == wkbPolygon25D) {
 	/* register exterior ring in offset array */
 	/* register exterior ring in offset array */
-	fInfo->offset.array[fInfo->offset.array_num++] = 0; 
+	offset_info->array[offset_info->array_num++] = 0; 
     }
     }
       
       
     /* destroy */
     /* destroy */
@@ -278,97 +205,11 @@ off_t V1_write_line_ogr(struct Map_info *Map, int type,
 }
 }
 
 
 /*!
 /*!
-  \brief Writes feature on level 2
-
-  \param Map pointer to Map_info structure
-  \param type feature type
-  \param points pointer to line_pnts structure (feature geometry) 
-  \param cats pointer to line_cats structure (feature categories)
-  
-  \return feature offset into file
-  \return -1 on error
-*/
-off_t V2_write_line_ogr(struct Map_info *Map, int type,
-			const struct line_pnts *points, const struct line_cats *cats)
-{
-#ifdef HAVE_OGR
-    int line;
-    off_t offset;
-    struct Plus_head *plus;
-    struct bound_box box;
-
-    line = 0;
-    plus = &(Map->plus);
-    
-    G_debug(3, "V2_write_line_ogr()");
-    
-    offset = V1_write_line_ogr(Map, type, points, cats);
-    if (offset < 0)
-	return -1;
-    
-    /* Update topology */
-    if (plus->built >= GV_BUILD_BASE) {
-	dig_line_box(points, &box);
-	line = dig_add_line(plus, type, points, &box, offset);
-	G_debug(3, "\tline added to topo with line = %d", line);
-	if (line == 1)
-	    Vect_box_copy(&(plus->box), &box);
-	else
-	    Vect_box_extend(&(plus->box), &box);
-
-	if (type == GV_BOUNDARY) {
-	    int ret, cline;
-	    long FID;
-	    double x, y;
-	    
-	    struct bound_box box;
-	    struct line_pnts *CPoints;
-
-	    /* add virtual centroid to pseudo-topology */
-	    ret = Vect_get_point_in_poly(points, &x, &y);
-	    if (ret == 0) {
-		CPoints = Vect_new_line_struct();
-		Vect_append_point(CPoints, x, y, 0.0);
-		
-		FID = Map->fInfo.ogr.offset.array[offset];
-
-		dig_line_box(CPoints, &box);
-		cline = dig_add_line(plus, GV_CENTROID,
-				     CPoints, &box, FID);
-		G_debug(4, "\tCentroid: x = %f, y = %f, cat = %lu, line = %d",
-			x, y, FID, cline);	  
-		dig_cidx_add_cat(plus, 1, (int) FID,
-				 cline, GV_CENTROID);
-		
-		Vect_destroy_line_struct(CPoints);
-	    }
-	    else {
-		G_warning(_("Unable to calculate centroid for area"));
-	    }
-	}
-	V2__add_line_to_topo_ogr(Map, line, points, cats);
-    }
-
-
-    G_debug(3, "updated lines : %d , updated nodes : %d", plus->uplist.n_uplines,
-	    plus->uplist.n_upnodes);
-
-    /* returns int line, but is defined as off_t for compatibility with
-     * Write_line_array in write.c */
-    
-    return line;
-#else
-    G_fatal_error(_("GRASS is not compiled with OGR support"));
-    return -1;
-#endif
-}
-
-/*!
-  \brief Rewrites feature at the given offset (level 1)
+  \brief Rewrites feature at the given offset (level 1) (OGR interface)
   
   
   \param Map pointer to Map_info structure
   \param Map pointer to Map_info structure
   \param offset feature offset
   \param offset feature offset
-  \param type feature type
+  \param type feature type (GV_POINT, GV_LINE, ...)
   \param points feature geometry
   \param points feature geometry
   \param cats feature categories
   \param cats feature categories
   
   
@@ -376,9 +217,7 @@ off_t V2_write_line_ogr(struct Map_info *Map, int type,
   \return -1 on error
   \return -1 on error
 */
 */
 off_t V1_rewrite_line_ogr(struct Map_info *Map,
 off_t V1_rewrite_line_ogr(struct Map_info *Map,
-			  int line,
-			  int type,
-			  off_t offset,
+			  int line, int type, off_t offset,
 			  const struct line_pnts *points, const struct line_cats *cats)
 			  const struct line_pnts *points, const struct line_cats *cats)
 {
 {
     G_debug(3, "V1_rewrite_line_ogr(): line=%d type=%d offset=%llu",
     G_debug(3, "V1_rewrite_line_ogr(): line=%d type=%d offset=%llu",
@@ -404,7 +243,7 @@ off_t V1_rewrite_line_ogr(struct Map_info *Map,
   
   
   \param Map pointer to Map_info structure
   \param Map pointer to Map_info structure
   \param line feature id
   \param line feature id
-  \param type feature type
+  \param type feature type (GV_POINT, GV_LINE, ...)
   \param offset unused
   \param offset unused
   \param points feature geometry
   \param points feature geometry
   \param cats feature categories
   \param cats feature categories
@@ -426,7 +265,7 @@ off_t V2_rewrite_line_ogr(struct Map_info *Map, int line, int type, off_t offset
 
 
     V2_delete_line_ogr(Map, line);
     V2_delete_line_ogr(Map, line);
 
 
-    return V2_write_line_ogr(Map, type, points, cats);
+    return V2_write_line_sfa(Map, type, points, cats);
 #else
 #else
     G_fatal_error(_("GRASS is not compiled with OGR support"));
     G_fatal_error(_("GRASS is not compiled with OGR support"));
     return -1;
     return -1;
@@ -445,18 +284,22 @@ off_t V2_rewrite_line_ogr(struct Map_info *Map, int line, int type, off_t offset
 int V1_delete_line_ogr(struct Map_info *Map, off_t offset)
 int V1_delete_line_ogr(struct Map_info *Map, off_t offset)
 {
 {
 #ifdef HAVE_OGR
 #ifdef HAVE_OGR
+    struct Format_info_ogr *ogr_info;
+    
     G_debug(3, "V1_delete_line_ogr(), offset = %lu", (unsigned long) offset);
     G_debug(3, "V1_delete_line_ogr(), offset = %lu", (unsigned long) offset);
 
 
-    if (!Map->fInfo.ogr.layer) {
+    ogr_info = &(Map->fInfo.ogr);
+    
+    if (!ogr_info->layer) {
 	G_warning(_("OGR layer not defined"));
 	G_warning(_("OGR layer not defined"));
 	return -1;
 	return -1;
     }
     }
     
     
-    if (offset >= Map->fInfo.ogr.offset.array_num)
+    if (offset >= ogr_info->offset.array_num)
 	return -1;
 	return -1;
     
     
-    if (OGR_L_DeleteFeature(Map->fInfo.ogr.layer,
-			    Map->fInfo.ogr.offset.array[offset]) != OGRERR_NONE)
+    if (OGR_L_DeleteFeature(ogr_info->layer,
+			    ogr_info->offset.array[offset]) != OGRERR_NONE)
 	return -1;
 	return -1;
     
     
     return 0;
     return 0;

+ 554 - 0
lib/vector/Vlib/write_pg.c

@@ -0,0 +1,554 @@
+/*!
+   \file lib/vector/Vlib/write_pg.c
+
+   \brief Vector library - write vector feature (PostGIS format)
+
+   Higher level functions for reading/writing/manipulating vectors.
+
+   Inspired by OGR PostgreSQL driver.
+
+   \todo PostGIS version of V2__add_line_to_topo_nat()
+   \todo OGR version of V2__delete_area_cats_from_cidx_nat()
+   \todo function to delete corresponding entry in fidx
+   \todo OGR version of V2__add_area_cats_to_cidx_nat
+   \todo OGR version of V2__add_line_to_topo_nat
+
+   (C) 2012 by Martin Landa, and the GRASS Development Team
+
+   This program is free software under the GNU General Public License
+   (>=v2). Read the file COPYING that comes with GRASS for details.
+
+   \author Martin Landa <landa.martin gmail.com>
+ */
+
+#include <grass/vector.h>
+#include <grass/glocale.h>
+
+#ifdef HAVE_POSTGRES
+#include "pg_local_proto.h"
+
+#define WKBSRIDFLAG 0x20000000
+
+static char *binary_to_hex(int, const unsigned char *);
+static unsigned char *point_to_wkb(int, const struct line_pnts *,
+				   int, int*);
+static unsigned char *linestring_to_wkb(int, const struct line_pnts *,
+					int, int*);
+static unsigned char *polygon_to_wkb(int, const struct line_pnts *,
+				     int, int*);
+static int write_feature(const struct Format_info_pg *, 
+			 int, const struct line_pnts *, int, int);
+#endif
+
+/*!
+  \brief Writes feature on level 1 (PostGIS interface)
+
+  Note:
+   - centroids are not supported in PostGIS, pseudotopo holds virtual
+     centroids
+   - boundaries are not supported in PostGIS, pseudotopo treats polygons
+     as boundaries
+     
+  \param Map pointer to Map_info structure
+  \param type feature type (GV_POINT, GV_LINE, ...)
+  \param points pointer to line_pnts structure (feature geometry) 
+  \param cats pointer to line_cats structure (feature categories)
+  
+  \return feature offset into file
+  \return -1 on error
+*/
+off_t V1_write_line_pg(struct Map_info *Map, int type,
+		       const struct line_pnts *points,
+		       const struct line_cats *cats)
+{
+#ifdef HAVE_POSTGRES
+    int cat;
+    off_t offset;
+    
+    SF_FeatureType sf_type;
+
+    struct field_info *Fi;
+    struct Format_info_pg *pg_info;
+    struct Format_info_offset *offset_info;
+    
+    pg_info = &(Map->fInfo.pg);
+    offset_info = &(pg_info->offset);
+    
+    /* create PostGIS layer if doesn't exist ? */
+    
+    cat = -1; /* no attributes to be written */
+    if (cats->n_cats > 0 && Vect_get_num_dblinks(Map) > 0) {
+	/* check for attributes */
+	Fi = Vect_get_dblink(Map, 0);
+	if (Fi) {
+	    if (!Vect_cat_get(cats, Fi->number, &cat))
+		G_warning(_("No category defined for layer %d"), Fi->number);
+	    if (cats->n_cats > 1) {
+		G_warning(_("Feature has more categories, using "
+			    "category %d (from layer %d)"),
+			  cat, cats->field[0]);
+	    }
+	}
+    }
+
+    sf_type = pg_info->feature_type;
+    
+    /* determine matching PostGIS feature geometry type */
+    if (type & (GV_POINT | GV_KERNEL)) {
+	if (sf_type != SF_POINT &&
+	    sf_type != SF_POINT25D) {
+	    G_warning(_("Feature is not a point. Skipping."));
+	    return -1;
+	}
+    }
+    else if (type & GV_LINE) {
+	if (sf_type != SF_LINESTRING &&
+	    sf_type != SF_LINESTRING25D) {
+	    G_warning(_("Feature is not a line. Skipping."));
+	    return -1;
+	}
+    }
+    else if (type & GV_BOUNDARY) {
+	if (sf_type != SF_POLYGON) {
+	    G_warning(_("Feature is not a polygon. Skipping."));
+	    return -1;
+	}
+    }
+    else if (type & GV_FACE) {
+	if (sf_type != SF_POLYGON25D) {
+	    G_warning(_("Feature is not a face. Skipping."));
+	    return -1;
+	}
+    }
+    else {
+	G_warning(_("Unsupported feature type (%d)"), type);
+	return -1;
+    }
+
+    G_debug(3, "V1_write_line_pg(): type = %d n_points = %d cat = %d",
+	    type, points->n_points, cat);
+
+    if (sf_type == SF_POLYGON || sf_type == SF_POLYGON25D) {
+	int npoints;
+	
+	npoints = points->n_points - 1;
+	if (points->x[0] != points->x[npoints] ||
+	    points->y[0] != points->y[npoints] ||
+	    points->z[0] != points->z[npoints]) {
+	    G_warning(_("Boundary is not closed. Skipping."));
+	    return -1;
+	}
+    }
+
+    if (execute(pg_info->conn, "BEGIN") == -1)
+	return -1;
+    
+    /* write feature's geometry and fid */
+    if (-1 == write_feature(pg_info, type, points,
+			    Vect_is_3d(Map) ? WITH_Z : WITHOUT_Z, cat))
+	return -1;
+
+    /* write attributes */
+    /* ? */
+    
+    if (execute(pg_info->conn, "COMMIT") == -1)
+	return -1;
+    
+    /* update offset array */
+    if (offset_info->array_num >= offset_info->array_alloc) {
+	offset_info->array_alloc += 1000;
+	offset_info->array = (int *) G_realloc(offset_info->array,
+					       offset_info->array_alloc *
+					       sizeof(int));
+    }
+
+    offset = offset_info->array_num;
+    
+    offset_info->array[offset_info->array_num++] = (int) cat;
+    if (sf_type == SF_POLYGON || sf_type == SF_POLYGON25D) {
+	/* register exterior ring in offset array */
+	offset_info->array[offset_info->array_num++] = 0; 
+    }
+    
+    G_debug(3, "V1_write_line_ogr(): -> offset = %lu", (unsigned long) offset);
+
+    return offset;
+#else
+    G_fatal_error(_("GRASS is not compiled with PostgreSQL support"));
+    return -1;
+#endif
+}
+
+#ifdef HAVE_POSTGRES
+/*!
+  \brief Binary data to HEX
+
+  Allocated buffer should be freed by G_free().
+  
+  \param nbytes number of bytes to allocate
+  \param wkb_data WKB data
+
+  \return allocated buffer with HEX data
+*/
+char *binary_to_hex(int nbytes, const unsigned char *wkb_data)
+{
+    char *hex_data;
+    int i, nlow, nhigh;
+    static const char ach_hex[] = "0123456789ABCDEF";
+
+    hex_data = (char *) G_malloc(nbytes * 2 + 1);
+    hex_data[nbytes * 2] = '\0';
+
+    for (i = 0; i < nbytes; i++) {
+        nlow  = wkb_data[i] & 0x0f;
+        nhigh = (wkb_data[i] & 0xf0) >> 4;
+
+        hex_data[i * 2]     = ach_hex[nhigh];
+        hex_data[i * 2 + 1] = ach_hex[nlow];
+    }
+
+    return hex_data;
+}
+
+/*!
+  \bried Write point into WKB buffer
+
+  See OGRPoint::exportToWkb from GDAL/OGR library
+
+  \param byte_order byte order (LITTLE_ENDIAN or BIG_ENDIAN)
+  \param points feature geometry
+  \param with_z WITH_Z for 3D data
+  \param[out] nsize buffer size
+  
+  \return allocated WKB buffer
+  \return NULL on error
+*/
+unsigned char *point_to_wkb(int byte_order,
+			    const struct line_pnts *points, int with_z,
+			    int *nsize)
+{
+    unsigned char *wkb_data;
+    unsigned int sf_type;
+    
+    if (points->n_points != 1)
+	return NULL;
+    
+    /* allocate buffer */
+    *nsize = with_z ? 29 : 21;
+    wkb_data = G_malloc(*nsize);
+    G_zero(wkb_data, *nsize);
+    
+    G_debug(5, "\t->point size=%d (with_z = %d)", *nsize, with_z);
+    
+    /* set the byte order */
+    if (byte_order == LITTLE_ENDIAN)
+	wkb_data[0] = '\001';
+    else
+	wkb_data[0] = '\000';
+    
+    /* set the geometry feature type */
+    sf_type = with_z ? SF_POINT25D : SF_POINT;
+    
+    if (byte_order == LITTLE_ENDIAN)
+	sf_type = LSBWORD32(sf_type);
+    else
+        sf_type = MSBWORD32(sf_type);
+    memcpy(wkb_data + 1, &sf_type, 4);
+    
+    /* copy in the raw data */
+    memcpy(wkb_data + 5, &(points->x[0]), 8);
+    memcpy(wkb_data + 5 + 8, &(points->y[0]), 8);
+
+    if (with_z) {
+        memcpy(wkb_data + 5 + 16, &(points->z[0]), 8);
+    }
+
+    /* swap if needed */
+    if (byte_order == ENDIAN_BIG) {
+	SWAPDOUBLE(wkb_data + 5);
+        SWAPDOUBLE(wkb_data + 5 + 8);
+	
+        if (with_z)
+            SWAPDOUBLE(wkb_data + 5 + 16);
+    }
+
+    return wkb_data;
+}
+
+/*!
+  \bried Write linestring into WKB buffer
+
+  See OGRLineString::exportToWkb from GDAL/OGR library
+
+  \param byte_order byte order (LITTLE_ENDIAN or BIG_ENDIAN)
+  \param points feature geometry
+  \param with_z WITH_Z for 3D data
+  \param[out] nsize buffer size
+  
+  \return allocated WKB buffer
+  \return NULL on error
+*/
+unsigned char *linestring_to_wkb(int byte_order,
+				 const struct line_pnts *points, int with_z,
+				 int *nsize)
+{
+    int i, point_size;
+    unsigned char *wkb_data;
+    unsigned int sf_type;
+
+    if (points->n_points < 1)
+	return NULL;
+
+    /* allocate buffer */
+    point_size = 8 * (with_z ? 3 : 2);
+    *nsize = 5 + 4 + points->n_points * point_size;
+    wkb_data = G_malloc(*nsize);
+    G_zero(wkb_data, *nsize);
+
+    G_debug(5, "\t->linestring size=%d (with_z = %d)", *nsize, with_z);
+
+    /* set the byte order */
+    if (byte_order == LITTLE_ENDIAN)
+	wkb_data[0] = '\001';
+    else
+	wkb_data[0] = '\000';
+
+    /* set the geometry feature type */
+    sf_type = with_z ? SF_LINESTRING25D : SF_LINESTRING;
+    
+    if (byte_order == LITTLE_ENDIAN)
+	sf_type = LSBWORD32(sf_type);
+    else
+        sf_type = MSBWORD32(sf_type);
+    memcpy(wkb_data + 1, &sf_type, 4);
+    
+    /* copy in the data count */
+    memcpy(wkb_data + 5, &(points->n_points), 4);
+    
+    /* copy in the raw data */
+    for (i = 0; i < points->n_points; i++) {
+	memcpy(wkb_data + 9 + point_size * i, &(points->x[i]), 8);
+	memcpy(wkb_data + 9 + 8 + point_size * i, &(points->y[i]), 8);
+	
+	if (with_z) {
+	    memcpy(wkb_data + 9 + 16 + point_size * i, &(points->z[i]), 8);
+	}
+    }
+    
+    /* swap if needed */
+    if (byte_order == ENDIAN_BIG) {
+        int npoints, nitems;
+	
+        npoints = SWAP32(points->n_points);
+        memcpy(wkb_data+5, &npoints, 4);
+
+	nitems = (with_z ? 3 : 2) * points->n_points;
+        for(i = 0; i < nitems; i++ )
+        {
+            SWAPDOUBLE(wkb_data + 9 + 4 + 8 * i);
+        }
+    }
+
+    return wkb_data;
+}
+
+/*!
+  \bried Write polygon into WKB buffer
+
+  See OGRPolygon::exportToWkb from GDAL/OGR library
+
+  \param byte_order byte order (LITTLE_ENDIAN or BIG_ENDIAN)
+  \param points feature geometry
+  \param with_z WITH_Z for 3D data
+  \param[out] nsize buffer size
+  
+  \return allocated WKB buffer
+  \return NULL on error
+*/
+unsigned char *polygon_to_wkb(int byte_order,
+			      const struct line_pnts *points, int with_z,
+			      int *nsize)
+{
+    int i, point_size, nrings;
+    unsigned char *wkb_data;
+    unsigned int sf_type;
+
+    if (points->n_points < 3)
+	return NULL;
+
+    /* allocate buffer */
+    point_size = 8 * (with_z ? 3 : 2);
+    /* one ring only */
+    nrings = 1;
+    *nsize = 9 + (4 + point_size * points->n_points);
+    wkb_data = G_malloc(*nsize);
+    G_zero(wkb_data, *nsize);
+
+    G_debug(5, "\t->polygon size=%d (with_z = %d)", *nsize, with_z);
+    
+    /* set the byte order */
+    if (byte_order == LITTLE_ENDIAN)
+	wkb_data[0] = '\001';
+    else
+	wkb_data[0] = '\000';
+
+    /* set the geometry feature type */
+    sf_type = with_z ? SF_POLYGON25D : SF_POLYGON;
+    
+    if (byte_order == LITTLE_ENDIAN)
+	sf_type = LSBWORD32(sf_type);
+    else
+        sf_type = MSBWORD32(sf_type);
+    memcpy(wkb_data + 1, &sf_type, 4);
+ 
+    /* copy in the raw data */
+    if (byte_order == ENDIAN_BIG) {
+        int ncount;
+	
+        ncount = CPL_SWAP32(nrings);
+        memcpy(wkb_data + 5, &ncount, 4);
+    }
+    else {
+        memcpy(wkb_data + 5, &nrings, 4);
+    }
+    
+    /* serialize ring */
+    memcpy(wkb_data + 9, &(points->n_points), 4);
+    for (i = 0; i < points->n_points; i++) {
+	memcpy(wkb_data + 9 + 4 + point_size * i, &(points->x[i]), 8);
+	memcpy(wkb_data + 9 + 4 + 8 + point_size * i, &(points->y[i]), 8);
+	
+	if (with_z) {
+	    memcpy(wkb_data + 9 + 4 + 16 + point_size * i, &(points->z[i]), 8);
+	}
+    }
+
+    /* swap if needed */
+    if (byte_order == ENDIAN_BIG) {
+        int npoints, nitems;
+	
+        npoints = SWAP32(points->n_points);
+        memcpy(wkb_data+5, &npoints, 4);
+
+	nitems = (with_z ? 3 : 2) * points->n_points;
+        for(i = 0; i < nitems; i++ )
+        {
+            CPL_SWAPDOUBLE(wkb_data + 9 + 4 + 8 * i);
+        }
+    }
+
+    return wkb_data;
+}
+/*!
+  \brief Insert feature into table
+
+  \param pg_info pointer to Format_info_pg struct
+  \param type feature type (GV_POINT, GV_LINE, ...)
+  \param points pointer to line_pnts struct
+  \param with_z WITH_Z for 3D data
+  \param fid feature id
+
+  \return -1 on error
+  \retirn 0 on success
+*/
+int write_feature(const struct Format_info_pg *pg_info,
+		  int type, const struct line_pnts *points, int with_z,
+		  int fid)
+{
+    int   byte_order, nbytes, nsize;
+    unsigned int sf_type;
+    unsigned char *wkb_data;
+    char  *stmt, *text_data, *text_data_p, *hex_data;
+    
+    if (with_z && pg_info->coor_dim != 3) {
+	G_warning(_("Trying to insert 3D data into feature table "
+		    "which store 2D data only"));
+	return -1;
+    }
+    if (!with_z && pg_info->coor_dim != 2) {
+	G_warning(_("Trying to insert 2D data into feature table "
+		    "which store 3D data only"));
+	return -1;
+    }
+    
+    byte_order = LITTLE_ENDIAN; /* ? */
+    
+    /* get wkb data */
+    nbytes = -1;
+    wkb_data = NULL;
+    if (type == GV_POINT) 
+	wkb_data = point_to_wkb(byte_order, points, with_z, &nbytes);
+    else if (type == GV_LINE)
+	wkb_data = linestring_to_wkb(byte_order, points, with_z, &nbytes);
+    else if (type == GV_BOUNDARY) 
+	wkb_data = polygon_to_wkb(byte_order, points, with_z, &nbytes);
+    
+    if (!wkb_data || nbytes < 1) {
+	G_warning(_("Unsupported feature type %d"), type);
+	return -1;
+    }
+    
+    /* When converting to hex, each byte takes 2 hex characters. In
+       addition we add in 8 characters to represent the SRID integer
+       in hex, and one for a null terminator */
+    nsize = nbytes * 2 + 8 + 1;
+    text_data = text_data_p = (char *) G_malloc(nsize);
+    
+    /* convert the 1st byte, which is the endianess flag, to hex */
+    hex_data = binary_to_hex(1, wkb_data);
+    strcpy(text_data_p, hex_data);
+    G_free (hex_data);
+    text_data_p += 2;
+
+    /* get the geom type which is bytes 2 through 5 */
+    memcpy(&sf_type, wkb_data + 1, 4);
+
+    /* add the SRID flag if an SRID is provided */
+    if (pg_info->srid > 0) {
+	unsigned int srs_flag;
+	/* change the flag to little endianess */
+        srs_flag = LSBWORD32(WKBSRIDFLAG);
+        /* apply the flag */
+        sf_type = sf_type | srs_flag;
+    }
+
+    /* write the geom type which is 4 bytes */
+    hex_data = binary_to_hex(4, (unsigned char*) &sf_type);
+    strcpy(text_data_p, hex_data);
+    G_free(hex_data);
+    text_data_p += 8;
+
+    /* include SRID if provided */
+    if (pg_info->srid > 0) {
+	unsigned int srs_id;
+        /* force the srsid to little endianess */
+        srs_id = LSBWORD32(pg_info->srid);
+        hex_data = binary_to_hex(sizeof(srs_id), (unsigned char*) &srs_id);
+        strcpy(text_data_p, hex_data);
+        G_free(hex_data);
+        text_data_p += 8;
+    }
+
+    /* copy the rest of the data over - subtract 5 since we already
+       copied 5 bytes above */
+    hex_data = binary_to_hex(nbytes - 5, wkb_data + 5);
+    strcpy(text_data_p, hex_data);
+    G_free(hex_data);
+
+    /* build INSERT statement */
+    stmt = NULL;
+    G_asprintf(&stmt, "INSERT INTO %s (%s, %s) VALUES (%d, '%s'::GEOMETRY)",
+	       pg_info->table_name, pg_info->fid_column, pg_info->geom_column,
+	       fid, text_data);
+    G_debug(2, "SQL: %s", stmt);
+    
+    if (execute(pg_info->conn, stmt) == -1)
+	return -1;
+
+    G_free(wkb_data);
+    G_free(text_data);
+    G_free(stmt);
+    
+    return 0;
+}
+#endif

+ 204 - 0
lib/vector/Vlib/write_sfa.c

@@ -0,0 +1,204 @@
+/*!
+   \file lib/vector/Vlib/write_sfa.c
+
+   \brief Vector library - write vector feature - simple feature access
+
+   Higher level functions for reading/writing/manipulating vectors.
+
+   See write_ogr.c (OGR interface) and write_pg.c (PostGIS interface)
+   for imlementation issues.
+
+   \todo SFA version of V2__delete_area_cats_from_cidx_nat()
+   \todo function to delete corresponding entry in fidx
+   \todo SFA version of V2__add_area_cats_to_cidx_nat
+   \todo SFA version of V2__add_line_to_topo_nat
+
+   (C) 2011-2012 by Martin Landa, and the GRASS Development Team
+
+   This program is free software under the GNU General Public License
+   (>=v2). Read the file COPYING that comes with GRASS for details.
+
+   \author Martin Landa <landa.martin gmail.com>
+ */
+
+#include <grass/vector.h>
+#include <grass/glocale.h>
+
+#if defined HAVE_OGR || defined HAVE_POSTGRES
+void V2__add_line_to_topo_sfa(struct Map_info *, int, const struct line_pnts *,
+			      const struct line_cats *);
+#endif
+
+/*!
+  \brief Writes feature on level 2 (OGR/PostGIS interface)
+
+  \param Map pointer to Map_info structure
+  \param type feature type (GV_POINT, GV_LINE, ...)
+  \param points pointer to line_pnts structure (feature geometry) 
+  \param cats pointer to line_cats structure (feature categories)
+  
+  \return feature offset into file
+  \return -1 on error
+*/
+off_t V2_write_line_sfa(struct Map_info *Map, int type,
+			const struct line_pnts *points, const struct line_cats *cats)
+{
+#if defined HAVE_OGR || defined HAVE_POSTGRES
+    int line;
+    off_t offset;
+    struct Plus_head *plus;
+    struct bound_box box;
+    struct Format_info_offset *offset_info;
+    
+    line = 0;
+    plus = &(Map->plus);
+    
+    G_debug(3, "V2_write_line_sfa(): type = %d (format = %d)",
+	    type, Map->format);
+    
+    if (Map->format == GV_FORMAT_POSTGIS) {
+	offset_info = &(Map->fInfo.pg.offset);
+	offset = V1_write_line_pg(Map, type, points, cats);
+    }
+    else {
+	offset_info = &(Map->fInfo.pg.offset);
+	offset = V1_write_line_ogr(Map, type, points, cats);
+    }
+    if (offset < 0)
+	return -1;
+    
+    /* Update topology */
+    if (plus->built >= GV_BUILD_BASE) {
+	dig_line_box(points, &box);
+	line = dig_add_line(plus, type, points, &box, offset);
+	G_debug(3, "\tline added to topo with line = %d", line);
+	if (line == 1)
+	    Vect_box_copy(&(plus->box), &box);
+	else
+	    Vect_box_extend(&(plus->box), &box);
+
+	if (type == GV_BOUNDARY) {
+	    int ret, cline;
+	    long FID;
+	    double x, y;
+	    
+	    struct bound_box box;
+	    struct line_pnts *CPoints;
+
+	    /* add virtual centroid to pseudo-topology */
+	    ret = Vect_get_point_in_poly(points, &x, &y);
+	    if (ret == 0) {
+		CPoints = Vect_new_line_struct();
+		Vect_append_point(CPoints, x, y, 0.0);
+		
+		FID = offset_info->array[offset];
+
+		dig_line_box(CPoints, &box);
+		cline = dig_add_line(plus, GV_CENTROID,
+				     CPoints, &box, FID);
+		G_debug(4, "\tCentroid: x = %f, y = %f, cat = %lu, line = %d",
+			x, y, FID, cline);	  
+		dig_cidx_add_cat(plus, 1, (int) FID,
+				 cline, GV_CENTROID);
+		
+		Vect_destroy_line_struct(CPoints);
+	    }
+	    else {
+		G_warning(_("Unable to calculate centroid for area"));
+	    }
+	}
+	V2__add_line_to_topo_sfa(Map, line, points, cats);
+    }
+
+
+    G_debug(3, "updated lines : %d , updated nodes : %d", plus->uplist.n_uplines,
+	    plus->uplist.n_upnodes);
+
+    /* returns int line, but is defined as off_t for compatibility with
+     * Write_line_array in write.c */
+    return line;
+#else
+    G_fatal_error(_("GRASS is not compiled with OGR/PostgreSQL support"));
+    return -1;
+#endif
+}
+
+#if defined HAVE_OGR || defined HAVE_POSTGRES
+/*!
+  \brief Add feature to topo file (internal use only)
+
+  \param Map pointer to Map_info structure
+  \param line feature id
+  \param points pointer to line_pnts structure (feature's geometry)
+  \param cats pointer to line_cats structure (feature's categories)
+*/
+void V2__add_line_to_topo_sfa(struct Map_info *Map, int line,
+			      const struct line_pnts *points,
+			      const struct line_cats *cats)
+{
+    int first, s, i;
+    int type, area, side;
+
+    struct Plus_head *plus;
+    struct P_line *Line;
+    
+    struct bound_box box, abox;
+    
+    G_debug(3, "V2__add_line_to_topo_ogr(): line = %d npoints = %d", line,
+	    points->n_points);
+
+    plus = &(Map->plus);
+    Line = plus->Line[line];
+    type = Line->type;
+
+    if (plus->built >= GV_BUILD_AREAS &&
+	type == GV_BOUNDARY) {	
+	struct P_topo_b *topo = (struct P_topo_b *)Line->topo;
+	
+	if (topo->N1 != topo->N2) {
+	    G_warning(_("Boundary is not closed. Skipping."));
+	    return;
+	}
+	
+	/* Build new areas/isles */
+	for (s = 0; s < 2; s++) {
+	    side = (s == 0 ? GV_LEFT : GV_RIGHT);
+	    area = Vect_build_line_area(Map, line, side);
+	    if (area > 0) {	/* area */
+		Vect_get_area_box(Map, area, &box);
+		if (first) {
+		    Vect_box_copy(&abox, &box);
+		    first = FALSE;
+		}
+		else
+		    Vect_box_extend(&abox, &box);
+	    }
+	    else if (area < 0) {
+		/* isle -> must be attached -> add to abox */
+		Vect_get_isle_box(Map, -area, &box);
+		if (first) {
+		    Vect_box_copy(&abox, &box);
+		    first = FALSE;
+		}
+		else
+		    Vect_box_extend(&abox, &box);
+	    }
+	    G_debug(4, "Vect_build_line_area(): -> area = %d", area);
+	}
+
+	/* Attach centroid/isle to the new area */
+	if (plus->built >= GV_BUILD_ATTACH_ISLES)
+	    Vect_attach_isles(Map, &abox);
+	if (plus->built >= GV_BUILD_CENTROIDS)
+	    Vect_attach_centroids(Map, &abox);
+    }
+    
+    /* Add category index */
+    for (i = 0; i < cats->n_cats; i++) {
+	dig_cidx_add_cat_sorted(plus, cats->field[i], cats->cat[i], line,
+				type);
+    }
+    
+    return;
+}
+#endif /* HAVE_OGR || HAVE_POSTGRES */