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; } }
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; }
/*! \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; }
/** * \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; }
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); }
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); } } }
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; }
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); }