int main(int argc, char *argv[]) { char *p; int i, j, k; int method, half, use_catno; const char *mapset; struct GModule *module; struct Option *point_opt, /* point vector */ *area_opt, /* area vector */ *point_type_opt, /* point type */ *point_field_opt, /* point layer */ *area_field_opt, /* area layer */ *method_opt, /* stats method */ *point_column_opt, /* point column for stats */ *count_column_opt, /* area column for point count */ *stats_column_opt, /* area column for stats result */ *fs_opt; /* field separator for printed output */ struct Flag *print_flag; char *fs; struct Map_info PIn, AIn; int point_type, point_field, area_field; struct line_pnts *Points; struct line_cats *ACats, *PCats; AREA_CAT *Area_cat; int pline, ptype, count; int area, nareas, nacats, nacatsalloc; int ctype, nrec; struct field_info *PFi, *AFi; dbString stmt; dbDriver *Pdriver, *Adriver; char buf[2000]; int update_ok, update_err; struct boxlist *List; struct bound_box box; dbCatValArray cvarr; dbColumn *column; struct pvalcat { double dval; int catno; } *pvalcats; int npvalcats, npvalcatsalloc; stat_func *statsvalue = NULL; double result; column = NULL; G_gisinit(argv[0]); module = G_define_module(); G_add_keyword(_("vector")); G_add_keyword(_("attribute table")); G_add_keyword(_("database")); G_add_keyword(_("univariate statistics")); G_add_keyword(_("zonal statistics")); module->description = _("Count points in areas, calculate statistics from point attributes."); point_opt = G_define_standard_option(G_OPT_V_INPUT); point_opt->key = "points"; point_opt->description = _("Name of existing vector map with points"); /* point_opt->guisection = _("Required"); */ area_opt = G_define_standard_option(G_OPT_V_INPUT); area_opt->key = "areas"; area_opt->description = _("Name of existing vector map with areas"); /* area_opt->guisection = _("Required"); */ point_type_opt = G_define_standard_option(G_OPT_V_TYPE); point_type_opt->key = "type"; point_type_opt->options = "point,centroid"; point_type_opt->answer = "point"; point_type_opt->label = _("Feature type"); point_type_opt->required = NO; point_field_opt = G_define_standard_option(G_OPT_V_FIELD); point_field_opt->key = "player"; point_field_opt->label = _("Layer number for points map"); area_field_opt = G_define_standard_option(G_OPT_V_FIELD); area_field_opt->key = "alayer"; area_field_opt->label = _("Layer number for area map"); method_opt = G_define_option(); method_opt->key = "method"; method_opt->type = TYPE_STRING; method_opt->required = NO; method_opt->multiple = NO; p = G_malloc(1024); for (i = 0; menu[i].name; i++) { if (i) strcat(p, ","); else *p = 0; strcat(p, menu[i].name); } method_opt->options = p; method_opt->description = _("Method for aggregate statistics"); point_column_opt = G_define_standard_option(G_OPT_DB_COLUMN); point_column_opt->key = "pcolumn"; point_column_opt->required = NO; point_column_opt->multiple = NO; point_column_opt->label = _("Column name of points map to use for statistics"); point_column_opt->description = _("Column of points map must be numeric"); count_column_opt = G_define_option(); count_column_opt->key = "ccolumn"; count_column_opt->type = TYPE_STRING; count_column_opt->required = NO; count_column_opt->multiple = NO; count_column_opt->label = _("Column name to upload points count"); count_column_opt->description = _("Column to hold points count, must be of type integer, will be created if not existing"); stats_column_opt = G_define_option(); stats_column_opt->key = "scolumn"; stats_column_opt->type = TYPE_STRING; stats_column_opt->required = NO; stats_column_opt->multiple = NO; stats_column_opt->label = _("Column name to upload statistics"); stats_column_opt->description = _("Column to hold statistics, must be of type double, will be created if not existing"); fs_opt = G_define_standard_option(G_OPT_F_SEP); print_flag = G_define_flag(); print_flag->key = 'p'; print_flag->label = _("Print output to stdout, do not update attribute table"); print_flag->description = _("First column is always area category"); if (G_parser(argc, argv)) exit(EXIT_FAILURE); point_type = Vect_option_to_types(point_type_opt); point_field = atoi(point_field_opt->answer); area_field = atoi(area_field_opt->answer); if (print_flag->answer) /* get field separator */ fs = G_option_to_separator(fs_opt); else fs = NULL; /* check for stats */ if (method_opt->answer) { if (!point_column_opt->answer) { G_fatal_error("Method but no point column selected"); } if (!print_flag->answer && !stats_column_opt->answer) G_fatal_error("Name for stats column is missing"); } if (point_column_opt->answer) { if (!method_opt->answer) G_fatal_error("No method for statistics selected"); if (!print_flag->answer && !stats_column_opt->answer) G_fatal_error("Name for stats column is missing"); } /* Open points vector */ if ((mapset = G_find_vector2(point_opt->answer, "")) == NULL) G_fatal_error(_("Vector map <%s> not found"), point_opt->answer); Vect_set_open_level(2); if (Vect_open_old(&PIn, point_opt->answer, mapset) < 0) G_fatal_error(_("Unable to open vector map <%s>"), point_opt->answer); /* Open areas vector */ if ((mapset = G_find_vector2(area_opt->answer, "")) == NULL) G_fatal_error(_("Vector map <%s> not found"), area_opt->answer); if (!print_flag->answer && strcmp(mapset, G_mapset()) != 0) G_fatal_error(_("Vector map <%s> is not in user mapset and cannot be updated"), area_opt->answer); Vect_set_open_level(2); if (Vect_open_old(&AIn, area_opt->answer, mapset) < 0) G_fatal_error(_("Unable to open vector map <%s>"), area_opt->answer); method = -1; use_catno = 0; half = 0; if (method_opt->answer) { /* get the method */ for (method = 0; (p = menu[method].name); method++) if ((strcmp(p, method_opt->answer) == 0)) break; if (!p) { G_warning(_("<%s=%s> unknown %s"), method_opt->key, method_opt->answer, method_opt->answer); G_usage(); exit(EXIT_FAILURE); } /* establish the statsvalue routine */ statsvalue = menu[method].method; /* category number of lowest/highest value */ if ((strcmp(menu[method].name, menu[5].name) == 0) || (strcmp(menu[method].name, menu[7].name) == 0)) use_catno = 1; G_debug(1, "method: %s, use cat value: %s", menu[method].name, (use_catno == 1 ? "yes" : "no")); } /* Open database driver */ db_init_string(&stmt); Adriver = NULL; if (!print_flag->answer) { AFi = Vect_get_field(&AIn, area_field); if (AFi == NULL) G_fatal_error(_("Database connection not defined for layer %d"), area_field); Adriver = db_start_driver_open_database(AFi->driver, AFi->database); if (Adriver == NULL) G_fatal_error(_("Unable to open database <%s> with driver <%s>"), AFi->database, AFi->driver); if (!count_column_opt->answer) G_fatal_error(_("ccolumn is required to upload point counts")); /* check if count column exists */ G_debug(1, "check if count column exists"); db_get_column(Adriver, AFi->table, count_column_opt->answer, &column); if (column) { /* check count column type */ if (db_column_Ctype(Adriver, AFi->table, count_column_opt->answer) != DB_C_TYPE_INT) G_fatal_error(_("ccolumn must be of type integer")); db_free_column(column); column = NULL; } else { /* create count column */ /* db_add_column() exists but is not implemented, * see lib/db/stubs/add_col.c */ sprintf(buf, "alter table %s add column %s integer", AFi->table, count_column_opt->answer); db_set_string(&stmt, buf); if (db_execute_immediate(Adriver, &stmt) != DB_OK) G_fatal_error(_("Unable to add column <%s>"), count_column_opt->answer); } if (method_opt->answer) { if (!stats_column_opt->answer) G_fatal_error(_("scolumn is required to upload point stats")); /* check if stats column exists */ G_debug(1, "check if stats column exists"); db_get_column(Adriver, AFi->table, stats_column_opt->answer, &column); if (column) { /* check stats column type */ if (db_column_Ctype (Adriver, AFi->table, stats_column_opt->answer) != DB_C_TYPE_DOUBLE) G_fatal_error(_("scolumn must be of type double")); db_free_column(column); column = NULL; } else { /* create stats column */ /* db_add_column() exists but is not implemented, * see lib/db/stubs/add_col.c */ sprintf(buf, "alter table %s add column %s double", AFi->table, stats_column_opt->answer); db_set_string(&stmt, buf); if (db_execute_immediate(Adriver, &stmt) != DB_OK) G_fatal_error(_("Unable to add column <%s>"), stats_column_opt->answer); } } } else AFi = NULL; Pdriver = NULL; if (method_opt->answer) { G_verbose_message(_("collecting attributes from points vector...")); PFi = Vect_get_field(&PIn, point_field); if (PFi == NULL) G_fatal_error(_("Database connection not defined for layer %d"), point_field); Pdriver = db_start_driver_open_database(PFi->driver, PFi->database); if (Pdriver == NULL) G_fatal_error(_("Unable to open database <%s> with driver <%s>"), PFi->database, PFi->driver); /* check if point column exists */ db_get_column(Pdriver, PFi->table, point_column_opt->answer, &column); if (column) { db_free_column(column); column = NULL; } else { G_fatal_error(_("Column <%s> not found in table <%s>"), point_column_opt->answer, PFi->table); } /* Check column type */ ctype = db_column_Ctype(Pdriver, PFi->table, point_column_opt->answer); if (ctype == DB_C_TYPE_INT) half = menu[method].half; else if (ctype == DB_C_TYPE_DOUBLE) half = 0; else G_fatal_error(_("column for points vector must be numeric")); db_CatValArray_init(&cvarr); nrec = db_select_CatValArray(Pdriver, PFi->table, PFi->key, point_column_opt->answer, NULL, &cvarr); G_debug(1, "selected values = %d", nrec); db_close_database_shutdown_driver(Pdriver); } Points = Vect_new_line_struct(); ACats = Vect_new_cats_struct(); PCats = Vect_new_cats_struct(); List = Vect_new_boxlist(0); /* Allocate space ( may be more than needed (duplicate cats and elements without cats) ) */ if ((nareas = Vect_get_num_areas(&AIn)) <= 0) G_fatal_error("No areas in area input vector"); nacatsalloc = nareas; Area_cat = (AREA_CAT *) G_calloc(nacatsalloc, sizeof(AREA_CAT)); /* Read all cats from 'area' */ nacats = 0; for (area = 1; area <= nareas; area++) { Vect_get_area_cats(&AIn, area, ACats); if (ACats->n_cats <= 0) continue; for (i = 0; i < ACats->n_cats; i++) { if (ACats->field[i] == area_field) { Area_cat[nacats].area_cat = ACats->cat[i]; Area_cat[nacats].count = 0; Area_cat[nacats].nvalues = 0; Area_cat[nacats].nalloc = 0; nacats++; if (nacats >= nacatsalloc) { nacatsalloc += 100; Area_cat = (AREA_CAT *) G_realloc(Area_cat, nacatsalloc * sizeof(AREA_CAT)); } } } } G_debug(1, "%d cats loaded from vector (including duplicates)", nacats); /* Sort by category */ qsort((void *)Area_cat, nacats, sizeof(AREA_CAT), cmp_area); /* remove duplicate categories */ for (i = 1; i < nacats; i++) { if (Area_cat[i].area_cat == Area_cat[i - 1].area_cat) { for (j = i; j < nacats - 1; j++) { Area_cat[j].area_cat = Area_cat[j + 1].area_cat; } nacats--; } } G_debug(1, "%d cats loaded from vector (unique)", nacats); /* Go through all areas in area vector and find points in points vector * falling into the area */ npvalcatsalloc = 10; npvalcats = 0; pvalcats = (struct pvalcat *)G_calloc(npvalcatsalloc, sizeof(struct pvalcat)); G_message(_("Selecting points for each area...")); count = 0; for (area = 1; area <= nareas; area++) { dbCatVal *catval; G_debug(3, "area = %d", area); G_percent(area, nareas, 2); Vect_get_area_cats(&AIn, area, ACats); if (ACats->n_cats <= 0) continue; /* select points by box */ Vect_get_area_box(&AIn, area, &box); box.T = PORT_DOUBLE_MAX; box.B = -PORT_DOUBLE_MAX; Vect_select_lines_by_box(&PIn, &box, point_type, List); G_debug(4, "%d points selected by box", List->n_values); /* For each point in box check if it is in the area */ for (i = 0; i < List->n_values; i++) { pline = List->id[i]; G_debug(4, "%d: point %d", i, pline); ptype = Vect_read_line(&PIn, Points, PCats, pline); if (!(ptype & point_type)) continue; /* point in area */ if (Vect_point_in_area(Points->x[0], Points->y[0], &AIn, area, &box)) { AREA_CAT *area_info, search_ai; int tmp_cat; /* stats on point column */ if (method_opt->answer) { npvalcats = 0; tmp_cat = -1; for (j = 0; j < PCats->n_cats; j++) { if (PCats->field[j] == point_field) { if (tmp_cat >= 0) G_debug(3, "More cats found in point layer (point=%d)", pline); tmp_cat = PCats->cat[j]; /* find cat in array */ db_CatValArray_get_value(&cvarr, tmp_cat, &catval); if (catval) { pvalcats[npvalcats].catno = tmp_cat; switch (cvarr.ctype) { case DB_C_TYPE_INT: pvalcats[npvalcats].dval = catval->val.i; npvalcats++; break; case DB_C_TYPE_DOUBLE: pvalcats[npvalcats].dval = catval->val.d; npvalcats++; break; } if (npvalcats >= npvalcatsalloc) { npvalcatsalloc += 10; pvalcats = (struct pvalcat *)G_realloc(pvalcats, npvalcatsalloc * sizeof (struct pvalcat)); } } } } } /* update count for all area cats of given field */ search_ai.area_cat = -1; for (j = 0; j < ACats->n_cats; j++) { if (ACats->field[j] == area_field) { if (search_ai.area_cat >= 0) G_debug(3, "More cats found in area layer (area=%d)", area); search_ai.area_cat = ACats->cat[j]; /* find cat in array */ area_info = (AREA_CAT *) bsearch((void *)&search_ai, Area_cat, nacats, sizeof(AREA_CAT), cmp_area); if (area_info->area_cat != search_ai.area_cat) G_fatal_error(_("could not find area category %d"), search_ai.area_cat); /* each point is counted once, also if it has * more than one category or no category * OK? */ area_info->count++; if (method_opt->answer) { /* ensure enough space */ if (area_info->nvalues + npvalcats >= area_info->nalloc) { if (area_info->nalloc == 0) { area_info->nalloc = npvalcats + 10; area_info->values = (double *)G_calloc(area_info->nalloc, sizeof(double)); area_info->cats = (int *)G_calloc(area_info->nalloc, sizeof(int)); } else area_info->nalloc += area_info->nvalues + npvalcats + 10; area_info->values = (double *)G_realloc(area_info->values, area_info->nalloc * sizeof(double)); area_info->cats = (int *)G_realloc(area_info->cats, area_info->nalloc * sizeof(int)); } for (k = 0; k < npvalcats; k++) { area_info->cats[area_info->nvalues] = pvalcats[k].catno; area_info->values[area_info->nvalues] = pvalcats[k].dval; area_info->nvalues++; } } } } count++; } } /* next point in box */ } /* next area */ G_debug(1, "count = %d", count); /* release catval array */ if (method_opt->answer) db_CatValArray_free(&cvarr); Vect_close(&PIn); /* Update table or print to stdout */ if (print_flag->answer) { /* print header */ fprintf(stdout, "area_cat%scount", fs); if (method_opt->answer) fprintf(stdout, "%s%s", fs, menu[method].name); fprintf(stdout, "\n"); } else { G_message("Updating attributes for area vector..."); update_err = update_ok = 0; } if (Adriver) db_begin_transaction(Adriver); for (i = 0; i < nacats; i++) { if (!print_flag->answer) G_percent(i, nacats, 2); result = 0; if (Area_cat[i].count > 0 && method_opt->answer) { /* get stats */ statsvalue(&result, Area_cat[i].values, Area_cat[i].nvalues, NULL); if (half) result += 0.5; else if (use_catno) result = Area_cat[i].cats[(int)result]; } if (print_flag->answer) { fprintf(stdout, "%d%s%d", Area_cat[i].area_cat, fs, Area_cat[i].count); if (method_opt->answer) { if (Area_cat[i].count > 0) fprintf(stdout, "%s%.15g", fs, result); else fprintf(stdout, "%snull", fs); } fprintf(stdout, "\n"); } else { sprintf(buf, "update %s set %s = %d", AFi->table, count_column_opt->answer, Area_cat[i].count); db_set_string(&stmt, buf); if (method_opt->answer) { if (Area_cat[i].count > 0) sprintf(buf, " , %s = %.15g", stats_column_opt->answer, result); else sprintf(buf, " , %s = null", stats_column_opt->answer); db_append_string(&stmt, buf); } sprintf(buf, " where %s = %d", AFi->key, Area_cat[i].area_cat); db_append_string(&stmt, buf); G_debug(2, "SQL: %s", db_get_string(&stmt)); if (db_execute_immediate(Adriver, &stmt) == DB_OK) { update_ok++; } else { update_err++; } } } if (Adriver) db_commit_transaction(Adriver); if (!print_flag->answer) { G_percent(nacats, nacats, 2); db_close_database_shutdown_driver(Adriver); db_free_string(&stmt); G_message(_("%d records updated"), update_ok); if (update_err > 0) G_message(_("%d update errors"), update_err); Vect_set_db_updated(&AIn); } Vect_close(&AIn); G_done_msg(" "); exit(EXIT_SUCCESS); }
int check_thematic(const struct GParams *params, int vlines) { int i, type; struct Map_info Map; struct Option *map, *layer, *color, *size, *width, *marker; struct field_info *Fi; dbDriver *driver; dbColumn *column; if (vlines) { map = params->vlines; layer = params->vline_layer; color = params->vline_color_column; size = NULL; width = params->vline_width_column; marker = NULL; } else { map = params->vpoints; layer = params->vpoint_layer; color = params->vpoint_color_column; size = params->vpoint_size_column; width = params->vpoint_width_column; marker = params->vpoint_marker_column; } for (i = 0; map->answers[i]; i++) { if (1 > Vect_open_old(&Map, map->answers[i], "")) G_fatal_error(_("Unable to open vector map <%s>"), map->answers[i]); Fi = Vect_get_field2(&Map, layer->answers[i]); if (!Fi) G_fatal_error(_("Database connection not defined for layer %s"), layer->answers[i]); driver = db_start_driver_open_database(Fi->driver, Fi->database); if (!driver) G_fatal_error(_("Unable to open database <%s> by driver <%s>"), Fi->database, Fi->driver); if (color->answers && color->answers[i]) { db_get_column(driver, Fi->table, color->answers[i], &column); if (!column) G_fatal_error(_("Column <%s> in table <%s> not found"), color->answers[i], Fi->table); if (db_column_Ctype(driver, Fi->table, color->answers[i]) != DB_C_TYPE_STRING) G_fatal_error(_("Data type of color column must be character")); } if (size && size->answers && size->answers[i]) { db_get_column(driver, Fi->table, size->answers[i], &column); if (!column) G_fatal_error(_("Column <%s> in table <%s> not found"), size->answers[i], Fi->table); type = db_column_Ctype(driver, Fi->table, size->answers[i]); if (type != DB_C_TYPE_INT && type != DB_C_TYPE_DOUBLE) G_fatal_error(_("Data type of size column must be numeric")); } if (width->answers && width->answers[i]) { db_get_column(driver, Fi->table, width->answers[i], &column); if (!column) G_fatal_error(_("Column <%s> in table <%s> not found"), width->answers[i], Fi->table); type = db_column_Ctype(driver, Fi->table, width->answers[i]); if (type != DB_C_TYPE_INT && type != DB_C_TYPE_DOUBLE) G_fatal_error(_("Data type of width column must be numeric")); } if (marker && marker->answers && marker->answers[i]) { db_get_column(driver, Fi->table, marker->answers[i], &column); if (!column) G_fatal_error(_("Column <%s> in table <%s> not found"), marker->answers[i], Fi->table); type = db_column_Ctype(driver, Fi->table, marker->answers[i]); if (db_column_Ctype(driver, Fi->table, marker->answers[i]) != DB_C_TYPE_STRING) G_fatal_error(_("Data type of marker column must be character")); } } return Fi->number; }
/*! \brief Check vector map \param params parameters \param index answers array index \param vlines TRUE for lines otherwise points \param[out] field number \param[out] WITH_Z for 3D maps \return 0 on success otherwise 1 */ int check_map(const struct GParams *params, int index, int vlines, int *field, int *with_z) { int type; struct Map_info Map; const char *map, *layer, *color, *size, *width, *marker; struct field_info *Fi; dbDriver *driver; dbColumn *column; Fi = NULL; driver = NULL; if (vlines) { map = params->vlines->answers[index]; layer = params->vline_layer->answers[index]; color = params->vline_color_column->answers ? params->vline_color_column->answers[index] : NULL; size = NULL; width = params->vline_width_column->answers ? params->vline_width_column->answers[index] : NULL; marker = NULL; } else { map = params->vpoints->answers[index]; layer = params->vpoint_layer->answers[index]; color = params->vpoint_color_column->answers ? params->vpoint_color_column->answers[index] : NULL; size = params->vpoint_size_column->answers ? params->vpoint_size_column->answers[index] : NULL; width = params->vpoint_width_column->answers ? params->vpoint_width_column->answers[index] : NULL; marker = params->vpoint_marker_column->answers ? params->vpoint_marker_column->answers[index] : NULL; } if (!map) return 1; if (1 > Vect_open_old(&Map, map, "")) G_fatal_error(_("Unable to open vector map <%s>"), map); Vect_set_error_handler_io(&Map, NULL); if (with_z) *with_z = Vect_is_3d(&Map); *field = -1; Fi = Vect_get_field2(&Map, layer); if (Fi) { *field = Fi->number; driver = db_start_driver_open_database(Fi->driver, Fi->database); if (!driver) G_fatal_error(_("Unable to open database <%s> by driver <%s>"), Fi->database, Fi->driver); db_set_error_handler_driver(driver); if (color) { db_get_column(driver, Fi->table, color, &column); if (!column) G_fatal_error(_("Column <%s> in table <%s> not found"), color, Fi->table); if (db_column_Ctype(driver, Fi->table, color) != DB_C_TYPE_STRING) G_fatal_error(_("Data type of color column must be character")); } if (size) { db_get_column(driver, Fi->table, size, &column); if (!column) G_fatal_error(_("Column <%s> in table <%s> not found"), size, Fi->table); type = db_column_Ctype(driver, Fi->table, size); if (type != DB_C_TYPE_INT && type != DB_C_TYPE_DOUBLE) G_fatal_error(_("Data type of size column must be numeric")); } if (width) { db_get_column(driver, Fi->table, width, &column); if (!column) G_fatal_error(_("Column <%s> in table <%s> not found"), width, Fi->table); type = db_column_Ctype(driver, Fi->table, width); if (type != DB_C_TYPE_INT && type != DB_C_TYPE_DOUBLE) G_fatal_error(_("Data type of width column must be numeric")); } if (marker) { db_get_column(driver, Fi->table, marker, &column); if (!column) G_fatal_error(_("Column <%s> in table <%s> not found"), marker, Fi->table); type = db_column_Ctype(driver, Fi->table, marker); if (db_column_Ctype(driver, Fi->table, marker) != DB_C_TYPE_STRING) G_fatal_error(_("Data type of marker column must be character")); } db_close_database_shutdown_driver(driver); } Vect_close(&Map); return 0; }
int main(int argc, char *argv[]) { struct GModule *module; struct { struct Option *input, *output, *zshift, *height, *elevation, *hcolumn, *type, *field, *cats, *where, *interp, *scale, *null; } opt; struct { struct Flag *trace; } flag; struct Map_info In, Out; struct line_pnts *Points; struct line_cats *Cats; struct bound_box map_box; struct cat_list *cat_list; struct Cell_head window; int field; int only_type, cat; int fdrast, interp_method, trace; double objheight, objheight_default, voffset; double scale, null_val; struct field_info *Fi; dbDriver *driver = NULL; char *comment; module = G_define_module(); G_add_keyword(_("vector")); G_add_keyword(_("geometry")); G_add_keyword(_("sampling")); G_add_keyword(_("3D")); module->label = _("Extrudes flat vector features to 3D vector features with defined height."); module->description = _("Optionally the height can be derived from sampling of elevation raster map."); flag.trace = G_define_flag(); flag.trace->key = 't'; flag.trace->description = _("Trace elevation"); flag.trace->guisection = _("Elevation"); opt.input = G_define_standard_option(G_OPT_V_INPUT); opt.field = G_define_standard_option(G_OPT_V_FIELD_ALL); opt.field->guisection = _("Selection"); opt.cats = G_define_standard_option(G_OPT_V_CATS); opt.cats->guisection = _("Selection"); opt.where = G_define_standard_option(G_OPT_DB_WHERE); opt.where->guisection = _("Selection"); opt.type = G_define_standard_option(G_OPT_V_TYPE); opt.type->answer = "point,line,area"; opt.type->options = "point,line,area"; opt.type->guisection = _("Selection"); opt.output = G_define_standard_option(G_OPT_V_OUTPUT); opt.zshift = G_define_option(); opt.zshift->key = "zshift"; opt.zshift->description = _("Shifting value for z coordinates"); opt.zshift->type = TYPE_DOUBLE; opt.zshift->required = NO; opt.zshift->answer = "0"; opt.zshift->guisection = _("Height"); opt.height = G_define_option(); opt.height->key = "height"; opt.height->type = TYPE_DOUBLE; opt.height->required = NO; opt.height->multiple = NO; opt.height->description = _("Fixed height for 3D vector features"); opt.height->guisection = _("Height"); opt.hcolumn = G_define_standard_option(G_OPT_DB_COLUMN); opt.hcolumn->key = "height_column"; opt.hcolumn->multiple = NO; opt.hcolumn->description = _("Name of attribute column with feature height"); opt.hcolumn->guisection = _("Height"); /* raster sampling */ opt.elevation = G_define_standard_option(G_OPT_R_ELEV); opt.elevation->required = NO; opt.elevation->description = _("Elevation raster map for height extraction"); opt.elevation->guisection = _("Elevation"); opt.interp = G_define_standard_option(G_OPT_R_INTERP_TYPE); opt.interp->answer = "nearest"; opt.interp->guisection = _("Elevation"); opt.scale = G_define_option(); opt.scale->key = "scale"; opt.scale->type = TYPE_DOUBLE; opt.scale->description = _("Scale factor sampled raster values"); opt.scale->answer = "1.0"; opt.scale->guisection = _("Elevation"); opt.null = G_define_option(); opt.null->key = "null_value"; opt.null->type = TYPE_DOUBLE; opt.null->description = _("Height for sampled raster NULL values"); opt.null->guisection = _("Elevation"); G_gisinit(argv[0]); if (G_parser(argc, argv)) exit(EXIT_FAILURE); if (!opt.height->answer && !opt.hcolumn->answer) { G_fatal_error(_("One of '%s' or '%s' parameters must be set"), opt.height->key, opt.hcolumn->key); } sscanf(opt.zshift->answer, "%lf", &voffset); G_debug(1, "voffset = %f", voffset); if (opt.height->answer) sscanf(opt.height->answer, "%lf", &objheight); else objheight = 0.; G_debug(1, "objheight = %f", objheight); objheight_default = objheight; only_type = Vect_option_to_types(opt.type); /* sampling method */ interp_method = Rast_option_to_interp_type(opt.interp); /* used to scale sampled raster values */ scale = atof(opt.scale->answer); /* is null value defined */ if (opt.null->answer) null_val = atof(opt.null->answer); /* trace elevation */ trace = flag.trace->answer ? TRUE : FALSE; /* set input vector map name and mapset */ Vect_check_input_output_name(opt.input->answer, opt.output->answer, G_FATAL_EXIT); Points = Vect_new_line_struct(); Cats = Vect_new_cats_struct(); Vect_set_open_level(2); /* topology required for input */ /* opening input vector map */ if (Vect_open_old2(&In, opt.input->answer, "", opt.field->answer) < 0) G_fatal_error(_("Unable to open vector map <%s>"), opt.input->answer); Vect_set_error_handler_io(&In, &Out); /* creating output vector map */ if (Vect_open_new(&Out, opt.output->answer, WITH_Z) < 0) G_fatal_error(_("Unable to create vector map <%s>"), opt.output->answer); field = Vect_get_field_number(&In, opt.field->answer); if ((opt.hcolumn->answer || opt.cats->answer || opt.where->answer) && field == -1) { G_warning(_("Invalid layer number (%d). " "Parameter '%s', '%s' or '%s' specified, assuming layer '1'."), field, opt.hcolumn->key, opt.cats->key, opt.where->key); field = 1; } /* set constraint for cats or where */ cat_list = NULL; if (field > 0) cat_list = Vect_cats_set_constraint(&In, field, opt.where->answer, opt.cats->answer); Vect_hist_copy(&In, &Out); Vect_hist_command(&Out); /* opening database connection, if required */ if (opt.hcolumn->answer) { int ctype; dbColumn *column; if ((Fi = Vect_get_field(&In, field)) == NULL) G_fatal_error(_("Database connection not defined for layer %d"), field); if ((driver = db_start_driver_open_database(Fi->driver, Fi->database)) == NULL) G_fatal_error(_("Unable to open database <%s> by driver <%s>"), Fi->database, Fi->driver); db_set_error_handler_driver(driver); if (db_get_column(driver, Fi->table, opt.hcolumn->answer, &column) != DB_OK) G_fatal_error(_("Column <%s> does not exist"), opt.hcolumn->answer); else db_free_column(column); ctype = db_column_Ctype(driver, Fi->table, opt.hcolumn->answer); if (ctype != DB_C_TYPE_INT && ctype != DB_C_TYPE_STRING && ctype != DB_C_TYPE_DOUBLE) { G_fatal_error(_("Column <%s>: invalid data type"), opt.hcolumn->answer); } } /* do we work with elevation raster? */ fdrast = -1; if (opt.elevation->answer) { /* raster setup */ G_get_window(&window); /* open the elev raster, and check for error condition */ fdrast = Rast_open_old(opt.elevation->answer, ""); } /* if area */ if (only_type & GV_AREA) { int area, nareas, centroid; nareas = Vect_get_num_areas(&In); G_debug(2, "n_areas = %d", nareas); if (nareas > 0) G_message(_("Extruding areas...")); for (area = 1; area <= nareas; area++) { G_debug(3, "area = %d", area); G_percent(area, nareas, 2); if (!Vect_area_alive(&In, area)) continue; centroid = Vect_get_area_centroid(&In, area); if (!centroid) { G_warning(_("Skipping area %d without centroid"), area); continue; } Vect_read_line(&In, NULL, Cats, centroid); if (field > 0 && !Vect_cats_in_constraint(Cats, field, cat_list)) continue; /* height attribute */ if (opt.hcolumn->answer) { cat = Vect_get_area_cat(&In, area, field); if (cat == -1) { G_warning(_("No category defined for area %d. Using default fixed height %f."), area, objheight_default); objheight = objheight_default; } if (get_height(Fi, opt.hcolumn->answer, driver, cat, &objheight) != 0) { G_warning(_("Unable to fetch height from DB for area %d. Using default fixed height %f."), area, objheight_default); objheight = objheight_default; } } /* if opt.hcolumn->answer */ Vect_get_area_points(&In, area, Points); G_debug(3, "area: %d height: %f", area, objheight); extrude(&In, &Out, Cats, Points, fdrast, trace, interp_method, scale, opt.null->answer ? TRUE : FALSE, null_val, objheight, voffset, &window, GV_AREA, centroid); } /* foreach area */ } if (only_type > 0) { int line, nlines; int type; G_debug(1, "other than areas"); /* loop through each line in the dataset */ nlines = Vect_get_num_lines(&In); G_message(_("Extruding features...")); for (line = 1; line <= nlines; line++) { /* progress feedback */ G_percent(line, nlines, 2); if (!Vect_line_alive(&In, line)) continue; /* read line */ type = Vect_read_line(&In, Points, Cats, line); if (!(type & only_type)) continue; if (field > 0 && !Vect_cats_in_constraint(Cats, field, cat_list)) continue; /* height attribute */ if (opt.hcolumn->answer) { cat = Vect_get_line_cat(&In, line, field); if (cat == -1) { G_warning(_("No category defined for feature %d. Using default fixed height %f."), line, objheight_default); objheight = objheight_default; } if (get_height(Fi, opt.hcolumn->answer, driver, cat, &objheight) != 0) { G_warning(_("Unable to fetch height from DB for line %d. Using default fixed height %f."), line, objheight_default); objheight = objheight_default; } } /* if opt.hcolumn->answer */ extrude(&In, &Out, Cats, Points, fdrast, trace, interp_method, scale, opt.null->answer ? TRUE : FALSE, null_val, objheight, voffset, &window, type, -1); } /* for each line */ } /* else if area */ if (driver) { db_close_database(driver); db_shutdown_driver(driver); } G_important_message(_("Copying attribute table...")); if (field < 0) Vect_copy_tables(&In, &Out, 0); else Vect_copy_table_by_cat_list(&In, &Out, field, field, NULL, GV_1TABLE, cat_list); Vect_build(&Out); /* header */ G_asprintf(&comment, "Generated by %s from vector map <%s>", G_program_name(), Vect_get_full_name(&In)); Vect_set_comment(&Out, comment); G_free(comment); Vect_get_map_box(&Out, &map_box); Vect_close(&In); Vect_close(&Out); Vect_destroy_line_struct(Points); Vect_destroy_cats_struct(Cats); G_done_msg("T: %f B: %f.", map_box.T, map_box.B); exit(EXIT_SUCCESS); }
int main(int argc, char *argv[]) { int i, j, k; int print_as_matrix; /* only for all */ int all; /* calculate from each to each within the threshold */ struct GModule *module; struct Option *from_opt, *to_opt, *from_type_opt, *to_type_opt, *from_field_opt, *to_field_opt; struct Option *out_opt, *max_opt, *min_opt, *table_opt; struct Option *upload_opt, *column_opt, *to_column_opt; struct Flag *print_flag, *all_flag; struct Map_info From, To, Out, *Outp; int from_type, to_type, from_field, to_field; double max, min; double *max_step; int n_max_steps, curr_step; struct line_pnts *FPoints, *TPoints; struct line_cats *FCats, *TCats; NEAR *Near, *near; int anear; /* allocated space, used only for all */ UPLOAD *Upload; /* zero terminated */ int ftype, fcat, tcat, count; int nfrom, nto, nfcats, fline, tline, tseg, tarea, area, isle, nisles; double tx, ty, tz, dist, talong, tmp_tx, tmp_ty, tmp_tz, tmp_dist, tmp_talong; struct field_info *Fi, *toFi; dbString stmt, dbstr; dbDriver *driver, *to_driver; int *catexist, ncatexist, *cex; char buf1[2000], buf2[2000]; int update_ok, update_err, update_exist, update_notexist, update_dupl, update_notfound; struct boxlist *List; struct bound_box box; dbCatValArray cvarr; dbColumn *column; all = 0; print_as_matrix = 0; column = NULL; G_gisinit(argv[0]); module = G_define_module(); G_add_keyword(_("vector")); G_add_keyword(_("database")); G_add_keyword(_("attribute table")); module->description = _("Finds the nearest element in vector map 'to' for elements in vector map 'from'."); from_opt = G_define_standard_option(G_OPT_V_INPUT); from_opt->key = "from"; from_opt->description = _("Name of existing vector map (from)"); from_opt->guisection = _("From"); from_field_opt = G_define_standard_option(G_OPT_V_FIELD); from_field_opt->key = "from_layer"; from_field_opt->label = _("Layer number or name (from)"); from_field_opt->guisection = _("From"); from_type_opt = G_define_standard_option(G_OPT_V_TYPE); from_type_opt->key = "from_type"; from_type_opt->options = "point,centroid"; from_type_opt->answer = "point"; from_type_opt->label = _("Feature type (from)"); from_type_opt->guisection = _("From"); to_opt = G_define_standard_option(G_OPT_V_INPUT); to_opt->key = "to"; to_opt->description = _("Name of existing vector map (to)"); to_opt->guisection = _("To"); to_field_opt = G_define_standard_option(G_OPT_V_FIELD); to_field_opt->key = "to_layer"; to_field_opt->label = _("Layer number or name (to)"); to_field_opt->guisection = _("To"); to_type_opt = G_define_standard_option(G_OPT_V_TYPE); to_type_opt->key = "to_type"; to_type_opt->options = "point,line,boundary,centroid,area"; to_type_opt->answer = "point,line,area"; to_type_opt->label = _("Feature type (to)"); to_type_opt->guisection = _("To"); out_opt = G_define_standard_option(G_OPT_V_OUTPUT); out_opt->key = "output"; out_opt->required = NO; out_opt->description = _("Name for output vector map containing lines " "connecting nearest elements"); max_opt = G_define_option(); max_opt->key = "dmax"; max_opt->type = TYPE_DOUBLE; max_opt->required = NO; max_opt->answer = "-1"; max_opt->description = _("Maximum distance or -1 for no limit"); min_opt = G_define_option(); min_opt->key = "dmin"; min_opt->type = TYPE_DOUBLE; min_opt->required = NO; min_opt->answer = "-1"; min_opt->description = _("Minimum distance or -1 for no limit"); upload_opt = G_define_option(); upload_opt->key = "upload"; upload_opt->type = TYPE_STRING; upload_opt->required = YES; upload_opt->multiple = YES; upload_opt->options = "cat,dist,to_x,to_y,to_along,to_angle,to_attr"; upload_opt->description = _("Values describing the relation between two nearest features"); upload_opt->descriptions = _("cat;category of the nearest feature;" "dist;minimum distance to nearest feature;" "to_x;x coordinate of the nearest point on 'to' feature;" "to_y;y coordinate of the nearest point on 'to' feature;" "to_along;distance between points/centroids in 'from' map and the linear feature's " "start point in 'to' map, along this linear feature;" "to_angle;angle between the linear feature in 'to' map and the positive x axis, at " "the location of point/centroid in 'from' map, counterclockwise, in radians, which " "is between -PI and PI inclusive;" "to_attr;attribute of nearest feature given by to_column option"); /* "from_x - x coordinate of the nearest point on 'from' feature;" */ /* "from_y - y coordinate of the nearest point on 'from' feature;" */ /* "from_along - distance to the nearest point on 'from' feature along linear feature;" */ column_opt = G_define_standard_option(G_OPT_DB_COLUMN); column_opt->required = YES; column_opt->multiple = YES; column_opt->description = _("Column name(s) where values specified by 'upload' option will be uploaded"); column_opt->guisection = _("From_map"); to_column_opt = G_define_standard_option(G_OPT_DB_COLUMN); to_column_opt->key = "to_column"; to_column_opt->description = _("Column name of nearest feature (used with upload=to_attr)"); to_column_opt->guisection = _("To"); table_opt = G_define_standard_option(G_OPT_DB_TABLE); table_opt->gisprompt = "new_dbtable,dbtable,dbtable"; table_opt->description = _("Name of table created for output when the distance to all flag is used"); print_flag = G_define_flag(); print_flag->key = 'p'; print_flag->label = _("Print output to stdout, don't update attribute table"); print_flag->description = _("First column is always category of 'from' feature called from_cat"); all_flag = G_define_flag(); all_flag->key = 'a'; all_flag->label = _("Calculate distances to all features within the threshold"); all_flag->description = _("The output is written to stdout but may be uploaded " "to a new table created by this module. " "From categories are may be multiple."); /* huh? */ /* GUI dependency */ from_opt->guidependency = G_store(from_field_opt->key); sprintf(buf1, "%s,%s", to_field_opt->key, to_column_opt->key); to_opt->guidependency = G_store(buf1); to_field_opt->guidependency = G_store(to_column_opt->key); if (G_parser(argc, argv)) exit(EXIT_FAILURE); from_type = Vect_option_to_types(from_type_opt); to_type = Vect_option_to_types(to_type_opt); from_field = atoi(from_field_opt->answer); max = atof(max_opt->answer); min = atof(min_opt->answer); if (all_flag->answer) all = 1; /* Read upload and column options */ /* count */ i = 0; while (upload_opt->answers[i]) i++; if (strcmp(from_opt->answer, to_opt->answer) == 0 && all && !table_opt->answer && i == 1) print_as_matrix = 1; /* alloc */ Upload = (UPLOAD *) G_calloc(i + 1, sizeof(UPLOAD)); /* read upload */ i = 0; while (upload_opt->answers[i]) { if (strcmp(upload_opt->answers[i], "cat") == 0) Upload[i].upload = CAT; else if (strcmp(upload_opt->answers[i], "from_x") == 0) Upload[i].upload = FROM_X; else if (strcmp(upload_opt->answers[i], "from_y") == 0) Upload[i].upload = FROM_Y; else if (strcmp(upload_opt->answers[i], "to_x") == 0) Upload[i].upload = TO_X; else if (strcmp(upload_opt->answers[i], "to_y") == 0) Upload[i].upload = TO_Y; else if (strcmp(upload_opt->answers[i], "from_along") == 0) Upload[i].upload = FROM_ALONG; else if (strcmp(upload_opt->answers[i], "to_along") == 0) Upload[i].upload = TO_ALONG; else if (strcmp(upload_opt->answers[i], "dist") == 0) Upload[i].upload = DIST; else if (strcmp(upload_opt->answers[i], "to_angle") == 0) Upload[i].upload = TO_ANGLE; else if (strcmp(upload_opt->answers[i], "to_attr") == 0) { if (!(to_column_opt->answer)) { G_fatal_error(_("to_column option missing")); } Upload[i].upload = TO_ATTR; } i++; } Upload[i].upload = END; /* read columns */ i = 0; while (column_opt->answers[i]) { if (Upload[i].upload == END) { G_warning(_("Too many column names")); break; } Upload[i].column = G_store(column_opt->answers[i]); i++; } if (Upload[i].upload != END) G_fatal_error(_("Not enough column names")); /* Open 'from' vector */ Vect_set_open_level(2); Vect_open_old(&From, from_opt->answer, G_mapset()); /* Open 'to' vector */ Vect_set_open_level(2); Vect_open_old2(&To, to_opt->answer, "", to_field_opt->answer); to_field = Vect_get_field_number(&To, to_field_opt->answer); /* Open output vector */ if (out_opt->answer) { Vect_open_new(&Out, out_opt->answer, WITHOUT_Z); Vect_hist_command(&Out); Outp = &Out; } else { Outp = NULL; } /* TODO: add maxdist = -1 to Vect_select_ !!! */ /* Calc maxdist */ n_max_steps = 1; if (max != 0) { struct bound_box fbox, tbox; double dx, dy, dz, tmp_max; int n_features = 0; Vect_get_map_box(&From, &fbox); Vect_get_map_box(&To, &tbox); Vect_box_extend(&fbox, &tbox); dx = fbox.E - fbox.W; dy = fbox.N - fbox.S; if (Vect_is_3d(&From)) dz = fbox.T - fbox.B; else dz = 0.0; tmp_max = sqrt(dx * dx + dy * dy + dz * dz); if (max < 0) max = tmp_max; /* how to determine a reasonable number of steps to increase the search box? */ /* with max > 0 but max <<< tmp_max, 2 steps are sufficient, first 0 then max * a reasonable number of steps also depends on the number of features in To * e.g. only one area in To, no need to step */ nto = Vect_get_num_lines(&To); for (tline = 1; tline <= nto; tline++) { /* TODO: Vect_get_line_type() */ n_features += ((to_type & To.plus.Line[tline]->type) != 0); } if (to_type & GV_AREA) { if (Vect_get_num_areas(&To) > n_features) n_features = Vect_get_num_areas(&To); } if (n_features == 0) G_fatal_error(_("No features of selected type in To vector <%s>"), to_opt->answer); n_max_steps = sqrt(n_features) * max / tmp_max; /* max 9 steps from testing */ if (n_max_steps > 9) n_max_steps = 9; if (n_max_steps < 2) n_max_steps = 2; if (n_max_steps > n_features) n_max_steps = n_features; G_debug(2, "max = %f", max); G_debug(2, "maximum reasonable search distance = %f", tmp_max); G_debug(2, "n_features = %d", n_features); G_debug(2, "n_max_steps = %d", n_max_steps); } if (min > max) G_fatal_error("dmin can not be larger than dmax"); if (n_max_steps > 1) { /* set up steps to increase search box */ max_step = G_malloc(n_max_steps * sizeof(double)); /* first step always 0 */ max_step[0] = 0; for (curr_step = 1; curr_step < n_max_steps - 1; curr_step++) { /* for 9 steps, this would be max / [128, 64, 32, 16, 8, 4, 2] */ max_step[curr_step] = max / (2 << (n_max_steps - 1 - curr_step)); } /* last step always max */ max_step[n_max_steps - 1] = max; } else { max_step = G_malloc(sizeof(double)); max_step[0] = max; } /* Open database driver */ db_init_string(&stmt); db_init_string(&dbstr); driver = NULL; if (!print_flag->answer) { if (!all) { Fi = Vect_get_field(&From, from_field); if (Fi == NULL) G_fatal_error(_("Database connection not defined for layer %d"), from_field); driver = db_start_driver_open_database(Fi->driver, Fi->database); if (driver == NULL) G_fatal_error(_("Unable to open database <%s> by driver <%s>"), Fi->database, Fi->driver); /* check if column exists */ i = 0; while (column_opt->answers[i]) { db_get_column(driver, Fi->table, column_opt->answers[i], &column); if (column) { db_free_column(column); column = NULL; } else { G_fatal_error(_("Column <%s> not found in table <%s>"), column_opt->answers[i], Fi->table); } i++; } } else { driver = db_start_driver_open_database(NULL, NULL); if (driver == NULL) G_fatal_error(_("Unable to open default database")); } } to_driver = NULL; if (to_column_opt->answer) { toFi = Vect_get_field(&To, to_field); if (toFi == NULL) G_fatal_error(_("Database connection not defined for layer %d"), to_field); to_driver = db_start_driver_open_database(toFi->driver, toFi->database); if (to_driver == NULL) G_fatal_error(_("Unable to open database <%s> by driver <%s>"), toFi->database, toFi->driver); /* check if to_column exists */ db_get_column(to_driver, toFi->table, to_column_opt->answer, &column); if (column) { db_free_column(column); column = NULL; } else { G_fatal_error(_("Column <%s> not found in table <%s>"), to_column_opt->answer, toFi->table); } /* Check column types */ if (!print_flag->answer && !all) { char *fcname = NULL; int fctype, tctype; i = 0; while (column_opt->answers[i]) { if (Upload[i].upload == TO_ATTR) { fcname = column_opt->answers[i]; break; } i++; } if (fcname) { fctype = db_column_Ctype(driver, Fi->table, fcname); tctype = db_column_Ctype(to_driver, toFi->table, to_column_opt->answer); if (((tctype == DB_C_TYPE_STRING || tctype == DB_C_TYPE_DATETIME) && (fctype == DB_C_TYPE_INT || fctype == DB_C_TYPE_DOUBLE)) || ((tctype == DB_C_TYPE_INT || tctype == DB_C_TYPE_DOUBLE) && (fctype == DB_C_TYPE_STRING || fctype == DB_C_TYPE_DATETIME)) ) { G_fatal_error(_("Incompatible column types")); } } } } FPoints = Vect_new_line_struct(); TPoints = Vect_new_line_struct(); FCats = Vect_new_cats_struct(); TCats = Vect_new_cats_struct(); List = Vect_new_boxlist(1); /* Allocate space ( may be more than needed (duplicate cats and elements without cats) ) */ nfrom = Vect_get_num_lines(&From); nto = Vect_get_num_lines(&To); if (all) { /* Attention with space for all, it can easily run out of memory */ anear = 2 * nfrom; Near = (NEAR *) G_calloc(anear, sizeof(NEAR)); } else { Near = (NEAR *) G_calloc(nfrom, sizeof(NEAR)); } /* Read all cats from 'from' */ if (!all) { nfcats = 0; for (i = 1; i <= nfrom; i++) { ftype = Vect_read_line(&From, NULL, FCats, i); /* This keeps also categories of areas for future (if area s in from_type) */ if (!(ftype & from_type) && (ftype != GV_CENTROID || !(from_type & GV_AREA))) continue; Vect_cat_get(FCats, from_field, &fcat); if (fcat < 0) continue; Near[nfcats].from_cat = fcat; nfcats++; } G_debug(1, "%d cats loaded from vector (including duplicates)", nfcats); /* Sort by cats and remove duplicates */ qsort((void *)Near, nfcats, sizeof(NEAR), cmp_near); /* remove duplicates */ for (i = 1; i < nfcats; i++) { if (Near[i].from_cat == Near[i - 1].from_cat) { for (j = i; j < nfcats - 1; j++) { Near[j].from_cat = Near[j + 1].from_cat; } nfcats--; } } G_debug(1, "%d cats loaded from vector (unique)", nfcats); } /* Go through all lines in 'from' and find nearest in 'to' for each */ /* Note: as from_type is restricted to GV_POINTS (for now) everything is simple */ count = 0; /* count of distances in 'all' mode */ /* Find nearest lines */ if (to_type & (GV_POINTS | GV_LINES)) { struct line_pnts *LLPoints; if (G_projection() == PROJECTION_LL) { LLPoints = Vect_new_line_struct(); } else { LLPoints = NULL; } G_message(_("Finding nearest feature...")); for (fline = 1; fline <= nfrom; fline++) { int tmp_tcat; double tmp_tangle, tangle; double tmp_min = (min < 0 ? 0 : min); double box_edge = 0; int done = 0; curr_step = 0; G_debug(3, "fline = %d", fline); G_percent(fline, nfrom, 2); ftype = Vect_read_line(&From, FPoints, FCats, fline); if (!(ftype & from_type)) continue; Vect_cat_get(FCats, from_field, &fcat); if (fcat < 0 && !all) continue; while (!done) { done = 1; if (!all) { /* enlarge search box until we get a hit */ /* the objective is to enlarge the search box * in the first iterations just a little bit * to keep the number of hits low */ Vect_reset_boxlist(List); while (curr_step < n_max_steps) { box_edge = max_step[curr_step]; if (box_edge < tmp_min) continue; box.E = FPoints->x[0] + box_edge; box.W = FPoints->x[0] - box_edge; box.N = FPoints->y[0] + box_edge; box.S = FPoints->y[0] - box_edge; box.T = PORT_DOUBLE_MAX; box.B = -PORT_DOUBLE_MAX; Vect_select_lines_by_box(&To, &box, to_type, List); curr_step++; if (List->n_values > 0) break; } } else { box.E = FPoints->x[0] + max; box.W = FPoints->x[0] - max; box.N = FPoints->y[0] + max; box.S = FPoints->y[0] - max; box.T = PORT_DOUBLE_MAX; box.B = -PORT_DOUBLE_MAX; Vect_select_lines_by_box(&To, &box, to_type, List); } G_debug(3, " %d lines in box", List->n_values); tline = 0; dist = PORT_DOUBLE_MAX; for (i = 0; i < List->n_values; i++) { tmp_tcat = -1; Vect_read_line(&To, TPoints, TCats, List->id[i]); tseg = Vect_line_distance(TPoints, FPoints->x[0], FPoints->y[0], FPoints->z[0], (Vect_is_3d(&From) && Vect_is_3d(&To)) ? WITH_Z : WITHOUT_Z, &tmp_tx, &tmp_ty, &tmp_tz, &tmp_dist, NULL, &tmp_talong); Vect_point_on_line(TPoints, tmp_talong, NULL, NULL, NULL, &tmp_tangle, NULL); if (tmp_dist > max || tmp_dist < min) continue; /* not in threshold */ /* TODO: more cats of the same field */ Vect_cat_get(TCats, to_field, &tmp_tcat); if (G_projection() == PROJECTION_LL) { /* calculate distances in meters not degrees (only 2D) */ Vect_reset_line(LLPoints); Vect_append_point(LLPoints, FPoints->x[0], FPoints->y[0], FPoints->z[0]); Vect_append_point(LLPoints, tmp_tx, tmp_ty, tmp_tz); tmp_dist = Vect_line_geodesic_length(LLPoints); Vect_reset_line(LLPoints); for (k = 0; k < tseg; k++) Vect_append_point(LLPoints, TPoints->x[k], TPoints->y[k], TPoints->z[k]); Vect_append_point(LLPoints, tmp_tx, tmp_ty, tmp_tz); tmp_talong = Vect_line_geodesic_length(LLPoints); } G_debug(4, " tmp_dist = %f tmp_tcat = %d", tmp_dist, tmp_tcat); if (all) { if (anear <= count) { anear += 10 + nfrom / 10; Near = (NEAR *) G_realloc(Near, anear * sizeof(NEAR)); } near = &(Near[count]); /* store info about relation */ near->from_cat = fcat; near->to_cat = tmp_tcat; /* -1 is OK */ near->dist = tmp_dist; near->from_x = FPoints->x[0]; near->from_y = FPoints->y[0]; near->from_z = FPoints->z[0]; near->to_x = tmp_tx; near->to_y = tmp_ty; near->to_z = tmp_tz; near->to_along = tmp_talong; /* 0 for points */ near->to_angle = tmp_tangle; near->count++; count++; } else { if (tline == 0 || (tmp_dist < dist)) { tline = List->id[i]; tcat = tmp_tcat; dist = tmp_dist; tx = tmp_tx; ty = tmp_ty; tz = tmp_tz; talong = tmp_talong; tangle = tmp_tangle; } } } G_debug(4, " dist = %f", dist); if (curr_step < n_max_steps) { /* enlarging the search box is possible */ if (tline > 0 && dist > box_edge) { /* line found but distance > search edge: * line bbox overlaps with search box, line itself is outside search box */ done = 0; } else if (tline == 0) { /* no line within max dist, but search box can still be enlarged */ done = 0; } } if (done && !all && tline > 0) { /* find near by cat */ near = (NEAR *) bsearch((void *)&fcat, Near, nfcats, sizeof(NEAR), cmp_near); G_debug(4, " near.from_cat = %d near.count = %d", near->from_cat, near->count); /* store info about relation */ if (near->count == 0 || near->dist > dist) { near->to_cat = tcat; /* -1 is OK */ near->dist = dist; near->from_x = FPoints->x[0]; near->from_y = FPoints->y[0]; near->from_z = FPoints->z[0]; near->to_x = tx; near->to_y = ty; near->to_z = tz; near->to_along = talong; /* 0 for points */ near->to_angle = tangle; } near->count++; } } /* done */ } /* next feature */ if (LLPoints) { Vect_destroy_line_struct(LLPoints); } } /* Find nearest areas */ if (to_type & GV_AREA) { G_message(_("Finding nearest areas...")); for (fline = 1; fline <= nfrom; fline++) { double tmp_min = (min < 0 ? 0 : min); double box_edge = 0; int done = 0; curr_step = 0; G_debug(3, "fline = %d", fline); G_percent(fline, nfrom, 2); ftype = Vect_read_line(&From, FPoints, FCats, fline); if (!(ftype & from_type)) continue; Vect_cat_get(FCats, from_field, &fcat); if (fcat < 0 && !all) continue; while (!done) { done = 1; if (!all) { /* enlarge search box until we get a hit */ /* the objective is to enlarge the search box * in the first iterations just a little bit * to keep the number of hits low */ Vect_reset_boxlist(List); while (curr_step < n_max_steps) { box_edge = max_step[curr_step]; if (box_edge < tmp_min) continue; box.E = FPoints->x[0] + box_edge; box.W = FPoints->x[0] - box_edge; box.N = FPoints->y[0] + box_edge; box.S = FPoints->y[0] - box_edge; box.T = PORT_DOUBLE_MAX; box.B = -PORT_DOUBLE_MAX; Vect_select_areas_by_box(&To, &box, List); curr_step++; if (List->n_values > 0) break; } } else { box.E = FPoints->x[0] + max; box.W = FPoints->x[0] - max; box.N = FPoints->y[0] + max; box.S = FPoints->y[0] - max; box.T = PORT_DOUBLE_MAX; box.B = -PORT_DOUBLE_MAX; Vect_select_areas_by_box(&To, &box, List); } G_debug(4, "%d areas selected by box", List->n_values); /* For each area in box check the distance */ tarea = 0; dist = PORT_DOUBLE_MAX; for (i = 0; i < List->n_values; i++) { int tmp_tcat; area = List->id[i]; G_debug(4, "%d: area %d", i, area); Vect_get_area_points(&To, area, TPoints); /* Find the distance to this area */ if (Vect_point_in_area(FPoints->x[0], FPoints->y[0], &To, area, List->box[i])) { /* in area */ tmp_dist = 0; tmp_tx = FPoints->x[0]; tmp_ty = FPoints->y[0]; } else if (Vect_point_in_poly(FPoints->x[0], FPoints->y[0], TPoints) > 0) { /* in isle */ nisles = Vect_get_area_num_isles(&To, area); for (j = 0; j < nisles; j++) { double tmp2_dist, tmp2_tx, tmp2_ty; isle = Vect_get_area_isle(&To, area, j); Vect_get_isle_points(&To, isle, TPoints); Vect_line_distance(TPoints, FPoints->x[0], FPoints->y[0], FPoints->z[0], WITHOUT_Z, &tmp2_tx, &tmp2_ty, NULL, &tmp2_dist, NULL, NULL); if (j == 0 || tmp2_dist < tmp_dist) { tmp_dist = tmp2_dist; tmp_tx = tmp2_tx; tmp_ty = tmp2_ty; } } } else { /* outside area */ Vect_line_distance(TPoints, FPoints->x[0], FPoints->y[0], FPoints->z[0], WITHOUT_Z, &tmp_tx, &tmp_ty, NULL, &tmp_dist, NULL, NULL); } if (tmp_dist > max || tmp_dist < min) continue; /* not in threshold */ Vect_get_area_cats(&To, area, TCats); tmp_tcat = -1; /* TODO: all cats of given field ? */ for (j = 0; j < TCats->n_cats; j++) { if (TCats->field[j] == to_field) { if (tmp_tcat >= 0) G_warning(_("More cats found in to_layer (area=%d)"), area); tmp_tcat = TCats->cat[j]; } } G_debug(4, " tmp_dist = %f tmp_tcat = %d", tmp_dist, tmp_tcat); if (all) { if (anear <= count) { anear += 10 + nfrom / 10; Near = (NEAR *) G_realloc(Near, anear * sizeof(NEAR)); } near = &(Near[count]); /* store info about relation */ near->from_cat = fcat; near->to_cat = tmp_tcat; /* -1 is OK */ near->dist = tmp_dist; near->from_x = FPoints->x[0]; near->from_y = FPoints->y[0]; near->to_x = tmp_tx; near->to_y = tmp_ty; near->to_along = 0; /* nonsense for areas */ near->to_angle = 0; /* not supported for areas */ near->count++; count++; } else if (tarea == 0 || tmp_dist < dist) { tarea = area; tcat = tmp_tcat; dist = tmp_dist; tx = tmp_tx; ty = tmp_ty; } } if (curr_step < n_max_steps) { /* enlarging the search box is possible */ if (tarea > 0 && dist > box_edge) { /* area found but distance > search edge: * area bbox overlaps with search box, area itself is outside search box */ done = 0; } else if (tarea == 0) { /* no area within max dist, but search box can still be enlarged */ done = 0; } } if (done && !all && tarea > 0) { /* find near by cat */ near = (NEAR *) bsearch((void *)&fcat, Near, nfcats, sizeof(NEAR), cmp_near); G_debug(4, "near.from_cat = %d near.count = %d dist = %f", near->from_cat, near->count, near->dist); /* store info about relation */ if (near->count == 0 || near->dist > dist) { near->to_cat = tcat; /* -1 is OK */ near->dist = dist; near->from_x = FPoints->x[0]; near->from_y = FPoints->y[0]; near->to_x = tx; near->to_y = ty; near->to_along = 0; /* nonsense for areas */ near->to_angle = 0; /* not supported for areas */ } near->count++; } } /* done */ } /* next feature */ } G_debug(3, "count = %d", count); /* Update database / print to stdout / create output map */ if (print_flag->answer) { /* print header */ fprintf(stdout, "from_cat"); i = 0; while (Upload[i].upload != END) { fprintf(stdout, "|%s", Upload[i].column); i++; } fprintf(stdout, "\n"); } else if (all && table_opt->answer) { /* create new table */ db_set_string(&stmt, "create table "); db_append_string(&stmt, table_opt->answer); db_append_string(&stmt, " (from_cat integer"); j = 0; while (Upload[j].upload != END) { db_append_string(&stmt, ", "); switch (Upload[j].upload) { case CAT: sprintf(buf2, "%s integer", Upload[j].column); break; case DIST: case FROM_X: case FROM_Y: case TO_X: case TO_Y: case FROM_ALONG: case TO_ALONG: case TO_ANGLE: sprintf(buf2, "%s double precision", Upload[j].column); } db_append_string(&stmt, buf2); j++; } db_append_string(&stmt, " )"); G_debug(3, "SQL: %s", db_get_string(&stmt)); if (db_execute_immediate(driver, &stmt) != DB_OK) G_fatal_error(_("Unable to create table: '%s'"), db_get_string(&stmt)); if (db_grant_on_table(driver, table_opt->answer, DB_PRIV_SELECT, DB_GROUP | DB_PUBLIC) != DB_OK) G_fatal_error(_("Unable to grant privileges on table <%s>"), table_opt->answer); } else if (!all) { /* read existing cats from table */ ncatexist = db_select_int(driver, Fi->table, Fi->key, NULL, &catexist); G_debug(1, "%d cats selected from the table", ncatexist); } update_ok = update_err = update_exist = update_notexist = update_dupl = update_notfound = 0; if (!all) { count = nfcats; } else if (print_as_matrix) { qsort((void *)Near, count, sizeof(NEAR), cmp_near_to); } if (driver) db_begin_transaction(driver); /* select 'to' attributes */ if (to_column_opt->answer) { int nrec; db_CatValArray_init(&cvarr); nrec = db_select_CatValArray(to_driver, toFi->table, toFi->key, to_column_opt->answer, NULL, &cvarr); G_debug(3, "selected values = %d", nrec); if (cvarr.ctype == DB_C_TYPE_DATETIME) { G_warning(_("DATETIME type not yet supported, no attributes will be uploaded")); } db_close_database_shutdown_driver(to_driver); } if (!(print_flag->answer || (all && !table_opt->answer))) /* no printing */ G_message("Update database..."); for (i = 0; i < count; i++) { dbCatVal *catval = 0; if (!(print_flag->answer || (all && !table_opt->answer))) /* no printing */ G_percent(i, count, 1); /* Write line connecting nearest points */ if (Outp != NULL) { Vect_reset_line(FPoints); Vect_reset_cats(FCats); Vect_append_point(FPoints, Near[i].from_x, Near[i].from_y, 0); if (Near[i].dist == 0) { Vect_write_line(Outp, GV_POINT, FPoints, FCats); } else { Vect_append_point(FPoints, Near[i].to_x, Near[i].to_y, 0); Vect_write_line(Outp, GV_LINE, FPoints, FCats); } } if (Near[i].count > 1) update_dupl++; if (Near[i].count == 0) update_notfound++; if (to_column_opt->answer && Near[i].count > 0) { db_CatValArray_get_value(&cvarr, Near[i].to_cat, &catval); } if (print_flag->answer || (all && !table_opt->answer)) { /* print only */ /* input and output is the same && calculate distances && only one upload option given -> print as a matrix */ if (print_as_matrix) { if (i == 0) { for (j = 0; j < nfrom; j++) { if (j == 0) fprintf(stdout, " "); fprintf(stdout, "|%d", Near[j].to_cat); } fprintf(stdout, "\n"); } if (i % nfrom == 0) { fprintf(stdout, "%d", Near[i].from_cat); for (j = 0; j < nfrom; j++) { print_upload(Near, Upload, i + j, &cvarr, catval); } fprintf(stdout, "\n"); } } else { fprintf(stdout, "%d", Near[i].from_cat); print_upload(Near, Upload, i, &cvarr, catval); fprintf(stdout, "\n"); } } else if (all) { /* insert new record */ sprintf(buf1, "insert into %s values ( %d ", table_opt->answer, Near[i].from_cat); db_set_string(&stmt, buf1); j = 0; while (Upload[j].upload != END) { db_append_string(&stmt, ","); switch (Upload[j].upload) { case CAT: sprintf(buf2, " %d", Near[i].to_cat); break; case DIST: sprintf(buf2, " %f", Near[i].dist); break; case FROM_X: sprintf(buf2, " %f", Near[i].from_x); break; case FROM_Y: sprintf(buf2, " %f", Near[i].from_y); break; case TO_X: sprintf(buf2, " %f", Near[i].to_x); break; case TO_Y: sprintf(buf2, " %f", Near[i].to_y); break; case FROM_ALONG: sprintf(buf2, " %f", Near[i].from_along); break; case TO_ALONG: sprintf(buf2, " %f", Near[i].to_along); break; case TO_ANGLE: sprintf(buf2, " %f", Near[i].to_angle); break; case TO_ATTR: if (catval) { switch (cvarr.ctype) { case DB_C_TYPE_INT: sprintf(buf2, " %d", catval->val.i); break; case DB_C_TYPE_DOUBLE: sprintf(buf2, " %.15e", catval->val.d); break; case DB_C_TYPE_STRING: db_set_string(&dbstr, db_get_string(catval->val.s)); db_double_quote_string(&dbstr); sprintf(buf2, " '%s'", db_get_string(&dbstr)); break; case DB_C_TYPE_DATETIME: /* TODO: formating datetime */ sprintf(buf2, " null"); break; } } else { sprintf(buf2, " null"); } break; } db_append_string(&stmt, buf2); j++; } db_append_string(&stmt, " )"); G_debug(3, "SQL: %s", db_get_string(&stmt)); if (db_execute_immediate(driver, &stmt) == DB_OK) { update_ok++; } else { update_err++; } } else { /* update table */ /* check if exists in table */ cex = (int *)bsearch((void *)&(Near[i].from_cat), catexist, ncatexist, sizeof(int), cmp_exist); if (cex == NULL) { /* cat does not exist in DB */ update_notexist++; continue; } update_exist++; sprintf(buf1, "update %s set", Fi->table); db_set_string(&stmt, buf1); j = 0; while (Upload[j].upload != END) { if (j > 0) db_append_string(&stmt, ","); sprintf(buf2, " %s =", Upload[j].column); db_append_string(&stmt, buf2); if (Near[i].count == 0) { /* no nearest found */ db_append_string(&stmt, " null"); } else { switch (Upload[j].upload) { case CAT: if (Near[i].to_cat > 0) sprintf(buf2, " %d", Near[i].to_cat); else sprintf(buf2, " null"); break; case DIST: sprintf(buf2, " %f", Near[i].dist); break; case FROM_X: sprintf(buf2, " %f", Near[i].from_x); break; case FROM_Y: sprintf(buf2, " %f", Near[i].from_y); break; case TO_X: sprintf(buf2, " %f", Near[i].to_x); break; case TO_Y: sprintf(buf2, " %f", Near[i].to_y); break; case FROM_ALONG: sprintf(buf2, " %f", Near[i].from_along); break; case TO_ALONG: sprintf(buf2, " %f", Near[i].to_along); break; case TO_ANGLE: sprintf(buf2, " %f", Near[i].to_angle); break; case TO_ATTR: if (catval) { switch (cvarr.ctype) { case DB_C_TYPE_INT: sprintf(buf2, " %d", catval->val.i); break; case DB_C_TYPE_DOUBLE: sprintf(buf2, " %.15e", catval->val.d); break; case DB_C_TYPE_STRING: db_set_string(&dbstr, db_get_string(catval->val.s)); db_double_quote_string(&dbstr); sprintf(buf2, " '%s'", db_get_string(&dbstr)); break; case DB_C_TYPE_DATETIME: /* TODO: formating datetime */ sprintf(buf2, " null"); break; } } else { sprintf(buf2, " null"); } break; } db_append_string(&stmt, buf2); } j++; } sprintf(buf2, " where %s = %d", Fi->key, Near[i].from_cat); db_append_string(&stmt, buf2); G_debug(2, "SQL: %s", db_get_string(&stmt)); if (db_execute_immediate(driver, &stmt) == DB_OK) { update_ok++; } else { update_err++; } } } G_percent(count, count, 1); if (driver) db_commit_transaction(driver); /* print stats */ if (update_dupl > 0) G_message(_("%d categories with more than 1 feature in vector map <%s>"), update_dupl, from_opt->answer); if (update_notfound > 0) G_message(_("%d categories - no nearest feature found"), update_notfound); if (!print_flag->answer) { db_close_database_shutdown_driver(driver); db_free_string(&stmt); /* print stats */ if (all && table_opt->answer) { G_message(_("%d distances calculated"), count); G_message(_("%d records inserted"), update_ok); if (update_err > 0) G_message(_("%d insert errors"), update_err); } else if (!all) { if (nfcats > 0) G_message(_("%d categories read from the map"), nfcats); if (ncatexist > 0) G_message(_("%d categories exist in the table"), ncatexist); if (update_exist > 0) G_message(_("%d categories read from the map exist in the table"), update_exist); if (update_notexist > 0) G_message(_("%d categories read from the map don't exist in the table"), update_notexist); G_message(_("%d records updated"), update_ok); if (update_err > 0) G_message(_("%d update errors"), update_err); G_free(catexist); } Vect_set_db_updated(&From); } Vect_close(&From); if (Outp != NULL) { Vect_build(Outp); Vect_close(Outp); } G_done_msg(" "); exit(EXIT_SUCCESS); }