void QgsGrassEditSplitLine::mouseClick( QgsPoint & point, Qt::MouseButton button )
{
  double thresh = e->threshold();

  switch ( button )
  {
    case Qt::LeftButton:
      // Split previously selected line
      if ( e->mSelectedLine > 0 )
      {
        e->eraseDynamic();
        e->eraseElement( e->mSelectedLine );

        int type = e->mProvider->readLine( e->mPoints, e->mCats, e->mSelectedLine );

        double xl, yl;
        Vect_line_distance( e->mPoints, e->mLastPoint.x(), e->mLastPoint.y(), 0.0, 0,
                            &xl, &yl, NULL, NULL, NULL, NULL );

        e->mPoints->n_points = e->mSelectedPart;
        Vect_append_point( e->mPoints, xl, yl, 0.0 );
        e->mProvider->rewriteLine( e->mSelectedLine, type, e->mPoints, e->mCats );
        e->updateSymb();
        e->displayUpdated();

        Vect_reset_line( e->mPoints );
        Vect_append_point( e->mPoints, xl, yl, 0.0 );
        for ( int i = e->mSelectedPart; i < e->mEditPoints->n_points; i++ )
        {
          Vect_append_point( e->mPoints, e->mEditPoints->x[i], e->mEditPoints->y[i], 0.0 );
        }

        e->mProvider->writeLine( type, e->mPoints, e->mCats );

        e->updateSymb();
        e->displayUpdated();

        e->mSelectedLine = 0;
        Vect_reset_line( e->mEditPoints );
        e->setCanvasPrompt( tr( "Select position on line" ), "", "" );
      }
      else
      {
        // Select new/next line
        e->mSelectedLine = e->mProvider->findLine( point.x(), point.y(), GV_LINES, thresh );

        if ( e->mSelectedLine )   // highlite
        {
          e->mProvider->readLine( e->mEditPoints, NULL, e->mSelectedLine );

          e->displayElement( e->mSelectedLine, e->mSymb[QgsGrassEdit::SYMB_HIGHLIGHT], e->mSize );

          double xl, yl; // nearest point on the line

          // Note first segment is 1!
          e->mSelectedPart = Vect_line_distance( e->mEditPoints, point.x(), point.y(), 0.0, 0,
                                                 &xl, &yl, NULL, NULL, NULL, NULL );

          e->displayDynamic( xl, yl, QgsVertexMarker::ICON_X, e->mSize );

          e->setCanvasPrompt( tr( "Split the line" ), "", tr( "Release the line" ) );
        }
        else
        {
          e->setCanvasPrompt( tr( "Select point on line" ), "", "" );
        }

      }
      break;

    case Qt::RightButton:
      e->eraseDynamic();
      e->displayElement( e->mSelectedLine, e->mSymb[e->mLineSymb[e->mSelectedLine]], e->mSize );
      e->mSelectedLine = 0;
      Vect_reset_line( e->mEditPoints );

      e->setCanvasPrompt( tr( "Select point on line" ), "", "" );
      break;

    default:
      // ignore others
      break;
  }
}
void QgsGrassEditAddVertex::mouseClick( QgsPoint & point, Qt::MouseButton button )
{
  double thresh = e->threshold();

  switch ( button )
  {
    case Qt::LeftButton:
      // Add vertex to previously selected line
      if ( e->mSelectedLine > 0 )
      {
        e->eraseDynamic();
        e->eraseElement( e->mSelectedLine );

        // Move vertex
        int type = e->mProvider->readLine( e->mPoints, e->mCats, e->mSelectedLine );

        if ( e->mAddVertexEnd && e->mSelectedPart == e->mEditPoints->n_points - 1 )
        {
          e->snap( point );
          Vect_append_point( e->mPoints, point.x(), point.y(), 0.0 );
        }
        else
        {
          Vect_line_insert_point( e->mPoints, e->mSelectedPart, point.x(), point.y(), 0.0 );
        }

        Vect_line_prune( e->mPoints );
        e->mProvider->rewriteLine( e->mSelectedLine, type, e->mPoints, e->mCats );
        e->updateSymb();
        e->displayUpdated();

        e->mSelectedLine = 0;
        Vect_reset_line( e->mEditPoints );

        e->setCanvasPrompt( tr( "Select line segment" ), "", "" );
      }
      else
      {
        // Select new line
        e->mSelectedLine = e->mProvider->findLine( point.x(), point.y(), GV_LINES, thresh );

        if ( e->mSelectedLine )   // highlite
        {
          e->mProvider->readLine( e->mEditPoints, NULL, e->mSelectedLine );
          e->displayElement( e->mSelectedLine, e->mSymb[QgsGrassEdit::SYMB_HIGHLIGHT], e->mSize );

          double xl, yl; // nearest point on the line

          // Note first segment is 1!
          e->mSelectedPart = Vect_line_distance( e->mEditPoints, point.x(), point.y(), 0.0, 0,
                                                 &xl, &yl, NULL, NULL, NULL, NULL );

          double dist1 = Vect_points_distance( xl, yl, 0.0, e->mEditPoints->x[e->mSelectedPart-1],
                                               e->mEditPoints->y[e->mSelectedPart-1], 0.0, 0 );
          double dist2 = Vect_points_distance( xl, yl, 0.0, e->mEditPoints->x[e->mSelectedPart],
                                               e->mEditPoints->y[e->mSelectedPart], 0.0, 0 );

          double maxdist = ( dist1 + dist2 ) / 4;

          if ( e->mSelectedPart == 1 && dist1 < maxdist )
          {
            e->mSelectedPart = 0;
            e->mAddVertexEnd = true;
          }
          else if ( e->mSelectedPart == e->mEditPoints->n_points - 1 && dist2 < maxdist )
          {
            e->mAddVertexEnd = true;
          }
          else
          {
            e->mAddVertexEnd = false;
          }

          e->setCanvasPrompt( tr( "New vertex position" ), "", tr( "Release" ) );
        }
        else
        {
          e->setCanvasPrompt( tr( "Select line segment" ), "", "" );
        }
      }
      break;

    case Qt::RightButton:
      e->eraseDynamic();
      e->displayElement( e->mSelectedLine, e->mSymb[e->mLineSymb[e->mSelectedLine]], e->mSize );
      e->mSelectedLine = 0;
      Vect_reset_line( e->mEditPoints );

      e->setCanvasPrompt( tr( "Select line segment" ), "", "" );
      break;

    default:
      // ignore others
      break;
  }

}
void QgsGrassEditDeleteVertex::mouseClick( QgsPoint & point, Qt::MouseButton button )
{
  double thresh = e->threshold();

  switch ( button )
  {
    case Qt::LeftButton:
      // Delete previously selected vertex
      if ( e->mSelectedLine > 0 )
      {
        e->eraseDynamic();
        e->eraseElement( e->mSelectedLine );

        // Move vertex
        int type = e->mProvider->readLine( e->mPoints, e->mCats, e->mSelectedLine );
        Vect_line_delete_point( e->mPoints, e->mSelectedPart );

        if ( e->mPoints->n_points < 2 ) // delete line
        {
          e->mProvider->deleteLine( e->mSelectedLine );

          // Check orphan records
          for ( int i = 0 ; i < e->mCats->n_cats; i++ )
          {
            e->checkOrphan( e->mCats->field[i], e->mCats->cat[i] );
          }
        }
        else
        {
          e->mProvider->rewriteLine( e->mSelectedLine, type, e->mPoints, e->mCats );
        }

        e->updateSymb();
        e->displayUpdated();

        e->mSelectedLine = 0;
        Vect_reset_line( e->mEditPoints );

        e->setCanvasPrompt( tr( "Select vertex" ), "", "" );
      }
      else
      {
        // Select new/next line
        e->mSelectedLine = e->mProvider->findLine( point.x(), point.y(), GV_LINES, thresh );

        if ( e->mSelectedLine )   // highlite
        {
          e->mProvider->readLine( e->mEditPoints, NULL, e->mSelectedLine );

          e->displayElement( e->mSelectedLine, e->mSymb[QgsGrassEdit::SYMB_HIGHLIGHT], e->mSize );

          double xl, yl; // nearest point on the line

          // Note first segment is 1!
          e->mSelectedPart = Vect_line_distance( e->mEditPoints, point.x(), point.y(), 0.0, 0,
                                                 &xl, &yl, NULL, NULL, NULL, NULL );

          double dist1 = Vect_points_distance( xl, yl, 0.0, e->mEditPoints->x[e->mSelectedPart-1],
                                               e->mEditPoints->y[e->mSelectedPart-1], 0.0, 0 );
          double dist2 = Vect_points_distance( xl, yl, 0.0, e->mEditPoints->x[e->mSelectedPart],
                                               e->mEditPoints->y[e->mSelectedPart], 0.0, 0 );

          if ( dist1 < dist2 )
            e->mSelectedPart--;

          e->displayDynamic( e->mEditPoints->x[e->mSelectedPart], e->mEditPoints->y[e->mSelectedPart],
                             QgsVertexMarker::ICON_BOX, e->mSize );

          e->setCanvasPrompt( tr( "Delete vertex" ), "", tr( "Release vertex" ) );
        }
        else
        {
          e->setCanvasPrompt( tr( "Select vertex" ), "", "" );
        }
      }
      break;

    case Qt::RightButton:
      e->eraseDynamic();
      e->displayElement( e->mSelectedLine, e->mSymb[e->mLineSymb[e->mSelectedLine]], e->mSize );
      e->mSelectedLine = 0;
      Vect_reset_line( e->mEditPoints );

      e->setCanvasPrompt( tr( "Select vertex" ), "", "" );
      break;

    default:
      // ignore others
      break;
  }
}
Пример #4
0
int connect_lines(struct Map_info *Map, int first, int line_from, int line_to,
		  double thresh, struct ilist *List)
{
    int line_new;
    int type_from, type_to;
    int n_points, seg, is;
    double x, y, px, py, x1, y1;
    double dist, spdist, lpdist, length, dist_p;
    double angle_t, angle_f, angle;

    struct line_pnts *Points_from, *Points_to, *Points_final;
    struct line_cats *Cats_from, *Cats_to;

    Points_from = Vect_new_line_struct();
    Points_to = Vect_new_line_struct();
    Points_final = Vect_new_line_struct();
    Cats_from = Vect_new_cats_struct();
    Cats_to = Vect_new_cats_struct();

    type_from = Vect_read_line(Map, Points_from, Cats_from, line_from);
    type_to = Vect_read_line(Map, Points_to, Cats_to, line_to);

    line_new = 0;
    if (!(type_from & GV_LINES) || !(type_to & GV_LINES))
	line_new = -1;

    if (line_new > -1) {
	if (first) {
	    x = Points_from->x[0];
	    y = Points_from->y[0];
	}
	else {
	    n_points = Points_from->n_points - 1;
	    x = Points_from->x[n_points];
	    y = Points_from->y[n_points];
	}
	seg = Vect_line_distance(Points_to, x, y, 0.0, WITHOUT_Z,
				 &px, &py, NULL, &dist, &spdist, &lpdist);
	
	if (seg > 0 && dist > 0.0 && (thresh < 0. || dist <= thresh)) {
	    /* lines in threshold */
	    if (first)
		length = 0;
	    else
		length = Vect_line_length(Points_from);

	    if (Vect_point_on_line(Points_from, length,
				   NULL, NULL, NULL, &angle_f, NULL) > 0) {
		if (Vect_point_on_line(Points_to, lpdist,
				       NULL, NULL, NULL, &angle_t,
				       NULL) > 0) {
		    angle = angle_t - angle_f;
		    dist_p = fabs(dist / sin(angle));
		    
		    if (first) {
			if (angle_f < 0)
			    angle_f -= M_PI;
			else
			    angle_f += M_PI;
		    }

		    x1 = x + dist_p * cos(angle_f);
		    y1 = y + dist_p * sin(angle_f);

		    length = Vect_line_length(Points_to);
		    Vect_line_insert_point(Points_to, seg, x1, y1, 0.);
		    if (fabs(Vect_line_length(Points_to) - length) < length * 1e-3) {
			/* lines connected -> split line_to */
			/* update line_from */
			if (first) {
			    Points_from->x[0] = x1;
			    Points_from->y[0] = y1;
			}
			else {
			    Points_from->x[n_points] = x1;
			    Points_from->y[n_points] = y1;
			}
			
			line_new = Vect_rewrite_line(Map, line_from, type_from,
						     Points_from, Cats_from);
			/* Vect_list_append(List, line_new); */
			
			/* update line_to  -- first part */
			Vect_reset_line(Points_final);
			for (is = 0; is < seg; is++) {
			    Vect_append_point(Points_final, Points_to->x[is],
					  Points_to->y[is],
					      Points_to->z[is]);
			}
			Vect_append_point(Points_final, x1, y1, 0.0);
			line_new = Vect_rewrite_line(Map, line_to, type_to,
						     Points_final, Cats_to);
			/* Vect_list_append(List, line_new); */
			
			/* write second part */
			Vect_reset_line(Points_final);
			Vect_append_point(Points_final, x1, y1, 0.0);
			for (is = seg; is < Points_to->n_points; is++) {
			    Vect_append_point(Points_final, Points_to->x[is],
					      Points_to->y[is],
					      Points_to->z[is]);
			}
			
			/* rewrite first part */
			line_new = Vect_write_line(Map, type_to,
						   Points_final, Cats_to);
			/* Vect_list_append(List, line_new); */
		    }
		}
	    }
	}
    }

    Vect_destroy_line_struct(Points_from);
    Vect_destroy_line_struct(Points_to);
    Vect_destroy_line_struct(Points_final);
    Vect_destroy_cats_struct(Cats_from);
    Vect_destroy_cats_struct(Cats_to);

    return line_new > 0 ? 1 : 0;
}
Пример #5
0
/*!
  \brief Split selected lines on given position
  
  \param Map pointer to Map_info
  \param List list of selected lines
  \param coord points location
  \param thresh threshold
  \param[out] List_updated list of rewritten features (or NULL)
  
  \return number of modified lines
  \return -1 on error
*/
int Vedit_split_lines(struct Map_info *Map, struct ilist *List,
		      struct line_pnts *coord, double thresh,
		      struct ilist *List_updated)
{
    int i, j, l;
    int type, line, seg, newline;
    int nlines_modified;
    double px, py, spdist, lpdist, dist;
    double *x, *y, *z;

    struct line_pnts *Points, *Points2;
    struct line_cats *Cats;
    struct ilist *List_in_box;

    nlines_modified = 0;

    Points = Vect_new_line_struct();
    Points2 = Vect_new_line_struct();
    Cats = Vect_new_cats_struct();
    List_in_box = Vect_new_list();

    for (i = 0; i < List->n_values; i++) {
	line = List->value[i];

	if (!Vect_line_alive(Map, line))
	    continue;

	type = Vect_read_line(Map, Points, Cats, line);

	if (!(type & GV_LINES))
	    continue;

	x = Points->x;
	y = Points->y;
	z = Points->z;

	for (j = 0; j < coord->n_points; j++) {
	    seg =
		Vect_line_distance(Points, coord->x[j], coord->y[j],
				   coord->z[j], WITHOUT_Z, &px, &py, NULL,
				   &dist, &spdist, &lpdist);

	    if (dist > thresh) {
		continue;
	    }

	    G_debug(3, "Vedit_split_lines(): line=%d, x=%f, y=%f, px=%f, py=%f, seg=%d, "
		    "dist=%f, spdist=%f, lpdist=%f", line, coord->x[j],
		    coord->y[j], px, py, seg, dist, spdist, lpdist);
	    
	    if (spdist <= 0.0 || spdist >= Vect_line_length(Points))
		continue;

	    G_debug(3, "Vedit_split_lines(): line=%d", line);

	    /* copy first line part */
	    for (l = 0; l < seg; l++) {
		Vect_append_point(Points2, x[l], y[l], z[l]);
	    }

	    /* add last vertex */
	    Vect_append_point(Points2, px, py, 0.0);

	    /* rewrite the line */
	    newline = Vect_rewrite_line(Map, line, type, Points2, Cats);
	    if (newline < 0) {
		return -1;
	    }
	    if (List_updated)
		Vect_list_append(List_updated, newline);
	    Vect_reset_line(Points2);

	    /* add given vertex */
	    Vect_append_point(Points2, px, py, 0.0);

	    /* copy second line part */
	    for (l = seg; l < Points->n_points; l++) {
		Vect_append_point(Points2, x[l], y[l], z[l]);
	    }

	    /* rewrite the line */
	    newline = Vect_write_line(Map, type, Points2, Cats);
	    if (newline < 0) {
		return -1;
	    }
	    if (List_updated)
		Vect_list_append(List_updated, newline);

	    nlines_modified++;
	}			/* for each bounding box */
    }				/* for each selected line */

    Vect_destroy_line_struct(Points);
    Vect_destroy_line_struct(Points2);
    Vect_destroy_cats_struct(Cats);
    Vect_destroy_list(List_in_box);

    return nlines_modified;
}
Пример #6
0
/**
 * \brief Consolidate network arcs (edge) based on given point vector map (nodes)
 *
 * If there is no connection between network edge and point, new edge
 * is added, the line broken, and new point added to nfield layer
 *
 * \param In,Points input vector maps
 * \param Out output vector map
 * \param nfield nodes layer
 * \param thresh threshold value to find neareast line
 *
 * \return number of new arcs
 */
int connect_arcs(struct Map_info *In, struct Map_info *Pnts,
                 struct Map_info *Out, int afield, int nfield,
                 double thresh, int snap)
{
    int narcs;
    int type, line, seg, i, ltype, broken;
    double px, py, pz, spdist, dist;

    struct line_pnts *Points, *Pline, *Pout;
    struct line_cats *Cats, *Cline, *Cnew;
    int maxcat, findex, ncats;

    narcs = 0;

    Points = Vect_new_line_struct();
    Pline = Vect_new_line_struct();
    Pout = Vect_new_line_struct();
    Cats = Vect_new_cats_struct();
    Cline = Vect_new_cats_struct();
    Cnew = Vect_new_cats_struct();

    /* rewrite all primitives to output file */
    Vect_copy_map_lines(In, Out);
    Vect_build_partial(Out, GV_BUILD_BASE);

    findex = Vect_cidx_get_field_index(In, afield);
    ncats = Vect_cidx_get_num_cats_by_index(In, findex);
    Vect_cidx_get_cat_by_index(In, findex, ncats - 1, &maxcat, &type, &line);


    /* go thorough all points in point map and write a new arcs if missing */
    while ((type = Vect_read_next_line(Pnts, Points, Cats)) >= 0) {
        if (type != GV_POINT)
            continue;

        /* find the nearest line in given threshold */
        line = Vect_find_line(Out,
                              Points->x[0], Points->y[0], Points->z[0],
                              GV_LINES, thresh, WITHOUT_Z, 0);

        if (line < 1 || !Vect_line_alive(Out, line))
            continue;

        ltype = Vect_read_line(Out, Pline, Cline, line);

        /* find point on the line */
        seg = Vect_line_distance(Pline,
                                 Points->x[0], Points->y[0], Points->z[0],
                                 WITHOUT_Z, &px, &py, &pz, &dist, &spdist,
                                 NULL);

        if (seg == 0)
            G_fatal_error(_("Failed to find intersection segment"));
        /* break the line */
        broken = 0;
        Vect_reset_line(Pout);
        for (i = 0; i < seg; i++) {
            Vect_append_point(Pout, Pline->x[i], Pline->y[i], Pline->z[i]);
        }
        Vect_append_point(Pout, px, py, pz);
        Vect_line_prune(Pout);
        if (Pout->n_points > 1) {
            Vect_rewrite_line(Out, line, ltype, Pout, Cline);
            broken++;
        }

        Vect_reset_line(Pout);
        Vect_append_point(Pout, px, py, pz);
        for (i = seg; i < Pline->n_points; i++) {
            Vect_append_point(Pout, Pline->x[i], Pline->y[i], Pline->z[i]);
        }
        Vect_line_prune(Pout);
        if (Pout->n_points > 1) {
            if (broken)
                Vect_write_line(Out, ltype, Pout, Cline);
            else
                Vect_rewrite_line(Out, line, ltype, Pout, Cline);
            broken++;
        }
        if (broken == 2)
            narcs++;

        if (dist > 0.0) {
            if (snap) {
                /* snap point */
                Points->x[0] = px;
                Points->y[0] = py;
                Points->z[0] = pz;
            }
            else {
                /* write new arc */
                Vect_reset_line(Pout);
                Vect_append_point(Pout, px, py, pz);
                Vect_append_point(Pout, Points->x[0], Points->y[0], Points->z[0]);
                maxcat++;
                Vect_reset_cats(Cnew);
                Vect_cat_set(Cnew, afield, maxcat);
                Vect_write_line(Out, ltype, Pout, Cnew);

                narcs++;
            }
        }

        /* add points to 'nfield' layer */
        for (i = 0; i < Cats->n_cats; i++) {
            Cats->field[i] = nfield;	/* all points to 'nfield' layer */
        }

        Vect_write_line(Out, type, Points, Cats);
    }

    Vect_destroy_line_struct(Points);
    Vect_destroy_line_struct(Pline);
    Vect_destroy_line_struct(Pout);
    Vect_destroy_cats_struct(Cats);
    Vect_destroy_cats_struct(Cline);
    Vect_destroy_cats_struct(Cnew);

    return narcs;
}
Пример #7
0
Файл: main.c Проект: caomw/grass
int main(int argc, char **argv)
{
    int lfield, pfield, n_points, n_outside, n_found, n_no_record,
	n_many_records;
    int line, type, nlines;
    double thresh, multip;
    struct Option *lines_opt, *points_opt;
    struct Option *lfield_opt, *pfield_opt;
    struct Option *driver_opt, *database_opt, *table_opt, *thresh_opt;
    struct GModule *module;
    const char *mapset;
    struct Map_info LMap, PMap;
    struct line_cats *LCats, *PCats;
    struct line_pnts *LPoints, *PPoints;
    dbDriver *rsdriver;
    dbHandle rshandle;
    dbString rsstmt;

    G_gisinit(argv[0]);

    module = G_define_module();
    G_add_keyword(_("vector"));
    G_add_keyword(_("Linear Reference System"));
    G_add_keyword(_("networking"));
    module->description =
	_("Finds line id and real km+offset for given points in vector map "
	  "using linear reference system.");

    lines_opt = G_define_standard_option(G_OPT_V_INPUT);
    lines_opt->key = "lines";
    lines_opt->description = _("Input vector map containing lines");

    points_opt = G_define_standard_option(G_OPT_V_INPUT);
    points_opt->key = "points";
    points_opt->description = _("Input vector map containing points");

    lfield_opt = G_define_standard_option(G_OPT_V_FIELD);
    lfield_opt->key = "llayer";
    lfield_opt->answer = "1";
    lfield_opt->description = _("Line layer");

    pfield_opt = G_define_standard_option(G_OPT_V_FIELD);
    pfield_opt->key = "player";
    pfield_opt->answer = "1";
    pfield_opt->description = _("Point layer");

    driver_opt = G_define_option();
    driver_opt->key = "rsdriver";
    driver_opt->type = TYPE_STRING;
    driver_opt->required = NO;
    driver_opt->description = _("Driver name for reference system table");
    driver_opt->options = db_list_drivers();
    driver_opt->answer = db_get_default_driver_name();

    database_opt = G_define_option();
    database_opt->key = "rsdatabase";
    database_opt->type = TYPE_STRING;
    database_opt->required = NO;
    database_opt->description = _("Database name for reference system table");
    database_opt->answer = db_get_default_database_name();

    table_opt = G_define_option();
    table_opt->key = "rstable";
    table_opt->type = TYPE_STRING;
    table_opt->required = YES;
    table_opt->description = _("Name of the reference system table");

    thresh_opt = G_define_option();
    thresh_opt->key = "threshold";
    thresh_opt->type = TYPE_DOUBLE;
    thresh_opt->required = NO;
    thresh_opt->answer = "1000";
    thresh_opt->description = _("Maximum distance to nearest line");

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

    LCats = Vect_new_cats_struct();
    PCats = Vect_new_cats_struct();
    LPoints = Vect_new_line_struct();
    PPoints = Vect_new_line_struct();


    lfield = atoi(lfield_opt->answer);
    pfield = atoi(pfield_opt->answer);
    multip = 1000;		/* Number of map units per MP unit */
    thresh = atof(thresh_opt->answer);

    /* Open input lines */
    mapset = G_find_vector2(lines_opt->answer, NULL);
    if (mapset == NULL)
	G_fatal_error(_("Vector map <%s> not found"), lines_opt->answer);

    Vect_set_open_level(2);
    if (Vect_open_old(&LMap, lines_opt->answer, mapset) < 0)
	G_fatal_error(_("Unable to open vector map <%s>"), lines_opt->answer);

    /* Open input points */
    mapset = G_find_vector2(points_opt->answer, NULL);
    if (mapset == NULL)
	G_fatal_error(_("Vector map <%s> not found"), points_opt->answer);

    Vect_set_open_level(2);
    if (Vect_open_old(&PMap, points_opt->answer, mapset) < 0)
	G_fatal_error(_("Unable to open vector map <%s>"), points_opt->answer);

    db_init_handle(&rshandle);
    db_init_string(&rsstmt);
    rsdriver = db_start_driver(driver_opt->answer);
    db_set_handle(&rshandle, database_opt->answer, NULL);
    if (db_open_database(rsdriver, &rshandle) != DB_OK)
	G_fatal_error(_("Unable to open database for reference table"));

    n_points = n_outside = n_found = n_no_record = n_many_records = 0;

    nlines = Vect_get_num_lines(&PMap);
    G_debug(2, "nlines = %d", nlines);
    G_message("pcat|lid|mpost|offset");
    for (line = 1; line <= nlines; line++) {
	int nearest, pcat, lcat, lid, ret;
	double along, mpost, offset;

	G_debug(3, "point = %d", line);
	type = Vect_read_line(&PMap, PPoints, PCats, line);
	if (type != GV_POINT)
	    continue;
	Vect_cat_get(PCats, pfield, &pcat);
	if (pcat < 0)
	    continue;
	n_points++;

	nearest =
	    Vect_find_line(&LMap, PPoints->x[0], PPoints->y[0], 0.0, GV_LINE,
			   thresh, 0, 0);

	fprintf(stdout, "%d", pcat);

	if (nearest <= 0) {
	    fprintf(stdout, "|-|-  # outside threshold\n");
	    n_outside++;
	    continue;
	}

	/* Read nearest line */
	Vect_read_line(&LMap, LPoints, LCats, nearest);
	Vect_cat_get(LCats, lfield, &lcat);

	Vect_line_distance(LPoints, PPoints->x[0], PPoints->y[0], 0.0, 0,
			   NULL, NULL, NULL, NULL, NULL, &along);

	G_debug(3, "  nearest = %d lcat = %d along = %f", nearest, lcat,
		along);

	if (lcat >= 0) {
	    ret = LR_get_milepost(rsdriver, table_opt->answer, "lcat", "lid",
				  "start_map", "end_map", "start_mp",
				  "start_off", "end_mp", "end_off", lcat,
				  along, multip, &lid, &mpost, &offset);
	}
	else {
	    ret = 0;
	}

	if (ret == 0) {
	    n_no_record++;
	    fprintf(stdout, "|-|-  # no record\n");
	    continue;
	}
	if (ret == 2) {
	    n_many_records++;
	    fprintf(stdout, "|-|-  # too many records\n");
	    continue;
	}

	G_debug(3, "  lid = %d mpost = %f offset = %f", lid, mpost, offset);

	fprintf(stdout, "|%d|%f+%f\n", lid, mpost, offset);
	n_found++;
    }

    db_close_database(rsdriver);

    /* Free, close ... */
    Vect_close(&LMap);
    Vect_close(&PMap);

    G_message(_n("[%d] point read from input",
                 "[%d] points read from input",
                 n_points), n_points);
    G_message(_n("[%d] position found",
                 "[%d] positions found",
                 n_found), n_found);
    if (n_outside)
	G_message(_n("[%d] point outside threshold",
                     "[%d] points outside threshold",
                     n_outside), n_outside);
    if (n_no_record)
	G_message(_n("[%d] point - no record found",
                     "[%d] points - no record found",
                     n_no_record), n_no_record);
    if (n_many_records)
	G_message(_n("[%d] point - too many records found",
                     "[%d] points - too many records found",
                     n_many_records), n_many_records);

    exit(EXIT_SUCCESS);
}
Пример #8
0
void select_from_geometry(void)
{
    int i, j, k, ncats, *cats;
    int type;
    struct line_pnts *iPoints, *jPoints;

    iPoints = Vect_new_line_struct();
    jPoints = Vect_new_line_struct();

    if (where_opt->answer != NULL) {
	if (ofield < 1) {
	    G_fatal_error(_("'layer' must be > 0 for 'where'."));
	}
	Fi = Vect_get_field(&Map, ofield);
	if (!Fi) {
	    G_fatal_error(_("Database connection not defined for layer %d"),
			  ofield);
	}

	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);
        db_set_error_handler_driver(Driver);

	ncats = db_select_int(Driver, Fi->table, Fi->key, where_opt->answer,
			      &cats);
	if (ncats == -1)
		G_fatal_error(_("Unable select categories from table <%s>"), Fi->table);

	db_close_database_shutdown_driver(Driver);

    }
    else
	ncats = 0;

    count = 0;

    nlines = Vect_get_num_lines(&Map);
    G_message(_("Calculating geometric distances between %d primitives..."), nlines);
    /* Start calculating the statistics based on distance to all other primitives.
       Use the centroid of areas and the first point of lines */
    for (i = 1; i <= nlines; i++) {

	G_percent(i, nlines, 2);
	type = Vect_read_line(&Map, iPoints, Cats, i);

	if (!(type & otype))
	    continue;

	if (where_opt->answer) {
	    int ok = FALSE;

	    for (j = 0; j < Cats->n_cats; j++) {
		if (Vect_cat_in_array(Cats->cat[j], cats, ncats)) {
		    ok = TRUE;
		    break;
		}
	    }
	    if (!ok)
	        continue;
	}
	for (j = i + 1; j < nlines; j++) {
	    /* get distance to this object */
	    double val = 0.0;

	    type = Vect_read_line(&Map, jPoints, Cats, j);

	    if (!(type & otype))
		continue;

	    /* now calculate the min distance between each point in line i with line j */
	    for (k = 0; k < iPoints->n_points; k++) {
		double dmin = 0.0;

		Vect_line_distance(jPoints, iPoints->x[k], iPoints->y[k], iPoints->z[k], 1,
				       NULL, NULL, NULL, &dmin, NULL, NULL);
		if((k == 0) || (dmin < val)) {
		    val = dmin;
		}
	    }
	    if (val > 0 && iPoints->n_points > 1) {
		for (k = 0; k < jPoints->n_points; k++) {
		    double dmin = 0.0;

		    Vect_line_distance(iPoints, jPoints->x[k], jPoints->y[k], jPoints->z[k], 1,
					   NULL, NULL, NULL, &dmin, NULL, NULL);
		    if(dmin < val) {
			val = dmin;
		    }
		}
	    }
	    if (val > 0 && iPoints->n_points > 1 && jPoints->n_points > 1) {
		if (Vect_line_check_intersection(iPoints, jPoints, Vect_is_3d(&Map)))
		    val = 0;
	    }
	    if (val == 0) {
		nzero++;
		continue;
	    }
	    if (first) {
		max = val;
		min = val;
		first = 0;
	    }
	    else {
		if (val > max)
		    max = val;
		if (val < min)
		    min = val;
	    }
	    sum += val;
	    sumsq += val * val;
	    sumcb += val * val * val;
	    sumqt += val * val * val * val;
	    sum_abs += fabs(val);
	    count++;
	    G_debug(3, "i=%d j=%d sum = %f val=%f", i, j, sum, val);
	}
    }
}
Пример #9
0
int move_line_update(void *closure, int sxn, int syn, int button)
{
    struct move_line *ml = closure;
    double x = D_d_to_u_col(sxn);
    double y = D_d_to_u_row(syn);

    G_debug(3, "button = %d x = %d = %f y = %d = %f", button, sxn, x, syn, y);

    if (ml->last_line > 0) {
	display_line(ml->last_line, SYMB_DEFAULT, 1);
    }

    if (button == 3)
	return 1;

    if (button == 1) {		/* Select / new location */
	int type;

	if (ml->last_line == 0) {	/* Select line */
	    ml->line =
		Vect_find_line(&Map, x, y, 0, GV_POINT | GV_CENTROID,
			       ml->thresh, 0, 0);
	    G_debug(2, "point found = %d", ml->line);
	    if (ml->line == 0)
		ml->line =
		    Vect_find_line(&Map, x, y, 0, GV_LINE | GV_BOUNDARY,
				   ml->thresh, 0, 0);
	    G_debug(2, "line found = %d", ml->line);

	    /* Display new selected line if any */
	    if (ml->line > 0) {
		display_line(ml->line, SYMB_HIGHLIGHT, 1);

		/* Find the nearest point on the line */
		type = Vect_read_line(&Map, ml->Points, NULL, ml->line);
		Vect_line_distance(ml->Points, x, y, 0, 0, &ml->xo, &ml->yo,
				   NULL, NULL, NULL, NULL);
		set_location(D_u_to_d_col(ml->xo), D_u_to_d_row(ml->yo));

		i_prompt_buttons(_("New location"), _("Unselect"), _("Quit tool"));
	    }
	    ml->last_line = ml->line;
	}
	else {			/* Line is already selected */
	    int node1, node2;
	    int i;

	    display_line(ml->last_line, SYMB_BACKGROUND, 1);
	    Vect_get_line_nodes(&Map, ml->last_line, &node1, &node2);
	    display_node(node1, SYMB_BACKGROUND, 1);
	    display_node(node2, SYMB_BACKGROUND, 1);

	    type = Vect_read_line(&Map, ml->Points, ml->Cats, ml->last_line);
	    for (i = 0; i < ml->Points->n_points; i++) {
		ml->Points->x[i] = ml->Points->x[i] + x - ml->xo;
		ml->Points->y[i] = ml->Points->y[i] + y - ml->yo;
	    }

	    Vect_rewrite_line(&Map, ml->last_line, type, ml->Points,
			      ml->Cats);

	    updated_lines_and_nodes_erase_refresh_display();
	    ml->last_line = 0;
	}

    }
    if (button == 2) {		/* Unselect */
	if (ml->last_line > 0) {
	    ml->last_line = 0;
	}
    }

    if (ml->last_line == 0) {
	i_prompt_buttons(_("Select"), "", _("Quit tool"));
	set_mode(MOUSE_POINT);
    }
    else
	set_mode(MOUSE_LINE);

    return 0;
}
Пример #10
0
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);
}