コード例 #1
0
ファイル: main.c プロジェクト: caomw/grass
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);
}
コード例 #2
0
ファイル: main.c プロジェクト: imincik/pkg-grass
int main(int argc, char *argv[])
{
    int i, iopt;
    int operator;
    int aline, nalines, nskipped;
    int ltype, itype[2], ifield[2];
    int **cats, *ncats, nfields, *fields;
    char *mapset[2], *pre[2];
    struct GModule *module;
    struct GParm parm;
    struct GFlag flag;
    struct Map_info In[2], Out;
    struct field_info *IFi, *OFi;
    struct line_pnts *APoints, *BPoints;
    struct line_cats *ACats, *BCats;
    int *ALines;		/* List of lines: 0 do not output, 1 - write to output */
    struct ilist *List, *TmpList, *BoundList;

    G_gisinit(argv[0]);

    pre[0] = "a";
    pre[1] = "b";

    module = G_define_module();
    module->keywords = _("vector, spatial query");
    module->description =
	_("Selects features from vector map (A) by features from other vector map (B).");

    parse_options(&parm, &flag);
    
    if (G_parser(argc, argv))
	exit(EXIT_FAILURE);
    
    if (parm.operator->answer[0] == 'e')
	operator = OP_EQUALS;
    else if (parm.operator->answer[0] == 'd') {
	/* operator = OP_DISJOINT; */
	operator = OP_INTERSECTS;
	flag.reverse->answer = YES;
    }
    else if (parm.operator->answer[0] == 'i')
	operator = OP_INTERSECTS;
    else if (parm.operator->answer[0] == 't')
	operator = OP_TOUCHES;
    else if (parm.operator->answer[0] == 'c' && parm.operator->answer[1] == 'r')
	operator = OP_CROSSES;
    else if (parm.operator->answer[0] == 'w')
	operator = OP_WITHIN;
    else if (parm.operator->answer[0] == 'c' && parm.operator->answer[1] == 'o')
	operator = OP_CONTAINS;
    else if (parm.operator->answer[0] == 'o') {
	if (strcmp(parm.operator->answer, "overlaps") == 0)
	    operator = OP_OVERLAPS;
	else
	    operator = OP_OVERLAP;
    }
    else if (parm.operator->answer[0] == 'r')
	operator = OP_RELATE;
    else
	G_fatal_error(_("Unknown operator"));
    
    if (operator == OP_RELATE && !parm.relate->answer) {
	G_fatal_error(_("Required parameter <%s> not set"),
		      parm.relate->key);
    }
    
    for (iopt = 0; iopt < 2; iopt++) {
	itype[iopt] = Vect_option_to_types(parm.type[iopt]);
	ifield[iopt] = atoi(parm.field[iopt]->answer);

	Vect_check_input_output_name(parm.input[iopt]->answer, parm.output->answer,
				     GV_FATAL_EXIT);

	if ((mapset[iopt] =
	     G_find_vector2(parm.input[iopt]->answer, NULL)) == NULL) {
	    G_fatal_error(_("Vector map <%s> not found"),
			  parm.input[iopt]->answer);
	}
	
	Vect_set_open_level(2);
	Vect_open_old(&(In[iopt]), parm.input[iopt]->answer, mapset[iopt]);
    }
    
    /* Read field info */
    IFi = Vect_get_field(&(In[0]), ifield[0]);

    APoints = Vect_new_line_struct();
    BPoints = Vect_new_line_struct();
    ACats = Vect_new_cats_struct();
    BCats = Vect_new_cats_struct();
    List = Vect_new_list();
    TmpList = Vect_new_list();
    BoundList = Vect_new_list();

    /* Open output */
    Vect_open_new(&Out, parm.output->answer, Vect_is_3d(&(In[0])));
    Vect_set_map_name(&Out, _("Output from v.select"));
    Vect_set_person(&Out, G_whoami());
    Vect_copy_head_data(&(In[0]), &Out);
    Vect_hist_copy(&(In[0]), &Out);
    Vect_hist_command(&Out);

    nskipped = 0;
    nalines = Vect_get_num_lines(&(In[0]));

#ifdef HAVE_GEOS
    initGEOS(G_message, G_fatal_error);
    GEOSGeometry *AGeom = NULL;
#else
    void *AGeom = NULL;
#endif

    /* Alloc space for input lines array */
    ALines = (int *)G_calloc(nalines + 1, sizeof(int));

    G_message(_("Building spatial index..."));
    Vect_build_spatial_index(&In[0]);
    Vect_build_spatial_index(&In[1]);
    
    /* Lines in A. Go through all lines and mark those that meets condition */
    if (itype[0] & (GV_POINTS | GV_LINES)) {
	G_message(_("Processing features..."));
	
	for (aline = 1; aline <= nalines; aline++) {
	    BOUND_BOX abox;

	    G_debug(3, "aline = %d", aline);
	    G_percent(aline, nalines, 2);	/* must be before any continue */

	    /* Check category */
	    if (!flag.cat->answer && Vect_get_line_cat(&(In[0]), aline, ifield[0]) < 0) {
		nskipped++;
		continue;
	    }

	    /* Read line and check type */
	    if (operator != OP_OVERLAP) {
#ifdef HAVE_GEOS
		AGeom = Vect_read_line_geos(&(In[0]), aline, &ltype);
#endif
		if (!(ltype & (GV_POINT | GV_LINE)))
		    continue;

		if (!AGeom)
		    G_fatal_error(_("Unable to read line id %d from vector map <%s>"),
				  aline, Vect_get_full_name(&(In[0])));
	    }
	    else {
		ltype = Vect_read_line(&(In[0]), APoints, NULL, aline);
	    }
	    
	    if (!(ltype & itype[0]))
		continue;
	    
	    Vect_get_line_box(&(In[0]), aline, &abox);
	    abox.T = PORT_DOUBLE_MAX;
	    abox.B = -PORT_DOUBLE_MAX;

	    /* Check if this line overlaps any feature in B */
	    /* x Lines in B */
	    if (itype[1] & (GV_POINTS | GV_LINES)) {
		int i;
		int found = 0;
		
		/* Lines */
		Vect_select_lines_by_box(&(In[1]), &abox, itype[1], List);
		for (i = 0; i < List->n_values; i++) {
		    int bline;
		    
		    bline = List->value[i];
		    G_debug(3, "  bline = %d", bline);
		    
		    /* Check category */
		    if (!flag.cat->answer && Vect_get_line_cat(&(In[1]), bline, ifield[1]) < 0) {
			nskipped++;
			continue;
		    }
		    
		    if (operator != OP_OVERLAP) {
#ifdef HAVE_GEOS
			if(line_relate_geos(&(In[1]), AGeom,
					    bline, operator, parm.relate->answer)) {

			    found = 1;
			    break;
			}
#endif
		    }
		    else {
			Vect_read_line(&(In[1]), BPoints, NULL, bline);

			if (Vect_line_check_intersection(APoints, BPoints, 0)) {
			    found = 1;
			    break;
			}
		    }
		}
		
		if (found) {
		    ALines[aline] = 1;
		    continue;	/* Go to next A line */
		}
	    }
	    
	    /* x Areas in B. */
	    if (itype[1] & GV_AREA) {
		int i;
		
		Vect_select_areas_by_box(&(In[1]), &abox, List);
		for (i = 0; i < List->n_values; i++) {
		    int barea;
		    
		    barea = List->value[i];
		    G_debug(3, "  barea = %d", barea);
		    
		    if (Vect_get_area_cat(&(In[1]), barea, ifield[1]) < 0) {
			nskipped++;
			continue;
		    }

		    if (operator != OP_OVERLAP) {
#ifdef HAVE_GEOS
			if(area_relate_geos(&(In[1]), AGeom,
					    barea, operator, parm.relate->answer)) {
			    ALines[aline] = 1;
			    break;
			}
#endif
		    }
		    else {
			if (line_overlap_area(&(In[0]), aline, &(In[1]), barea)) {
			    ALines[aline] = 1;
			    break;
			}
		    }
		}
	    }
	    if (operator != OP_OVERLAP) {
#ifdef HAVE_GEOS
		GEOSGeom_destroy(AGeom);
#endif
		AGeom = NULL;
	    }
	}
    }
    
    /* Areas in A. */
    if (itype[0] & GV_AREA) {
	int aarea, naareas;

	G_message(_("Processing areas..."));
	
	naareas = Vect_get_num_areas(&(In[0]));

	for (aarea = 1; aarea <= naareas; aarea++) {
	    BOUND_BOX abox;

	    G_percent(aarea, naareas, 2);	/* must be before any continue */

	    if (Vect_get_area_cat(&(In[0]), aarea, ifield[0]) < 0) {
		nskipped++;
		continue;
	    }
	
	    Vect_get_area_box(&(In[0]), aarea, &abox);
	    abox.T = PORT_DOUBLE_MAX;
	    abox.B = -PORT_DOUBLE_MAX;

	    if (operator != OP_OVERLAP) {
#ifdef HAVE_GEOS
		AGeom = Vect_read_area_geos(&(In[0]), aarea);
#endif
		if (!AGeom)
		    G_fatal_error(_("Unable to read area id %d from vector map <%s>"),
				  aline, Vect_get_full_name(&(In[0])));
	    }

	    /* x Lines in B */
	    if (itype[1] & (GV_POINTS | GV_LINES)) {
		Vect_select_lines_by_box(&(In[1]), &abox, itype[1], List);

		for (i = 0; i < List->n_values; i++) {
		    int bline;

		    bline = List->value[i];

		    if (!flag.cat->answer && Vect_get_line_cat(&(In[1]), bline, ifield[1]) < 0) {
			nskipped++;
			continue;
		    }
		    
		    if (operator != OP_OVERLAP) {
#ifdef HAVE_GEOS
			if(line_relate_geos(&(In[1]), AGeom,
					    bline, operator, parm.relate->answer)) {
			    add_aarea(&(In[0]), aarea, ALines);
			    break;
			}
#endif
		    }
		    else {
			if (line_overlap_area(&(In[1]), bline, &(In[0]), aarea)) {
			    add_aarea(&(In[0]), aarea, ALines);
			    continue;
			}
		    }
		}
	    }

	    /* x Areas in B */
	    if (itype[1] & GV_AREA) {
		int naisles;
		int found = 0;

		/* List of areas B */

		/* Make a list of features forming area A */
		Vect_reset_list(List);

		Vect_get_area_boundaries(&(In[0]), aarea, BoundList);
		for (i = 0; i < BoundList->n_values; i++) {
		    Vect_list_append(List, abs(BoundList->value[i]));
		}

		naisles = Vect_get_area_num_isles(&(In[0]), aarea);

		for (i = 0; i < naisles; i++) {
		    int j, aisle;

		    aisle = Vect_get_area_isle(&(In[0]), aarea, i);

		    Vect_get_isle_boundaries(&(In[0]), aisle, BoundList);
		    for (j = 0; j < BoundList->n_values; j++) {
			Vect_list_append(List, BoundList->value[j]);
		    }
		}

		Vect_select_areas_by_box(&(In[1]), &abox, TmpList);

		for (i = 0; i < List->n_values; i++) {
		    int j, aline;

		    aline = abs(List->value[i]);

		    for (j = 0; j < TmpList->n_values; j++) {
			int barea, bcentroid;

			barea = TmpList->value[j];
			G_debug(3, "  barea = %d", barea);

			if (Vect_get_area_cat(&(In[1]), barea, ifield[1]) < 0) {
			    nskipped++;
			    continue;
			}

			/* Check if any centroid of area B is in area A.
			 * This test is important in if area B is completely within area A */
			bcentroid = Vect_get_area_centroid(&(In[1]), barea);
			Vect_read_line(&(In[1]), BPoints, NULL, bcentroid);

			if (operator != OP_OVERLAP) {
#ifdef HAVE_GEOS
			    if(area_relate_geos(&(In[1]), AGeom,
						barea, operator, parm.relate->answer)) {
				found = 1;
				break;
			    }
#endif
			}
			else {
			    if (Vect_point_in_area(&(In[0]), aarea,
						   BPoints->x[0], BPoints->y[0])) {
				found = 1;
				break;
			    }
			    
			    /* Check intersectin of lines from List with area B */
			    if (line_overlap_area(&(In[0]), aline,
						  &(In[1]), barea)) {
				found = 1;
				break;
			    }
			}
		    }
		    if (found) {
			add_aarea(&(In[0]), aarea, ALines);
			break;
		    }
		}
	    }
	    if (operator != OP_OVERLAP) {
#ifdef HAVE_GEOS
		GEOSGeom_destroy(AGeom);
#endif
		AGeom = NULL;
	    }
	}
    }
    
    Vect_close(&(In[1]));

#ifdef HAVE_GEOS
    finishGEOS();
#endif

    /* Write lines */
    nfields = Vect_cidx_get_num_fields(&(In[0]));
    cats = (int **)G_malloc(nfields * sizeof(int *));
    ncats = (int *)G_malloc(nfields * sizeof(int));
    fields = (int *)G_malloc(nfields * sizeof(int));
    for (i = 0; i < nfields; i++) {
	ncats[i] = 0;
	cats[i] =
	    (int *)G_malloc(Vect_cidx_get_num_cats_by_index(&(In[0]), i) *
			    sizeof(int));
	fields[i] = Vect_cidx_get_field_number(&(In[0]), i);
    }

    G_message(_("Writing selected features..."));
    for (aline = 1; aline <= nalines; aline++) {
	int atype;

	G_debug(4, "aline = %d ALines[aline] = %d", aline, ALines[aline]);
	G_percent(aline, nalines, 2);
	
	if ((!flag.reverse->answer && !(ALines[aline])) ||
	    (flag.reverse->answer && ALines[aline]))
	    continue;

	atype = Vect_read_line(&(In[0]), APoints, ACats, aline);
	Vect_write_line(&Out, atype, APoints, ACats);

	if (!(flag.table->answer) && (IFi != NULL)) {
	    for (i = 0; i < ACats->n_cats; i++) {
		int f, j;

		for (j = 0; j < nfields; j++) {	/* find field */
		    if (fields[j] == ACats->field[i]) {
			f = j;
			break;
		    }
		}
		cats[f][ncats[f]] = ACats->cat[i];
		ncats[f]++;
	    }
	}
    }

    /* Copy tables */
    if (!(flag.table->answer)) {
	int ttype, ntabs = 0;

	G_message(_("Writing attributes..."));

	/* Number of output tabs */
	for (i = 0; i < Vect_get_num_dblinks(&(In[0])); i++) {
	    int f, j;

	    IFi = Vect_get_dblink(&(In[0]), i);

	    for (j = 0; j < nfields; j++) {	/* find field */
		if (fields[j] == IFi->number) {
		    f = j;
		    break;
		}
	    }
	    if (ncats[f] > 0)
		ntabs++;
	}

	if (ntabs > 1)
	    ttype = GV_MTABLE;
	else
	    ttype = GV_1TABLE;

	for (i = 0; i < nfields; i++) {
	    int ret;

	    if (fields[i] == 0)
		continue;

	    /* Make a list of categories */
	    IFi = Vect_get_field(&(In[0]), fields[i]);
	    if (!IFi) {		/* no table */
		G_warning(_("Layer %d - no table"), fields[i]);
		continue;
	    }

	    OFi =
		Vect_default_field_info(&Out, IFi->number, IFi->name, ttype);

	    ret =
		db_copy_table_by_ints(IFi->driver, IFi->database, IFi->table,
				      OFi->driver,
				      Vect_subst_var(OFi->database, &Out),
				      OFi->table, IFi->key, cats[i],
				      ncats[i]);

	    if (ret == DB_FAILED) {
		G_warning(_("Layer %d - unable to copy table"), fields[i]);
	    }
	    else {
		Vect_map_add_dblink(&Out, OFi->number, OFi->name, OFi->table,
				    IFi->key, OFi->database, OFi->driver);
	    }
	}
    }

    Vect_close(&(In[0]));

    Vect_build(&Out);
    Vect_close(&Out);

    if (nskipped > 0) {
      G_warning(_("%d features without category skipped"), nskipped);
    }

    G_done_msg(_("%d features written to output."), Vect_get_num_lines(&Out));

    exit(EXIT_SUCCESS);
}
コード例 #3
0
ファイル: main.c プロジェクト: caomw/grass
int main(int argc, char *argv[])
{
    char *output, buf[DB_SQL_MAX];
    double (*rng)(void) = G_drand48;
    double zmin, zmax;
    int seed;
    int i, j, k, n, type, usefloat;
    int area, nareas, field;
    struct boxlist *List = NULL;
    BOX_SIZE *size_list = NULL;
    int alloc_size_list = 0;
    struct Map_info In, Out;
    struct line_pnts *Points;
    struct line_cats *Cats;
    struct cat_list *cat_list;
    struct bound_box box;
    struct Cell_head window;
    struct GModule *module;
    struct
    {
	struct Option *input, *field, *cats, *where, *output, *nsites,
		      *zmin, *zmax, *zcol, *ztype, *seed;
    } parm;
    struct
    {
	struct Flag *z, *notopo, *a;
    } flag;
    struct field_info *Fi;
    dbDriver *driver;
    dbTable *table;
    dbString sql;

    G_gisinit(argv[0]);

    module = G_define_module();
    G_add_keyword(_("vector"));
    G_add_keyword(_("sampling"));
    G_add_keyword(_("statistics"));
    G_add_keyword(_("random"));
    module->description = _("Generates random 2D/3D vector points.");

    parm.output = G_define_standard_option(G_OPT_V_OUTPUT);

    parm.nsites = G_define_option();
    parm.nsites->key = "n";
    parm.nsites->type = TYPE_INTEGER;
    parm.nsites->required = YES;
    parm.nsites->description = _("Number of points to be created");

    parm.input = G_define_standard_option(G_OPT_V_INPUT);
    parm.input->required = NO;
    parm.input->description = _("Restrict points to areas in input vector");
    parm.input->guisection = _("Selection");

    parm.field = G_define_standard_option(G_OPT_V_FIELD_ALL);
    parm.field->guisection = _("Selection");

    parm.cats = G_define_standard_option(G_OPT_V_CATS);
    parm.cats->guisection = _("Selection");
    
    parm.where = G_define_standard_option(G_OPT_DB_WHERE);
    parm.where->guisection = _("Selection");

    parm.zmin = G_define_option();
    parm.zmin->key = "zmin";
    parm.zmin->type = TYPE_DOUBLE;
    parm.zmin->required = NO;
    parm.zmin->description =
	_("Minimum z height (needs -z flag or column name)");
    parm.zmin->answer = "0.0";
    parm.zmin->guisection = _("3D output");

    parm.zmax = G_define_option();
    parm.zmax->key = "zmax";
    parm.zmax->type = TYPE_DOUBLE;
    parm.zmax->required = NO;
    parm.zmax->description =
	_("Maximum z height (needs -z flag or column name)");
    parm.zmax->answer = "0.0";
    parm.zmax->guisection = _("3D output");

    parm.seed = G_define_option();
    parm.seed->key = "seed";
    parm.seed->type = TYPE_INTEGER;
    parm.seed->required = NO;
    parm.seed->description =
	_("The seed to initialize the random generator. If not set the process ID is used");

    parm.zcol = G_define_standard_option(G_OPT_DB_COLUMN);
    parm.zcol->label = _("Name of column for z values");
    parm.zcol->description =
	_("Writes z values to column");
    parm.zcol->guisection = _("3D output");

    parm.ztype = G_define_option();
    parm.ztype->key = "column_type";
    parm.ztype->type = TYPE_STRING;
    parm.ztype->required = NO;
    parm.ztype->multiple = NO;
    parm.ztype->description = _("Type of column for z values");
    parm.ztype->options = "integer,double precision";
    parm.ztype->answer = "double precision";
    parm.ztype->guisection = _("3D output");

    flag.z = G_define_flag();
    flag.z->key = 'z';
    flag.z->description = _("Create 3D output");
    flag.z->guisection = _("3D output");

    flag.a = G_define_flag();
    flag.a->key = 'a';
    flag.a->description = _("Generate n points for each individual area");

    flag.notopo = G_define_standard_flag(G_FLG_V_TOPO);

    if (G_parser(argc, argv))
	exit(EXIT_FAILURE);

    output = parm.output->answer;
    n = atoi(parm.nsites->answer);
    
    if(parm.seed->answer)
        seed = atoi(parm.seed->answer);

    if (n <= 0) {
	G_fatal_error(_("Number of points must be > 0 (%d given)"), n);
    }

    nareas = 0;
    cat_list = NULL;
    field = -1;
    if (parm.input->answer) {
	Vect_set_open_level(2); /* topology required */
	if (2 > Vect_open_old2(&In, parm.input->answer, "", parm.field->answer))
	    G_fatal_error(_("Unable to open vector map <%s>"),
			  parm.input->answer);

	if (parm.field->answer)
	    field = Vect_get_field_number(&In, parm.field->answer);

	if ((parm.cats->answer || parm.where->answer) && field == -1) {
	    G_warning(_("Invalid layer number (%d). Parameter '%s' or '%s' specified, assuming layer '1'."),
		      field, parm.cats->key, parm.where->key);
	    field = 1;
	}
	if (field > 0)
	    cat_list = Vect_cats_set_constraint(&In, field, parm.where->answer,
						parm.cats->answer);
	nareas = Vect_get_num_areas(&In);
	if (nareas == 0) {
	    Vect_close(&In);
	    G_fatal_error(_("No areas in vector map <%s>"), parm.input->answer);
	}
    }
    else {
	if (flag.a->answer)
	    G_fatal_error(_("The <-%c> flag requires an input vector with areas"),
	                  flag.a->key);
    }

    /* create new vector map */
    if (-1 == Vect_open_new(&Out, output, flag.z->answer ? WITH_Z : WITHOUT_Z))
        G_fatal_error(_("Unable to create vector map <%s>"), output);
    Vect_set_error_handler_io(NULL, &Out);

    /* Do we need to write random values into attribute table? */
    usefloat = -1;
    if (parm.zcol->answer) {
	Fi = Vect_default_field_info(&Out, 1, NULL, GV_1TABLE);
	driver =
	    db_start_driver_open_database(Fi->driver,
					  Vect_subst_var(Fi->database, &Out));
	if (driver == NULL) {
	    G_fatal_error(_("Unable to open database <%s> by driver <%s>"),
			  Vect_subst_var(Fi->database, &Out), Fi->driver);
	}
        db_set_error_handler_driver(driver);
        
	db_begin_transaction(driver);

	db_init_string(&sql);
	sprintf(buf, "create table %s (%s integer, %s %s)", Fi->table, GV_KEY_COLUMN,
		parm.zcol->answer, parm.ztype->answer);
	db_set_string(&sql, buf);
	Vect_map_add_dblink(&Out, 1, NULL, Fi->table, GV_KEY_COLUMN, Fi->database,
			    Fi->driver);

	/* Create table */
	G_debug(3, db_get_string(&sql));
	if (db_execute_immediate(driver, &sql) != DB_OK) {
	    G_fatal_error(_("Unable to create table: %s"),
			  db_get_string(&sql));
	}

	/* Create index */
	if (db_create_index2(driver, Fi->table, Fi->key) != DB_OK)
	    G_warning(_("Unable to create index"));

	/* Grant */
	if (db_grant_on_table
	    (driver, Fi->table, DB_PRIV_SELECT,
	     DB_GROUP | DB_PUBLIC) != DB_OK) {
	    G_fatal_error(_("Unable to grant privileges on table <%s>"),
			  Fi->table);
	}

	/* OK. Let's check what type of column user has created */
	db_set_string(&sql, Fi->table);
	if (db_describe_table(driver, &sql, &table) != DB_OK) {
	    G_fatal_error(_("Unable to describe table <%s>"), Fi->table);
	}

	if (db_get_table_number_of_columns(table) != 2) {
	    G_fatal_error(_("Table should contain only two columns"));
	}

	type = db_get_column_sqltype(db_get_table_column(table, 1));
	if (type == DB_SQL_TYPE_SMALLINT || type == DB_SQL_TYPE_INTEGER)
	    usefloat = 0;
	if (type == DB_SQL_TYPE_REAL || type == DB_SQL_TYPE_DOUBLE_PRECISION)
	    usefloat = 1;
	if (usefloat < 0) {
	    G_fatal_error(_("You have created unsupported column type. This module supports only INTEGER"
			   " and DOUBLE PRECISION column types."));
	}
    }

    Vect_hist_command(&Out);

    /* Init the random seed */
    if(parm.seed->answer)
	G_srand48(seed);
    else
	G_srand48_auto();

    G_get_window(&window);

    Points = Vect_new_line_struct();
    Cats = Vect_new_cats_struct();

    if (nareas > 0) {
	int first = 1, count;
	struct bound_box abox, bbox;

	box.W = window.west;
	box.E = window.east;
	box.S = window.south;
	box.N = window.north;
	box.B = -PORT_DOUBLE_MAX;
	box.T = PORT_DOUBLE_MAX;

	count = 0;

	for (i = 1; i <= nareas; i++) {
	    
	    if (!Vect_get_area_centroid(&In, i))
		continue;

	    if (field > 0) {
		if (Vect_get_area_cats(&In, i, Cats))
		    continue;

		if (!Vect_cats_in_constraint(Cats, field, cat_list))
		    continue;
	    }

	    Vect_get_area_box(&In, i, &abox);
	    if (!Vect_box_overlap(&abox, &box))
		continue;

	    if (first) {
		Vect_box_copy(&bbox, &abox);
		first = 0;
	    }
	    else
		Vect_box_extend(&bbox, &abox);
	    count++;
	}
	if (count == 0) {
	    Vect_close(&In);
	    Vect_close(&Out);
	    Vect_delete(output);
	    G_fatal_error(_("Selected areas in input vector <%s> do not overlap with the current region"),
			  parm.input->answer);
	}
	Vect_box_copy(&box, &bbox);

	/* does the vector overlap with the current region ? */
	if (box.W >= window.east || box.E <= window.west ||
	    box.S >= window.north || box.N <= window.south) {

	    Vect_close(&In);
	    Vect_close(&Out);
	    Vect_delete(output);
	    G_fatal_error(_("Input vector <%s> does not overlap with the current region"),
	                  parm.input->answer);
	}

	/* try to reduce the current region */
	if (window.east > box.E)
	    window.east = box.E;
	if (window.west < box.W)
	    window.west = box.W;
	if (window.north > box.N)
	    window.north = box.N;
	if (window.south < box.S)
	    window.south = box.S;

	List = Vect_new_boxlist(1);
	alloc_size_list = 10;
	size_list = G_malloc(alloc_size_list * sizeof(BOX_SIZE));
    }

    zmin = zmax = 0;
    if (flag.z->answer || parm.zcol->answer) {
	zmax = atof(parm.zmax->answer);
	zmin = atof(parm.zmin->answer);
    }

    G_message(_("Generating points..."));
    if (flag.a->answer && nareas > 0) {
	struct bound_box abox, bbox;
	int cat = 1;

	/* n points for each area */
	nareas = Vect_get_num_areas(&In);
	
	G_percent(0, nareas, 1);
	for (area = 1; area <= nareas; area++) {

	    G_percent(area, nareas, 1);

	    if (!Vect_get_area_centroid(&In, area))
		continue;

	    if (field > 0) {
		if (Vect_get_area_cats(&In, area, Cats))
		    continue;

		if (!Vect_cats_in_constraint(Cats, field, cat_list)) {
		    continue;
		}
	    }

	    box.W = window.west;
	    box.E = window.east;
	    box.S = window.south;
	    box.N = window.north;
	    box.B = -PORT_DOUBLE_MAX;
	    box.T = PORT_DOUBLE_MAX;
	    
	    Vect_get_area_box(&In, area, &abox);
	    if (!Vect_box_overlap(&box, &abox))
		continue;
		
	    bbox = abox;
	    if (bbox.W < box.W)
		bbox.W = box.W;
	    if (bbox.E > box.E)
		bbox.E = box.E;
	    if (bbox.S < box.S)
		bbox.S = box.S;
	    if (bbox.N > box.N)
		bbox.N = box.N;

	    for (i = 0; i < n; ++i) {
		double x, y, z;
		int outside = 1;
		int ret;

		Vect_reset_line(Points);
		Vect_reset_cats(Cats);

		while (outside) {
		    x = rng() * (bbox.W - bbox.E) + bbox.E;
		    y = rng() * (bbox.N - bbox.S) + bbox.S;
		    z = rng() * (zmax - zmin) + zmin;

		    ret = Vect_point_in_area(x, y, &In, area, &abox);

		    G_debug(3, "    area = %d Vect_point_in_area() = %d", area, ret);

		    if (ret >= 1) {
			outside = 0;
		    }
		}

		if (flag.z->answer)
		    Vect_append_point(Points, x, y, z);
		else
		    Vect_append_point(Points, x, y, 0.0);

		if (parm.zcol->answer) {
		    sprintf(buf, "insert into %s values ( %d, ", Fi->table, i + 1);
		    db_set_string(&sql, buf);
		    /* Round random value if column is integer type */
		    if (usefloat)
			sprintf(buf, "%f )", z);
		    else
			sprintf(buf, "%.0f )", z);
		    db_append_string(&sql, buf);

		    G_debug(3, db_get_string(&sql));
		    if (db_execute_immediate(driver, &sql) != DB_OK) {
			G_fatal_error(_("Cannot insert new row: %s"),
				      db_get_string(&sql));
		    }
		}

		Vect_cat_set(Cats, 1, cat++);
		Vect_write_line(&Out, GV_POINT, Points, Cats);
	    }
	}
    }
    else {
	/* n points in total */
	for (i = 0; i < n; ++i) {
	    double x, y, z;

	    G_percent(i, n, 4);

	    Vect_reset_line(Points);
	    Vect_reset_cats(Cats);

	    x = rng() * (window.west - window.east) + window.east;
	    y = rng() * (window.north - window.south) + window.south;
	    z = rng() * (zmax - zmin) + zmin;
	    
	    if (nareas) {
		int outside = 1;

		do {
		    /* select areas by box */
		    box.E = x;
		    box.W = x;
		    box.N = y;
		    box.S = y;
		    box.T = PORT_DOUBLE_MAX;
		    box.B = -PORT_DOUBLE_MAX;
		    Vect_select_areas_by_box(&In, &box, List);
		    G_debug(3, "  %d areas selected by box", List->n_values);

		    /* sort areas by size, the smallest is likely to be the nearest */
		    if (alloc_size_list < List->n_values) {
			alloc_size_list = List->n_values;
			size_list = G_realloc(size_list, alloc_size_list * sizeof(BOX_SIZE));
		    }

		    k = 0;
		    for (j = 0; j < List->n_values; j++) {
			area = List->id[j];

			if (!Vect_get_area_centroid(&In, area))
			    continue;

			if (field > 0) {
			    if (Vect_get_area_cats(&In, area, Cats))
				continue;

			    if (!Vect_cats_in_constraint(Cats, field, cat_list)) {
				continue;
			    }
			}

			List->id[k] = List->id[j];
			List->box[k] = List->box[j];
			size_list[k].i = List->id[k];
			box = List->box[k];
			size_list[k].box = List->box[k];
			size_list[k].size = (box.N - box.S) * (box.E - box.W);
			k++;
		    }
		    List->n_values = k;
		    
		    if (List->n_values == 2) {
			/* simple swap */
			if (size_list[1].size < size_list[0].size) {
			    size_list[0].i = List->id[1];
			    size_list[1].i = List->id[0];
			    size_list[0].box = List->box[1];
			    size_list[1].box = List->box[0];
			}
		    }
		    else if (List->n_values > 2)
			qsort(size_list, List->n_values, sizeof(BOX_SIZE), sort_by_size);

		    for (j = 0; j < List->n_values; j++) {
			int ret;

			area = size_list[j].i;
			ret = Vect_point_in_area(x, y, &In, area, &size_list[j].box);

			G_debug(3, "    area = %d Vect_point_in_area() = %d", area, ret);

			if (ret >= 1) {
			    outside = 0;
			    break;
			}
		    }
		    if (outside) {
			x = rng() * (window.west - window.east) + window.east;
			y = rng() * (window.north - window.south) + window.south;
			z = rng() * (zmax - zmin) + zmin;
		    }
		} while (outside);
	    }

	    if (flag.z->answer)
		Vect_append_point(Points, x, y, z);
	    else
		Vect_append_point(Points, x, y, 0.0);

	    if (parm.zcol->answer) {
		sprintf(buf, "insert into %s values ( %d, ", Fi->table, i + 1);
		db_set_string(&sql, buf);
		/* Round random value if column is integer type */
		if (usefloat)
		    sprintf(buf, "%f )", z);
		else
		    sprintf(buf, "%.0f )", z);
		db_append_string(&sql, buf);

		G_debug(3, db_get_string(&sql));
		if (db_execute_immediate(driver, &sql) != DB_OK) {
		    G_fatal_error(_("Cannot insert new row: %s"),
				  db_get_string(&sql));
		}
	    }

	    Vect_cat_set(Cats, 1, i + 1);
	    Vect_write_line(&Out, GV_POINT, Points, Cats);
	}
	G_percent(1, 1, 1);
    }
    
    if (parm.zcol->answer) {
	db_commit_transaction(driver);
	db_close_database_shutdown_driver(driver);
    }

    if (!flag.notopo->answer) {
	Vect_build(&Out);
    }
    Vect_close(&Out);

    exit(EXIT_SUCCESS);
}
コード例 #4
0
ファイル: main.c プロジェクト: AsherBond/MondocosmOS
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);
}