123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700 |
- /*!
- \file lib/vector/Vlib/write_ogr.c
- \brief Vector library - write vector feature (OGR format)
- Higher level functions for reading/writing/manipulating vectors.
- Partly inspired by v.out.ogr's code.
- \todo How to deal with OGRNullFID
-
- (C) 2009-2013 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/dbmi.h>
- #include <grass/gprojects.h>
- #include <grass/glocale.h>
- #ifdef HAVE_OGR
- #include <ogr_api.h>
- #include <cpl_string.h>
- static dbDriver *create_table(OGRLayerH, const struct field_info *);
- static int create_ogr_layer(struct Map_info *, int);
- static off_t write_feature(struct Map_info *, int, const struct line_pnts **, int,
- const struct line_cats *);
- static int write_attributes(dbDriver *, int, const struct field_info *,
- OGRLayerH, OGRFeatureH);
- static int sqltype_to_ogrtype(int);
- #endif
- /*!
- \brief Writes feature on level 1 (OGR interface)
- Note:
- - centroids are not supported in OGR, pseudotopo holds virtual
- centroids (it's coordinates determined from spatial index)
- - unclosed boundaries are not supported in OGR, pseudotopo treats
- polygons as boundaries
-
- Supported feature types:
- - GV_POINT (written as wkbPoint)
- - GV_LINE (wkbLineString)
- - GV_BOUNDARY (wkbPolygon)
- - GV_FACE (wkbPolygon25D)
- - GV_KERNEL (wkbPoint25D)
- \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 index in offset array (related to pseudo-topology)
- \return -1 on error
- */
- off_t V1_write_line_ogr(struct Map_info *Map, int type,
- const struct line_pnts *points,
- const struct line_cats *cats)
- {
- #ifdef HAVE_OGR
- return write_feature(Map, type, &points, 1, cats);
- #else
- G_fatal_error(_("GRASS is not compiled with OGR support"));
- return -1;
- #endif
- }
- /*!
- \brief Rewrites feature at the given offset on level 1 (OGR interface)
-
- This function simply calls V1_delete_line_ogr() and V1_write_line_ogr().
-
- \param Map pointer to Map_info structure
- \param offset feature offset
- \param type feature type (see V1_write_line_ogr() for supported types)
- \param points pointer to line_pnts structure (feature geometry)
- \param cats pointer to line_cats structure (feature categories)
-
- \return feature offset (rewritten feature)
- \return -1 on error
- */
- off_t V1_rewrite_line_ogr(struct Map_info *Map,
- off_t offset, int type,
- const struct line_pnts *points, const struct line_cats *cats)
- {
- G_debug(3, "V1_rewrite_line_ogr(): type=%d offset=%" PRI_OFF_T,
- type, offset);
- #ifdef HAVE_OGR
- if (type != V1_read_line_ogr(Map, NULL, NULL, offset)) {
- G_warning(_("Unable to rewrite feature (incompatible feature types)"));
- return -1;
- }
- /* delete old */
- V1_delete_line_ogr(Map, offset);
- return V1_write_line_ogr(Map, type, points, cats);
- #else
- G_fatal_error(_("GRASS is not compiled with OGR support"));
- return -1;
- #endif
- }
- /*!
- \brief Deletes feature at the given offset on level 1 (OGR interface)
-
- \param Map pointer Map_info structure
- \param offset offset of feature to be deleted
-
- \return 0 on success
- \return -1 on error
- */
- int V1_delete_line_ogr(struct Map_info *Map, off_t offset)
- {
- #ifdef HAVE_OGR
- struct Format_info_ogr *ogr_info;
-
- G_debug(3, "V1_delete_line_ogr(), offset = %lu", (unsigned long) offset);
- ogr_info = &(Map->fInfo.ogr);
-
- if (!ogr_info->layer) {
- G_warning(_("OGR layer not defined"));
- return -1;
- }
-
- if (offset >= ogr_info->offset.array_num) {
- G_warning(_("Invalid offset (%" PRI_OFF_T ")"), offset);
- return -1;
- }
-
- if (OGR_L_DeleteFeature(ogr_info->layer,
- ogr_info->offset.array[offset]) != OGRERR_NONE) {
- G_warning(_("Unable to delete feature"));
- return -1;
- }
-
- return 0;
- #else
- G_fatal_error(_("GRASS is not compiled with OGR support"));
- return -1;
- #endif
- }
- #ifdef HAVE_OGR
- /*!
- \brief Writes area on topological level (OGR Simple Features
- interface, internal use only)
- \param Map pointer to Map_info structure
- \param points feature geometry (exterior + interior rings)
- \param nparts number of parts including exterior ring
- \param cats feature categories
-
- \return feature offset
- \return -1 on error
- */
- off_t V2__write_area_ogr(struct Map_info *Map,
- const struct line_pnts **points, int nparts,
- const struct line_cats *cats)
- {
- return write_feature(Map, GV_BOUNDARY, points, nparts, cats);
- }
- dbDriver *create_table(OGRLayerH hLayer, const struct field_info *Fi)
- {
- int col, ncols;
- int sqltype, ogrtype, length;
-
- const char *colname;
-
- dbDriver *driver;
- dbHandle handle;
- dbCursor cursor;
- dbTable *table;
- dbColumn *column;
- dbString sql;
-
- OGRFieldDefnH hFieldDefn;
- OGRFeatureDefnH hFeatureDefn;
- db_init_string(&sql);
- db_init_handle(&handle);
-
- driver = db_start_driver(Fi->driver);
- if (!driver) {
- G_warning(_("Unable to start driver <%s>"), Fi->driver);
- return NULL;
- }
- db_set_handle(&handle, Fi->database, NULL);
- if (db_open_database(driver, &handle) != DB_OK) {
- G_warning(_("Unable to open database <%s> by driver <%s>"),
- Fi->database, Fi->driver);
- db_close_database_shutdown_driver(driver);
- return NULL;
- }
-
- /* to get no data */
- db_set_string(&sql, "select * from ");
- db_append_string(&sql, Fi->table);
- db_append_string(&sql, " where 0 = 1");
-
- if (db_open_select_cursor(driver, &sql, &cursor, DB_SEQUENTIAL) !=
- DB_OK) {
- G_warning(_("Unable to open select cursor: '%s'"),
- db_get_string(&sql));
- db_close_database_shutdown_driver(driver);
- return NULL;
- }
- table = db_get_cursor_table(&cursor);
- ncols = db_get_table_number_of_columns(table);
- hFeatureDefn = OGR_L_GetLayerDefn(hLayer);
-
- for (col = 0; col < ncols; col++) {
- column = db_get_table_column(table, col);
- colname = db_get_column_name(column);
- sqltype = db_get_column_sqltype(column);
- ogrtype = sqltype_to_ogrtype(sqltype);
- length = db_get_column_length(column);
-
- if (strcmp(OGR_L_GetFIDColumn(hLayer), colname) == 0 ||
- OGR_FD_GetFieldIndex(hFeatureDefn, colname) > -1) {
- /* field already exists */
- continue;
- }
- hFieldDefn = OGR_Fld_Create(colname, ogrtype);
- /* GDAL 1.9.0 (r22968) uses VARCHAR instead of CHAR */
- if (ogrtype == OFTString && length > 0)
- OGR_Fld_SetWidth(hFieldDefn, length);
- if (OGR_L_CreateField(hLayer, hFieldDefn, TRUE) != OGRERR_NONE) {
- G_warning(_("Creating field <%s> failed"), colname);
- db_close_database_shutdown_driver(driver);
- return NULL;
- }
-
- OGR_Fld_Destroy(hFieldDefn);
- }
- return driver;
- }
- /*!
- \brief Create new OGR layer in given OGR datasource (internal use only)
- V1_open_new_ogr() is required to be called before this function.
- List of currently supported types:
- - GV_POINT (wkbPoint)
- - GV_LINE (wkbLineString)
- - GV_BOUNDARY (wkb_Polygon)
- \param[in,out] Map pointer to Map_info structure
- \param type feature type (GV_POINT, GV_LINE, ...)
- \return 0 success
- \return -1 error
- */
- int create_ogr_layer(struct Map_info *Map, int type)
- {
- int ndblinks;
- OGRLayerH Ogr_layer;
- OGRSpatialReferenceH Ogr_spatial_ref;
-
- struct field_info *Fi;
- struct Key_Value *projinfo, *projunits, *projepsg;
- struct Format_info_ogr *ogr_info;
-
- OGRwkbGeometryType Ogr_geom_type;
- char **Ogr_layer_options;
-
- ogr_info = &(Map->fInfo.ogr);
-
- if (!ogr_info->driver_name ||
- !ogr_info->layer_name ||
- !ogr_info->ds)
- return -1;
-
- /* get spatial reference */
- projinfo = G_get_projinfo();
- projunits = G_get_projunits();
- projepsg = G_get_projepsg();
- Ogr_spatial_ref = GPJ_grass_to_osr2(projinfo, projunits, projepsg);
- G_free_key_value(projinfo);
- G_free_key_value(projunits);
-
- /* determine geometry type */
- switch(type) {
- case GV_POINT:
- Ogr_geom_type = wkbPoint;
- break;
- case GV_LINE:
- Ogr_geom_type = wkbLineString;
- break;
- case GV_BOUNDARY:
- Ogr_geom_type = wkbPolygon;
- break;
- default:
- G_warning(_("Unsupported geometry type (%d)"), type);
- return -1;
- }
-
- /* check creation options */
- Ogr_layer_options = ogr_info->layer_options;
- if (Vect_is_3d(Map)) {
- if (strcmp(ogr_info->driver_name, "PostgreSQL") == 0) {
- Ogr_layer_options = CSLSetNameValue(Ogr_layer_options, "DIM", "3");
- }
- }
- else {
- if (strcmp(ogr_info->driver_name, "PostgreSQL") == 0) {
- Ogr_layer_options = CSLSetNameValue(Ogr_layer_options, "DIM", "2");
- }
- }
- /* create new OGR layer */
- Ogr_layer = OGR_DS_CreateLayer(ogr_info->ds, ogr_info->layer_name,
- Ogr_spatial_ref, Ogr_geom_type, Ogr_layer_options);
- CSLDestroy(Ogr_layer_options);
- if (!Ogr_layer) {
- G_warning(_("Unable to create OGR layer <%s> in '%s'"),
- ogr_info->layer_name, ogr_info->dsn);
- return -1;
- }
- ogr_info->layer = Ogr_layer;
- ndblinks = Vect_get_num_dblinks(Map);
- if (ndblinks > 0) {
- /* write also attributes */
- Fi = Vect_get_dblink(Map, 0);
- if (Fi) {
- if (ndblinks > 1)
- G_warning(_("More layers defined, using driver <%s> and "
- "database <%s>"), Fi->driver, Fi->database);
- ogr_info->dbdriver = create_table(ogr_info->layer, Fi);
- G_free(Fi);
- }
- else
- G_warning(_("Database connection not defined. "
- "Unable to write attributes."));
- }
-
- if (OGR_L_TestCapability(ogr_info->layer, OLCTransactions) &&
- (OGR_L_StartTransaction(ogr_info->layer) != OGRERR_NONE)) {
- G_warning(_("OGR transaction with layer <%s> failed to start"),
- ogr_info->layer_name);
- return -1;
- }
- return 0;
- }
- /*!
- \brief Write OGR feature
- \param Map pointer to Map_info structure
- \param type feature type (GV_POINT, GV_LINE, ...)
- \param bpoints feature geometry
- \param cats feature categories
- \param ipoints isle geometry for polygons on NULL
- \param nisles number of isles
-
- \return feature offset into file
- \return -1 on error
- */
- off_t write_feature(struct Map_info *Map, int type,
- const struct line_pnts **p_points, int nparts,
- const struct line_cats *cats)
- {
- int i, cat, ret;
- struct field_info *Fi;
- const struct line_pnts *points;
- struct Format_info_ogr *ogr_info;
- struct Format_info_offset *offset_info;
-
- off_t offset;
-
- OGRGeometryH Ogr_geometry;
- OGRFeatureH Ogr_feature;
- OGRFeatureDefnH Ogr_featuredefn;
- OGRwkbGeometryType Ogr_geom_type;
- ogr_info = &(Map->fInfo.ogr);
- offset_info = &(ogr_info->offset);
-
- if (nparts < 1)
- return -1;
-
- points = p_points[0]; /* feature geometry */
-
- if (!ogr_info->layer) {
- /* create OGR layer if doesn't exist */
- if (create_ogr_layer(Map, type) < 0)
- return -1;
- }
- if (!points)
- return 0;
- 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]);
- }
- }
- }
-
- Ogr_featuredefn = OGR_L_GetLayerDefn(ogr_info->layer);
- Ogr_geom_type = OGR_FD_GetGeomType(Ogr_featuredefn);
-
- /* determine matching OGR feature geometry type */
- if (type & (GV_POINT | GV_KERNEL)) {
- if (Ogr_geom_type != wkbPoint &&
- Ogr_geom_type != wkbPoint25D) {
- G_warning(_("Feature is not a point. Skipping."));
- return -1;
- }
- Ogr_geometry = OGR_G_CreateGeometry(wkbPoint);
- }
- else if (type & GV_LINE) {
- if (Ogr_geom_type != wkbLineString &&
- Ogr_geom_type != wkbLineString25D) {
- G_warning(_("Feature is not a line. Skipping."));
- return -1;
- }
- Ogr_geometry = OGR_G_CreateGeometry(wkbLineString);
- }
- else if (type & GV_BOUNDARY) {
- if (Ogr_geom_type != wkbPolygon) {
- G_warning(_("Feature is not a polygon. Skipping."));
- return -1;
- }
- Ogr_geometry = OGR_G_CreateGeometry(wkbPolygon);
- }
- else if (type & GV_FACE) {
- if (Ogr_geom_type != wkbPolygon25D) {
- G_warning(_("Feature is not a face. Skipping."));
- return -1;
- }
- Ogr_geometry = OGR_G_CreateGeometry(wkbPolygon25D);
- }
- else {
- G_warning(_("Unsupported feature type (%d)"), type);
- return -1;
- }
- G_debug(3, "V1_write_line_ogr(): type = %d", type);
- if (Ogr_geom_type == wkbPolygon || Ogr_geom_type == wkbPolygon25D) {
- int iring, npoints;
-
- /* add rings (first is exterior ring) */
- for (iring = 0; iring < nparts; iring++) {
- OGRGeometryH Ogr_ring;
-
- points = p_points[iring];
- npoints = points->n_points - 1;
- Ogr_ring = OGR_G_CreateGeometry(wkbLinearRing);
- 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. Feature skipped."));
- return -1;
- }
-
- /* add points */
- for (i = 0; i < npoints; i++) {
- OGR_G_AddPoint(Ogr_ring, points->x[i], points->y[i],
- points->z[i]);
- }
- G_debug(4, " ring(%d): n_points = %d", iring, npoints);
- OGR_G_AddGeometry(Ogr_geometry, Ogr_ring);
- }
- }
- else {
- for (i = 0; i < points->n_points; i++) {
- OGR_G_AddPoint(Ogr_geometry, points->x[i], points->y[i],
- points->z[i]);
- }
- G_debug(4, " n_points = %d", points->n_points);
- }
-
- /* create feature & set geometry */
- Ogr_feature = OGR_F_Create(Ogr_featuredefn);
- OGR_F_SetGeometry(Ogr_feature, Ogr_geometry);
- /* write attributes */
- if (cat > -1 && ogr_info->dbdriver) {
- if (0 > write_attributes(ogr_info->dbdriver,
- cat, Fi, ogr_info->layer, Ogr_feature))
- G_warning(_("Unable to writes feature attributes"));
- G_free(Fi);
- }
- /* write feature into layer */
- ret = OGR_L_CreateFeature(ogr_info->layer, Ogr_feature);
- /* 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) OGR_F_GetFID(Ogr_feature);
- if (Ogr_geom_type == wkbPolygon || Ogr_geom_type == wkbPolygon25D) {
- /* register exterior ring in offset array */
- offset_info->array[offset_info->array_num++] = 0;
- }
-
- /* destroy */
- OGR_G_DestroyGeometry(Ogr_geometry);
- OGR_F_Destroy(Ogr_feature);
-
- if (ret != OGRERR_NONE)
- return -1;
-
- G_debug(3, "write_feature(): -> offset = %lu offset_num = %d cat = %d",
- (unsigned long) offset, offset_info->array_num, cat);
- return offset;
- }
- /*!
- \brief Writes attributes
- \param driver pointer to dbDriver
- \param Fi pointer to field_info struct
- \param[in,out] Ogr_layer OGR layer
- \param[in,out] Ogr_feature OGR feature to modify
- \return 1 on success
- \return 0 no attributes
- \return -1 on error
- */
- int write_attributes(dbDriver *driver, int cat, const struct field_info *Fi,
- OGRLayerH Ogr_layer, OGRFeatureH Ogr_feature)
- {
- int j, ogrfieldnum;
- char buf[2000];
- int ncol, sqltype, ctype, ogrtype, more;
- const char *fidcol, *colname;
- dbTable *table;
- dbString dbstring;
- dbColumn *column;
- dbCursor cursor;
- dbValue *value;
- OGRFieldDefnH hFieldDefn;
-
- G_debug(3, "write_attributes(): cat = %d", cat);
- if (cat < 0) {
- G_warning(_("Feature without category of layer %d"), Fi->number);
- return 0;
- }
- db_init_string(&dbstring);
-
- /* read & set attributes */
- sprintf(buf, "SELECT * FROM %s WHERE %s = %d", Fi->table, Fi->key,
- cat);
- G_debug(4, "SQL: %s", buf);
- db_set_string(&dbstring, buf);
- /* select data */
- if (db_open_select_cursor(driver, &dbstring, &cursor, DB_SEQUENTIAL) != DB_OK) {
- G_warning(_("Unable to select attributes for category %d"),
- cat);
- return -1;
- }
-
- if (db_fetch(&cursor, DB_NEXT, &more) != DB_OK) {
- G_warning(_("Unable to fetch data from table <%s>"),
- Fi->table);
- return -1;
- }
-
- if (!more) {
- G_warning(_("No database record for category %d, "
- "no attributes will be written"),
- cat);
- return -1;
- }
- fidcol = OGR_L_GetFIDColumn(Ogr_layer);
- table = db_get_cursor_table(&cursor);
- ncol = db_get_table_number_of_columns(table);
- for (j = 0; j < ncol; j++) {
- column = db_get_table_column(table, j);
- colname = db_get_column_name(column);
- if (fidcol && *fidcol && strcmp(colname, fidcol) == 0) {
- /* skip fid column */
- continue;
- }
- value = db_get_column_value(column);
- /* for debug only */
- db_convert_column_value_to_string(column, &dbstring);
- G_debug(3, "col %d : val = %s", j,
- db_get_string(&dbstring));
-
- sqltype = db_get_column_sqltype(column);
- ctype = db_sqltype_to_Ctype(sqltype);
- ogrtype = sqltype_to_ogrtype(sqltype);
- G_debug(3, " colctype = %d", ctype);
-
- ogrfieldnum = OGR_F_GetFieldIndex(Ogr_feature, colname);
- if (ogrfieldnum < 0) {
- /* create field if not exists */
- hFieldDefn = OGR_Fld_Create(colname, ogrtype);
- if (OGR_L_CreateField(Ogr_layer, hFieldDefn, TRUE) != OGRERR_NONE)
- G_warning(_("Unable to create field <%s>"), colname);
- ogrfieldnum = OGR_F_GetFieldIndex(Ogr_feature, colname);
- }
-
- /* Reset */
- OGR_F_UnsetField(Ogr_feature, ogrfieldnum);
-
- /* prevent writing NULL values */
- if (!db_test_value_isnull(value)) {
- switch (ctype) {
- case DB_C_TYPE_INT:
- OGR_F_SetFieldInteger(Ogr_feature, ogrfieldnum,
- db_get_value_int(value));
- break;
- case DB_C_TYPE_DOUBLE:
- OGR_F_SetFieldDouble(Ogr_feature, ogrfieldnum,
- db_get_value_double(value));
- break;
- case DB_C_TYPE_STRING:
- OGR_F_SetFieldString(Ogr_feature, ogrfieldnum,
- db_get_value_string(value));
- break;
- case DB_C_TYPE_DATETIME:
- db_convert_column_value_to_string(column,
- &dbstring);
- OGR_F_SetFieldString(Ogr_feature, ogrfieldnum,
- db_get_string(&dbstring));
- break;
- default:
- G_warning(_("Unsupported column type %d"), ctype);
- break;
- }
- }
- }
- db_close_cursor (&cursor);
-
- db_free_string(&dbstring);
-
- return 1;
- }
- int sqltype_to_ogrtype(int sqltype)
- {
- int ctype, ogrtype;
- ctype = db_sqltype_to_Ctype(sqltype);
-
- switch(ctype) {
- case DB_C_TYPE_INT:
- ogrtype = OFTInteger;
- break;
- case DB_C_TYPE_DOUBLE:
- ogrtype = OFTReal;
- break;
- case DB_C_TYPE_STRING:
- ogrtype = OFTString;
- break;
- case DB_C_TYPE_DATETIME:
- ogrtype = OFTString;
- break;
- default:
- ogrtype = OFTString;
- break;
- }
-
- return ogrtype;
- }
- #endif /* HAVE_OGR */
|