/*************************************************************** * * MODULE: v.distance * * AUTHOR(S): - J.Soimasuo 15.9.1994, University of Joensuu, * Faculty of Forestry, Finland * - some additions 2002 Markus Neteler * - updated to 5.7 by Radim Blazek 2003 * * PURPOSE: Calculates distance from a point to nearest line or point in vector layer. * * COPYRIGHT: (C) 2002 by the GRASS Development Team * * This program is free software under the * GNU General Public License (>=v2). * Read the file COPYING that comes with GRASS * for details. * **************************************************************/ #include #include #include #include #include #include #include #include #include /* TODO: support all types (lines, boundaries, areas for 'from' (from_type) */ /* define codes for characteristics of relation between two nearest features */ #define CAT 1 /* category of nearest feature */ #define FROM_X 2 /* x coordinate of nearest point on 'from' feature */ #define FROM_Y 3 /* y coordinate of nearest point on 'from' feature */ #define TO_X 4 /* x coordinate of nearest point on 'to' feature */ #define TO_Y 5 /* y coordinate of nearest point on 'to' feature */ #define FROM_ALONG 6 /* distance to nearest point on 'from' along linear feature */ #define TO_ALONG 7 /* distance to nearest point on 'to' along linear feature */ #define DIST 8 /* minimum distance to nearest feature */ #define TO_ANGLE 9 /* angle of linear feature in nearest point */ #define TO_ATTR 10 /* attribute of nearest feature */ #define END 11 /* end of list */ /* Structure where are stored infos about nearest feature for each category */ typedef struct { int from_cat; /* category (from) */ int count; /* number of features already found */ int to_cat; /* category (to) */ double from_x, from_y, to_x, to_y; /* coordinates of nearest point */ double from_along, to_along; /* distance along a linear feature to the nearest point */ double to_angle; /* angle of linear feature in nearest point */ double dist; /* distance to nearest feature */ } NEAR; /* Upload and column store */ typedef struct { int upload; /* code */ char *column; /* column name */ } UPLOAD; static int cmp_near ( const void *, const void *); static int cmp_exist ( const void *, const void *); int main (int argc, char *argv[]) { int i, j; int all = 0; /* calculate from each to each within the threshold */ char *mapset; 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, *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; 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, dist, talong, tmp_tx, tmp_ty, 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 ilist *List; BOUND_BOX box; dbCatValArray cvarr; G_gisinit (argv[0]); module = G_define_module(); module->keywords = _("vector"); module->description = "Find the nearest element in vector 'to' for elements in vector 'from'. " "Various information about this relation may be uploaded to the attribute table of " "input vector 'from' or printed to stdout"; from_opt = G_define_standard_option(G_OPT_V_INPUT); from_opt->key = "from" ; from_opt->description = _("Name of existing vector file"); to_opt = G_define_standard_option(G_OPT_V_INPUT); to_opt->key = "to" ; to_opt->description = _("Name of existing vector file"); 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->description = _("From type"); 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->description = _("To type"); from_field_opt = G_define_standard_option(G_OPT_V_FIELD) ; from_field_opt->key = "from_layer"; from_field_opt->description = _("From layer"); to_field_opt = G_define_standard_option(G_OPT_V_FIELD) ; to_field_opt->key = "to_layer"; to_field_opt->description = _("To layer"); out_opt = G_define_standard_option(G_OPT_V_OUTPUT); out_opt->key = "output"; out_opt->required = NO; out_opt->description = _("New 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"); 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:\n" "\tcat - category of the nearest feature\n" "\tdist - minimum distance to nearest feature\n" "\tto_x - x coordinate of the nearest point on 'to' feature\n" "\tto_y - y coordinate of the nearest point on 'to' feature\n" "\tto_along - distance to the nearest point on 'from' feature along linear feature\n" "\tto_angle - angle of linear feature in nearest point, counterclockwise from positive " "x axis, in radians, which is between -PI and PI inclusive\n" "\tto_attr - attribute of nearest feature given by to_column option"); /* "\tfrom_x - x coordinate of the nearest point on 'from' feature\n" */ /* "\tfrom_y - y coordinate of the nearest point on 'from' feature\n" */ /* "\tfrom_along - distance to the nearest point on 'from' feature along linear feature\n" */ column_opt = G_define_option(); column_opt->key = "column"; column_opt->type = TYPE_STRING; column_opt->required = YES; column_opt->multiple = YES; column_opt->description = _("Column name(s) where values specified by 'upload' option will be uploaded"); to_column_opt = G_define_option(); to_column_opt->key = "to_column"; to_column_opt->type = TYPE_STRING; to_column_opt->required = NO; to_column_opt->multiple = NO; to_column_opt->description = _("Column name of nearest feature (used with upload=to_attr)"); table_opt = G_define_option(); table_opt->key = "table"; table_opt->type = TYPE_STRING; table_opt->required = NO; table_opt->multiple = NO; table_opt->description = _("The name of the table created for output when -a flag is used"); print_flag = G_define_flag(); print_flag->key = 'p'; print_flag->description = _("Print output to stdout, don't update attribute table. " "First column is always category of 'from' feature called from_cat"); all_flag = G_define_flag(); all_flag->key = 'a'; all_flag->description = _("Calculate distances to all features within the threshold. " "The output is written to stdout but may be uploaded to a new table created by this module. " "From categories are may be multiple"); 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); to_field = atoi ( to_field_opt->answer); max = atof (max_opt->answer); /* Read upload and column options */ /* count */ i = 0; while (upload_opt->answers[i]) i++; /* 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")); if ( all_flag->answer ) all = 1; /* Open 'from' vector */ if ((mapset = G_find_vector2 (from_opt->answer, "")) == NULL ) G_fatal_error(_("Could not find input map <%s>"), from_opt->answer); if ( !print_flag->answer && strcmp(mapset,G_mapset()) != 0 ) G_fatal_error(_("Vector 'from' is not in user mapset and cannot be updated")); Vect_set_open_level (2); Vect_open_old (&From, from_opt->answer, mapset); /* Open 'to' vector */ mapset = G_find_vector2 (to_opt->answer, ""); Vect_set_open_level (2); Vect_open_old (&To, to_opt->answer, mapset); /* 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 */ if ( max < 0 ) { BOUND_BOX fbox, tbox; double dx, dy; 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; max = sqrt ( dx*dx + dy*dy ); G_debug ( 2, "max = %f", 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(_("Cannot get layer info")); driver = db_start_driver_open_database ( Fi->driver, Fi->database ); if ( driver == NULL ) G_fatal_error(_("Cannot open database %s by driver %s"), Fi->database, Fi->driver); } else { driver = db_start_driver_open_database ( NULL, NULL ); if ( driver == NULL ) G_fatal_error(_("Cannot open default database")); } } to_driver = NULL; if ( to_column_opt->answer ) { toFi = Vect_get_field ( &To, to_field); if ( toFi == NULL ) G_fatal_error(_("Cannot get layer info")); to_driver = db_start_driver_open_database ( toFi->driver, toFi->database ); if ( to_driver == NULL ) G_fatal_error(_("Cannot open database %s by driver %s"), toFi->database, toFi->driver); /* Check column types */ if ( !print_flag->answer ) { char *fcname; int fctype, tctype; i = 0; while (column_opt->answers[i]) { if ( Upload[i].upload == TO_ATTR ) { fcname = column_opt->answers[i]; break; } i++; } 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(_("Incomatible 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_list (); /* 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) ) { for ( fline = 1; fline <= nfrom ; fline++ ) { int tmp_tcat; double tmp_tangle, tangle; G_debug (3, "fline = %d", fline); 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; 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; for (i = 0; i < List->n_values; i++) { Vect_read_line ( &To, TPoints, TCats, List->value[i] ); tseg = Vect_line_distance ( TPoints, FPoints->x[0], FPoints->y[0], 0, 0, &tmp_tx, &tmp_ty, NULL, &tmp_dist, NULL, &tmp_talong); Vect_point_on_line ( TPoints, tmp_talong, NULL, NULL, NULL, &tmp_tangle, NULL ); if ( tmp_dist > max ) continue; /* not in threshold */ /* TODO: more cats of the same field */ Vect_cat_get(TCats, to_field, &tmp_tcat); 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 = tmp_talong; /* 0 for points */ near->to_angle = tmp_tangle; near->count++; count++; } else { if ( tline == 0 || (tmp_dist <= dist)) { tline = List->value[i]; tcat = tmp_tcat; dist = tmp_dist; tx = tmp_tx; ty = tmp_ty; talong = tmp_talong; tangle = tmp_tangle; } } } G_debug (4, " dist = %f", dist ); if ( !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->to_x = tx; near->to_y = ty; near->to_along = talong; /* 0 for points */ near->to_angle = tangle; } near->count++; } } } /* Find nearest areas */ if ( to_type & GV_AREA ) { for ( fline = 1; fline <= nfrom ; fline++ ) { G_debug (3, "fline = %d", fline); 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; /* select areas by box */ 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->value[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 ( &To, area, FPoints->x[0], FPoints->y[0] ) ) { /* 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], 0, 0, &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], 0, 0, &tmp_tx, &tmp_ty, NULL, &tmp_dist, NULL, NULL); } if ( tmp_dist > max ) 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 of to_layer" ); 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 ( !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++; } } } 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(_("Cannot 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(_("Cannot grant privileges on table %s"), table_opt->answer); } else { /* 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; 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 ); } for ( i = 0; i < count; i++ ) { dbCatVal *catval = 0; /* 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 */ fprintf(stdout, "%d", Near[i].from_cat ); j = 0; while ( Upload[j].upload != END ) { if ( Near[i].count == 0 ) { /* no nearest found */ fprintf(stdout, "|null" ); } else { switch ( Upload[j].upload ) { case CAT: if ( Near[i].to_cat >= 0 ) fprintf(stdout, "|%d", Near[i].to_cat ); else fprintf(stdout, "|null" ); break; case DIST: fprintf(stdout, "|%f", Near[i].dist ); break; case FROM_X: fprintf(stdout, "|%f", Near[i].from_x ); break; case FROM_Y: fprintf(stdout, "|%f", Near[i].from_y ); break; case TO_X: fprintf(stdout, "|%f", Near[i].to_x ); break; case TO_Y: fprintf(stdout, "|%f", Near[i].to_y ); break; case FROM_ALONG: fprintf(stdout, "|%f", Near[i].from_along ); break; case TO_ALONG: fprintf(stdout, "|%f", Near[i].to_along ); break; case TO_ANGLE: fprintf(stdout, "|%f", Near[i].to_angle ); break; case TO_ATTR: if ( catval ) { switch (cvarr.ctype) { case DB_C_TYPE_INT: fprintf(stdout, "|%d", catval->val.i ); break; case DB_C_TYPE_DOUBLE: fprintf(stdout, "|%.15e", catval->val.d ); break; case DB_C_TYPE_STRING: fprintf(stdout, "|%s", db_get_string(catval->val.s) ); break; case DB_C_TYPE_DATETIME: /* TODO: formating datetime */ fprintf(stdout, "|" ); break; } } else { fprintf(stdout, "|null" ); } break; } } j++; } 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++; } } } if ( driver ) db_commit_transaction ( driver ); /* print stats */ G_message(_("Statistics:")); G_message(_("%d categories with more than 1 feature in 'from'"), update_dupl); 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 ) { G_message(_("%d distances calculated"), count); G_message(_("%d records inserted"), update_ok); G_message(_("%d insert errors"), update_err); } else { G_message(_("%d categories read from the map"), nfcats); G_message(_("%d categories exist in the table"), ncatexist); G_message(_("%d categories read from the map exist in the table"), update_exist); G_message(_("%d categories read from the map don't exist in the table"), update_notexist); G_message(_("%d records updated"), update_ok); G_message(_("%d update errors"), update_err); G_free ( catexist ); } Vect_set_db_updated ( &From ); } Vect_close (&From); if ( Outp != NULL ) { G_message(_("\nBuilding topology for %s ..."), Vect_get_name(Outp)); Vect_build ( Outp, stderr ); Vect_close ( Outp ); } G_done_msg(""); exit (EXIT_SUCCESS); } static int cmp_near ( const void *pa, const void *pb) { NEAR *p1 = (NEAR *) pa; NEAR *p2 = (NEAR *) pb; if( p1->from_cat < p2->from_cat) return -1; if( p1->from_cat > p2->from_cat) return 1; return 0; } static int cmp_exist ( const void *pa, const void *pb) { int *p1 = (int *) pa; int *p2 = (int *) pb; if( *p1 < *p2 ) return -1; if( *p1 > *p2) return 1; return 0; }