#include #include #include #include #include #include #define KEY(x) (G_strcasecmp(key,x)==0) int inpt (FILE *rulefd, char *buf); int key_data (char *buf, char **k, char **d); int reclass ( struct Map_info *In, struct Map_info *Out, int type, int field, dbCatValArray *cvarr, int optiond); static int cmpcat ( const void *pa, const void *pb) { dbCatVal *p1 = (dbCatVal *) pa; dbCatVal *p2 = (dbCatVal *) pb; if( p1->cat < p2->cat ) return -1; if( p1->cat > p2->cat ) return 1; return 0; } int main (int argc, char *argv[]) { struct GModule *module; /* struct Flag *d_flag; */ struct Option *in_opt, *out_opt, *type_opt, *field_opt, *rules_opt, *col_opt; char *mapset, *key, *data, buf[1024]; int rclelem, type, field; struct Map_info In, Out; struct field_info *Fi; dbDriver *Driver; dbCatValArray cvarr; int i, n, ttype; G_gisinit (argv[0]); module = G_define_module(); module->keywords = _("vector, attribute table"); module->description = "Changes vector category values for an existing vector map " "according to results of SQL queries or a value in attribute table column."; in_opt = G_define_standard_option(G_OPT_V_INPUT); out_opt = G_define_standard_option(G_OPT_V_OUTPUT); rules_opt = G_define_option(); rules_opt->key = "rules"; rules_opt->required = NO; rules_opt->type = TYPE_STRING; rules_opt->description = "Full path to the reclass rule file"; col_opt = G_define_option(); col_opt->key = "column"; col_opt->type = TYPE_STRING; col_opt->required = NO; col_opt->multiple = NO; col_opt->description = "The name of the column values of which are used as new categories. " "The column must be type integer."; type_opt = G_define_standard_option(G_OPT_V_TYPE); type_opt->description = "Select type"; type_opt->options = "point,line,boundary,centroid"; type_opt->answer = "point,line,boundary,centroid"; field_opt = G_define_standard_option(G_OPT_V_FIELD); if (G_parser(argc, argv)) exit(1); type = Vect_option_to_types ( type_opt ); field = atoi (field_opt->answer); if ( (!(rules_opt->answer) && !(col_opt->answer)) || (rules_opt->answer && col_opt->answer) ) { G_fatal_error ( "Either 'rules' or 'col' must be specified."); } Vect_check_input_output_name ( in_opt->answer, out_opt->answer, GV_FATAL_EXIT ); mapset = G_find_vector2 (in_opt->answer, NULL); if(mapset == NULL) G_fatal_error ("Could not find input %s\n", in_opt->answer); Vect_set_open_level ( 2 ); Vect_open_old (&In, in_opt->answer, mapset); Vect_open_new ( &Out, out_opt->answer, Vect_is_3d (&In) ); Vect_copy_head_data (&In, &Out); Vect_hist_copy (&In, &Out); Vect_hist_command ( &Out ); /* Read column values from database */ db_CatValArray_init ( &cvarr ); Fi = Vect_get_field( &In, field); if ( Fi == NULL ) G_fatal_error ("Cannot get layer info for vector map"); 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); if ( col_opt->answer ) { int ctype; int nrec; /* Check column type */ ctype = db_column_Ctype ( Driver, Fi->table, col_opt->answer ); if ( ctype == -1 ) { G_fatal_error ("Column <%s> not found", col_opt->answer ); } else if ( ctype == DB_C_TYPE_INT ) { nrec = db_select_CatValArray ( Driver, Fi->table, Fi->key, col_opt->answer, NULL, &cvarr ); G_debug (3, "nrec = %d", nrec ); } else if ( ctype == DB_C_TYPE_STRING ) { int i, more, nrows, newval, len; dbString stmt, stmt2; dbString lastval; dbCursor cursor; dbColumn *column; dbValue *value; dbTable *table; struct field_info *NewFi; dbDriver *Driver2; db_init_string ( &stmt ); db_init_string ( &stmt2 ); db_init_string ( &lastval ); NewFi = Vect_default_field_info ( &Out, field, NULL, GV_1TABLE ); Vect_map_add_dblink ( &Out, field, NULL, NewFi->table, "cat", NewFi->database, NewFi->driver); Driver2 = db_start_driver_open_database ( NewFi->driver, Vect_subst_var(NewFi->database, &Out) ); sprintf( buf, "SELECT %s, %s FROM %s ORDER BY %s", Fi->key, col_opt->answer, Fi->table, col_opt->answer); db_set_string ( &stmt, buf); G_debug (3, " SQL: %s", db_get_string ( &stmt ) ); if (db_open_select_cursor(Driver, &stmt, &cursor, DB_SEQUENTIAL) != DB_OK) G_fatal_error("Cannot open select cursor: %s", db_get_string ( &stmt ) ); nrows = db_get_num_rows ( &cursor ); G_debug (3, " %d rows selected", nrows ); if ( nrows < 0 ) G_fatal_error ( "Cannot select records from database"); db_CatValArray_alloc( &cvarr, nrows ); table = db_get_cursor_table (&cursor); /* Check if key column is integer */ column = db_get_table_column(table, 0); ctype = db_sqltype_to_Ctype( db_get_column_sqltype(column) ); G_debug (3, " key type = %d", type ); if ( ctype != DB_C_TYPE_INT ) { G_fatal_error ( "Key column type is not integer" ); /* shouldnot happen */ } cvarr.ctype = DB_C_TYPE_INT; /* String column length */ column = db_get_table_column(table, 1); len = db_get_column_length(column); /* Create table */ sprintf ( buf, "create table %s (cat integer, %s varchar(%d))", NewFi->table, col_opt->answer, len ); db_set_string ( &stmt2, buf); if (db_execute_immediate (Driver2, &stmt2) != DB_OK ) { Vect_close (&Out); db_close_database_shutdown_driver ( Driver ); db_close_database_shutdown_driver ( Driver2 ); G_fatal_error ( "Cannot create table: %s", db_get_string (&stmt2) ); } if ( db_create_index2(Driver2, NewFi->table, "cat" ) != DB_OK ) G_warning ( "Cannot create index" ); if (db_grant_on_table (Driver2, NewFi->table, DB_PRIV_SELECT, DB_GROUP|DB_PUBLIC ) != DB_OK ) { G_fatal_error ( "Cannot grant privileges on table %s", NewFi->table ); } newval = 0; /* fetch the data */ for ( i = 0; i < nrows; i++ ) { if(db_fetch (&cursor, DB_NEXT, &more) != DB_OK) { G_fatal_error ( "Cannot fetch data" ); } column = db_get_table_column(table, 1); value = db_get_column_value(column); if ( i == 0 || strcmp(db_get_value_string(value),db_get_string(&lastval))!=0 ) { newval++; db_set_string ( &lastval, db_get_value_string(value) ); G_debug (3, " newval = %d string = %s", newval, db_get_value_string(value) ); db_set_string ( &stmt2, db_get_value_string(value) ); db_double_quote_string (&stmt2) ; sprintf ( buf, "insert into %s values (%d, '%s')", NewFi->table, newval, db_get_string(&stmt2) ); db_set_string ( &stmt2, buf); if (db_execute_immediate (Driver2, &stmt2) != DB_OK ) { Vect_close (&Out); db_close_database_shutdown_driver ( Driver ); db_close_database_shutdown_driver ( Driver2 ); G_fatal_error ( "Cannot insert data: %s", db_get_string (&stmt2) ); } } column = db_get_table_column(table, 0); /* first column */ value = db_get_column_value(column); cvarr.value[i].cat = db_get_value_int(value); cvarr.value[i].val.i = newval; G_debug (4, " cat = %d newval = %d", cvarr.value[i].cat, newval ); } cvarr.n_values = nrows; db_close_database_shutdown_driver(Driver2); db_close_cursor(&cursor); db_free_string ( &stmt ); db_free_string ( &lastval ); qsort( (void *) cvarr.value, nrows, sizeof(dbCatVal), cmpcat); } else { G_fatal_error ( "Column type must be integer or string." ); } } else { int cat; char *label, *where; FILE *rulefd; G_debug (2, "Reading rules"); if ( (rulefd = fopen(rules_opt->answer, "r")) == NULL ) G_fatal_error ("Unable to open rule file %s", rules_opt->answer); db_CatValArray_alloc ( &cvarr, Vect_get_num_lines(&In) ); cat = 0; where = label = NULL; while ( inpt(rulefd, buf) ) { if (!key_data(buf, &key, &data)) continue ; G_strip(data); G_debug (3, "key = %s data = %s", key, data); if (KEY("cat")) { if ( cat > 0 ) G_fatal_error ( "Category %d overwritten by %s", cat, data); cat = atoi ( data ); if( cat <= 0 ) G_fatal_error ( "Category '%s' invalid", data); } else if (KEY("label")) { if ( label ) G_fatal_error ( "Label '%s' overwritten by '%s'", label, data); label = G_store ( data ); } else if (KEY("where")) { if ( where ) G_fatal_error ( "Condition '%s' overwritten by '%s'", where, data); where = G_store(data); } else { G_fatal_error ( "Unknown rule option: '%s'", key ); } if ( cat > 0 && where ) { int i, over, *cats, ncats; dbCatVal *catval; G_debug (2, "cat = %d, where = '%s'", cat, where); if ( !label ) label = where; ncats = db_select_int ( Driver, Fi->table, Fi->key, where, &cats); if ( ncats == -1 ) G_fatal_error("Cannot select values from database."); G_debug (3, " ncats = %d", ncats); /* If the category already exists, overwrite it cvarr, set to 0 in cats * and don't add second time */ over = 0; for ( i = 0; i < ncats; i++ ) { if ( db_CatValArray_get_value ( &cvarr, cats[i], &catval ) == DB_OK ) { catval->val.i = cat; cats[i] = 0; over++; } } if (over > 0) G_warning ("%d previously set categories overwritten by new category %d.", over, cat); for ( i = 0; i < ncats; i++ ) { if ( cats[i] <= 0 ) continue; if ( cvarr.n_values == cvarr.alloc ) { db_CatValArray_realloc ( &cvarr, (int)10+cvarr.alloc/3 ); } G_debug (3, "Add old cat %d", cats[i]); cvarr.value[cvarr.n_values].cat = cats[i]; cvarr.value[cvarr.n_values].val.i = cat; cvarr.n_values++; } db_CatValArray_sort ( &cvarr ); G_free ( cats ); cat = 0; where = label = NULL; } } if ( cat > 0 || where ) G_fatal_error ( "Incomplete rule"); } db_close_database_shutdown_driver(Driver); /* reclass vector map */ rclelem = reclass ( &In, &Out, type, field, &cvarr, 0); /* Copy tables */ n = Vect_get_num_dblinks ( &In ); for ( i = 0; i < Vect_get_num_dblinks ( &In ); i++ ) { Fi = Vect_get_dblink ( &In, i ); if ( Fi->number == field ) { n--; break; } } if ( n > 1 ) ttype = GV_MTABLE; else ttype = GV_1TABLE; for ( i = 0; i < Vect_get_num_dblinks ( &In ); i++ ) { Fi = Vect_get_dblink ( &In, i ); if ( Fi->number == field ) { continue; } Vect_copy_table ( &In, &Out, Fi->number, Fi->number, Fi->name, ttype ); } Vect_close (&In); Vect_build (&Out, stderr); Vect_close (&Out); fprintf(stderr,"%d features reclassed\n", rclelem); exit(0); }