123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893 |
- /*
- * v.lrs.create - Create Linear reference system
- */
- /******************************************************************************
- * Copyright (c) 2004, Radim Blazek (blazek@itc.it)
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included
- * in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
- * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *****************************************************************************/
- #include <stdlib.h>
- #include <string.h>
- #include <time.h>
- #include <grass/gis.h>
- #include <grass/Vect.h>
- #include <grass/dbmi.h>
- #include <grass/glocale.h>
- #include "../lib/lrs.h"
- /* MP is milepost */
- #define TO_TYPE_FROM 1 /* the same as specified fo from_ */
- #define TO_TYPE_MAP 2 /* calculated from map along the line from previous MP */
- #define TO_TYPE_USER 3 /* defined by user */
- #define DIR_FORWARD 1
- #define DIR_BACKWARD 2
- #define DIR_UNKNOWN 3
- #define ERR_OK 0 /* No error */
- #define ERR_END_GT_START 1 /* MP: end > start */
- #define ERR_THRESHOLD 2 /* MP is outside threshold */
- #define ERR_IDENT 3 /* more MPs with identical distance along the line */
- #define ERR_ORDER 4 /* MP in wrong order (used for points) */
- #define ERR_NO_POINT 5 /* No MP point found for MP DB record */
- #define ERR_NO_MP 6 /* Line without MP */
- #define ERR_ONE_MP 7 /* Line with 1 MP only */
- #define ERR_NO_DIR 8 /* Unknown direction of line */
- #define ERR_LINE_ORDER 9 /* Wrong order of MP along line (used for lines) */
- typedef struct
- {
- double x, y;
- int cat;
- int line_idx; /* index to line in 'lines' array */
- double dist_along; /* distance from the beginning of the line */
- double start_mp, start_off; /* milepost, offset for the beginning of ref. segment */
- double end_mp, end_off; /* milepost, offset for the end of ref. segment */
- int to_type; /* type of the end_mp, end_off */
- int err; /* error number */
- } MILEPOST;
- typedef struct
- {
- int line, cat; /* line number in 'In' vector and category of nearest line */
- int nmposts; /* number of attached MPs */
- int first_mpost_idx; /* index of first/last MP in 'mposts' */
- int direction; /* direction DIR_FORWARD/DIR_BACKWARD if MPs have increasing/decreasing */
- /* values along the line */
- double length; /* line length */
- int err; /* error number */
- } RLINE;
- int cmp_along(const void *pa, const void *pb);
- int main(int argc, char **argv)
- {
- int i, j, k, lid, more, found, ret, totype, rsid, dir, forward, backward,
- order;
- int *Lid_int, nLid, aLid;
- int cat, *cats, ncat, lfield, pfield;
- int line, first, last, point, type;
- int nrlines, nmposts, nallmposts, mpost;
- MILEPOST *mposts /* table of currently referenced MPs */ ;
- RLINE *rlines; /* table of currently referenced lines */
- double thresh, dist_to, dist_along, distance_to;
- double end_mp, end_off;
- double multip = 1000; /* Number of map units per MP unit */
- struct Option *in_lines_opt, *out_lines_opt, *points_opt, *err_opt;
- struct Option *lfield_opt, *pfield_opt;
- struct Option *lidcol_opt, *pidcol_opt;
- struct Option *start_mp_opt, *start_off_opt, *end_mp_opt, *end_off_opt;
- struct Option *driver_opt, *database_opt, *table_opt, *thresh_opt;
- struct GModule *module;
- char *mapset, buf[2000];
- const char *drv, *db;
- struct Map_info In, Out, PMap, EMap;
- struct line_cats *LCats, *PCats;
- struct line_pnts *LPoints, *L2Points, *PPoints;
- struct field_info *Lfi, *Pfi;
- dbDriver *ldriver, *pdriver, *rsdriver;
- dbHandle lhandle, phandle, rshandle;
- dbString lstmt, pstmt, rsstmt;
- dbCursor lcursor, pcursor;
- dbTable *ltable, *ptable;
- dbColumn *lcolumn, *pcolumn;
- dbValue *lvalue, *pvalue;
- int lcoltype, lccoltype;
- int debug = 2;
- G_gisinit(argv[0]);
- module = G_define_module();
- module->keywords = _("vector, LRS, networking");
- module->description = _("Create Linear Reference System");
- in_lines_opt = G_define_standard_option(G_OPT_V_INPUT);
- in_lines_opt->key = "in_lines";
- in_lines_opt->description = _("Input vector map containing lines");
- out_lines_opt = G_define_standard_option(G_OPT_V_OUTPUT);
- out_lines_opt->key = "out_lines";
- out_lines_opt->description =
- _("Output vector map where oriented lines are written");
- err_opt = G_define_standard_option(G_OPT_V_OUTPUT);
- err_opt->key = "err";
- err_opt->required = NO;
- err_opt->description = _("Output vector map of errors");
- points_opt = G_define_standard_option(G_OPT_V_INPUT);
- points_opt->key = "points";
- points_opt->description =
- _("Input vector map containing reference points");
- lfield_opt = G_define_standard_option(G_OPT_V_FIELD);
- lfield_opt->key = "llayer";
- lfield_opt->description = _("Line layer");
- pfield_opt = G_define_standard_option(G_OPT_V_FIELD);
- pfield_opt->key = "player";
- pfield_opt->description = _("Point layer");
- lidcol_opt = G_define_option();
- lidcol_opt->key = "lidcol";
- lidcol_opt->type = TYPE_STRING;
- lidcol_opt->required = YES;
- lidcol_opt->description =
- _("Column containing line identifiers for lines");
- pidcol_opt = G_define_option();
- pidcol_opt->key = "pidcol";
- pidcol_opt->type = TYPE_STRING;
- pidcol_opt->required = YES;
- pidcol_opt->description =
- _("Column containing line identifiers for points");
- start_mp_opt = G_define_option();
- start_mp_opt->key = "start_mp";
- start_mp_opt->type = TYPE_STRING;
- start_mp_opt->required = NO;
- start_mp_opt->answer = "start_mp";
- start_mp_opt->description =
- _("Column containing milepost position for the beginning "
- "of next segment");
- start_off_opt = G_define_option();
- start_off_opt->key = "start_off";
- start_off_opt->type = TYPE_STRING;
- start_off_opt->required = NO;
- start_off_opt->answer = "start_off";
- start_off_opt->description =
- _("Column containing offset from milepost for the beginning "
- "of next segment");
- end_mp_opt = G_define_option();
- end_mp_opt->key = "end_mp";
- end_mp_opt->type = TYPE_STRING;
- end_mp_opt->required = NO;
- end_mp_opt->answer = "end_mp";
- end_mp_opt->description =
- _("Column containing milepost position for the end "
- "of previous segment");
- end_off_opt = G_define_option();
- end_off_opt->key = "end_off";
- end_off_opt->type = TYPE_STRING;
- end_off_opt->required = NO;
- end_off_opt->answer = "end_off";
- end_off_opt->description =
- _("Column containing offset from milepost for the end "
- "of previous segment");
- 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");
- if ((drv = db_get_default_driver_name()))
- driver_opt->answer = drv;
- 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");
- if ((db = db_get_default_database_name()))
- database_opt->answer = db;
- table_opt = G_define_option();
- table_opt->key = "rstable";
- table_opt->type = TYPE_STRING;
- table_opt->required = YES;
- table_opt->label =
- _("Name of table where the reference system will be written");
- table_opt->description = _("New table is created by this module");
- thresh_opt = G_define_option();
- thresh_opt->key = "thresh";
- thresh_opt->type = TYPE_DOUBLE;
- thresh_opt->required = NO;
- thresh_opt->answer = "1";
- thresh_opt->description = _("Maximum distance of point to line allowed");
- if (G_parser(argc, argv))
- exit(EXIT_FAILURE);
- LCats = Vect_new_cats_struct();
- PCats = Vect_new_cats_struct();
- LPoints = Vect_new_line_struct();
- L2Points = Vect_new_line_struct();
- PPoints = Vect_new_line_struct();
- lfield = atoi(lfield_opt->answer);
- pfield = atoi(pfield_opt->answer);
- thresh = atof(thresh_opt->answer);
- G_debug(debug, "Creating LRS ...");
- /* Open input lines */
- mapset = G_find_vector2(in_lines_opt->answer, NULL);
- if (mapset == NULL)
- G_fatal_error(_("Vector map <%s> not found"), in_lines_opt->answer);
- Vect_open_old(&In, in_lines_opt->answer, mapset);
- /* Open input ipoints */
- mapset = G_find_vector2(points_opt->answer, NULL);
- if (mapset == NULL)
- G_fatal_error(_("Vector map <%s> not found"), points_opt->answer);
- Vect_open_old(&PMap, points_opt->answer, mapset);
- /* Open output lines */
- Vect_open_new(&Out, out_lines_opt->answer, Vect_is_3d(&In));
- /* Open output error map */
- if (err_opt->answer)
- Vect_open_new(&EMap, err_opt->answer, Vect_is_3d(&In));
- /* Because the line feature identified by one id (lidcol) may be split
- * to more line parts, and milepost may be in threshold for more such parts,
- * so that if each line part would be processed separetely, it could be attached
- * to more parts, it is better to process always whole line feature (all parts)
- * of one id at the same time, and attache mileposts always to nearest one */
- /* Open the database for lines and points */
- Lfi = Vect_get_field(&In, lfield);
- if (Lfi == NULL)
- G_fatal_error(_("Cannot get layer info for lines"));
- Pfi = Vect_get_field(&PMap, pfield);
- if (Pfi == NULL)
- G_fatal_error(_("Cannot get layer info for points"));
- db_init_handle(&lhandle);
- db_init_string(&lstmt);
- ldriver = db_start_driver(Lfi->driver);
- db_set_handle(&lhandle, Lfi->database, NULL);
- if (db_open_database(ldriver, &lhandle) != DB_OK)
- G_fatal_error(_("Unable to open database <%s> by driver <%s>"),
- Lfi->database, Lfi->driver);
- db_init_handle(&phandle);
- db_init_string(&pstmt);
- pdriver = db_start_driver(Pfi->driver);
- db_set_handle(&phandle, Pfi->database, NULL);
- if (db_open_database(pdriver, &phandle) != DB_OK)
- G_fatal_error(_("Unable to open database <%s> by driver <%s>"),
- Pfi->database, Pfi->driver);
- /* Open database for RS table */
- 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"));
- /* Create new reference table */
- /* perhaps drop table to be conditionalized upon --o ? */
- if (db_table_exists(db_get_default_driver_name(),
- db_get_default_database_name(),
- table_opt->answer) == 1) {
- db_init_string(&rsstmt);
- sprintf(buf, "drop table %s", table_opt->answer);
- db_append_string(&rsstmt, buf);
- if (db_execute_immediate(rsdriver, &rsstmt) != DB_OK)
- G_warning(_("Unable to drop table: %s"), buf);
- }
- db_init_string(&rsstmt);
- sprintf(buf,
- "create table %s (rsid int, lcat int, lid int, start_map double precision, "
- "end_map double precision, start_mp double precision, start_off double precision, "
- "end_mp double precision, end_off double precision, end_type int)",
- table_opt->answer);
- G_debug(debug, "ref tab SQL: %s", buf);
- db_append_string(&rsstmt, buf);
- if (db_execute_immediate(rsdriver, &rsstmt) != DB_OK)
- G_fatal_error(_("Unable to create table: %s"), buf);
- /* Select all lid from line table */
- sprintf(buf, "select %s from %s", lidcol_opt->answer, Lfi->table);
- G_debug(debug, "line tab lid SQL: %s", buf);
- db_append_string(&lstmt, buf);
- if (db_open_select_cursor(ldriver, &lstmt, &lcursor, DB_SEQUENTIAL) !=
- DB_OK)
- G_fatal_error(_("Unable to select line id values from %s.%s."),
- Lfi->table, lidcol_opt->answer);
- /* TODO: we expect line id to be integer, extend to string later */
- ltable = db_get_cursor_table(&lcursor);
- lcolumn = db_get_table_column(ltable, 0); /* first column */
- lcoltype = db_get_column_sqltype(lcolumn);
- lccoltype = db_sqltype_to_Ctype(lcoltype);
- if (lccoltype != DB_C_TYPE_INT)
- G_fatal_error(_("Line id column must be integer"));
- lvalue = db_get_column_value(lcolumn);
- /* Fetch all line id and store it as unique value */
- G_debug(debug, "Fetch all line id");
- nLid = 0;
- aLid = 1;
- Lid_int = (int *)G_malloc(aLid * sizeof(int));
- while (1) {
- if (db_fetch(&lcursor, DB_NEXT, &more) != DB_OK)
- G_fatal_error(_("Unable to fetch line id from line table"));
- if (!more)
- break;
- lid = db_get_value_int(lvalue);
- found = 0;
- for (i = 0; i < nLid; i++) {
- if (Lid_int[i] == lid) {
- G_debug(debug, "lid = %d (duplicate)", lid);
- found = 1;
- break;
- }
- }
- if (!found) { /* add new lid */
- G_debug(debug, "lid = %d (new)", lid);
- if (nLid == aLid) {
- aLid += 1;
- Lid_int = (int *)G_realloc(Lid_int, aLid * sizeof(int));
- }
- Lid_int[nLid] = lid;
- nLid++;
- }
- }
- db_close_cursor(&lcursor);
- /* Allocate space for lines and points (maybe more than used, but not less ) */
- rlines = (RLINE *) G_malloc(Vect_get_num_lines(&In) * sizeof(RLINE));
- mposts =
- (MILEPOST *) G_malloc(Vect_get_num_lines(&PMap) * sizeof(MILEPOST));
- /* Go throuhg each line id */
- G_debug(debug, "Process each line id");
- rsid = 1;
- for (i = 0; i < nLid; i++) {
- lid = Lid_int[i];
- G_debug(debug, "lid = %d", lid);
- /* Select all LINES for current lid */
- sprintf(buf, "%s = %d", lidcol_opt->answer, lid);
- ncat = db_select_int(ldriver, Lfi->table, Lfi->key, buf, &cats);
- G_debug(debug, " %d cats selected:", ncat);
- /* Go through all lines and store numbers of those matching category */
- nrlines = 0;
- for (line = 1; line <= Vect_get_num_lines(&In); line++) {
- type = Vect_read_line(&In, LPoints, LCats, line);
- if (!(type & GV_LINES))
- continue;
- if (!(Vect_cat_get(LCats, lfield, &cat))) {
- G_warning(_("Line [%d] without category (layer [%d])"),
- line, lfield);
- continue;
- }
- for (j = 0; j < ncat; j++) {
- if (cat == cats[j]) {
- rlines[nrlines].line = line;
- rlines[nrlines].cat = cat;
- rlines[nrlines].length = Vect_line_length(LPoints);
- rlines[nrlines].nmposts = 0;
- rlines[nrlines].err = ERR_OK;
- nrlines++;
- break;
- }
- }
- }
- G_debug(debug, " %d lines selected for line id %d", nrlines, lid);
- free(cats);
- if (nrlines == 0) {
- G_warning(_("No lines selected for line id [%d]"), lid);
- continue;
- }
- /* Select all POINTS for current lid */
- /* Note: all attributes of MPs are saved in mposts, but if point of that
- * cat does not exist, or line is not in threshold line_idx is set
- * to PORT_DOUBLE_MAX, and such records are not used after qsort */
- /* Select all attributes for points */
- sprintf(buf, "select %s, %s, %s, %s, %s from %s where %s = %d",
- Pfi->key, start_mp_opt->answer, start_off_opt->answer,
- end_mp_opt->answer, end_off_opt->answer, Pfi->table,
- pidcol_opt->answer, lid);
- G_debug(debug, " SQL: %s", buf);
- db_init_string(&pstmt);
- db_append_string(&pstmt, buf);
- if (db_open_select_cursor(pdriver, &pstmt, &pcursor, DB_SEQUENTIAL) !=
- DB_OK)
- G_fatal_error(_("Unable to select point attributes from <%s>"),
- Pfi->table);
- ptable = db_get_cursor_table(&pcursor);
- nmposts = 0;
- while (1) {
- double mp_tmp, mp_tmp2, off_tmp, off_tmp2;
- if (db_fetch(&pcursor, DB_NEXT, &more) != DB_OK)
- G_fatal_error(_("Unable to fetch line id from line table"));
- if (!more)
- break;
- pcolumn = db_get_table_column(ptable, 0); /* first column */
- pvalue = db_get_column_value(pcolumn);
- mposts[nmposts].cat = db_get_value_int(pvalue);
- pcolumn = db_get_table_column(ptable, 1);
- pvalue = db_get_column_value(pcolumn);
- mp_tmp = db_get_value_double(pvalue);
- pcolumn = db_get_table_column(ptable, 2);
- pvalue = db_get_column_value(pcolumn);
- off_tmp = db_get_value_double(pvalue);
- /* mp must be integer */
- if (floor(mp_tmp) != mp_tmp) {
- mp_tmp2 = floor(mp_tmp);
- off_tmp2 = off_tmp + multip * (mp_tmp - mp_tmp2);
- G_warning(_("Milepost (start) %f+%f used as %f+%f (change MP to integer)"),
- mp_tmp, off_tmp, mp_tmp2, off_tmp2);
- }
- else {
- mp_tmp2 = mp_tmp;
- off_tmp2 = off_tmp;
- }
- mposts[nmposts].start_mp = mp_tmp2;
- mposts[nmposts].start_off = off_tmp2;
- pcolumn = db_get_table_column(ptable, 3);
- pvalue = db_get_column_value(pcolumn);
- mp_tmp = db_get_value_double(pvalue);
- pcolumn = db_get_table_column(ptable, 4);
- pvalue = db_get_column_value(pcolumn);
- off_tmp = db_get_value_double(pvalue);
- /* mp must be integer */
- if (floor(mp_tmp) != mp_tmp) {
- mp_tmp2 = floor(mp_tmp);
- off_tmp2 = off_tmp + multip * (mp_tmp - mp_tmp2);
- G_warning(_("Milepost (end) %f+%f used as %f+%f (change MP to integer)"),
- mp_tmp, off_tmp, mp_tmp2, off_tmp2);
- }
- else {
- mp_tmp2 = mp_tmp;
- off_tmp2 = off_tmp;
- }
- mposts[nmposts].end_mp = mp_tmp2;
- mposts[nmposts].end_off = off_tmp2;
- mposts[nmposts].line_idx = PORT_INT_MAX;
- mposts[nmposts].err = ERR_NO_POINT;
- nmposts++;
- }
- db_close_cursor(&pcursor);
- G_debug(debug, " %d mileposts selected from db", nmposts);
- /* Go through all points and store numbers of those matching category and within
- * threshold of any line*/
- for (point = 1; point <= Vect_get_num_lines(&PMap); point++) {
- type = Vect_read_line(&PMap, PPoints, PCats, point);
- if (!(type & GV_POINT))
- continue;
- if (!(Vect_cat_get(PCats, pfield, &cat))) {
- G_warning(_("Point [%d] without category (layer [%d])"),
- point, pfield);
- continue;
- }
- mpost = -1;
- for (j = 0; j < nmposts; j++) {
- if (cat == mposts[j].cat) {
- mpost = j;
- break;
- }
- }
- if (mpost >= 0) {
- G_debug(debug, " Point %d:", point);
- mposts[mpost].line_idx = ERR_OK;
- mposts[mpost].x = PPoints->x[0];
- mposts[mpost].y = PPoints->y[0];
- /* Find the nearest line from selection set and fill in the
- * structure the distance from the beginning */
- distance_to = PORT_DOUBLE_MAX;
- for (j = 0; j < nrlines; j++) {
- line = rlines[j].line;
- Vect_read_line(&In, LPoints, NULL, line);
- Vect_line_distance(LPoints, PPoints->x[0], PPoints->y[0],
- 0, 0, NULL, NULL, NULL, &dist_to, NULL,
- &dist_along);
- G_debug(debug,
- " line %d dist to line = %f, dist along line = %f",
- line, dist_to, dist_along);
- if (dist_to < distance_to) {
- distance_to = dist_to;
- mposts[mpost].line_idx = j;
- mposts[mpost].dist_along = dist_along;
- }
- }
- /* Check if in threshold */
- if (distance_to <= thresh) {
- G_debug(debug,
- "Point = %d cat = %d line = %d, distance = %f",
- point, cat, mposts[mpost].line_idx, distance_to);
- G_debug(debug,
- " start_mp = %f start_off = %f end_mp = %f end_off = %f",
- mposts[mpost].start_mp, mposts[mpost].start_off,
- mposts[mpost].end_mp, mposts[mpost].end_off);
- }
- else {
- mposts[mpost].line_idx = PORT_INT_MAX;
- mposts[mpost].err = ERR_THRESHOLD;
- G_warning(_("Point [%d] cat [%d] is out of threshold (distance = %f)"),
- point, cat, distance_to);
- }
- }
- }
- G_debug(debug, " %d points attached to line(s) of line id %d",
- nmposts, lid);
- /* Sort MPs according to line_idx, and dist_along */
- qsort((void *)mposts, nmposts, sizeof(MILEPOST), cmp_along);
- /* Reduce nmposts to exclude not attached db records of MPs from processing */
- nallmposts = nmposts;
- for (j = 0; j < nmposts; j++) {
- if (mposts[j].line_idx == PORT_INT_MAX) {
- nmposts = j;
- break;
- }
- }
- G_debug(debug, " %d mileposts attached to line(s)", nmposts);
- /* Go thourough all attached MPs and fill in info about MPs to 'lines' table */
- last = -1;
- for (j = 0; j < nmposts; j++) {
- G_debug(debug, " line_idx = %d, point cat = %d dist_along = %f",
- mposts[j].line_idx, mposts[j].cat, mposts[j].dist_along);
- if (mposts[j].line_idx != last) { /* line_index changed */
- rlines[mposts[j].line_idx].first_mpost_idx = j;
- rlines[mposts[j].line_idx].nmposts = 1;
- }
- else {
- rlines[mposts[j].line_idx].nmposts++;
- }
- last = mposts[j].line_idx;
- }
- /* 1) Check number of MP
- * 2) Guess direction: find direction for each segment between 2 MPs and at the end
- * compare number of segmnets in both directions, if equal assign DIR_UNKNOWN. */
- for (j = 0; j < nrlines; j++) {
- G_debug(debug,
- " Guess direction line_idx = %d, cat = %d, nmposts = %d first_mpost_idx = %d",
- j, rlines[j].cat, rlines[j].nmposts,
- rlines[j].first_mpost_idx);
- forward = 0;
- backward = 0;
- rlines[j].direction = DIR_UNKNOWN;
- if (rlines[j].nmposts == 0)
- rlines[j].err = ERR_NO_MP;
- if (rlines[j].nmposts == 1)
- rlines[j].err = ERR_ONE_MP;
- if (rlines[j].nmposts < 2) {
- /* TODO?: in some cases could be possible to guess from one point?
- * E.g. start_mp = 0 for -+------------- */
- continue;
- }
- first = rlines[j].first_mpost_idx;
- last = first + rlines[j].nmposts - 1;
- for (k = first; k < last; k++) {
- if ((mposts[k].start_mp < mposts[k + 1].start_mp) ||
- ((mposts[k].start_mp == mposts[k + 1].start_mp) &&
- (mposts[k].start_off < mposts[k + 1].start_off))) {
- forward++;
- G_debug(debug, " segment direction forward %d",
- mposts[k].cat);
- }
- else {
- backward++;
- G_debug(debug, " segment direction backward %d",
- mposts[k].cat);
- }
- }
- G_debug(debug, " forward = %d backward = %d", forward, backward);
- if (forward > backward) {
- rlines[j].direction = DIR_FORWARD;
- G_debug(debug, " line direction forward");
- }
- else if (forward < backward) {
- rlines[j].direction = DIR_BACKWARD;
- /* Recalculate distances from the other end */
- for (k = first; k <= last; k++)
- mposts[k].dist_along =
- rlines[j].length - mposts[k].dist_along;
- G_debug(debug, " line direction backward");
- }
- else {
- rlines[j].err = ERR_NO_DIR;
- G_debug(debug, " line direction unknown");
- }
- }
- /* Sort MPs again according to line_idx, and dist_along but with correct direction */
- qsort((void *)mposts, nmposts, sizeof(MILEPOST), cmp_along);
- /* Check order of MPs along the line and write LRS for line */
- for (j = 0; j < nrlines; j++) {
- G_debug(debug,
- "MAKE LR: line_idx = %d, nmposts = %d first_mpost_idx = %d",
- j, rlines[j].nmposts, rlines[j].first_mpost_idx);
- first = rlines[j].first_mpost_idx;
- last = first + rlines[j].nmposts - 1;
- /* Check order of MPs along the line if possible */
- order = 0; /* wrong order in case nmposts < 2 or DIR_UNKNOWN */
- if (rlines[j].nmposts >= 2 && rlines[j].direction != DIR_UNKNOWN) {
- /* Note: some MP could have more errors at the time, only first is recorded */
- G_debug(debug, "Check order of MPs along the line");
- order = 1; /* first we expect that MP order is OK */
- for (k = first; k <= last; k++) {
- G_debug(debug, " point cat = %d dist_along = %f",
- mposts[k].cat, mposts[k].dist_along);
- G_debug(debug,
- " start_mp = %f start_off = %f end_mp = %f end_off = %f",
- mposts[k].start_mp, mposts[k].start_off,
- mposts[k].end_mp, mposts[k].end_off);
- /* Do not break after first error, to get printed all errors */
- /* 1) For each MP must be end <= start */
- ret =
- LR_cmp_mileposts(mposts[k].end_mp, mposts[k].end_off,
- mposts[k].start_mp,
- mposts[k].start_off);
- if (ret == 1) { /* end > start */
- G_warning(_("End > start for point cat [%d]"),
- mposts[k].cat);
- mposts[k].err = ERR_END_GT_START;
- order = 0;
- continue;
- }
- if (k < last) { /* each segment ( MP <-> nextMP ) */
- if (mposts[k + 1].end_mp > 0 ||
- mposts[k + 1].end_off > 0) {
- /* 2) For 2 MPs must be first.start < second.end
- * if end > 0 ( otherwise it is considered to be NULL ) */
- ret =
- LR_cmp_mileposts(mposts[k].start_mp,
- mposts[k].start_off,
- mposts[k + 1].end_mp,
- mposts[k + 1].end_off);
- if (ret > -1) { /* start >= end */
- G_warning(_("Start of 1. MP >= end of 2. MP for points' "
- "cats %[d], [%d]"), mposts[k].cat,
- mposts[k + 1].cat);
- mposts[k].err = ERR_END_GT_START;
- order = 0;
- continue;
- }
- }
- else {
- /* 3) For 2 MPs must be first.start < second.start
- * if end = 0 ( NULL, not used ) */
- ret =
- LR_cmp_mileposts(mposts[k].start_mp,
- mposts[k].start_off,
- mposts[k + 1].start_mp,
- mposts[k + 1].start_off);
- if (ret > -1) { /* start > end */
- G_warning(_("Start of 1. MP >= start of 2. MP for points' "
- "cats [%d], [%d]"), mposts[k].cat,
- mposts[k + 1].cat);
- mposts[k].err = ERR_END_GT_START;
- order = 0;
- continue;
- }
- }
- /* 4) For 2 MPs must be distance along line different (duplicate points) */
- if (mposts[k].dist_along == mposts[k + 1].dist_along) {
- G_warning(_("Distance along line identical for points' "
- "cats [%d], [%d]"), mposts[k].cat,
- mposts[k + 1].cat);
- mposts[k].err = ERR_IDENT;
- mposts[k + 1].err = ERR_IDENT;
- order = 0;
- }
- }
- }
- }
- /* Write errors if any ( and continue ) */
- if (!order) { /* something is wrong */
- if (rlines[j].nmposts < 2) { /* Impossible to get reference/direction */
- G_warning(_("Not enough points (%d) attached to the line (cat %d), "
- "line skip."), rlines[j].nmposts,
- rlines[j].cat);
- }
- else if (rlines[j].direction == DIR_UNKNOWN) { /* Unknown direction */
- G_warning(_("Unable to guess direction for the line (cat %d), "
- "line skip."), rlines[j].cat);
- }
- else {
- G_warning(_("Incorrect order of points along line cat [%d]"),
- rlines[j].cat);
- rlines[j].err = ERR_LINE_ORDER;
- }
- /* Write line errors */
- if (err_opt->answer) {
- Vect_reset_cats(LCats);
- Vect_read_line(&In, LPoints, NULL, rlines[j].line);
- Vect_cat_set(LCats, 1, rlines[j].err);
- Vect_write_line(&EMap, GV_LINE, LPoints, LCats);
- }
- continue;
- }
- /* Order is correct and we can store reference records for this line */
- G_debug(debug,
- " lcat | lid | start_map | end_map | start_mp | start_off | end_mp | end_off | end type");
- for (k = first; k < last; k++) {
- /* Decide which value to use for end */
- if (mposts[k + 1].end_mp > 0 || mposts[k + 1].end_off > 0) { /* TODO: use NULL ? */
- totype = TO_TYPE_USER;
- end_mp = mposts[k + 1].end_mp;
- end_off = mposts[k + 1].end_off;
- }
- else { /* values not specified -> use start values from next MP */
- totype = TO_TYPE_MAP;
- end_mp = mposts[k + 1].start_mp;
- end_off = mposts[k + 1].start_off;
- }
- G_debug(debug,
- " %5d | %5d | %9.3f | %9.3f | %9.3f | %9.3f | %9.3f | %9.3f | %1d",
- rlines[j].cat, lid, mposts[k].dist_along,
- mposts[k + 1].dist_along, mposts[k].start_mp,
- mposts[k].start_off, end_mp, end_off, totype);
- sprintf(buf,
- "insert into %s (rsid, lcat, lid, start_map, end_map, "
- "start_mp, start_off, end_mp, end_off, end_type) "
- "values ( %d, %d, %d, %f, %f, %f, %f, %f, %f, %d )",
- table_opt->answer, rsid, rlines[j].cat, lid,
- mposts[k].dist_along, mposts[k + 1].dist_along,
- mposts[k].start_mp, mposts[k].start_off, end_mp,
- end_off, totype);
- G_debug(debug, " SQL: %s", buf);
- db_init_string(&rsstmt);
- db_append_string(&rsstmt, buf);
- if (db_execute_immediate(rsdriver, &rsstmt) != DB_OK)
- G_fatal_error(_("Unable to insert reference records: %s"),
- buf);
- rsid++;
- }
- /* Write the line to output */
- type = Vect_read_line(&In, LPoints, LCats, rlines[j].line);
- Vect_reset_line(L2Points);
- if (rlines[j].direction == DIR_FORWARD)
- dir = GV_FORWARD;
- else
- dir = GV_BACKWARD;
- Vect_append_points(L2Points, LPoints, dir);
- Vect_write_line(&Out, type, L2Points, LCats);
- }
- /* Write MP errors for all points, also those out of threshold */
- if (err_opt->answer) {
- for (k = 0; k < nallmposts; k++) {
- if (mposts[k].err != ERR_OK && mposts[k].err != ERR_NO_POINT) {
- Vect_reset_line(PPoints);
- Vect_reset_cats(PCats);
- Vect_append_point(PPoints, mposts[k].x, mposts[k].y, 0);
- Vect_cat_set(PCats, 1, mposts[k].err);
- Vect_write_line(&EMap, GV_POINT, PPoints, PCats);
- }
- }
- }
- }
- db_close_database_shutdown_driver(rsdriver);
- db_close_database_shutdown_driver(pdriver);
- db_close_database_shutdown_driver(ldriver);
- Vect_close(&In);
- Vect_close(&PMap);
- G_message(_("Building topology for output (out_lines) map..."));
- Vect_build(&Out, stderr);
- Vect_close(&Out);
- /* Write errors */
- if (err_opt->answer) {
- G_message(_("Building topology for error (err) map..."));
- Vect_build(&EMap, stderr);
- Vect_close(&EMap);
- }
- exit(EXIT_SUCCESS);
- }
- int cmp_along(const void *pa, const void *pb)
- {
- MILEPOST *p1 = (MILEPOST *) pa;
- MILEPOST *p2 = (MILEPOST *) pb;
- /* compare line_idx */
- if (p1->line_idx < p2->line_idx)
- return -1;
- if (p1->line_idx > p2->line_idx)
- return 1;
- /* the same line_idx -> compare dist_along */
- if (p1->dist_along < p2->dist_along)
- return -1;
- if (p1->dist_along > p2->dist_along)
- return 1;
- return 0;
- }
|