main.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365
  1. /*
  2. * v.lrs.segment - Generate segments or points from input map for existing
  3. * linear reference system
  4. */
  5. /******************************************************************************
  6. * Copyright (c) 2004-2007, Radim Blazek (blazek@itc.it),
  7. * Hamish Bowman (side_offset bits)
  8. *
  9. * Permission is hereby granted, free of charge, to any person obtaining a
  10. * copy of this software and associated documentation files (the "Software"),
  11. * to deal in the Software without restriction, including without limitation
  12. * the rights to use, copy, modify, merge, publish, distribute, sublicense,
  13. * and/or sell copies of the Software, and to permit persons to whom the
  14. * Software is furnished to do so, subject to the following conditions:
  15. *
  16. * The above copyright notice and this permission notice shall be included
  17. * in all copies or substantial portions of the Software.
  18. *
  19. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
  20. * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  21. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
  22. * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  23. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  24. * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  25. * DEALINGS IN THE SOFTWARE.
  26. *****************************************************************************/
  27. #include <stdlib.h>
  28. #include <string.h>
  29. #include <time.h>
  30. #include <math.h>
  31. #include <grass/gis.h>
  32. #include <grass/Vect.h>
  33. #include <grass/dbmi.h>
  34. #include <grass/glocale.h>
  35. #include "../lib/lrs.h"
  36. int find_line ( struct Map_info *Map, int lfield, int cat );
  37. void offset_pt_90(double *, double *, double, double);
  38. int main(int argc, char **argv)
  39. {
  40. FILE *in_file;
  41. int ret, points_written, lines_written, points_read, lines_read;
  42. int lfield;
  43. int line;
  44. int id, lid, lcat1, lcat2;
  45. double mpost, offset, mpost2, offset2, map_offset1, map_offset2, multip, side_offset;
  46. double x, y, z, angle, len;
  47. char stype;
  48. struct Option *in_opt, *out_opt, *driver_opt, *database_opt;
  49. struct Option *lfield_opt, *file_opt;
  50. struct Option *table_opt;
  51. struct GModule *module;
  52. char *mapset, buf[2000];
  53. const char *drv, *db;
  54. struct Map_info In, Out;
  55. struct line_cats *LCats, *SCats;
  56. struct line_pnts *LPoints, *SPoints, *PlPoints;
  57. dbDriver *rsdriver;
  58. dbHandle rshandle;
  59. dbString rsstmt;
  60. G_gisinit (argv[0]) ;
  61. module = G_define_module();
  62. module->keywords = _("vector, LRS, networking");
  63. module->description =
  64. _("Creates points/segments from input lines, linear reference "
  65. "system and positions read from stdin or a file.");
  66. in_opt = G_define_standard_option(G_OPT_V_INPUT);
  67. in_opt->description = _("Input vector map containing lines");
  68. out_opt = G_define_standard_option(G_OPT_V_OUTPUT);
  69. out_opt->description = _("Output vector map where segments will be written");
  70. lfield_opt = G_define_standard_option(G_OPT_V_FIELD);
  71. lfield_opt->key = "llayer";
  72. lfield_opt->answer = "1";
  73. lfield_opt->description = _("Line layer");
  74. driver_opt = G_define_option() ;
  75. driver_opt->key = "rsdriver" ;
  76. driver_opt->type = TYPE_STRING ;
  77. driver_opt->required = NO;
  78. driver_opt->description = _("Driver name for reference system table");
  79. if ( (drv=db_get_default_driver_name()) )
  80. driver_opt->answer = drv;
  81. database_opt = G_define_option() ;
  82. database_opt->key = "rsdatabase" ;
  83. database_opt->type = TYPE_STRING ;
  84. database_opt->required = NO;
  85. database_opt->description = _("Database name for reference system table");
  86. if ( (db=db_get_default_database_name()) )
  87. database_opt->answer = db;
  88. table_opt = G_define_option() ;
  89. table_opt->key = "rstable" ;
  90. table_opt->type = TYPE_STRING ;
  91. table_opt->required = YES;
  92. table_opt->description = _("Name of the reference system table");
  93. file_opt = G_define_standard_option(G_OPT_F_INPUT);
  94. file_opt->key = "file";
  95. file_opt->required = NO;
  96. file_opt->description = _("Name of file containing segment rules. "
  97. "If not given, read from stdin.");
  98. if(G_parser(argc,argv))
  99. exit(EXIT_FAILURE);
  100. LCats = Vect_new_cats_struct ();
  101. SCats = Vect_new_cats_struct ();
  102. LPoints = Vect_new_line_struct ();
  103. SPoints = Vect_new_line_struct ();
  104. PlPoints = Vect_new_line_struct ();
  105. lfield = atoi (lfield_opt->answer);
  106. multip = 1000; /* Number of map units per MP unit */
  107. if(file_opt->answer) {
  108. /* open input file */
  109. if((in_file = fopen(file_opt->answer, "r" )) == NULL )
  110. G_fatal_error(_("Unable to open input file <%s>"), file_opt->answer);
  111. }
  112. /* Open input lines */
  113. mapset = G_find_vector2 (in_opt->answer, NULL);
  114. if(mapset == NULL)
  115. G_fatal_error(_("Vector map <%s> not found"), in_opt->answer);
  116. Vect_set_open_level ( 2 );
  117. Vect_open_old (&In, in_opt->answer, mapset);
  118. /* Open output segments */
  119. Vect_open_new ( &Out, out_opt->answer, Vect_is_3d (&In) );
  120. db_init_handle (&rshandle);
  121. db_init_string (&rsstmt);
  122. rsdriver = db_start_driver(driver_opt->answer);
  123. db_set_handle (&rshandle, database_opt->answer, NULL);
  124. if (db_open_database(rsdriver, &rshandle) != DB_OK)
  125. G_fatal_error(_("Unable to open database for reference table"));
  126. points_read = 0; lines_read = 0;
  127. points_written = 0; lines_written = 0;
  128. while (1) {
  129. if(!file_opt->answer) {
  130. if(fgets(buf, sizeof(buf), stdin) == NULL) break;
  131. }
  132. else {
  133. if(G_getl2(buf, sizeof(buf)-1, in_file) == 0) break;
  134. }
  135. G_debug ( 2, "SEGMENT: %s", G_chop(buf));
  136. side_offset = 0;
  137. Vect_reset_line ( SPoints );
  138. Vect_reset_cats ( SCats );
  139. switch ( buf[0] ) {
  140. case 'P':
  141. side_offset = 0;
  142. ret = sscanf ( buf, "%c %d %d %lf+%lf %lf", &stype, &id,
  143. &lid, &mpost, &offset, &side_offset);
  144. if ( ret < 5 ) {
  145. G_warning (_("Cannot read input: %s"), buf);
  146. break;
  147. }
  148. points_read++;
  149. G_debug (2, "point: %d %d %f+%f %f", id, lid, mpost, offset, side_offset);
  150. ret = LR_get_offset( rsdriver, table_opt->answer, "lcat", "lid",
  151. "start_map", "end_map", "start_mp", "start_off", "end_mp", "end_off",
  152. lid, mpost, offset, multip, &lcat1, &map_offset1 );
  153. if ( ret == 0 ) {
  154. G_warning (_("No record in LR table for: %s"), buf);
  155. break;
  156. }
  157. if ( ret == 3 ) {
  158. G_warning (_("More than one record in LR table for: %s"), buf);
  159. break;
  160. }
  161. /* OK, write point */
  162. line = find_line ( &In, lfield, lcat1 );
  163. if ( line == 0 ) {
  164. G_warning (_("Unable to find line of cat [%d]"), lcat1);
  165. break;
  166. }
  167. Vect_read_line ( &In, LPoints, LCats, line );
  168. ret = Vect_point_on_line ( LPoints, map_offset1, &x, &y, &z, &angle, NULL);
  169. if ( ret == 0 ) {
  170. len = Vect_line_length ( LPoints );
  171. G_warning (_("Cannot get point on line: cat = [%d] "
  172. "distance = [%f] (line length = %f)\n%s"),
  173. lcat1, map_offset1, len, buf);
  174. break;
  175. }
  176. if(fabs(side_offset) > 0.0)
  177. offset_pt_90(&x, &y, angle, side_offset);
  178. Vect_append_point ( SPoints, x, y, z );
  179. Vect_cat_set ( SCats, 1, id );
  180. Vect_write_line ( &Out, GV_POINT, SPoints, SCats);
  181. points_written++;
  182. break;
  183. case 'L':
  184. side_offset = 0;
  185. ret = sscanf ( buf, "%c %d %d %lf+%lf %lf+%lf %lf", &stype, &id,
  186. &lid, &mpost, &offset, &mpost2, &offset2, &side_offset);
  187. if ( ret < 7 ) {
  188. G_warning (_("Cannot read input: %s"), buf);
  189. break;
  190. }
  191. lines_read++;
  192. G_debug (2, "line: %d %d %f+%f %f+%f %f", id, lid, mpost, offset,
  193. mpost2, offset2, side_offset);
  194. /* Find both points */
  195. /* Nearest up */
  196. ret = LR_get_nearest_offset( rsdriver, table_opt->answer, "lcat", "lid",
  197. "start_map", "end_map", "start_mp", "start_off", "end_mp", "end_off",
  198. lid, mpost, offset, multip, 0, &lcat1, &map_offset1 );
  199. if ( ret == 0 ) {
  200. G_warning (_("No record in LRS table for 1. point of:\n %s"), buf);
  201. break;
  202. }
  203. if ( ret == 3 ) {
  204. G_warning (_("Using last from more offsets found for 1. "
  205. "point of:\n %s"), buf);
  206. }
  207. if ( ret == 2 ) {
  208. G_warning (_("Requested offset for the 1. point not found, "
  209. "using nearest found:\n %s"), buf);
  210. }
  211. /* Nearest down */
  212. ret = LR_get_nearest_offset( rsdriver, table_opt->answer, "lcat", "lid",
  213. "start_map", "end_map", "start_mp", "start_off", "end_mp", "end_off",
  214. lid, mpost2, offset2, multip, 1, &lcat2, &map_offset2 );
  215. if ( ret == 0 ) {
  216. G_warning (_("No record in LRS table for 2. point of:\n %s"), buf);
  217. break;
  218. }
  219. if ( ret == 2 ) {
  220. G_warning (_("Requested offset for the 2. point not found, "
  221. "using nearest found:\n %s"), buf);
  222. }
  223. if ( ret == 3 ) {
  224. G_warning (_("Using first from more offsets found for 2. "
  225. "point of:\n %s"), buf);
  226. }
  227. /* Check if both points are at the same line */
  228. if ( lcat1 != lcat2 ) {
  229. G_warning (_("Segment over 2 (or more) segments, not yet supported"));
  230. break;
  231. }
  232. G_debug (2, "segment: lcat = %d : %f - %f", lcat1, map_offset1, map_offset2);
  233. line = find_line ( &In, lfield, lcat1 );
  234. if ( line == 0 ) {
  235. G_warning (_("Unable to find line of cat [%d]"), lcat1);
  236. break;
  237. }
  238. Vect_read_line ( &In, LPoints, LCats, line );
  239. len = Vect_line_length ( LPoints );
  240. if ( map_offset2 > len ) {
  241. /* This is mostly caused by calculation only -> use a threshold for warning */
  242. if ( fabs(map_offset2-len) > 1e-6 ) { /* usually around 1e-7 */
  243. G_warning (_("End of segment > line length (%e) -> cut"),
  244. fabs(map_offset2-len));
  245. }
  246. map_offset2 = len;
  247. }
  248. ret = Vect_line_segment ( LPoints, map_offset1, map_offset2, SPoints );
  249. if ( ret == 0 ) {
  250. G_warning (_("Cannot make line segment: cat = %d : "
  251. "%f - %f (line length = %f)\n%s"),
  252. lcat1, map_offset1, map_offset2, len, buf);
  253. break;
  254. }
  255. Vect_cat_set ( SCats, 1, id );
  256. if(fabs(side_offset) > 0.0) {
  257. Vect_line_parallel(SPoints, side_offset, side_offset/10., TRUE, PlPoints);
  258. Vect_write_line ( &Out, GV_LINE, PlPoints, SCats);
  259. G_debug ( 3, " segment n_points = %d", PlPoints->n_points);
  260. }
  261. else {
  262. Vect_write_line ( &Out, GV_LINE, SPoints, SCats);
  263. G_debug ( 3, " segment n_points = %d", SPoints->n_points);
  264. }
  265. lines_written++;
  266. G_debug ( 3, " -> written.");
  267. break;
  268. default:
  269. G_warning (_("Incorrect segment type: %s"), buf );
  270. }
  271. }
  272. db_close_database(rsdriver);
  273. Vect_build (&Out, stderr);
  274. /* Free, close ... */
  275. Vect_close(&In);
  276. Vect_close(&Out);
  277. if(file_opt->answer)
  278. fclose(in_file);
  279. G_message (_("[%d] points read from input"), points_read);
  280. G_message (_("[%d] points written to output map (%d lost)"),
  281. points_written, points_read-points_written);
  282. G_message (_("[%d] lines read from input"), lines_read);
  283. G_message (_("[%d] lines written to output map (%d lost)"),
  284. lines_written, lines_read-lines_written);
  285. exit(EXIT_SUCCESS);
  286. }
  287. /* Find line by cat, returns 0 if not found */
  288. /* TODO: use category index */
  289. int
  290. find_line ( struct Map_info *Map, int lfield, int lcat )
  291. {
  292. int i, nlines, type, cat;
  293. struct line_cats *Cats;
  294. G_debug (2, "find_line(): lfield = %d lcat = %d", lfield, lcat);
  295. Cats = Vect_new_cats_struct ();
  296. nlines = Vect_get_num_lines ( Map );
  297. for ( i = 1; i <= nlines; i++ ) {
  298. type = Vect_read_line ( Map, NULL, Cats, i );
  299. if ( !(type & GV_LINE) ) continue;
  300. Vect_cat_get ( Cats, lfield, &cat );
  301. if ( cat == lcat ) return i;
  302. }
  303. return 0;
  304. }
  305. /* calculate a point perpendicular to the current line angle, offset by a distance
  306. * works in the x,y plane.
  307. */
  308. void offset_pt_90(double *x, double *y, double angle, double distance)
  309. {
  310. *x -= distance * cos(M_PI_2 + angle);
  311. *y -= distance * sin(M_PI_2 + angle);
  312. }