main.c 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893
  1. /*
  2. * v.lrs.create - Create Linear reference system
  3. */
  4. /******************************************************************************
  5. * Copyright (c) 2004, Radim Blazek (blazek@itc.it)
  6. *
  7. * Permission is hereby granted, free of charge, to any person obtaining a
  8. * copy of this software and associated documentation files (the "Software"),
  9. * to deal in the Software without restriction, including without limitation
  10. * the rights to use, copy, modify, merge, publish, distribute, sublicense,
  11. * and/or sell copies of the Software, and to permit persons to whom the
  12. * Software is furnished to do so, subject to the following conditions:
  13. *
  14. * The above copyright notice and this permission notice shall be included
  15. * in all copies or substantial portions of the Software.
  16. *
  17. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
  18. * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  19. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
  20. * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  21. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  22. * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  23. * DEALINGS IN THE SOFTWARE.
  24. *****************************************************************************/
  25. #include <stdlib.h>
  26. #include <string.h>
  27. #include <time.h>
  28. #include <grass/gis.h>
  29. #include <grass/Vect.h>
  30. #include <grass/dbmi.h>
  31. #include <grass/glocale.h>
  32. #include "../lib/lrs.h"
  33. /* MP is milepost */
  34. #define TO_TYPE_FROM 1 /* the same as specified fo from_ */
  35. #define TO_TYPE_MAP 2 /* calculated from map along the line from previous MP */
  36. #define TO_TYPE_USER 3 /* defined by user */
  37. #define DIR_FORWARD 1
  38. #define DIR_BACKWARD 2
  39. #define DIR_UNKNOWN 3
  40. #define ERR_OK 0 /* No error */
  41. #define ERR_END_GT_START 1 /* MP: end > start */
  42. #define ERR_THRESHOLD 2 /* MP is outside threshold */
  43. #define ERR_IDENT 3 /* more MPs with identical distance along the line */
  44. #define ERR_ORDER 4 /* MP in wrong order (used for points) */
  45. #define ERR_NO_POINT 5 /* No MP point found for MP DB record */
  46. #define ERR_NO_MP 6 /* Line without MP */
  47. #define ERR_ONE_MP 7 /* Line with 1 MP only */
  48. #define ERR_NO_DIR 8 /* Unknown direction of line */
  49. #define ERR_LINE_ORDER 9 /* Wrong order of MP along line (used for lines) */
  50. typedef struct
  51. {
  52. double x, y;
  53. int cat;
  54. int line_idx; /* index to line in 'lines' array */
  55. double dist_along; /* distance from the beginning of the line */
  56. double start_mp, start_off; /* milepost, offset for the beginning of ref. segment */
  57. double end_mp, end_off; /* milepost, offset for the end of ref. segment */
  58. int to_type; /* type of the end_mp, end_off */
  59. int err; /* error number */
  60. } MILEPOST;
  61. typedef struct
  62. {
  63. int line, cat; /* line number in 'In' vector and category of nearest line */
  64. int nmposts; /* number of attached MPs */
  65. int first_mpost_idx; /* index of first/last MP in 'mposts' */
  66. int direction; /* direction DIR_FORWARD/DIR_BACKWARD if MPs have increasing/decreasing */
  67. /* values along the line */
  68. double length; /* line length */
  69. int err; /* error number */
  70. } RLINE;
  71. int cmp_along(const void *pa, const void *pb);
  72. int main(int argc, char **argv)
  73. {
  74. int i, j, k, lid, more, found, ret, totype, rsid, dir, forward, backward,
  75. order;
  76. int *Lid_int, nLid, aLid;
  77. int cat, *cats, ncat, lfield, pfield;
  78. int line, first, last, point, type;
  79. int nrlines, nmposts, nallmposts, mpost;
  80. MILEPOST *mposts /* table of currently referenced MPs */ ;
  81. RLINE *rlines; /* table of currently referenced lines */
  82. double thresh, dist_to, dist_along, distance_to;
  83. double end_mp, end_off;
  84. double multip = 1000; /* Number of map units per MP unit */
  85. struct Option *in_lines_opt, *out_lines_opt, *points_opt, *err_opt;
  86. struct Option *lfield_opt, *pfield_opt;
  87. struct Option *lidcol_opt, *pidcol_opt;
  88. struct Option *start_mp_opt, *start_off_opt, *end_mp_opt, *end_off_opt;
  89. struct Option *driver_opt, *database_opt, *table_opt, *thresh_opt;
  90. struct GModule *module;
  91. char *mapset, buf[2000];
  92. const char *drv, *db;
  93. struct Map_info In, Out, PMap, EMap;
  94. struct line_cats *LCats, *PCats;
  95. struct line_pnts *LPoints, *L2Points, *PPoints;
  96. struct field_info *Lfi, *Pfi;
  97. dbDriver *ldriver, *pdriver, *rsdriver;
  98. dbHandle lhandle, phandle, rshandle;
  99. dbString lstmt, pstmt, rsstmt;
  100. dbCursor lcursor, pcursor;
  101. dbTable *ltable, *ptable;
  102. dbColumn *lcolumn, *pcolumn;
  103. dbValue *lvalue, *pvalue;
  104. int lcoltype, lccoltype;
  105. int debug = 2;
  106. G_gisinit(argv[0]);
  107. module = G_define_module();
  108. module->keywords = _("vector, LRS, networking");
  109. module->description = _("Create Linear Reference System");
  110. in_lines_opt = G_define_standard_option(G_OPT_V_INPUT);
  111. in_lines_opt->key = "in_lines";
  112. in_lines_opt->description = _("Input vector map containing lines");
  113. out_lines_opt = G_define_standard_option(G_OPT_V_OUTPUT);
  114. out_lines_opt->key = "out_lines";
  115. out_lines_opt->description =
  116. _("Output vector map where oriented lines are written");
  117. err_opt = G_define_standard_option(G_OPT_V_OUTPUT);
  118. err_opt->key = "err";
  119. err_opt->required = NO;
  120. err_opt->description = _("Output vector map of errors");
  121. points_opt = G_define_standard_option(G_OPT_V_INPUT);
  122. points_opt->key = "points";
  123. points_opt->description =
  124. _("Input vector map containing reference points");
  125. lfield_opt = G_define_standard_option(G_OPT_V_FIELD);
  126. lfield_opt->key = "llayer";
  127. lfield_opt->description = _("Line layer");
  128. pfield_opt = G_define_standard_option(G_OPT_V_FIELD);
  129. pfield_opt->key = "player";
  130. pfield_opt->description = _("Point layer");
  131. lidcol_opt = G_define_option();
  132. lidcol_opt->key = "lidcol";
  133. lidcol_opt->type = TYPE_STRING;
  134. lidcol_opt->required = YES;
  135. lidcol_opt->description =
  136. _("Column containing line identifiers for lines");
  137. pidcol_opt = G_define_option();
  138. pidcol_opt->key = "pidcol";
  139. pidcol_opt->type = TYPE_STRING;
  140. pidcol_opt->required = YES;
  141. pidcol_opt->description =
  142. _("Column containing line identifiers for points");
  143. start_mp_opt = G_define_option();
  144. start_mp_opt->key = "start_mp";
  145. start_mp_opt->type = TYPE_STRING;
  146. start_mp_opt->required = NO;
  147. start_mp_opt->answer = "start_mp";
  148. start_mp_opt->description =
  149. _("Column containing milepost position for the beginning "
  150. "of next segment");
  151. start_off_opt = G_define_option();
  152. start_off_opt->key = "start_off";
  153. start_off_opt->type = TYPE_STRING;
  154. start_off_opt->required = NO;
  155. start_off_opt->answer = "start_off";
  156. start_off_opt->description =
  157. _("Column containing offset from milepost for the beginning "
  158. "of next segment");
  159. end_mp_opt = G_define_option();
  160. end_mp_opt->key = "end_mp";
  161. end_mp_opt->type = TYPE_STRING;
  162. end_mp_opt->required = NO;
  163. end_mp_opt->answer = "end_mp";
  164. end_mp_opt->description =
  165. _("Column containing milepost position for the end "
  166. "of previous segment");
  167. end_off_opt = G_define_option();
  168. end_off_opt->key = "end_off";
  169. end_off_opt->type = TYPE_STRING;
  170. end_off_opt->required = NO;
  171. end_off_opt->answer = "end_off";
  172. end_off_opt->description =
  173. _("Column containing offset from milepost for the end "
  174. "of previous segment");
  175. driver_opt = G_define_option();
  176. driver_opt->key = "rsdriver";
  177. driver_opt->type = TYPE_STRING;
  178. driver_opt->required = NO;
  179. driver_opt->description = _("Driver name for reference system table");
  180. if ((drv = db_get_default_driver_name()))
  181. driver_opt->answer = drv;
  182. database_opt = G_define_option();
  183. database_opt->key = "rsdatabase";
  184. database_opt->type = TYPE_STRING;
  185. database_opt->required = NO;
  186. database_opt->description = _("Database name for reference system table");
  187. if ((db = db_get_default_database_name()))
  188. database_opt->answer = db;
  189. table_opt = G_define_option();
  190. table_opt->key = "rstable";
  191. table_opt->type = TYPE_STRING;
  192. table_opt->required = YES;
  193. table_opt->label =
  194. _("Name of table where the reference system will be written");
  195. table_opt->description = _("New table is created by this module");
  196. thresh_opt = G_define_option();
  197. thresh_opt->key = "thresh";
  198. thresh_opt->type = TYPE_DOUBLE;
  199. thresh_opt->required = NO;
  200. thresh_opt->answer = "1";
  201. thresh_opt->description = _("Maximum distance of point to line allowed");
  202. if (G_parser(argc, argv))
  203. exit(EXIT_FAILURE);
  204. LCats = Vect_new_cats_struct();
  205. PCats = Vect_new_cats_struct();
  206. LPoints = Vect_new_line_struct();
  207. L2Points = Vect_new_line_struct();
  208. PPoints = Vect_new_line_struct();
  209. lfield = atoi(lfield_opt->answer);
  210. pfield = atoi(pfield_opt->answer);
  211. thresh = atof(thresh_opt->answer);
  212. G_debug(debug, "Creating LRS ...");
  213. /* Open input lines */
  214. mapset = G_find_vector2(in_lines_opt->answer, NULL);
  215. if (mapset == NULL)
  216. G_fatal_error(_("Vector map <%s> not found"), in_lines_opt->answer);
  217. Vect_open_old(&In, in_lines_opt->answer, mapset);
  218. /* Open input ipoints */
  219. mapset = G_find_vector2(points_opt->answer, NULL);
  220. if (mapset == NULL)
  221. G_fatal_error(_("Vector map <%s> not found"), points_opt->answer);
  222. Vect_open_old(&PMap, points_opt->answer, mapset);
  223. /* Open output lines */
  224. Vect_open_new(&Out, out_lines_opt->answer, Vect_is_3d(&In));
  225. /* Open output error map */
  226. if (err_opt->answer)
  227. Vect_open_new(&EMap, err_opt->answer, Vect_is_3d(&In));
  228. /* Because the line feature identified by one id (lidcol) may be split
  229. * to more line parts, and milepost may be in threshold for more such parts,
  230. * so that if each line part would be processed separetely, it could be attached
  231. * to more parts, it is better to process always whole line feature (all parts)
  232. * of one id at the same time, and attache mileposts always to nearest one */
  233. /* Open the database for lines and points */
  234. Lfi = Vect_get_field(&In, lfield);
  235. if (Lfi == NULL)
  236. G_fatal_error(_("Cannot get layer info for lines"));
  237. Pfi = Vect_get_field(&PMap, pfield);
  238. if (Pfi == NULL)
  239. G_fatal_error(_("Cannot get layer info for points"));
  240. db_init_handle(&lhandle);
  241. db_init_string(&lstmt);
  242. ldriver = db_start_driver(Lfi->driver);
  243. db_set_handle(&lhandle, Lfi->database, NULL);
  244. if (db_open_database(ldriver, &lhandle) != DB_OK)
  245. G_fatal_error(_("Unable to open database <%s> by driver <%s>"),
  246. Lfi->database, Lfi->driver);
  247. db_init_handle(&phandle);
  248. db_init_string(&pstmt);
  249. pdriver = db_start_driver(Pfi->driver);
  250. db_set_handle(&phandle, Pfi->database, NULL);
  251. if (db_open_database(pdriver, &phandle) != DB_OK)
  252. G_fatal_error(_("Unable to open database <%s> by driver <%s>"),
  253. Pfi->database, Pfi->driver);
  254. /* Open database for RS table */
  255. db_init_handle(&rshandle);
  256. db_init_string(&rsstmt);
  257. rsdriver = db_start_driver(driver_opt->answer);
  258. db_set_handle(&rshandle, database_opt->answer, NULL);
  259. if (db_open_database(rsdriver, &rshandle) != DB_OK)
  260. G_fatal_error(_("Unable to open database for reference table"));
  261. /* Create new reference table */
  262. /* perhaps drop table to be conditionalized upon --o ? */
  263. if (db_table_exists(db_get_default_driver_name(),
  264. db_get_default_database_name(),
  265. table_opt->answer) == 1) {
  266. db_init_string(&rsstmt);
  267. sprintf(buf, "drop table %s", table_opt->answer);
  268. db_append_string(&rsstmt, buf);
  269. if (db_execute_immediate(rsdriver, &rsstmt) != DB_OK)
  270. G_warning(_("Unable to drop table: %s"), buf);
  271. }
  272. db_init_string(&rsstmt);
  273. sprintf(buf,
  274. "create table %s (rsid int, lcat int, lid int, start_map double precision, "
  275. "end_map double precision, start_mp double precision, start_off double precision, "
  276. "end_mp double precision, end_off double precision, end_type int)",
  277. table_opt->answer);
  278. G_debug(debug, "ref tab SQL: %s", buf);
  279. db_append_string(&rsstmt, buf);
  280. if (db_execute_immediate(rsdriver, &rsstmt) != DB_OK)
  281. G_fatal_error(_("Unable to create table: %s"), buf);
  282. /* Select all lid from line table */
  283. sprintf(buf, "select %s from %s", lidcol_opt->answer, Lfi->table);
  284. G_debug(debug, "line tab lid SQL: %s", buf);
  285. db_append_string(&lstmt, buf);
  286. if (db_open_select_cursor(ldriver, &lstmt, &lcursor, DB_SEQUENTIAL) !=
  287. DB_OK)
  288. G_fatal_error(_("Unable to select line id values from %s.%s."),
  289. Lfi->table, lidcol_opt->answer);
  290. /* TODO: we expect line id to be integer, extend to string later */
  291. ltable = db_get_cursor_table(&lcursor);
  292. lcolumn = db_get_table_column(ltable, 0); /* first column */
  293. lcoltype = db_get_column_sqltype(lcolumn);
  294. lccoltype = db_sqltype_to_Ctype(lcoltype);
  295. if (lccoltype != DB_C_TYPE_INT)
  296. G_fatal_error(_("Line id column must be integer"));
  297. lvalue = db_get_column_value(lcolumn);
  298. /* Fetch all line id and store it as unique value */
  299. G_debug(debug, "Fetch all line id");
  300. nLid = 0;
  301. aLid = 1;
  302. Lid_int = (int *)G_malloc(aLid * sizeof(int));
  303. while (1) {
  304. if (db_fetch(&lcursor, DB_NEXT, &more) != DB_OK)
  305. G_fatal_error(_("Unable to fetch line id from line table"));
  306. if (!more)
  307. break;
  308. lid = db_get_value_int(lvalue);
  309. found = 0;
  310. for (i = 0; i < nLid; i++) {
  311. if (Lid_int[i] == lid) {
  312. G_debug(debug, "lid = %d (duplicate)", lid);
  313. found = 1;
  314. break;
  315. }
  316. }
  317. if (!found) { /* add new lid */
  318. G_debug(debug, "lid = %d (new)", lid);
  319. if (nLid == aLid) {
  320. aLid += 1;
  321. Lid_int = (int *)G_realloc(Lid_int, aLid * sizeof(int));
  322. }
  323. Lid_int[nLid] = lid;
  324. nLid++;
  325. }
  326. }
  327. db_close_cursor(&lcursor);
  328. /* Allocate space for lines and points (maybe more than used, but not less ) */
  329. rlines = (RLINE *) G_malloc(Vect_get_num_lines(&In) * sizeof(RLINE));
  330. mposts =
  331. (MILEPOST *) G_malloc(Vect_get_num_lines(&PMap) * sizeof(MILEPOST));
  332. /* Go throuhg each line id */
  333. G_debug(debug, "Process each line id");
  334. rsid = 1;
  335. for (i = 0; i < nLid; i++) {
  336. lid = Lid_int[i];
  337. G_debug(debug, "lid = %d", lid);
  338. /* Select all LINES for current lid */
  339. sprintf(buf, "%s = %d", lidcol_opt->answer, lid);
  340. ncat = db_select_int(ldriver, Lfi->table, Lfi->key, buf, &cats);
  341. G_debug(debug, " %d cats selected:", ncat);
  342. /* Go through all lines and store numbers of those matching category */
  343. nrlines = 0;
  344. for (line = 1; line <= Vect_get_num_lines(&In); line++) {
  345. type = Vect_read_line(&In, LPoints, LCats, line);
  346. if (!(type & GV_LINES))
  347. continue;
  348. if (!(Vect_cat_get(LCats, lfield, &cat))) {
  349. G_warning(_("Line [%d] without category (layer [%d])"),
  350. line, lfield);
  351. continue;
  352. }
  353. for (j = 0; j < ncat; j++) {
  354. if (cat == cats[j]) {
  355. rlines[nrlines].line = line;
  356. rlines[nrlines].cat = cat;
  357. rlines[nrlines].length = Vect_line_length(LPoints);
  358. rlines[nrlines].nmposts = 0;
  359. rlines[nrlines].err = ERR_OK;
  360. nrlines++;
  361. break;
  362. }
  363. }
  364. }
  365. G_debug(debug, " %d lines selected for line id %d", nrlines, lid);
  366. free(cats);
  367. if (nrlines == 0) {
  368. G_warning(_("No lines selected for line id [%d]"), lid);
  369. continue;
  370. }
  371. /* Select all POINTS for current lid */
  372. /* Note: all attributes of MPs are saved in mposts, but if point of that
  373. * cat does not exist, or line is not in threshold line_idx is set
  374. * to PORT_DOUBLE_MAX, and such records are not used after qsort */
  375. /* Select all attributes for points */
  376. sprintf(buf, "select %s, %s, %s, %s, %s from %s where %s = %d",
  377. Pfi->key, start_mp_opt->answer, start_off_opt->answer,
  378. end_mp_opt->answer, end_off_opt->answer, Pfi->table,
  379. pidcol_opt->answer, lid);
  380. G_debug(debug, " SQL: %s", buf);
  381. db_init_string(&pstmt);
  382. db_append_string(&pstmt, buf);
  383. if (db_open_select_cursor(pdriver, &pstmt, &pcursor, DB_SEQUENTIAL) !=
  384. DB_OK)
  385. G_fatal_error(_("Unable to select point attributes from <%s>"),
  386. Pfi->table);
  387. ptable = db_get_cursor_table(&pcursor);
  388. nmposts = 0;
  389. while (1) {
  390. double mp_tmp, mp_tmp2, off_tmp, off_tmp2;
  391. if (db_fetch(&pcursor, DB_NEXT, &more) != DB_OK)
  392. G_fatal_error(_("Unable to fetch line id from line table"));
  393. if (!more)
  394. break;
  395. pcolumn = db_get_table_column(ptable, 0); /* first column */
  396. pvalue = db_get_column_value(pcolumn);
  397. mposts[nmposts].cat = db_get_value_int(pvalue);
  398. pcolumn = db_get_table_column(ptable, 1);
  399. pvalue = db_get_column_value(pcolumn);
  400. mp_tmp = db_get_value_double(pvalue);
  401. pcolumn = db_get_table_column(ptable, 2);
  402. pvalue = db_get_column_value(pcolumn);
  403. off_tmp = db_get_value_double(pvalue);
  404. /* mp must be integer */
  405. if (floor(mp_tmp) != mp_tmp) {
  406. mp_tmp2 = floor(mp_tmp);
  407. off_tmp2 = off_tmp + multip * (mp_tmp - mp_tmp2);
  408. G_warning(_("Milepost (start) %f+%f used as %f+%f (change MP to integer)"),
  409. mp_tmp, off_tmp, mp_tmp2, off_tmp2);
  410. }
  411. else {
  412. mp_tmp2 = mp_tmp;
  413. off_tmp2 = off_tmp;
  414. }
  415. mposts[nmposts].start_mp = mp_tmp2;
  416. mposts[nmposts].start_off = off_tmp2;
  417. pcolumn = db_get_table_column(ptable, 3);
  418. pvalue = db_get_column_value(pcolumn);
  419. mp_tmp = db_get_value_double(pvalue);
  420. pcolumn = db_get_table_column(ptable, 4);
  421. pvalue = db_get_column_value(pcolumn);
  422. off_tmp = db_get_value_double(pvalue);
  423. /* mp must be integer */
  424. if (floor(mp_tmp) != mp_tmp) {
  425. mp_tmp2 = floor(mp_tmp);
  426. off_tmp2 = off_tmp + multip * (mp_tmp - mp_tmp2);
  427. G_warning(_("Milepost (end) %f+%f used as %f+%f (change MP to integer)"),
  428. mp_tmp, off_tmp, mp_tmp2, off_tmp2);
  429. }
  430. else {
  431. mp_tmp2 = mp_tmp;
  432. off_tmp2 = off_tmp;
  433. }
  434. mposts[nmposts].end_mp = mp_tmp2;
  435. mposts[nmposts].end_off = off_tmp2;
  436. mposts[nmposts].line_idx = PORT_INT_MAX;
  437. mposts[nmposts].err = ERR_NO_POINT;
  438. nmposts++;
  439. }
  440. db_close_cursor(&pcursor);
  441. G_debug(debug, " %d mileposts selected from db", nmposts);
  442. /* Go through all points and store numbers of those matching category and within
  443. * threshold of any line*/
  444. for (point = 1; point <= Vect_get_num_lines(&PMap); point++) {
  445. type = Vect_read_line(&PMap, PPoints, PCats, point);
  446. if (!(type & GV_POINT))
  447. continue;
  448. if (!(Vect_cat_get(PCats, pfield, &cat))) {
  449. G_warning(_("Point [%d] without category (layer [%d])"),
  450. point, pfield);
  451. continue;
  452. }
  453. mpost = -1;
  454. for (j = 0; j < nmposts; j++) {
  455. if (cat == mposts[j].cat) {
  456. mpost = j;
  457. break;
  458. }
  459. }
  460. if (mpost >= 0) {
  461. G_debug(debug, " Point %d:", point);
  462. mposts[mpost].line_idx = ERR_OK;
  463. mposts[mpost].x = PPoints->x[0];
  464. mposts[mpost].y = PPoints->y[0];
  465. /* Find the nearest line from selection set and fill in the
  466. * structure the distance from the beginning */
  467. distance_to = PORT_DOUBLE_MAX;
  468. for (j = 0; j < nrlines; j++) {
  469. line = rlines[j].line;
  470. Vect_read_line(&In, LPoints, NULL, line);
  471. Vect_line_distance(LPoints, PPoints->x[0], PPoints->y[0],
  472. 0, 0, NULL, NULL, NULL, &dist_to, NULL,
  473. &dist_along);
  474. G_debug(debug,
  475. " line %d dist to line = %f, dist along line = %f",
  476. line, dist_to, dist_along);
  477. if (dist_to < distance_to) {
  478. distance_to = dist_to;
  479. mposts[mpost].line_idx = j;
  480. mposts[mpost].dist_along = dist_along;
  481. }
  482. }
  483. /* Check if in threshold */
  484. if (distance_to <= thresh) {
  485. G_debug(debug,
  486. "Point = %d cat = %d line = %d, distance = %f",
  487. point, cat, mposts[mpost].line_idx, distance_to);
  488. G_debug(debug,
  489. " start_mp = %f start_off = %f end_mp = %f end_off = %f",
  490. mposts[mpost].start_mp, mposts[mpost].start_off,
  491. mposts[mpost].end_mp, mposts[mpost].end_off);
  492. }
  493. else {
  494. mposts[mpost].line_idx = PORT_INT_MAX;
  495. mposts[mpost].err = ERR_THRESHOLD;
  496. G_warning(_("Point [%d] cat [%d] is out of threshold (distance = %f)"),
  497. point, cat, distance_to);
  498. }
  499. }
  500. }
  501. G_debug(debug, " %d points attached to line(s) of line id %d",
  502. nmposts, lid);
  503. /* Sort MPs according to line_idx, and dist_along */
  504. qsort((void *)mposts, nmposts, sizeof(MILEPOST), cmp_along);
  505. /* Reduce nmposts to exclude not attached db records of MPs from processing */
  506. nallmposts = nmposts;
  507. for (j = 0; j < nmposts; j++) {
  508. if (mposts[j].line_idx == PORT_INT_MAX) {
  509. nmposts = j;
  510. break;
  511. }
  512. }
  513. G_debug(debug, " %d mileposts attached to line(s)", nmposts);
  514. /* Go thourough all attached MPs and fill in info about MPs to 'lines' table */
  515. last = -1;
  516. for (j = 0; j < nmposts; j++) {
  517. G_debug(debug, " line_idx = %d, point cat = %d dist_along = %f",
  518. mposts[j].line_idx, mposts[j].cat, mposts[j].dist_along);
  519. if (mposts[j].line_idx != last) { /* line_index changed */
  520. rlines[mposts[j].line_idx].first_mpost_idx = j;
  521. rlines[mposts[j].line_idx].nmposts = 1;
  522. }
  523. else {
  524. rlines[mposts[j].line_idx].nmposts++;
  525. }
  526. last = mposts[j].line_idx;
  527. }
  528. /* 1) Check number of MP
  529. * 2) Guess direction: find direction for each segment between 2 MPs and at the end
  530. * compare number of segmnets in both directions, if equal assign DIR_UNKNOWN. */
  531. for (j = 0; j < nrlines; j++) {
  532. G_debug(debug,
  533. " Guess direction line_idx = %d, cat = %d, nmposts = %d first_mpost_idx = %d",
  534. j, rlines[j].cat, rlines[j].nmposts,
  535. rlines[j].first_mpost_idx);
  536. forward = 0;
  537. backward = 0;
  538. rlines[j].direction = DIR_UNKNOWN;
  539. if (rlines[j].nmposts == 0)
  540. rlines[j].err = ERR_NO_MP;
  541. if (rlines[j].nmposts == 1)
  542. rlines[j].err = ERR_ONE_MP;
  543. if (rlines[j].nmposts < 2) {
  544. /* TODO?: in some cases could be possible to guess from one point?
  545. * E.g. start_mp = 0 for -+------------- */
  546. continue;
  547. }
  548. first = rlines[j].first_mpost_idx;
  549. last = first + rlines[j].nmposts - 1;
  550. for (k = first; k < last; k++) {
  551. if ((mposts[k].start_mp < mposts[k + 1].start_mp) ||
  552. ((mposts[k].start_mp == mposts[k + 1].start_mp) &&
  553. (mposts[k].start_off < mposts[k + 1].start_off))) {
  554. forward++;
  555. G_debug(debug, " segment direction forward %d",
  556. mposts[k].cat);
  557. }
  558. else {
  559. backward++;
  560. G_debug(debug, " segment direction backward %d",
  561. mposts[k].cat);
  562. }
  563. }
  564. G_debug(debug, " forward = %d backward = %d", forward, backward);
  565. if (forward > backward) {
  566. rlines[j].direction = DIR_FORWARD;
  567. G_debug(debug, " line direction forward");
  568. }
  569. else if (forward < backward) {
  570. rlines[j].direction = DIR_BACKWARD;
  571. /* Recalculate distances from the other end */
  572. for (k = first; k <= last; k++)
  573. mposts[k].dist_along =
  574. rlines[j].length - mposts[k].dist_along;
  575. G_debug(debug, " line direction backward");
  576. }
  577. else {
  578. rlines[j].err = ERR_NO_DIR;
  579. G_debug(debug, " line direction unknown");
  580. }
  581. }
  582. /* Sort MPs again according to line_idx, and dist_along but with correct direction */
  583. qsort((void *)mposts, nmposts, sizeof(MILEPOST), cmp_along);
  584. /* Check order of MPs along the line and write LRS for line */
  585. for (j = 0; j < nrlines; j++) {
  586. G_debug(debug,
  587. "MAKE LR: line_idx = %d, nmposts = %d first_mpost_idx = %d",
  588. j, rlines[j].nmposts, rlines[j].first_mpost_idx);
  589. first = rlines[j].first_mpost_idx;
  590. last = first + rlines[j].nmposts - 1;
  591. /* Check order of MPs along the line if possible */
  592. order = 0; /* wrong order in case nmposts < 2 or DIR_UNKNOWN */
  593. if (rlines[j].nmposts >= 2 && rlines[j].direction != DIR_UNKNOWN) {
  594. /* Note: some MP could have more errors at the time, only first is recorded */
  595. G_debug(debug, "Check order of MPs along the line");
  596. order = 1; /* first we expect that MP order is OK */
  597. for (k = first; k <= last; k++) {
  598. G_debug(debug, " point cat = %d dist_along = %f",
  599. mposts[k].cat, mposts[k].dist_along);
  600. G_debug(debug,
  601. " start_mp = %f start_off = %f end_mp = %f end_off = %f",
  602. mposts[k].start_mp, mposts[k].start_off,
  603. mposts[k].end_mp, mposts[k].end_off);
  604. /* Do not break after first error, to get printed all errors */
  605. /* 1) For each MP must be end <= start */
  606. ret =
  607. LR_cmp_mileposts(mposts[k].end_mp, mposts[k].end_off,
  608. mposts[k].start_mp,
  609. mposts[k].start_off);
  610. if (ret == 1) { /* end > start */
  611. G_warning(_("End > start for point cat [%d]"),
  612. mposts[k].cat);
  613. mposts[k].err = ERR_END_GT_START;
  614. order = 0;
  615. continue;
  616. }
  617. if (k < last) { /* each segment ( MP <-> nextMP ) */
  618. if (mposts[k + 1].end_mp > 0 ||
  619. mposts[k + 1].end_off > 0) {
  620. /* 2) For 2 MPs must be first.start < second.end
  621. * if end > 0 ( otherwise it is considered to be NULL ) */
  622. ret =
  623. LR_cmp_mileposts(mposts[k].start_mp,
  624. mposts[k].start_off,
  625. mposts[k + 1].end_mp,
  626. mposts[k + 1].end_off);
  627. if (ret > -1) { /* start >= end */
  628. G_warning(_("Start of 1. MP >= end of 2. MP for points' "
  629. "cats %[d], [%d]"), mposts[k].cat,
  630. mposts[k + 1].cat);
  631. mposts[k].err = ERR_END_GT_START;
  632. order = 0;
  633. continue;
  634. }
  635. }
  636. else {
  637. /* 3) For 2 MPs must be first.start < second.start
  638. * if end = 0 ( NULL, not used ) */
  639. ret =
  640. LR_cmp_mileposts(mposts[k].start_mp,
  641. mposts[k].start_off,
  642. mposts[k + 1].start_mp,
  643. mposts[k + 1].start_off);
  644. if (ret > -1) { /* start > end */
  645. G_warning(_("Start of 1. MP >= start of 2. MP for points' "
  646. "cats [%d], [%d]"), mposts[k].cat,
  647. mposts[k + 1].cat);
  648. mposts[k].err = ERR_END_GT_START;
  649. order = 0;
  650. continue;
  651. }
  652. }
  653. /* 4) For 2 MPs must be distance along line different (duplicate points) */
  654. if (mposts[k].dist_along == mposts[k + 1].dist_along) {
  655. G_warning(_("Distance along line identical for points' "
  656. "cats [%d], [%d]"), mposts[k].cat,
  657. mposts[k + 1].cat);
  658. mposts[k].err = ERR_IDENT;
  659. mposts[k + 1].err = ERR_IDENT;
  660. order = 0;
  661. }
  662. }
  663. }
  664. }
  665. /* Write errors if any ( and continue ) */
  666. if (!order) { /* something is wrong */
  667. if (rlines[j].nmposts < 2) { /* Impossible to get reference/direction */
  668. G_warning(_("Not enough points (%d) attached to the line (cat %d), "
  669. "line skip."), rlines[j].nmposts,
  670. rlines[j].cat);
  671. }
  672. else if (rlines[j].direction == DIR_UNKNOWN) { /* Unknown direction */
  673. G_warning(_("Unable to guess direction for the line (cat %d), "
  674. "line skip."), rlines[j].cat);
  675. }
  676. else {
  677. G_warning(_("Incorrect order of points along line cat [%d]"),
  678. rlines[j].cat);
  679. rlines[j].err = ERR_LINE_ORDER;
  680. }
  681. /* Write line errors */
  682. if (err_opt->answer) {
  683. Vect_reset_cats(LCats);
  684. Vect_read_line(&In, LPoints, NULL, rlines[j].line);
  685. Vect_cat_set(LCats, 1, rlines[j].err);
  686. Vect_write_line(&EMap, GV_LINE, LPoints, LCats);
  687. }
  688. continue;
  689. }
  690. /* Order is correct and we can store reference records for this line */
  691. G_debug(debug,
  692. " lcat | lid | start_map | end_map | start_mp | start_off | end_mp | end_off | end type");
  693. for (k = first; k < last; k++) {
  694. /* Decide which value to use for end */
  695. if (mposts[k + 1].end_mp > 0 || mposts[k + 1].end_off > 0) { /* TODO: use NULL ? */
  696. totype = TO_TYPE_USER;
  697. end_mp = mposts[k + 1].end_mp;
  698. end_off = mposts[k + 1].end_off;
  699. }
  700. else { /* values not specified -> use start values from next MP */
  701. totype = TO_TYPE_MAP;
  702. end_mp = mposts[k + 1].start_mp;
  703. end_off = mposts[k + 1].start_off;
  704. }
  705. G_debug(debug,
  706. " %5d | %5d | %9.3f | %9.3f | %9.3f | %9.3f | %9.3f | %9.3f | %1d",
  707. rlines[j].cat, lid, mposts[k].dist_along,
  708. mposts[k + 1].dist_along, mposts[k].start_mp,
  709. mposts[k].start_off, end_mp, end_off, totype);
  710. sprintf(buf,
  711. "insert into %s (rsid, lcat, lid, start_map, end_map, "
  712. "start_mp, start_off, end_mp, end_off, end_type) "
  713. "values ( %d, %d, %d, %f, %f, %f, %f, %f, %f, %d )",
  714. table_opt->answer, rsid, rlines[j].cat, lid,
  715. mposts[k].dist_along, mposts[k + 1].dist_along,
  716. mposts[k].start_mp, mposts[k].start_off, end_mp,
  717. end_off, totype);
  718. G_debug(debug, " SQL: %s", buf);
  719. db_init_string(&rsstmt);
  720. db_append_string(&rsstmt, buf);
  721. if (db_execute_immediate(rsdriver, &rsstmt) != DB_OK)
  722. G_fatal_error(_("Unable to insert reference records: %s"),
  723. buf);
  724. rsid++;
  725. }
  726. /* Write the line to output */
  727. type = Vect_read_line(&In, LPoints, LCats, rlines[j].line);
  728. Vect_reset_line(L2Points);
  729. if (rlines[j].direction == DIR_FORWARD)
  730. dir = GV_FORWARD;
  731. else
  732. dir = GV_BACKWARD;
  733. Vect_append_points(L2Points, LPoints, dir);
  734. Vect_write_line(&Out, type, L2Points, LCats);
  735. }
  736. /* Write MP errors for all points, also those out of threshold */
  737. if (err_opt->answer) {
  738. for (k = 0; k < nallmposts; k++) {
  739. if (mposts[k].err != ERR_OK && mposts[k].err != ERR_NO_POINT) {
  740. Vect_reset_line(PPoints);
  741. Vect_reset_cats(PCats);
  742. Vect_append_point(PPoints, mposts[k].x, mposts[k].y, 0);
  743. Vect_cat_set(PCats, 1, mposts[k].err);
  744. Vect_write_line(&EMap, GV_POINT, PPoints, PCats);
  745. }
  746. }
  747. }
  748. }
  749. db_close_database_shutdown_driver(rsdriver);
  750. db_close_database_shutdown_driver(pdriver);
  751. db_close_database_shutdown_driver(ldriver);
  752. Vect_close(&In);
  753. Vect_close(&PMap);
  754. G_message(_("Building topology for output (out_lines) map..."));
  755. Vect_build(&Out, stderr);
  756. Vect_close(&Out);
  757. /* Write errors */
  758. if (err_opt->answer) {
  759. G_message(_("Building topology for error (err) map..."));
  760. Vect_build(&EMap, stderr);
  761. Vect_close(&EMap);
  762. }
  763. exit(EXIT_SUCCESS);
  764. }
  765. int cmp_along(const void *pa, const void *pb)
  766. {
  767. MILEPOST *p1 = (MILEPOST *) pa;
  768. MILEPOST *p2 = (MILEPOST *) pb;
  769. /* compare line_idx */
  770. if (p1->line_idx < p2->line_idx)
  771. return -1;
  772. if (p1->line_idx > p2->line_idx)
  773. return 1;
  774. /* the same line_idx -> compare dist_along */
  775. if (p1->dist_along < p2->dist_along)
  776. return -1;
  777. if (p1->dist_along > p2->dist_along)
  778. return 1;
  779. return 0;
  780. }