From fac39810d2e82823eb7b728696c713cc044ca716 Mon Sep 17 00:00:00 2001 From: Markus Metz <33666869+metzm@users.noreply.github.com> Date: Wed, 27 Nov 2024 22:52:09 +0100 Subject: [PATCH] v.out.ogr: faster export with many attributes (#4741) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * v.out.ogr: faster export with many attributes --------- Co-authored-by: Edouard Choinière <27212526+echoix@users.noreply.github.com> --- vector/v.out.ogr/args.c | 13 + vector/v.out.ogr/attrb_fast.c | 155 ++++++++ vector/v.out.ogr/export_areas.c | 4 +- vector/v.out.ogr/export_areas_fast.c | 539 ++++++++++++++++++++++++++ vector/v.out.ogr/export_lines_fast.c | 546 +++++++++++++++++++++++++++ vector/v.out.ogr/local_proto.h | 19 +- vector/v.out.ogr/main.c | 38 +- 7 files changed, 1301 insertions(+), 13 deletions(-) create mode 100644 vector/v.out.ogr/attrb_fast.c create mode 100644 vector/v.out.ogr/export_areas_fast.c create mode 100644 vector/v.out.ogr/export_lines_fast.c diff --git a/vector/v.out.ogr/args.c b/vector/v.out.ogr/args.c index 18841837e24..8310c302c50 100644 --- a/vector/v.out.ogr/args.c +++ b/vector/v.out.ogr/args.c @@ -77,6 +77,19 @@ void parse_args(int argc, char **argv, struct Options *options, _("OGR layer creation option (format specific, NAME=VALUE)"); options->lco->guisection = _("Creation"); + options->method = G_define_option(); + options->method->key = "method"; + options->method->type = TYPE_STRING; + options->method->required = NO; + options->method->options = "fast,slow"; + options->method->answer = "fast"; + options->method->label = _("Method to use for export, " + "default is fast export, " + "use slow export in case of problems with " + "the fast method"); + G_asprintf((char **)&options->method->descriptions, "fast;%s;slow;%s", + _("new, faster method"), _("old, slower method")); + flags->update = G_define_flag(); flags->update->key = 'u'; flags->update->description = diff --git a/vector/v.out.ogr/attrb_fast.c b/vector/v.out.ogr/attrb_fast.c new file mode 100644 index 00000000000..45ee832f076 --- /dev/null +++ b/vector/v.out.ogr/attrb_fast.c @@ -0,0 +1,155 @@ +#include + +#include "local_proto.h" + +int mk_att_fast(int cat, struct field_info *Fi, int ncol, int *colctype, + const char **colname, int doatt, int nocat, + OGRFeatureH Ogr_feature, int *noatt, dbCursor *cursor, + int *more, int *db_cat, int key_col_index) +{ + int j, ogrfieldnum; + dbTable *Table; + static int first = 1; + static dbString dbstring; + dbColumn *Column; + dbValue *Value; + + G_debug(2, "mk_att() cat = %d, doatt = %d", cat, doatt); + + /* init constants */ + if (first) { + db_init_string(&dbstring); + first = 0; + } + + /* Attributes */ + /* Reset */ + if (!doatt) { + ogrfieldnum = OGR_F_GetFieldIndex(Ogr_feature, GV_KEY_COLUMN); + if (ogrfieldnum > -1) + OGR_F_UnsetField(Ogr_feature, ogrfieldnum); + /* doatt reset moved into have cat loop as the table needs to be + open to know the OGR field ID. Hopefully this has no ill consequences + */ + } + + /* Read & set attributes */ + if (cat >= 0) { /* Line with category */ + if (doatt) { + /* get current entries from cursor, + * check cat value in attributes */ + + Table = db_get_cursor_table(cursor); + while (*more && cat > *db_cat) { + Column = db_get_table_column(Table, key_col_index); + Value = db_get_column_value(Column); + + /* yes, the key column is sometimes of type double */ + switch (colctype[key_col_index]) { + case DB_C_TYPE_INT: + *db_cat = db_get_value_int(Value); + break; + case DB_C_TYPE_DOUBLE: + *db_cat = (int)db_get_value_double(Value); + break; + } + + G_debug(2, "found db_cat %d for cat %d in column %s", *db_cat, + cat, db_get_column_name(Column)); + + if (cat > *db_cat) { + if (db_fetch(cursor, DB_NEXT, more) != DB_OK) { + G_fatal_error(_("Unable to fetch data from table")); + } + } + } + + if (!(*more) || cat != *db_cat) { + G_debug(1, "No database record for cat = %d", cat); + /* Set at least key column to category */ + if (!nocat) { + ogrfieldnum = OGR_F_GetFieldIndex(Ogr_feature, Fi->key); + OGR_F_SetFieldInteger(Ogr_feature, ogrfieldnum, cat); + (*noatt)++; + } + else { + G_fatal_error(_("No database record for cat = %d and " + "export of 'cat' disabled"), + cat); + } + } + else { + for (j = 0; j < ncol; j++) { + Column = db_get_table_column(Table, j); + Value = db_get_column_value(Column); + db_convert_column_value_to_string( + Column, &dbstring); /* for debug only */ + G_debug(2, "col %d : val = %s", j, + db_get_string(&dbstring)); + + G_debug(2, " colctype = %d", colctype[j]); + + if (nocat && strcmp(Fi->key, colname[j]) == 0) + continue; + + ogrfieldnum = OGR_F_GetFieldIndex(Ogr_feature, colname[j]); + G_debug(2, " column = %s -> fieldnum = %d", colname[j], + ogrfieldnum); + + if (ogrfieldnum < 0) { + G_debug(4, + "Could not get OGR field number for column %s", + colname[j]); + continue; + } + + /* Reset */ + if ((nocat && strcmp(Fi->key, colname[j]) == 0) == 0) { + /* if this is 'cat', then execute the following only if + * the '-s' flag was NOT given */ + OGR_F_SetFieldNull(Ogr_feature, ogrfieldnum); + } + + /* prevent writing NULL values */ + if (!db_test_value_isnull(Value)) { + if ((nocat && strcmp(Fi->key, colname[j]) == 0) == 0) { + /* if this is 'cat', then execute the following only + * if the '-s' flag was NOT given */ + + switch (colctype[j]) { + 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; + } + } + } + else + OGR_F_SetFieldNull(Ogr_feature, ogrfieldnum); + } + } + } + else { /* Use cat only */ + ogrfieldnum = OGR_F_GetFieldIndex(Ogr_feature, GV_KEY_COLUMN); + OGR_F_SetFieldInteger(Ogr_feature, ogrfieldnum, cat); + } + } + + return 1; +} diff --git a/vector/v.out.ogr/export_areas.c b/vector/v.out.ogr/export_areas.c index acab7164a8d..5cbc78c408d 100644 --- a/vector/v.out.ogr/export_areas.c +++ b/vector/v.out.ogr/export_areas.c @@ -13,8 +13,9 @@ static int export_areas_multi(struct Map_info *, int, int, OGRFeatureDefnH, static OGRGeometryH create_polygon(struct Map_info *, int, struct line_pnts *, int); +#if 0 /* maybe useful */ -void reverse_points(struct line_pnts *Points) +static void reverse_points(struct line_pnts *Points) { int i, j, nhalf; double tmp; @@ -35,6 +36,7 @@ void reverse_points(struct line_pnts *Points) Points->z[j] = tmp; } } +#endif /* export areas as single/multi-polygons */ int export_areas(struct Map_info *In, int field, int multi, int donocat, diff --git a/vector/v.out.ogr/export_areas_fast.c b/vector/v.out.ogr/export_areas_fast.c new file mode 100644 index 00000000000..cad77b2279a --- /dev/null +++ b/vector/v.out.ogr/export_areas_fast.c @@ -0,0 +1,539 @@ +#include + +#include "local_proto.h" + +static int export_areas_single(struct Map_info *, int, int, OGRFeatureDefnH, + OGRLayerH, struct field_info *, dbDriver *, int, + int *, const char **, int, int, int *, int *, + int); +static int export_areas_multi(struct Map_info *, int, int, OGRFeatureDefnH, + OGRLayerH, struct field_info *, dbDriver *, int, + int *, const char **, int, int, int *, int *, + int); +static OGRGeometryH create_polygon(struct Map_info *, int, struct line_pnts *, + int); + +#if 0 +/* maybe useful */ +static void reverse_points(struct line_pnts *Points) +{ + int i, j, nhalf; + double tmp; + + nhalf = Points->n_points / 2; + + for (i = 0, j = Points->n_points - 1; i < nhalf; i++, j--) { + tmp = Points->x[i]; + Points->x[i] = Points->x[j]; + Points->x[j] = tmp; + + tmp = Points->y[i]; + Points->y[i] = Points->y[j]; + Points->y[j] = tmp; + + tmp = Points->z[i]; + Points->z[i] = Points->z[j]; + Points->z[j] = tmp; + } +} +#endif + +/* export areas as single/multi-polygons */ +int export_areas_fast(struct Map_info *In, int field, int multi, int donocat, + OGRFeatureDefnH Ogr_featuredefn, OGRLayerH Ogr_layer, + struct field_info *Fi, dbDriver *driver, int ncol, + int *colctype, const char **colname, int doatt, int nocat, + int *noatt, int *fout, int outer_ring_ccw) +{ + if (multi) + /* export as multi-polygons */ + return export_areas_multi( + In, field, donocat, Ogr_featuredefn, Ogr_layer, Fi, driver, ncol, + colctype, colname, doatt, nocat, noatt, fout, outer_ring_ccw); + + /* export as polygons */ + return export_areas_single(In, field, donocat, Ogr_featuredefn, Ogr_layer, + Fi, driver, ncol, colctype, colname, doatt, + nocat, noatt, fout, outer_ring_ccw); +} + +int export_areas_single(struct Map_info *In, int field, int donocat, + OGRFeatureDefnH Ogr_featuredefn, OGRLayerH Ogr_layer, + struct field_info *Fi, dbDriver *driver, int ncol, + int *colctype, const char **colname, int doatt, + int nocat, int *n_noatt, int *n_nocat, + int outer_ring_ccw) +{ + int i; + int cat, last_cat, db_cat, centroid, area; + int n_exported; + + struct line_pnts *Points; + struct line_cats *Cats; + + int findex; + struct Cat_index *ci; + int cat_index, n_cats; + + dbString dbstring; + char buf[SQL_BUFFER_SIZE]; + dbCursor cursor; + int more; + int key_col_index; + + OGRGeometryH Ogr_geometry; + OGRFeatureH Ogr_feature; + + Points = Vect_new_line_struct(); + Cats = Vect_new_cats_struct(); + + n_exported = 0; + + /* get category index for given field */ + findex = Vect_cidx_get_field_index(In, field); + if (findex == -1) { + G_fatal_error(_("Unable to export multi-features. No category index " + "for layer %d."), + field); + } + + ci = &(In->plus.cidx[findex]); + n_cats = ci->n_cats; + + if (donocat) + G_message(_("Exporting features with category...")); + + /* select attributes ordered by category value */ + db_init_string(&dbstring); + sprintf(buf, "SELECT * FROM %s ORDER BY %s ASC", Fi->table, Fi->key); + G_debug(2, "SQL: %s", buf); + db_set_string(&dbstring, buf); + if (db_open_select_cursor(driver, &dbstring, &cursor, DB_SEQUENTIAL) != + DB_OK) { + G_fatal_error(_("Cannot select attributes sorted by %s"), Fi->key); + } + + if (db_fetch(&cursor, DB_NEXT, &more) != DB_OK) + G_fatal_error(_("Unable to fetch data from table")); + + /* get index of key column */ + key_col_index = -1; + for (i = 0; i < ncol; i++) { + if (strcmp(Fi->key, colname[i]) == 0) { + key_col_index = i; + break; + } + } + + last_cat = -1; + db_cat = -1; + for (cat_index = 0; cat_index < n_cats; cat_index++) { + + G_percent(cat_index, n_cats, 5); + + /* get area's category */ + if (!(ci->cat[cat_index][1] & GV_CENTROID)) + continue; + + cat = ci->cat[cat_index][0]; + /* make sure the cidx is ordered by cat */ + if (cat < last_cat) + G_fatal_error(_("Category index is not sorted ascending by cat!")); + last_cat = cat; + + centroid = ci->cat[cat_index][2]; + + area = Vect_get_centroid_area(In, centroid); + + if (area < 1) { + /* centroid not in area or duplicate centroid */ + continue; + } + + /* create polygon from area */ + Ogr_geometry = create_polygon(In, area, Points, outer_ring_ccw); + + /* add feature */ + Ogr_feature = OGR_F_Create(Ogr_featuredefn); + OGR_F_SetGeometry(Ogr_feature, Ogr_geometry); + /* get attributes */ + mk_att_fast(cat, Fi, ncol, colctype, colname, doatt, nocat, Ogr_feature, + n_noatt, &cursor, &more, &db_cat, key_col_index); + if (OGR_L_CreateFeature(Ogr_layer, Ogr_feature) != OGRERR_NONE) { + G_fatal_error(_("Failed to create OGR feature")); + } + else + n_exported++; + + OGR_F_Destroy(Ogr_feature); + OGR_G_DestroyGeometry(Ogr_geometry); + } + + if (donocat) + G_message(_("Exporting features without category...")); + + if (doatt) { + db_close_cursor(&cursor); + if (donocat) { + cat = -1; + if (db_open_select_cursor(driver, &dbstring, &cursor, + DB_SEQUENTIAL) != DB_OK) { + G_fatal_error(_("Cannot select attributes for cat = %d"), cat); + } + if (db_fetch(&cursor, DB_NEXT, &more) != DB_OK) + G_fatal_error(_("Unable to fetch data from table")); + } + } + + for (area = 1; area <= Vect_get_num_areas(In); area++) { + centroid = Vect_get_area_centroid(In, area); + /* skip areas without centroid */ + if (centroid == 0) + continue; + + /* get areas's category */ + Vect_get_area_cats(In, area, Cats); + Vect_cat_get(Cats, field, &cat); + /* skip areas with category */ + if (cat >= 0) + continue; + /* skip areas without category, do not export not labeled */ + if (cat < 0 && !donocat) { + (*n_nocat)++; + continue; + } + + (*n_nocat)++; + + /* create polygon from area */ + Ogr_geometry = create_polygon(In, area, Points, outer_ring_ccw); + + /* add feature */ + Ogr_feature = OGR_F_Create(Ogr_featuredefn); + OGR_F_SetGeometry(Ogr_feature, Ogr_geometry); + /* no attributes for features without category */ + cat = -1; + db_cat = -2; + mk_att_fast(cat, Fi, ncol, colctype, colname, doatt, nocat, Ogr_feature, + n_noatt, &cursor, &more, &db_cat, key_col_index); + if (OGR_L_CreateFeature(Ogr_layer, Ogr_feature) != OGRERR_NONE) { + G_fatal_error(_("Failed to create OGR feature")); + } + else + n_exported++; + + OGR_F_Destroy(Ogr_feature); + OGR_G_DestroyGeometry(Ogr_geometry); + } + + if (donocat && doatt) + db_close_cursor(&cursor); + + Vect_destroy_line_struct(Points); + + return n_exported; +} + +int export_areas_multi(struct Map_info *In, int field, int donocat, + OGRFeatureDefnH Ogr_featuredefn, OGRLayerH Ogr_layer, + struct field_info *Fi, dbDriver *driver, int ncol, + int *colctype, const char **colname, int doatt, + int nocat, int *n_noatt, int *n_nocat, + int outer_ring_ccw) +{ + int i, n_exported, area, centroid; + int cat, last_cat, db_cat, line, findex, ipart; + + struct line_pnts *Points; + struct line_cats *Cats; + struct ilist *line_list, *lcats; + + struct Cat_index *ci; + int cat_index, n_cats; + + dbString dbstring; + char buf[SQL_BUFFER_SIZE]; + dbCursor cursor; + int more; + int key_col_index; + + OGRGeometryH Ogr_geometry, Ogr_geometry_part; + OGRFeatureH Ogr_feature; + OGRwkbGeometryType wkbtype, wkbtype_part; + + Points = Vect_new_line_struct(); + Cats = Vect_new_cats_struct(); + line_list = Vect_new_list(); + lcats = Vect_new_list(); + + n_exported = 0; + + /* check if category index is available for given field */ + findex = Vect_cidx_get_field_index(In, field); + if (findex == -1) { + G_fatal_error(_("Unable to export multi-features. No category index " + "for layer %d."), + field); + } + + ci = &(In->plus.cidx[findex]); + n_cats = ci->n_cats; + + /* determine type */ + wkbtype_part = wkbPolygon; + wkbtype = get_multi_wkbtype(wkbtype_part); + + if (donocat) + G_message(_("Exporting features with category...")); + + key_col_index = -1; + more = 1; + if (doatt) { + /* select attributes ordered by category value */ + db_init_string(&dbstring); + sprintf(buf, "SELECT * FROM %s ORDER BY %s ASC", Fi->table, Fi->key); + G_debug(2, "SQL: %s", buf); + db_set_string(&dbstring, buf); + if (db_open_select_cursor(driver, &dbstring, &cursor, DB_SEQUENTIAL) != + DB_OK) { + G_fatal_error(_("Cannot select attributes sorted by %s"), Fi->key); + } + + if (db_fetch(&cursor, DB_NEXT, &more) != DB_OK) + G_fatal_error(_("Unable to fetch data from table")); + + /* get index of key column */ + key_col_index = -1; + for (i = 0; i < ncol; i++) { + if (strcmp(Fi->key, colname[i]) == 0) { + key_col_index = i; + break; + } + } + } + + last_cat = -1; + db_cat = -1; + cat_index = 0; + while (cat_index < n_cats) { + + G_percent(cat_index, n_cats, 5); + + cat = ci->cat[cat_index][0]; + + /* make sure the cidx is ordered by cat */ + if (cat < last_cat) + G_fatal_error(_("Category index is not sorted ascending by cat!")); + last_cat = cat; + + /* collect all features with current cat */ + Vect_reset_list(line_list); + while (cat_index < n_cats && ci->cat[cat_index][0] == cat) { + if (ci->cat[cat_index][1] & GV_CENTROID) { + Vect_list_append(line_list, ci->cat[cat_index][2]); + } + cat_index++; + } + + /* create multi-feature */ + Ogr_geometry = OGR_G_CreateGeometry(wkbtype); + + /* build simple features geometry, go through all parts */ + for (ipart = 0; ipart < line_list->n_values; ipart++) { + line = line_list->value[ipart]; + G_debug(3, "cat=%d, line=%d -> part=%d", cat, line, ipart); + + /* get centroid's category */ + Vect_read_line(In, NULL, Cats, line); + /* check for category consistency */ + Vect_field_cat_get(Cats, field, lcats); + if (!Vect_val_in_list(lcats, cat)) + G_fatal_error(_("Unable to create multi-feature. " + "Category %d not found in line %d, field %d"), + cat, line, field); + + /* find corresponding area */ + area = Vect_get_centroid_area(In, line); + if (area <= 0) + continue; + + /* create polygon from area */ + Ogr_geometry_part = + create_polygon(In, area, Points, outer_ring_ccw); + + /* add part */ + OGR_G_AddGeometryDirectly(Ogr_geometry, Ogr_geometry_part); + } + + if (!OGR_G_IsEmpty(Ogr_geometry)) { + /* write multi-feature */ + Ogr_feature = OGR_F_Create(Ogr_featuredefn); + OGR_F_SetGeometry(Ogr_feature, Ogr_geometry); + /* get attributes */ + mk_att_fast(cat, Fi, ncol, colctype, colname, doatt, nocat, + Ogr_feature, n_noatt, &cursor, &more, &db_cat, + key_col_index); + if (OGR_L_CreateFeature(Ogr_layer, Ogr_feature) != OGRERR_NONE) { + G_fatal_error(_("Failed to create OGR feature")); + } + else + n_exported++; + + OGR_F_Destroy(Ogr_feature); + } + else { + /* skip empty features */ + G_debug(3, "multi-feature is empty -> skipped"); + } + + OGR_G_DestroyGeometry(Ogr_geometry); + } + + if (donocat) + G_message(_("Exporting features without category...")); + + /* check areas without category, if -c flag is given write them as + * one multi-feature */ + Ogr_geometry = OGR_G_CreateGeometry(wkbtype); + + if (doatt) { + db_close_cursor(&cursor); + if (donocat) { + cat = -1; + if (db_open_select_cursor(driver, &dbstring, &cursor, + DB_SEQUENTIAL) != DB_OK) { + G_fatal_error(_("Cannot select attributes for cat = %d"), cat); + } + if (db_fetch(&cursor, DB_NEXT, &more) != DB_OK) + G_fatal_error(_("Unable to fetch data from table")); + } + } + + for (area = 1; area <= Vect_get_num_areas(In); area++) { + centroid = Vect_get_area_centroid(In, area); + /* skip areas without centroid */ + if (centroid == 0) + continue; + + /* get areas's category */ + Vect_get_area_cats(In, area, Cats); + Vect_cat_get(Cats, field, &cat); + /* skip areas with category */ + if (cat >= 0) + continue; + /* skip areas without category, do not export not labeled */ + if (cat < 0 && !donocat) { + (*n_nocat)++; + continue; + } + + /* create polygon from area */ + Ogr_geometry_part = create_polygon(In, area, Points, outer_ring_ccw); + + /* add part */ + OGR_G_AddGeometryDirectly(Ogr_geometry, Ogr_geometry_part); + + (*n_nocat)++; + } + + if (!OGR_G_IsEmpty(Ogr_geometry)) { + /* write multi-feature */ + Ogr_feature = OGR_F_Create(Ogr_featuredefn); + OGR_F_SetGeometry(Ogr_feature, Ogr_geometry); + /* no attributes for features without category */ + cat = -1; + db_cat = -2; + mk_att_fast(cat, Fi, ncol, colctype, colname, doatt, nocat, Ogr_feature, + n_noatt, &cursor, &more, &db_cat, key_col_index); + if (OGR_L_CreateFeature(Ogr_layer, Ogr_feature) != OGRERR_NONE) { + G_fatal_error(_("Failed to create OGR feature")); + } + else + n_exported++; + + OGR_F_Destroy(Ogr_feature); + } + else { + /* skip empty features */ + G_debug(3, "multi-feature is empty -> skipped"); + } + + OGR_G_DestroyGeometry(Ogr_geometry); + + if (donocat && doatt) + db_close_cursor(&cursor); + + Vect_destroy_line_struct(Points); + Vect_destroy_cats_struct(Cats); + Vect_destroy_list(line_list); + Vect_destroy_list(lcats); + + return n_exported; +} + +OGRGeometryH create_polygon(struct Map_info *In, int area, + struct line_pnts *Points, int outer_ring_ccw) +{ + int j, k; + OGRGeometryH Ogr_geometry, ring; + + Vect_get_area_points(In, area, Points); + + Ogr_geometry = OGR_G_CreateGeometry(wkbPolygon); + ring = OGR_G_CreateGeometry(wkbLinearRing); + + /* Area */ + if (Vect_is_3d(In)) { + if (outer_ring_ccw) { + for (j = Points->n_points - 1; j >= 0; j--) + OGR_G_AddPoint(ring, Points->x[j], Points->y[j], Points->z[j]); + } + else { + for (j = 0; j < Points->n_points; j++) + OGR_G_AddPoint(ring, Points->x[j], Points->y[j], Points->z[j]); + } + } + else { + if (outer_ring_ccw) { + for (j = Points->n_points - 1; j >= 0; j--) + OGR_G_AddPoint_2D(ring, Points->x[j], Points->y[j]); + } + else { + for (j = 0; j < Points->n_points; j++) + OGR_G_AddPoint_2D(ring, Points->x[j], Points->y[j]); + } + } + + OGR_G_AddGeometryDirectly(Ogr_geometry, ring); + + /* Isles */ + for (k = 0; k < Vect_get_area_num_isles(In, area); k++) { + Vect_get_isle_points(In, Vect_get_area_isle(In, area, k), Points); + ring = OGR_G_CreateGeometry(wkbLinearRing); + if (Vect_is_3d(In)) { + if (outer_ring_ccw) { + for (j = Points->n_points - 1; j >= 0; j--) + OGR_G_AddPoint(ring, Points->x[j], Points->y[j], + Points->z[j]); + } + else { + for (j = 0; j < Points->n_points; j++) + OGR_G_AddPoint(ring, Points->x[j], Points->y[j], + Points->z[j]); + } + } + else { + if (outer_ring_ccw) { + for (j = Points->n_points - 1; j >= 0; j--) + OGR_G_AddPoint_2D(ring, Points->x[j], Points->y[j]); + } + else { + for (j = 0; j < Points->n_points; j++) + OGR_G_AddPoint_2D(ring, Points->x[j], Points->y[j]); + } + } + OGR_G_AddGeometryDirectly(Ogr_geometry, ring); + } + + return Ogr_geometry; +} diff --git a/vector/v.out.ogr/export_lines_fast.c b/vector/v.out.ogr/export_lines_fast.c new file mode 100644 index 00000000000..e9509c8d972 --- /dev/null +++ b/vector/v.out.ogr/export_lines_fast.c @@ -0,0 +1,546 @@ +#include + +#include "local_proto.h" + +static int export_lines_single(struct Map_info *, int, int, int, int, + OGRFeatureDefnH, OGRLayerH, struct field_info *, + dbDriver *, int, int *, const char **, int, int, + int *, int *); +static int export_lines_multi(struct Map_info *, int, int, int, int, + OGRFeatureDefnH, OGRLayerH, struct field_info *, + dbDriver *, int, int *, const char **, int, int, + int *, int *); + +static void line_to_polygon(OGRGeometryH, const struct line_pnts *); + +static void add_part(OGRGeometryH, OGRwkbGeometryType, int, struct line_pnts *); + +static OGRGeometryH build_geometry(struct Map_info *, struct line_pnts *, int, + int, int); + +/* export primitives as single/multi-features */ +int export_lines_fast(struct Map_info *In, int field, int otype, int multi, + int donocat, int force_poly, + OGRFeatureDefnH Ogr_featuredefn, OGRLayerH Ogr_layer, + struct field_info *Fi, dbDriver *driver, int ncol, + int *colctype, const char **colname, int doatt, int nocat, + int *n_noatt, int *n_nocat) +{ + if (multi) + /* export as multi-features */ + return export_lines_multi(In, field, otype, donocat, force_poly, + Ogr_featuredefn, Ogr_layer, Fi, driver, ncol, + colctype, colname, doatt, nocat, n_noatt, + n_nocat); + + /* export as single features */ + return export_lines_single( + In, field, otype, donocat, force_poly, Ogr_featuredefn, Ogr_layer, Fi, + driver, ncol, colctype, colname, doatt, nocat, n_noatt, n_nocat); +} + +/* export line as single feature */ +int export_lines_single(struct Map_info *In, int field, int otype, int donocat, + int force_poly, OGRFeatureDefnH Ogr_featuredefn, + OGRLayerH Ogr_layer, struct field_info *Fi, + dbDriver *driver, int ncol, int *colctype, + const char **colname, int doatt, int nocat, + int *n_noatt, int *n_nocat) +{ + int i, n_exported; + int cat, last_cat, db_cat, type; + + struct line_pnts *Points; + struct line_cats *Cats; + + int findex; + struct Cat_index *ci; + int cat_index, n_cats; + + dbString dbstring; + char buf[SQL_BUFFER_SIZE]; + dbCursor cursor; + int more; + int key_col_index; + + OGRGeometryH Ogr_geometry; + OGRFeatureH Ogr_feature; + + Points = Vect_new_line_struct(); + Cats = Vect_new_cats_struct(); + + n_exported = 0; + + /* get category index for given field */ + findex = Vect_cidx_get_field_index(In, field); + if (findex == -1) { + G_fatal_error(_("Unable to export multi-features. No category index " + "for layer %d."), + field); + } + + ci = &(In->plus.cidx[findex]); + n_cats = ci->n_cats; + + if (donocat) + G_message(_("Exporting features with category...")); + + key_col_index = -1; + more = 1; + if (doatt) { + /* select attributes ordered by category value */ + db_init_string(&dbstring); + sprintf(buf, "SELECT * FROM %s ORDER BY %s ASC", Fi->table, Fi->key); + G_debug(2, "SQL: %s", buf); + db_set_string(&dbstring, buf); + if (db_open_select_cursor(driver, &dbstring, &cursor, DB_SEQUENTIAL) != + DB_OK) { + G_fatal_error(_("Cannot select attributes sorted by %s"), Fi->key); + } + + if (db_fetch(&cursor, DB_NEXT, &more) != DB_OK) + G_fatal_error(_("Unable to fetch data from table")); + + /* get index of key column */ + for (i = 0; i < ncol; i++) { + if (strcmp(Fi->key, colname[i]) == 0) { + key_col_index = i; + break; + } + } + } + + last_cat = -1; + db_cat = -1; + for (cat_index = 0; cat_index < n_cats; cat_index++) { + + G_percent(cat_index, n_cats, 5); + + if (!(ci->cat[cat_index][1] & otype)) + continue; + + cat = ci->cat[cat_index][0]; + /* make sure the cidx is ordered by cat */ + if (cat < last_cat) + G_fatal_error(_("Category index is not sorted ascending by cat!")); + last_cat = cat; + + i = ci->cat[cat_index][2]; + + /* read line */ + type = Vect_read_line(In, Points, Cats, i); + G_debug(2, "line = %d type = %d", i, type); + if (!(otype & type)) { + /* skip lines with different type */ + G_debug(2, "type %d not specified -> skipping", type); + continue; + } + + Ogr_geometry = build_geometry(In, Points, type, otype, force_poly); + + /* add feature */ + Ogr_feature = OGR_F_Create(Ogr_featuredefn); + OGR_F_SetGeometry(Ogr_feature, Ogr_geometry); + /* get attributes */ + mk_att_fast(cat, Fi, ncol, colctype, colname, doatt, nocat, Ogr_feature, + n_noatt, &cursor, &more, &db_cat, key_col_index); + if (OGR_L_CreateFeature(Ogr_layer, Ogr_feature) != OGRERR_NONE) { + G_fatal_error(_("Failed to create OGR feature")); + } + else + n_exported++; + + OGR_F_Destroy(Ogr_feature); + OGR_G_DestroyGeometry(Ogr_geometry); + } + + if (donocat) + G_message(_("Exporting features without category...")); + + if (doatt) { + db_close_cursor(&cursor); + if (donocat) { + cat = -1; + if (db_open_select_cursor(driver, &dbstring, &cursor, + DB_SEQUENTIAL) != DB_OK) { + G_fatal_error(_("Cannot select attributes for cat = %d"), cat); + } + if (db_fetch(&cursor, DB_NEXT, &more) != DB_OK) + G_fatal_error(_("Unable to fetch data from table")); + } + } + + /* this loop is needed to count features without category in the layer + * to be exported */ + Vect_rewind(In); + while (TRUE) { + type = Vect_read_next_line(In, Points, Cats); + if (type < 0) + break; + + Vect_cat_get(Cats, field, &cat); + if (cat >= 0) + continue; /* skip features with category */ + if (cat < 0 && !donocat) { + (*n_nocat)++; + continue; /* skip lines without category, do not export + * not labeled */ + } + + (*n_nocat)++; + + db_cat = -2; + cat = -1; + + /* code duplicated from above --> */ + Ogr_geometry = build_geometry(In, Points, type, otype, force_poly); + + /* add feature */ + Ogr_feature = OGR_F_Create(Ogr_featuredefn); + OGR_F_SetGeometry(Ogr_feature, Ogr_geometry); + /* no attributes for features without category */ + cat = -1; + db_cat = -2; + mk_att_fast(cat, Fi, ncol, colctype, colname, doatt, nocat, Ogr_feature, + n_noatt, &cursor, &more, &db_cat, key_col_index); + if (OGR_L_CreateFeature(Ogr_layer, Ogr_feature) != OGRERR_NONE) { + G_fatal_error(_("Failed to create OGR feature")); + } + else + n_exported++; + + OGR_F_Destroy(Ogr_feature); + OGR_G_DestroyGeometry(Ogr_geometry); + /* <-- code duplicated from above */ + } + if (doatt && donocat) + db_close_cursor(&cursor); + + Vect_destroy_line_struct(Points); + Vect_destroy_cats_struct(Cats); + + return n_exported; +} + +/* export line as multi-feature */ +int export_lines_multi(struct Map_info *In, int field, int otype, int donocat, + int force_poly, OGRFeatureDefnH Ogr_featuredefn, + OGRLayerH Ogr_layer, struct field_info *Fi, + dbDriver *driver, int ncol, int *colctype, + const char **colname, int doatt, int nocat, int *n_noatt, + int *n_nocat) +{ + int i, n_exported; + int cat, last_cat, db_cat, type; + int line, findex, ipart; + + struct line_pnts *Points; + struct line_cats *Cats; + struct ilist *line_list, *lcats; + + struct Cat_index *ci; + int cat_index, n_cats; + + dbString dbstring; + char buf[SQL_BUFFER_SIZE]; + dbCursor cursor; + int more; + int key_col_index; + + OGRGeometryH Ogr_geometry; + OGRFeatureH Ogr_feature; + OGRwkbGeometryType wkbtype, wkbtype_part; + + Points = Vect_new_line_struct(); + Cats = Vect_new_cats_struct(); + line_list = Vect_new_list(); + lcats = Vect_new_list(); + + n_exported = 0; + + /* check if category index is available for given field */ + findex = Vect_cidx_get_field_index(In, field); + if (findex == -1) { + G_fatal_error(_("Unable to export multi-features. No category index " + "for layer %d."), + field); + } + + ci = &(In->plus.cidx[findex]); + n_cats = ci->n_cats; + + if (donocat) + G_message(_("Exporting features with category...")); + + /* determine type */ + type = -1; /* unknown -> GeometryCollection */ + if (Vect_cidx_get_num_types_by_index(In, findex) == 1) + Vect_cidx_get_type_count_by_index(In, findex, 0, &type, NULL); + if (force_poly) + wkbtype_part = wkbPolygon; + else + wkbtype_part = get_wkbtype(type, otype); + wkbtype = get_multi_wkbtype(wkbtype_part); + + key_col_index = -1; + more = 1; + if (doatt) { + /* select attributes ordered by category value */ + db_init_string(&dbstring); + sprintf(buf, "SELECT * FROM %s ORDER BY %s ASC", Fi->table, Fi->key); + G_debug(2, "SQL: %s", buf); + db_set_string(&dbstring, buf); + if (db_open_select_cursor(driver, &dbstring, &cursor, DB_SEQUENTIAL) != + DB_OK) { + G_fatal_error(_("Cannot select attributes sorted by %s"), Fi->key); + } + + if (db_fetch(&cursor, DB_NEXT, &more) != DB_OK) + G_fatal_error(_("Unable to fetch data from table")); + + /* get index of key column */ + key_col_index = -1; + for (i = 0; i < ncol; i++) { + if (strcmp(Fi->key, colname[i]) == 0) { + key_col_index = i; + break; + } + } + } + + last_cat = -1; + db_cat = -1; + cat_index = 0; + while (cat_index < n_cats) { + + G_percent(cat_index, n_cats, 5); + + cat = ci->cat[cat_index][0]; + /* make sure the cidx is ordered by cat */ + if (cat < last_cat) + G_fatal_error(_("Category index is not sorted ascending by cat!")); + last_cat = cat; + + /* collect all features with current cat */ + Vect_reset_list(line_list); + while (cat_index < n_cats && ci->cat[cat_index][0] == cat) { + if (ci->cat[cat_index][1] & otype) { + Vect_list_append(line_list, ci->cat[cat_index][2]); + } + cat_index++; + } + + /* create multi-feature */ + Ogr_geometry = OGR_G_CreateGeometry(wkbtype); + + /* build simple features geometry, go through all parts */ + for (ipart = 0; ipart < line_list->n_values; ipart++) { + line = line_list->value[ipart]; + G_debug(3, "cat=%d, line=%d -> part=%d", cat, line, ipart); + + /* read line */ + type = Vect_read_line(In, Points, Cats, line); + + /* check for category consistency */ + Vect_field_cat_get(Cats, field, lcats); + if (!Vect_val_in_list(lcats, cat)) + G_fatal_error(_("Unable to create multi-feature. " + "Category %d not found in line %d, field %d"), + cat, line, field); + + /* add part */ + add_part(Ogr_geometry, wkbtype_part, type == GV_LINE && force_poly, + Points); + } + + if (!OGR_G_IsEmpty(Ogr_geometry)) { + /* write multi-feature */ + Ogr_feature = OGR_F_Create(Ogr_featuredefn); + OGR_F_SetGeometry(Ogr_feature, Ogr_geometry); + /* get attributes */ + mk_att_fast(cat, Fi, ncol, colctype, colname, doatt, nocat, + Ogr_feature, n_noatt, &cursor, &more, &db_cat, + key_col_index); + if (OGR_L_CreateFeature(Ogr_layer, Ogr_feature) != OGRERR_NONE) { + G_fatal_error(_("Failed to create OGR feature")); + } + else + n_exported++; + + OGR_F_Destroy(Ogr_feature); + } + else { + /* skip empty features */ + G_debug(3, "multi-feature is empty -> skipped"); + } + + OGR_G_DestroyGeometry(Ogr_geometry); + } + + if (donocat) + G_message(_("Exporting features without category...")); + + /* check lines without category, if -c flag is given write them as + * one multi-feature */ + Ogr_geometry = OGR_G_CreateGeometry(wkbtype); + + if (doatt) { + db_close_cursor(&cursor); + if (donocat) { + cat = -1; + if (db_open_select_cursor(driver, &dbstring, &cursor, + DB_SEQUENTIAL) != DB_OK) { + G_fatal_error(_("Cannot select attributes for cat = %d"), cat); + } + if (db_fetch(&cursor, DB_NEXT, &more) != DB_OK) + G_fatal_error(_("Unable to fetch data from table")); + } + } + + /* this loop is needed to count features without category in the layer + * to be exported */ + Vect_rewind(In); + while (TRUE) { + type = Vect_read_next_line(In, Points, Cats); + if (type < 0) + break; + + Vect_cat_get(Cats, field, &cat); + if (cat >= 0) + continue; /* skip features with category */ + if (cat < 0 && !donocat) { + (*n_nocat)++; + continue; /* skip lines without category, do not export + * not labeled */ + } + + (*n_nocat)++; + + /* add part */ + add_part(Ogr_geometry, wkbtype_part, type == GV_LINE && force_poly, + Points); + } + + if (!OGR_G_IsEmpty(Ogr_geometry)) { + /* write multi-feature */ + Ogr_feature = OGR_F_Create(Ogr_featuredefn); + OGR_F_SetGeometry(Ogr_feature, Ogr_geometry); + + /* no attributes for features without category */ + cat = -1; + db_cat = -2; + mk_att_fast(cat, Fi, ncol, colctype, colname, doatt, nocat, Ogr_feature, + n_noatt, &cursor, &more, &db_cat, key_col_index); + if (OGR_L_CreateFeature(Ogr_layer, Ogr_feature) != OGRERR_NONE) { + G_fatal_error(_("Failed to create OGR feature")); + } + else + n_exported++; + + OGR_F_Destroy(Ogr_feature); + } + else { + /* skip empty features */ + G_debug(3, "multi-feature is empty -> skipped"); + } + + OGR_G_DestroyGeometry(Ogr_geometry); + + if (donocat && doatt) + db_close_cursor(&cursor); + + Vect_destroy_line_struct(Points); + Vect_destroy_cats_struct(Cats); + Vect_destroy_list(line_list); + Vect_destroy_list(lcats); + + return n_exported; +} + +/* build polygon for closed line */ +void line_to_polygon(OGRGeometryH Ogr_geometry, const struct line_pnts *Points) +{ + int j; + OGRGeometryH ring; + + ring = OGR_G_CreateGeometry(wkbLinearRing); + + /* create a ring */ + for (j = 0; j < Points->n_points; j++) { + OGR_G_AddPoint(ring, Points->x[j], Points->y[j], Points->z[j]); + } + + /* close ring */ + if (Points->x[Points->n_points - 1] != Points->x[0] || + Points->y[Points->n_points - 1] != Points->y[0] || + Points->z[Points->n_points - 1] != Points->z[0]) { + OGR_G_AddPoint(ring, Points->x[0], Points->y[0], Points->z[0]); + } + + OGR_G_AddGeometryDirectly(Ogr_geometry, ring); +} + +void add_part(OGRGeometryH Ogr_geometry, OGRwkbGeometryType wkbtype_part, + int force_poly, struct line_pnts *Points) +{ + int j; + OGRGeometryH Ogr_geometry_part; + + Ogr_geometry_part = OGR_G_CreateGeometry(wkbtype_part); + if (force_poly) { + line_to_polygon(Ogr_geometry_part, Points); + } + else { + if (OGR_G_GetGeometryType(Ogr_geometry_part) == wkbPoint) { + /* GV_POINTS -> wkbPoint */ + OGR_G_AddPoint(Ogr_geometry_part, Points->x[0], Points->y[0], + Points->z[0]); + } + else { /* GV_LINES -> wkbLinestring */ + for (j = 0; j < Points->n_points; j++) { + OGR_G_AddPoint(Ogr_geometry_part, Points->x[j], Points->y[j], + Points->z[j]); + } + } + } + OGR_G_AddGeometryDirectly(Ogr_geometry, Ogr_geometry_part); +} + +static OGRGeometryH build_geometry(struct Map_info *In, + struct line_pnts *Points, int type, + int otype, int force_poly) +{ + OGRGeometryH Ogr_geometry; + + /* build simple features geometry */ + if ((type == GV_LINE && force_poly) || type == GV_FACE) { + /* lines to polygons + faces to 2.5D polygons */ + Ogr_geometry = OGR_G_CreateGeometry(wkbPolygon); + line_to_polygon(Ogr_geometry, Points); + } + else { + Ogr_geometry = OGR_G_CreateGeometry(get_wkbtype(type, otype)); + if (OGR_G_GetGeometryType(Ogr_geometry) == wkbPoint) { + /* GV_POINTS -> wkbPoint */ + if (Vect_is_3d(In)) + OGR_G_AddPoint(Ogr_geometry, Points->x[0], Points->y[0], + Points->z[0]); + else + OGR_G_AddPoint_2D(Ogr_geometry, Points->x[0], Points->y[0]); + } + else { + /* GV_LINES -> wkbLinestring */ + int j; + for (j = 0; j < Points->n_points; j++) { + if (Vect_is_3d(In)) + OGR_G_AddPoint(Ogr_geometry, Points->x[j], Points->y[j], + Points->z[j]); + else + OGR_G_AddPoint_2D(Ogr_geometry, Points->x[j], Points->y[j]); + } + } + } + + return Ogr_geometry; +} diff --git a/vector/v.out.ogr/local_proto.h b/vector/v.out.ogr/local_proto.h index a0406a350a1..07921ece8f3 100644 --- a/vector/v.out.ogr/local_proto.h +++ b/vector/v.out.ogr/local_proto.h @@ -12,7 +12,7 @@ struct Options { struct Option *input, *dsn, *layer, *type, *format, *field, *dsco, *lco, - *otype; + *otype, *method; }; struct Flags { @@ -23,10 +23,14 @@ struct Flags { /* args.c */ void parse_args(int, char **, struct Options *, struct Flags *); -/* attributes.c */ +/* attrb.c */ int mk_att(int, struct field_info *, dbDriver *, int, int *, const char **, int, int, OGRFeatureH, int *); +/* attrb_fast.c */ +int mk_att_fast(int, struct field_info *, int, int *, const char **, int, int, + OGRFeatureH, int *, dbCursor *, int *, int *, int); + /* dsn.c */ char *get_datasource_name(const char *, int); @@ -46,7 +50,18 @@ int export_lines(struct Map_info *, int, int, int, int, int, OGRFeatureDefnH, OGRLayerH, struct field_info *, dbDriver *, int, int *, const char **, int, int, int *, int *); +/* export_lines_fast.c */ +int export_lines_fast(struct Map_info *, int, int, int, int, int, + OGRFeatureDefnH, OGRLayerH, struct field_info *, + dbDriver *, int, int *, const char **, int, int, int *, + int *); + /* export_areas.c */ int export_areas(struct Map_info *, int, int, int, OGRFeatureDefnH, OGRLayerH, struct field_info *, dbDriver *, int, int *, const char **, int, int, int *, int *, int); + +/* export_areas_fast.c */ +int export_areas_fast(struct Map_info *, int, int, int, OGRFeatureDefnH, + OGRLayerH, struct field_info *, dbDriver *, int, int *, + const char **, int, int, int *, int *, int); diff --git a/vector/v.out.ogr/main.c b/vector/v.out.ogr/main.c index 788d27a3665..b0c2b34419d 100644 --- a/vector/v.out.ogr/main.c +++ b/vector/v.out.ogr/main.c @@ -820,11 +820,20 @@ int main(int argc, char *argv[]) Vect_get_num_primitives(&In, otype)), Vect_get_num_primitives(&In, otype)); - n_feat += export_lines( - &In, field, otype, flags.multi->answer ? TRUE : FALSE, donocat, - ftype == GV_BOUNDARY ? TRUE : FALSE, Ogr_featuredefn, Ogr_layer, Fi, - Driver, ncol, colctype, colname, doatt, - flags.nocat->answer ? TRUE : FALSE, &n_noatt, &n_nocat); + if (strcmp(options.method->answer, "slow") == 0) { + n_feat += export_lines( + &In, field, otype, flags.multi->answer ? TRUE : FALSE, donocat, + ftype == GV_BOUNDARY ? TRUE : FALSE, Ogr_featuredefn, Ogr_layer, + Fi, Driver, ncol, colctype, colname, doatt, + flags.nocat->answer ? TRUE : FALSE, &n_noatt, &n_nocat); + } + else { + n_feat += export_lines_fast( + &In, field, otype, flags.multi->answer ? TRUE : FALSE, donocat, + ftype == GV_BOUNDARY ? TRUE : FALSE, Ogr_featuredefn, Ogr_layer, + Fi, Driver, ncol, colctype, colname, doatt, + flags.nocat->answer ? TRUE : FALSE, &n_noatt, &n_nocat); + } } /* Areas (run always to count features of different type) */ @@ -834,11 +843,20 @@ int main(int argc, char *argv[]) Vect_get_num_areas(&In)), Vect_get_num_areas(&In)); - n_feat += export_areas(&In, field, flags.multi->answer ? TRUE : FALSE, - donocat, Ogr_featuredefn, Ogr_layer, Fi, Driver, - ncol, colctype, colname, doatt, - flags.nocat->answer ? TRUE : FALSE, &n_noatt, - &n_nocat, outer_ring_ccw); + if (strcmp(options.method->answer, "slow") == 0) { + n_feat += export_areas( + &In, field, flags.multi->answer ? TRUE : FALSE, donocat, + Ogr_featuredefn, Ogr_layer, Fi, Driver, ncol, colctype, colname, + doatt, flags.nocat->answer ? TRUE : FALSE, &n_noatt, &n_nocat, + outer_ring_ccw); + } + else { + n_feat += export_areas_fast( + &In, field, flags.multi->answer ? TRUE : FALSE, donocat, + Ogr_featuredefn, Ogr_layer, Fi, Driver, ncol, colctype, colname, + doatt, flags.nocat->answer ? TRUE : FALSE, &n_noatt, &n_nocat, + outer_ring_ccw); + } } /*