GRASS 8 Programmer's Manual 8.6.0dev(2026)-4bb960b182
Loading...
Searching...
No Matches
write_ogr.c
Go to the documentation of this file.
1/*!
2 \file lib/vector/Vlib/write_ogr.c
3
4 \brief Vector library - write vector feature (OGR format)
5
6 Higher level functions for reading/writing/manipulating vectors.
7
8 Partly inspired by v.out.ogr's code.
9
10 \todo How to deal with OGRNullFID
11
12 SPDX-FileCopyrightText: 2009-2013 Martin Landa
13 SPDX-FileCopyrightText: GRASS Development Team
14 SPDX-License-Identifier: GPL-2.0-or-later
15
16 \author Martin Landa <landa.martin gmail.com>
17 */
18
19#include <inttypes.h>
20#include <grass/vector.h>
21#include <grass/dbmi.h>
22#include <grass/gprojects.h>
23#include <grass/glocale.h>
24
25#include <ogr_api.h>
26#include <cpl_string.h>
27
28static dbDriver *create_table(OGRLayerH, const struct field_info *);
29static int create_ogr_layer(struct Map_info *, int);
30static off_t write_feature(struct Map_info *, int, const struct line_pnts **,
31 int, const struct line_cats *);
32static int write_attributes(dbDriver *, int, const struct field_info *,
34static int sqltype_to_ogrtype(int);
35
36/*!
37 \brief Writes feature on level 1 (OGR interface)
38
39 Note:
40 - centroids are not supported in OGR, pseudotopo holds virtual
41 centroids (it's coordinates determined from spatial index)
42 - unclosed boundaries are not supported in OGR, pseudotopo treats
43 polygons as boundaries
44
45 Supported feature types:
46 - GV_POINT (written as wkbPoint)
47 - GV_LINE (wkbLineString)
48 - GV_BOUNDARY (wkbPolygon)
49 - GV_FACE (wkbPolygon25D)
50 - GV_KERNEL (wkbPoint25D)
51
52 \param Map pointer to Map_info structure
53 \param type feature type
54 \param points pointer to line_pnts structure (feature geometry)
55 \param cats pointer to line_cats structure (feature categories)
56
57 \return feature index in offset array (related to pseudo-topology)
58 \return -1 on error
59 */
61 const struct line_pnts *points,
62 const struct line_cats *cats)
63{
64 return write_feature(Map, type, &points, 1, cats);
65}
66
67/*!
68 \brief Rewrites feature at the given offset on level 1 (OGR interface)
69
70 This function simply calls V1_delete_line_ogr() and V1_write_line_ogr().
71
72 \param Map pointer to Map_info structure
73 \param offset feature offset
74 \param type feature type (see V1_write_line_ogr() for supported types)
75 \param points pointer to line_pnts structure (feature geometry)
76 \param cats pointer to line_cats structure (feature categories)
77
78 \return feature offset (rewritten feature)
79 \return -1 on error
80 */
81off_t V1_rewrite_line_ogr(struct Map_info *Map, off_t offset, int type,
82 const struct line_pnts *points,
83 const struct line_cats *cats)
84{
85 G_debug(3, "V1_rewrite_line_ogr(): type=%d offset=%" PRId64, type, offset);
86 if (type != V1_read_line_ogr(Map, NULL, NULL, offset)) {
87 G_warning(_("Unable to rewrite feature (incompatible feature types)"));
88 return -1;
89 }
90
91 /* delete old */
92 V1_delete_line_ogr(Map, offset);
93
94 return V1_write_line_ogr(Map, type, points, cats);
95}
96
97/*!
98 \brief Deletes feature at the given offset on level 1 (OGR interface)
99
100 \param Map pointer Map_info structure
101 \param offset offset of feature to be deleted
102
103 \return 0 on success
104 \return -1 on error
105 */
107{
109
110 G_debug(3, "V1_delete_line_ogr(), offset = %lu", (unsigned long)offset);
111
112 ogr_info = &(Map->fInfo.ogr);
113
114 if (!ogr_info->layer) {
115 G_warning(_("OGR layer not defined"));
116 return -1;
117 }
118
119 if (offset >= ogr_info->offset.array_num) {
120 G_warning(_("Invalid offset (%" PRId64 ")"), offset);
121 return -1;
122 }
123
124 if (OGR_L_DeleteFeature(ogr_info->layer, ogr_info->offset.array[offset]) !=
125 OGRERR_NONE) {
126 G_warning(_("Unable to delete feature"));
127 return -1;
128 }
129
130 return 0;
131}
132
133/*!
134 \brief Writes area on topological level (OGR Simple Features
135 interface, internal use only)
136
137 \param Map pointer to Map_info structure
138 \param points feature geometry (exterior + interior rings)
139 \param nparts number of parts including exterior ring
140 \param cats feature categories
141
142 \return feature offset
143 \return -1 on error
144 */
145off_t V2__write_area_ogr(struct Map_info *Map, const struct line_pnts **points,
146 int nparts, const struct line_cats *cats)
147{
148 return write_feature(Map, GV_BOUNDARY, points, nparts, cats);
149}
150
151dbDriver *create_table(OGRLayerH hLayer, const struct field_info *Fi)
152{
153 int col, ncols;
154 int sqltype, ogrtype, length;
155
156 const char *colname;
157
159 dbHandle handle;
161 dbTable *table;
164
167
169 db_init_handle(&handle);
170
171 driver = db_start_driver(Fi->driver);
172 if (!driver) {
173 G_warning(_("Unable to start driver <%s>"), Fi->driver);
174 return NULL;
175 }
176 db_set_handle(&handle, Fi->database, NULL);
177 if (db_open_database(driver, &handle) != DB_OK) {
178 G_warning(_("Unable to open database <%s> by driver <%s>"),
179 Fi->database, Fi->driver);
181 return NULL;
182 }
183
184 /* to get no data */
185 db_set_string(&sql, "select * from ");
186 db_append_string(&sql, Fi->table);
187 db_append_string(&sql, " where 0 = 1");
188
190 G_warning(_("Unable to open select cursor: '%s'"), db_get_string(&sql));
192 return NULL;
193 }
194
195 table = db_get_cursor_table(&cursor);
196 ncols = db_get_table_number_of_columns(table);
197
199
200 for (col = 0; col < ncols; col++) {
204 ogrtype = sqltype_to_ogrtype(sqltype);
206
209 /* field already exists */
210 continue;
211 }
212
214 /* GDAL 1.9.0 (r22968) uses VARCHAR instead of CHAR */
215 if (ogrtype == OFTString && length > 0)
218 G_warning(_("Creating field <%s> failed"), colname);
220 return NULL;
221 }
222
224 }
225
226 return driver;
227}
228
229/*!
230 \brief Create new OGR layer in given OGR datasource (internal use only)
231
232 V1_open_new_ogr() is required to be called before this function.
233
234 List of currently supported types:
235 - GV_POINT (wkbPoint)
236 - GV_LINE (wkbLineString)
237 - GV_BOUNDARY (wkb_Polygon)
238 \param[in,out] Map pointer to Map_info structure
239 \param type feature type (GV_POINT, GV_LINE, ...)
240
241 \return 0 success
242 \return -1 error
243 */
244int create_ogr_layer(struct Map_info *Map, int type)
245{
246 int ndblinks;
249
250 struct field_info *Fi;
251 struct Key_Value *projinfo, *projunits, *projepsg;
253
255 char **Ogr_layer_options;
256
257 ogr_info = &(Map->fInfo.ogr);
258
259 if (!ogr_info->driver_name || !ogr_info->layer_name || !ogr_info->ds)
260 return -1;
261
262 /* get spatial reference */
263 projinfo = G_get_projinfo();
264 projunits = G_get_projunits();
265 projepsg = G_get_projepsg();
266 Ogr_spatial_ref = GPJ_grass_to_osr2(projinfo, projunits, projepsg);
267 G_free_key_value(projinfo);
268 G_free_key_value(projunits);
269
270 /* determine geometry type */
271 switch (type) {
272 case GV_POINT:
274 break;
275 case GV_LINE:
277 break;
278 case GV_BOUNDARY:
280 break;
281 default:
282 G_warning(_("Unsupported geometry type (%d)"), type);
283 return -1;
284 }
285
286 /* check creation options */
287 Ogr_layer_options = ogr_info->layer_options;
288 if (Vect_is_3d(Map)) {
289 if (strcmp(ogr_info->driver_name, "PostgreSQL") == 0) {
291 }
292 }
293 else {
294 if (strcmp(ogr_info->driver_name, "PostgreSQL") == 0) {
296 }
297 }
298
299 /* create new OGR layer */
300 Ogr_layer =
304 if (!Ogr_layer) {
305 G_warning(_("Unable to create OGR layer <%s> in '%s'"),
306 ogr_info->layer_name, ogr_info->dsn);
307 return -1;
308 }
309 ogr_info->layer = Ogr_layer;
310
312 if (ndblinks > 0) {
313 /* write also attributes */
314 Fi = Vect_get_dblink(Map, 0);
315 if (Fi) {
316 if (ndblinks > 1)
317 G_warning(_("More layers defined, using driver <%s> and "
318 "database <%s>"),
319 Fi->driver, Fi->database);
320 ogr_info->dbdriver = create_table(ogr_info->layer, Fi);
321 G_free(Fi);
322 }
323 else
324 G_warning(_("Database connection not defined. "
325 "Unable to write attributes."));
326 }
327
330 G_warning(_("OGR transaction with layer <%s> failed to start"),
331 ogr_info->layer_name);
332 return -1;
333 }
334
335 return 0;
336}
337
338/*!
339 \brief Write OGR feature
340
341 \param Map pointer to Map_info structure
342 \param type feature type (GV_POINT, GV_LINE, ...)
343 \param bpoints feature geometry
344 \param cats feature categories
345 \param ipoints isle geometry for polygons on NULL
346 \param nisles number of isles
347
348 \return feature offset into file
349 \return -1 on error
350 */
351off_t write_feature(struct Map_info *Map, int type,
352 const struct line_pnts **p_points, int nparts,
353 const struct line_cats *cats)
354{
355 int i, cat, ret;
356
357 struct field_info *Fi;
358 const struct line_pnts *points;
361
362 off_t offset;
363
368
369 ogr_info = &(Map->fInfo.ogr);
370 offset_info = &(ogr_info->offset);
371
372 if (nparts < 1)
373 return -1;
374
375 points = p_points[0]; /* feature geometry */
376
377 if (!ogr_info->layer) {
378 /* create OGR layer if doesn't exist */
379 if (create_ogr_layer(Map, type) < 0)
380 return -1;
381 }
382
383 if (!points)
384 return 0;
385
386 cat = -1; /* no attributes to be written */
387 if (cats->n_cats > 0 && Vect_get_num_dblinks(Map) > 0) {
388 /* check for attributes */
389 Fi = Vect_get_dblink(Map, 0);
390 if (Fi) {
391 if (!Vect_cat_get(cats, Fi->number, &cat))
392 G_warning(_("No category defined for layer %d"), Fi->number);
393 if (cats->n_cats > 1) {
394 G_warning(_("Feature has more categories, using "
395 "category %d (from layer %d)"),
396 cat, cats->field[0]);
397 }
398 }
399 }
400
403
404 /* determine matching OGR feature geometry type */
405 if (type & (GV_POINT | GV_KERNEL)) {
407 G_warning(_("Feature is not a point. Skipping."));
408 return -1;
409 }
411 }
412 else if (type & GV_LINE) {
415 G_warning(_("Feature is not a line. Skipping."));
416 return -1;
417 }
419 }
420 else if (type & GV_BOUNDARY) {
421 if (Ogr_geom_type != wkbPolygon) {
422 G_warning(_("Feature is not a polygon. Skipping."));
423 return -1;
424 }
426 }
427 else if (type & GV_FACE) {
429 G_warning(_("Feature is not a face. Skipping."));
430 return -1;
431 }
433 }
434 else {
435 G_warning(_("Unsupported feature type (%d)"), type);
436 return -1;
437 }
438
439 G_debug(3, "V1_write_line_ogr(): type = %d", type);
440
442 int iring, npoints;
443
444 /* add rings (first is exterior ring) */
445 for (iring = 0; iring < nparts; iring++) {
447
448 points = p_points[iring];
449 npoints = points->n_points - 1;
451 if (points->x[0] != points->x[npoints] ||
452 points->y[0] != points->y[npoints] ||
453 points->z[0] != points->z[npoints]) {
454 G_warning(_("Boundary is not closed. Feature skipped."));
455 return -1;
456 }
457
458 /* add points */
459 for (i = 0; i < npoints; i++) {
460 OGR_G_AddPoint(Ogr_ring, points->x[i], points->y[i],
461 points->z[i]);
462 }
463 G_debug(4, " ring(%d): n_points = %d", iring, npoints);
465 }
466 }
467 else {
468 for (i = 0; i < points->n_points; i++) {
469 OGR_G_AddPoint(Ogr_geometry, points->x[i], points->y[i],
470 points->z[i]);
471 }
472 G_debug(4, " n_points = %d", points->n_points);
473 }
474
475 /* create feature & set geometry */
478
479 /* write attributes */
480 if (cat > -1 && ogr_info->dbdriver) {
481 if (0 > write_attributes(ogr_info->dbdriver, cat, Fi, ogr_info->layer,
483 G_warning(_("Unable to writes feature attributes"));
484 G_free(Fi);
485 }
486 /* write feature into layer */
488
489 /* update offset array */
490 if (offset_info->array_num >= offset_info->array_alloc) {
491 offset_info->array_alloc += 1000;
492 offset_info->array = (int *)G_realloc(
493 offset_info->array, offset_info->array_alloc * sizeof(int));
494 }
495
496 offset = offset_info->array_num;
497
498 offset_info->array[offset_info->array_num++] =
501 /* register exterior ring in offset array */
502 offset_info->array[offset_info->array_num++] = 0;
503 }
504
505 /* destroy */
508
509 if (ret != OGRERR_NONE)
510 return -1;
511
512 G_debug(3, "write_feature(): -> offset = %lu offset_num = %d cat = %d",
513 (unsigned long)offset, offset_info->array_num, cat);
514
515 return offset;
516}
517
518/*!
519 \brief Writes attributes
520
521 \param driver pointer to dbDriver
522 \param Fi pointer to field_info struct
523 \param[in,out] Ogr_layer OGR layer
524 \param[in,out] Ogr_feature OGR feature to modify
525
526 \return 1 on success
527 \return 0 no attributes
528 \return -1 on error
529 */
530int write_attributes(dbDriver *driver, int cat, const struct field_info *Fi,
532{
533 int j, ogrfieldnum;
534 char buf[2000];
535 int ncol, sqltype, ctype, ogrtype, more;
536 const char *fidcol, *colname;
537 dbTable *table;
541 dbValue *value;
542
544
545 G_debug(3, "write_attributes(): cat = %d", cat);
546
547 if (cat < 0) {
548 G_warning(_("Feature without category of layer %d"), Fi->number);
549 return 0;
550 }
551
553
554 /* read & set attributes */
555 snprintf(buf, sizeof(buf), "SELECT * FROM %s WHERE %s = %d", Fi->table,
556 Fi->key, cat);
557 G_debug(4, "SQL: %s", buf);
558 db_set_string(&dbstring, buf);
559
560 /* select data */
562 DB_OK) {
563 G_warning(_("Unable to select attributes for category %d"), cat);
564 return -1;
565 }
566
567 if (db_fetch(&cursor, DB_NEXT, &more) != DB_OK) {
568 G_warning(_("Unable to fetch data from table <%s>"), Fi->table);
569 return -1;
570 }
571
572 if (!more) {
573 G_warning(_("No database record for category %d, "
574 "no attributes will be written"),
575 cat);
576 return -1;
577 }
578
580
581 table = db_get_cursor_table(&cursor);
583 for (j = 0; j < ncol; j++) {
584 column = db_get_table_column(table, j);
586 if (fidcol && *fidcol && strcmp(colname, fidcol) == 0) {
587 /* skip fid column */
588 continue;
589 }
591 /* for debug only */
593 G_debug(3, "col %d : val = %s", j, db_get_string(&dbstring));
594
597 ogrtype = sqltype_to_ogrtype(sqltype);
598 G_debug(3, " colctype = %d", ctype);
599
601 if (ogrfieldnum < 0) {
602 /* create field if not exists */
605 G_warning(_("Unable to create field <%s>"), colname);
607 }
608
609 /* Reset */
611
612 /* prevent writing NULL values */
613 if (!db_test_value_isnull(value)) {
614 switch (ctype) {
615 case DB_C_TYPE_INT:
617 db_get_value_int(value));
618 break;
619 case DB_C_TYPE_DOUBLE:
621 db_get_value_double(value));
622 break;
623 case DB_C_TYPE_STRING:
625 db_get_value_string(value));
626 break;
631 break;
632 default:
633 G_warning(_("Unsupported column type %d"), ctype);
634 break;
635 }
636 }
637 }
638
640
642
643 return 1;
644}
645
646int sqltype_to_ogrtype(int sqltype)
647{
648 int ctype, ogrtype;
649
651
652 switch (ctype) {
653 case DB_C_TYPE_INT:
655 break;
656 case DB_C_TYPE_DOUBLE:
658 break;
659 case DB_C_TYPE_STRING:
661 break;
664 break;
665 default:
667 break;
668 }
669
670 return ogrtype;
671}
#define NULL
Definition ccmath.h:32
Main header of GRASS DataBase Management Interface.
#define DB_C_TYPE_INT
Definition dbmi.h:106
#define DB_SEQUENTIAL
Definition dbmi.h:121
#define DB_C_TYPE_STRING
Definition dbmi.h:105
#define DB_C_TYPE_DOUBLE
Definition dbmi.h:107
#define DB_OK
Definition dbmi.h:69
#define DB_C_TYPE_DATETIME
Definition dbmi.h:108
#define DB_NEXT
Definition dbmi.h:112
int db_test_value_isnull(dbValue *)
Check of value is null.
Definition value.c:24
int db_get_column_length(dbColumn *)
Get column's length.
dbColumn * db_get_table_column(dbTable *, int)
Returns column structure for given table and column number.
double db_get_value_double(dbValue *)
Get double precision value.
Definition value.c:48
int db_sqltype_to_Ctype(int)
Get C data type based on given SQL data type.
Definition sqlCtype.c:22
dbValue * db_get_column_value(dbColumn *)
Returns column value for given column structure.
int db_get_column_sqltype(dbColumn *)
Returns column sqltype for column.
int db_open_database(dbDriver *, dbHandle *)
Open database connection.
Definition c_opendb.c:24
int db_close_database_shutdown_driver(dbDriver *)
Close driver/database connection.
Definition db.c:58
void db_free_string(dbString *)
Free allocated space for dbString.
Definition string.c:148
char * db_get_string(const dbString *)
Get string.
Definition string.c:138
dbTable * db_get_cursor_table(dbCursor *)
Get table allocated by cursor.
Definition cursor.c:64
int db_set_string(dbString *, const char *)
Inserts string to dbString (enlarge string)
Definition string.c:39
const char * db_get_column_name(dbColumn *)
Returns column name for given column.
int db_set_handle(dbHandle *, const char *, const char *)
Set handle (database and schema name)
Definition handle.c:36
int db_get_value_int(dbValue *)
Get integer value.
Definition value.c:36
dbDriver * db_start_driver(const char *)
Initialize a new dbDriver for db transaction.
Definition start.c:48
void db_init_handle(dbHandle *)
Initialize handle (i.e database/schema)
Definition handle.c:20
void db_init_string(dbString *)
Initialize dbString.
Definition string.c:23
int db_close_cursor(dbCursor *)
Close cursor.
Definition c_close_cur.c:24
int db_open_select_cursor(dbDriver *, dbString *, dbCursor *, int)
Open select cursor.
int db_append_string(dbString *, const char *)
Append string to dbString.
Definition string.c:203
int db_convert_column_value_to_string(dbColumn *, dbString *)
?
Definition columnfmt.c:60
const char * db_get_value_string(dbValue *)
Get string value.
Definition value.c:90
int db_fetch(dbCursor *, int, int *)
Fetch data from open cursor.
Definition c_fetch.c:25
int db_get_table_number_of_columns(dbTable *)
Return the number of columns of the table.
void G_free(void *)
Free allocated memory.
Definition gis/alloc.c:145
#define G_realloc(p, n)
Definition defs/gis.h:138
struct Key_Value * G_get_projinfo(void)
Gets projection information for location.
void G_warning(const char *,...) __attribute__((format(printf
struct Key_Value * G_get_projepsg(void)
Gets EPSG information for the current location.
void G_free_key_value(struct Key_Value *)
Free allocated Key_Value structure.
Definition key_value1.c:102
struct Key_Value * G_get_projunits(void)
Gets units information for location.
int G_debug(int, const char *,...) __attribute__((format(printf
OGRSpatialReferenceH GPJ_grass_to_osr2(const struct Key_Value *, const struct Key_Value *, const struct Key_Value *)
Converts a GRASS co-ordinate system to an OGRSpatialReferenceH object. EPSG code is preferred if avai...
Definition convert.c:345
int Vect_cat_get(const struct line_cats *, int, int *)
Get first found category of given field.
int V1_read_line_ogr(struct Map_info *, struct line_pnts *, struct line_cats *, off_t)
Read feature from OGR layer at given offset (level 1 without topology)
Definition read_ogr.c:165
struct field_info * Vect_get_dblink(struct Map_info *, int)
Get information about link to database.
Definition field.c:468
int Vect_get_num_dblinks(struct Map_info *)
Get number of defined dblinks.
Definition level_two.c:157
int Vect_is_3d(struct Map_info *)
Check if vector map is 3D.
#define GV_LINE
#define GV_POINT
Feature types used in memory on run time (may change)
#define GV_BOUNDARY
#define GV_FACE
#define GV_KERNEL
#define TRUE
Definition gis.h:75
#define _(str)
Definition glocale.h:10
Data structure used for building pseudo-topology.
Non-native format info (OGR)
OGRSFDriverH driver
Pointer to OGRDriver.
struct Format_info_offset offset
Offset list used for building pseudo-topology.
Vector map info.
Layer (old: field) information.
Feature category info.
int * field
Array of layers (fields)
int n_cats
Number of categories attached to element.
Feature geometry info - coordinates.
double * y
Array of Y coordinates.
double * x
Array of X coordinates.
int n_points
Number of points.
double * z
Array of Z coordinates.
int V1_delete_line_ogr(struct Map_info *Map, off_t offset)
Deletes feature at the given offset on level 1 (OGR interface)
Definition write_ogr.c:106
off_t V2__write_area_ogr(struct Map_info *Map, const struct line_pnts **points, int nparts, const struct line_cats *cats)
Writes area on topological level (OGR Simple Features interface, internal use only)
Definition write_ogr.c:145
off_t V1_write_line_ogr(struct Map_info *Map, int type, const struct line_pnts *points, const struct line_cats *cats)
Writes feature on level 1 (OGR interface)
Definition write_ogr.c:60
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)
Rewrites feature at the given offset on level 1 (OGR interface)
Definition write_ogr.c:81