[GRASS-SVN] r61060 - grass/trunk/vector/v.net

svn_grass at osgeo.org svn_grass at osgeo.org
Sun Jun 29 04:54:33 PDT 2014


Author: turek
Date: 2014-06-29 04:54:33 -0700 (Sun, 29 Jun 2014)
New Revision: 61060

Added:
   grass/trunk/vector/v.net/turntable.c
Modified:
   grass/trunk/vector/v.net/Makefile
   grass/trunk/vector/v.net/args.c
   grass/trunk/vector/v.net/main.c
   grass/trunk/vector/v.net/proto.h
   grass/trunk/vector/v.net/v.net.html
Log:
v.net: turntable operation added

Modified: grass/trunk/vector/v.net/Makefile
===================================================================
--- grass/trunk/vector/v.net/Makefile	2014-06-29 11:49:08 UTC (rev 61059)
+++ grass/trunk/vector/v.net/Makefile	2014-06-29 11:54:33 UTC (rev 61060)
@@ -3,8 +3,8 @@
 
 PGM=v.net
 
-LIBES = $(VECTORLIB) $(GISLIB)
-DEPENDENCIES = $(VECTORDEP) $(GISDEP)
+LIBES = $(VECTORLIB) $(GISLIB) $(DBMILIB)
+DEPENDENCIES = $(VECTORDEP) $(GISDEP) $(DBMIDEP)
 EXTRA_INC = $(VECT_INC)
 EXTRA_CFLAGS = $(VECT_CFLAGS)
  

Modified: grass/trunk/vector/v.net/args.c
===================================================================
--- grass/trunk/vector/v.net/args.c	2014-06-29 11:49:08 UTC (rev 61059)
+++ grass/trunk/vector/v.net/args.c	2014-06-29 11:54:33 UTC (rev 61060)
@@ -32,11 +32,11 @@
     opt->action->type = TYPE_STRING;
     opt->action->required = YES;
     opt->action->multiple = NO;
-    opt->action->options = "nodes,connect,arcs,report,nreport";
+    opt->action->options = "nodes,connect,arcs,report,nreport,turntable";
     opt->action->description = _("Operation to be performed");
     desc = NULL;
     G_asprintf(&desc,
-	       "nodes;%s;connect;%s;arcs;%s;report;%s;nreport;%s",
+	       "nodes;%s;connect;%s;arcs;%s;report;%s;nreport;%s;turntable;%s;",
 	       _("new point is placed on each node (line end) "
 		 "if doesn't exist"),
 	       _("connect still unconnected points to vector network "
@@ -46,7 +46,8 @@
 	       _("print to standard output "
 		 "{line_category start_point_category end_point_category}"),
 	       _("print to standard output "
-		 "{point_category line_category[,line_category...]}"));
+		 "{point_category line_category[,line_category...]}"),
+         _("create turntable on vector network"));
     opt->action->descriptions = desc;
 
     opt->afield_opt = G_define_standard_option(G_OPT_V_FIELD);
@@ -90,6 +91,36 @@
     opt->snap_flag->description =
 	_("For operation 'connect'. By default, a new line from the point to the network is created.");
     opt->snap_flag->guisection = _("Nodes");
+
+    opt->tfield = G_define_standard_option(G_OPT_V_FIELD);
+    opt->tfield->label = _("Turntable layer");
+    opt->tfield->description =
+    _
+    ("Layer where turntable will be attached. Format: layer number[/layer name]."
+     "Required for operation 'turntable'.");
+    opt->tfield->answer = "3";
+    opt->tfield->key = "tlayer";
+    opt->tfield->required = NO;
+    opt->tfield->guisection = _("Turntable");
+
+    opt->tucfield = G_define_standard_option(G_OPT_V_FIELD);
+    opt->tucfield->label = _("Layer with unique categories used in turntable");
+    opt->tucfield->description =
+    _
+    ("Layer with unique categories for every line in alayer and point on every node. "
+     " The categories are used in turntable. Format: layer number[/layer name]. "
+     "Required for operation 'turntable'.");
+    opt->tucfield->answer = "4";
+    opt->tucfield->key = "tuclayer";
+    opt->tucfield->required = NO;
+    opt->tucfield->guisection = _("Turntable");
+
+    opt->type = G_define_standard_option(G_OPT_V_TYPE);
+    opt->type->options = "line,boundary";
+    opt->type->answer = "line,boundary";
+    opt->type->label = _("Arc type");
+    opt->type->guisection = _("Turntable");
+
 }
 
 void parse_arguments(const struct opt *opt,
@@ -109,17 +140,21 @@
 	*act = TOOL_NREPORT;
     else if (strcmp(opt->action->answer, "arcs") == 0)
 	*act = TOOL_ARCS;
+    else if (strcmp(opt->action->answer, "turntable") == 0)
+    *act = TOOL_TURNTABLE;
     else
 	G_fatal_error(_("Unknown operation"));
 
     if (*act == TOOL_NODES || *act == TOOL_CONNECT ||
-	*act == TOOL_REPORT || *act == TOOL_NREPORT) {
+	*act == TOOL_REPORT || *act == TOOL_NREPORT ||
+    *act == TOOL_TURNTABLE) {
 	if (opt->input->answer == NULL)
 	    G_fatal_error(_("Required parameter <%s> not set"),
 			  opt->input->key);
     }
 
-    if (*act == TOOL_NODES || *act == TOOL_CONNECT) {
+    if (*act == TOOL_NODES || *act == TOOL_CONNECT ||
+        *act == TOOL_TURNTABLE) {
 	if (opt->output->answer == NULL)
 	    G_fatal_error(_("Required parameter <%s> not set"),
 			  opt->output->key);

Modified: grass/trunk/vector/v.net/main.c
===================================================================
--- grass/trunk/vector/v.net/main.c	2014-06-29 11:49:08 UTC (rev 61059)
+++ grass/trunk/vector/v.net/main.c	2014-06-29 11:54:33 UTC (rev 61060)
@@ -5,10 +5,12 @@
  * 
  * AUTHOR(S):    Radim Blazek
  *               Martin Landa <landa.martin gmail.com> (connect/arcs)
+ *               Stepan Turek <stepan.turek seznam.cz> (turns support)
+ *
  *               
  * PURPOSE:      Network maintenance
  *               
- * COPYRIGHT:    (C) 2001-2009 by the GRASS Development Team
+ * COPYRIGHT:    (C) 2001-2009,2014 by the GRASS Development Team
  *
  *               This program is free software under the 
  *               GNU General Public License (>=v2). 
@@ -37,7 +39,7 @@
     double thresh;
 
     char message[4096];
-    
+
     /*  initialize the GIS calls */
     G_gisinit(argv[0]);
 
@@ -57,9 +59,9 @@
     In = Points = Out = NULL;
     file_arcs = NULL;
     message[0] = '\0';
-    
+
     /* open input map */
-    if (act != TOOL_ARCS) {
+    if (act != TOOL_ARCS && act != TOOL_TURNTABLE) {
 	In = (struct Map_info *)G_malloc(sizeof(struct Map_info));
 	Vect_set_open_level(2);
 	if (Vect_open_old(In, opt.input->answer, "") == -1)
@@ -116,7 +118,8 @@
 	if (Vect_open_new(Out, opt.output->answer, is3d) == -1) {
 	    if (In)
 		Vect_close(In);
-	    G_fatal_error(_("Unable to open vector map <%s> at topology level %d"),
+	    G_fatal_error(_
+			  ("Unable to open vector map <%s> at topology level %d"),
 			  opt.output->answer, 2);
 	}
 
@@ -130,28 +133,31 @@
 	if (act == TOOL_NODES) {
 	    /* nodes */
 	    int nnodes;
+
 	    nnodes = nodes(In, Out, opt.cats_flag->answer, nfield);
 
-	    sprintf (message, _("%d new points (nodes) written to output."), nnodes);
+	    sprintf(message, _("%d new points (nodes) written to output."),
+		    nnodes);
 	}
 	else {			/* connect or arcs */
 	    int narcs;
 
 	    if (act == TOOL_CONNECT)
 		narcs = connect_arcs(In, Points, Out, afield, nfield,
-		                     thresh, opt.snap_flag->answer);
+				     thresh, opt.snap_flag->answer);
 	    else
 		narcs = create_arcs(file_arcs, Points, Out, afield, nfield);
 
-	    sprintf(message, _("%d lines (network arcs) written to output."), narcs);
+	    sprintf(message, _("%d lines (network arcs) written to output."),
+		    narcs);
 	}
 
 	if (In) {
-	  G_message (_("Copying attributes..."));
-	  if (Vect_copy_tables(In, Out, 0))
-	    G_warning(_("Failed to copy attribute table to output map"));
+	    G_message(_("Copying attributes..."));
+	    if (Vect_copy_tables(In, Out, 0))
+		G_warning(_("Failed to copy attribute table to output map"));
 	}
-	
+
 	/* support */
 	Vect_build_partial(Out, GV_BUILD_NONE);
 	Vect_build(Out);
@@ -161,6 +167,9 @@
 	if (Out)
 	    Vect_close(Out);
     }
+    else if (act == TOOL_TURNTABLE) {
+	turntable(&opt);
+    }
     else {			/* report */
 	report(In, afield, nfield, act);
     }

Modified: grass/trunk/vector/v.net/proto.h
===================================================================
--- grass/trunk/vector/v.net/proto.h	2014-06-29 11:49:08 UTC (rev 61059)
+++ grass/trunk/vector/v.net/proto.h	2014-06-29 11:54:33 UTC (rev 61060)
@@ -3,13 +3,16 @@
 #define TOOL_REPORT  2
 #define TOOL_NREPORT 3
 #define TOOL_ARCS    4
+#define TOOL_TURNTABLE    5
 
+
 struct opt {
     struct Option *input, *points;
     struct Option *output;
     struct Option *action;
-    struct Option *afield_opt, *nfield_opt, *thresh_opt;
+    struct Option *afield_opt, *tfield, *tucfield, *nfield_opt, *thresh_opt;
     struct Option *file;
+    struct Option *type;
     struct Flag *cats_flag, *snap_flag;
 };
 
@@ -33,3 +36,5 @@
 /* report.c */
 int report(struct Map_info *, int, int,
 	   int);
+
+void turntable(struct opt *);

Added: grass/trunk/vector/v.net/turntable.c
===================================================================
--- grass/trunk/vector/v.net/turntable.c	                        (rev 0)
+++ grass/trunk/vector/v.net/turntable.c	2014-06-29 11:54:33 UTC (rev 61060)
@@ -0,0 +1,771 @@
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <grass/gis.h>
+#include <grass/vector.h>
+#include <grass/dbmi.h>
+#include <grass/glocale.h>
+#include "proto.h"
+
+void close_db(void *p)
+{
+    dbDriver *driver = (dbDriver *) p;
+
+    db_close_database_shutdown_driver(driver);
+}
+
+static double compute_line_nodes_angle(struct line_pnts *points)
+{
+    double x_start, y_start, z;
+    double x_end, y_end;
+    double x, y;
+    int n_points = Vect_get_num_line_points(points);
+
+    if (n_points < 2)
+	return (-9.0);
+
+    Vect_line_get_point(points, 0, &x_start, &y_start, &z);
+    Vect_line_get_point(points, n_points - 1, &x_end, &y_end, &z);
+
+    x = x_end - x_start;
+    y = y_end - y_start;
+
+    if (y == 0.0 && x == 0.0)
+	return (0.0);
+    else
+	return (atan2(y, x));
+}
+
+/*\brief Compute angle of two lines, which is defined by start and end point of the lines
+   regardless regardless line segments between the points.
+
+   Parameters from_dir, to_dir defines defines line direction to node for which is angle defined 
+   (negative - line goes from node / positive line goes into node).
+
+   Angle is zero when lines are straight. If line_pnts_to is on the left from line_pnts_from
+   the angle is negative.
+
+   \return lines angle
+   \return -9.0 if line is defined by one point or has same start and end point
+ */
+
+static double compute_lines_angle(struct line_pnts *line_pnts_from,
+				  int from_dir,
+				  struct line_pnts *line_pnts_to, int to_dir)
+{
+    double angle_from, angle_to;
+    double angle;
+
+    double x1, x2, y1, y2, z;
+
+    int n_points_from = Vect_get_num_line_points(line_pnts_from);
+    int n_points_to = Vect_get_num_line_points(line_pnts_to);
+
+
+    /* If one of the lines has same begining and end, the angle cannot be 
+       calculated, because the angle is computed between lines given by start and 
+       end point regardless line segments between the points. */
+    Vect_line_get_point(line_pnts_from, 0, &x1, &y1, &z);
+    Vect_line_get_point(line_pnts_from, n_points_from - 1, &x2, &y2, &z);
+
+    if (x1 == x2 && y1 == y2)
+	return -9.0;
+
+    Vect_line_get_point(line_pnts_to, 0, &x1, &y1, &z);
+    Vect_line_get_point(line_pnts_to, n_points_to - 1, &x2, &y2, &z);
+
+    if (x1 == x2 && y1 == y2)
+	return -9.0;
+
+    if (from_dir > 0)
+	Vect_line_reverse(line_pnts_from);
+
+    if (to_dir < 0)
+	Vect_line_reverse(line_pnts_to);
+
+    angle_from = compute_line_nodes_angle(line_pnts_from);
+    angle_to = compute_line_nodes_angle(line_pnts_to);
+
+    if (angle_from == -9.0)
+	angle = angle_from;
+    else if (angle_to == -9.0)
+	angle = angle_to;
+    else {
+	angle = angle_from - angle_to;
+
+	if (angle > M_PI)
+	    angle = -2 * M_PI + angle;
+
+	if (angle < -M_PI)
+	    angle = 2 * M_PI + angle;
+    }
+
+    /* reverse it back to original order */
+    if (from_dir > 0)
+	Vect_line_reverse(line_pnts_from);
+
+    if (to_dir < 0)
+	Vect_line_reverse(line_pnts_to);
+
+    return angle;
+}
+
+/*\brief Add line uturns into turntable.
+
+   Add two records into turntable because every line has two possible U-turns.
+ */
+static int add_uturn(dbDriver * driver, char *ttb_name, int *next_ttb_cat,
+		     int ln_cat, int isec_start_cat, int isec_end_cat)
+{
+    int i, isec;
+    dbString db_buf;
+    char buf[DB_SQL_MAX];
+
+    db_init_string(&db_buf);
+
+    ln_cat = abs(ln_cat);
+
+    isec = isec_end_cat;
+    for (i = 0; i < 2; ++i) {
+	if (i == 1) {
+	    ln_cat = -1 * ln_cat;
+	    isec = isec_start_cat;
+	}
+	/* cat, ln_from, ln_to, cost, isec, angle */
+	sprintf(buf,
+		"INSERT INTO %s values ( %d, %d, %d, %f, %d, %f);",
+		ttb_name, (*next_ttb_cat), ln_cat, ln_cat * -1, 0.0,
+		isec, M_PI);
+	db_set_string(&db_buf, buf);
+
+	G_debug(3, "Adding u-turn into turntable:\n%s",
+		db_get_string(&db_buf));
+
+	if (db_execute_immediate(driver, &db_buf) != DB_OK) {
+	    db_free_string(&db_buf);
+	    return -1;
+	}
+	++(*next_ttb_cat);
+    }
+
+    db_free_string(&db_buf);
+    return 1;
+}
+
+
+/*\brief Add turns for two lines into turntable.
+
+   Add two records into turntable because we can take the turn from two opposite directions.
+ */
+static int add_turns(dbDriver * driver, char *ttb_name, int *next_ttb_cat,
+		     int ln_i_cat, struct line_pnts *line_pnts_i,
+		     int ln_j_cat, struct line_pnts *line_pnts_j,
+		     int isec_cat)
+{
+    int i;
+    int ln_f, ln_t;
+    dbString db_buf;
+    char buf[DB_SQL_MAX];
+    double angle;
+
+    db_init_string(&db_buf);
+
+    int ln_j_dir, ln_i_dir;
+    int ln_to_cat, ln_from_cat;
+
+    ln_i_dir = ln_i_cat;
+    ln_j_dir = ln_j_cat;
+
+    ln_i_cat = abs(ln_i_cat);
+    ln_j_cat = abs(ln_j_cat);
+
+    ln_from_cat = ln_i_cat;
+    ln_to_cat = ln_j_cat;
+
+    /*Find right lines nodes (positive or negative), will be connected by the turn. */
+    if (ln_j_dir < 0 && ln_i_dir < 0)
+	ln_to_cat *= -1;
+
+    else if (ln_j_dir > 0 && ln_i_dir > 0)
+	ln_from_cat *= -1;
+
+    else if (ln_j_dir < 0) {
+	ln_to_cat = ln_i_cat;
+	ln_from_cat = ln_j_cat;
+    }
+
+    /* compute angle if the lines angle is computed from ln_from_cat to ln_to_cat */
+    if (ln_to_cat == ln_i_cat)
+	angle =
+	    compute_lines_angle(line_pnts_j, ln_j_dir, line_pnts_i, ln_i_dir);
+    else
+	angle =
+	    compute_lines_angle(line_pnts_i, ln_i_dir, line_pnts_j, ln_j_dir);
+
+    ln_f = ln_from_cat;
+    ln_t = ln_to_cat;
+
+    /*Create first turn from i to j, then create turn in the opposite direction. */
+    for (i = 0; i < 2; ++i) {
+
+	/* connect right nodes for opposite turn */
+	if (i == 1) {
+	    ln_f = ln_to_cat * -1;
+	    ln_t = ln_from_cat * -1;
+	}
+
+	/* cat, ln_from, ln_to, cost, isec, angle */
+	sprintf(buf,
+		"INSERT INTO %s values ( %d, %d, %d, %f, %d,",
+		ttb_name, (*next_ttb_cat), ln_f, ln_t, 0.0, isec_cat);
+	db_set_string(&db_buf, buf);
+
+	if (angle == -9.0)
+	    db_append_string(&db_buf, "NULL)");
+	else {
+	    /* the angle is on the other side in opposite turn (e. g. left -> right) */
+	    if (i == 1)
+		angle *= -1;
+
+	    sprintf(buf, "%f)", angle);
+	    db_append_string(&db_buf, buf);
+	}
+
+	G_debug(3, "Adding turn into turntable:\n%s", db_get_string(&db_buf));
+
+	if (db_execute_immediate(driver, &db_buf) != DB_OK) {
+	    db_free_string(&db_buf);
+	    return -1;
+	}
+	++(*next_ttb_cat);
+    }
+
+    db_free_string(&db_buf);
+    return 1;
+
+}
+
+
+void populate_turntable(dbDriver * driver, struct Map_info *InMap,
+			struct Map_info *OutMap, char *ttb_name,
+			int tfield, int tucfield, int a_field, int arc_type)
+{
+    struct ilist *list;
+
+    int *features_id;
+
+    int n_node_lns, n_features, i_line, j_line, next_ttb_cat, i_ucat,
+	i_rew_lines, n_lines;
+    int n_nodes, pivot_node, outside_node, isec_start_ucat, isec_end_ucat,
+	node1, node2, found_pt_id;
+    int ln_i_id, ln_j_id, ln_i_ucat, ln_j_ucat;
+
+    int ltype_i, ltype_j;
+
+    struct line_pnts *line_pnts_i, *line_pnts_j;
+    struct line_cats *cats_i, *cats_j;
+
+    double x, y, z;
+
+    struct bound_box box;
+    struct boxlist *box_List;
+
+    line_pnts_i = Vect_new_line_struct();
+    line_pnts_j = Vect_new_line_struct();
+    cats_i = Vect_new_cats_struct();
+    cats_j = Vect_new_cats_struct();
+
+    n_lines = 0;
+    if (arc_type & GV_LINE)
+	n_lines += Vect_get_num_primitives(InMap, GV_LINE);
+    if (arc_type & GV_BOUNDARY)
+	n_lines += Vect_get_num_primitives(InMap, GV_BOUNDARY);
+
+    /*Converts feature input map id into current id in output map. 
+       When the feature is rewritten, it's original state still exists 
+       just marked as dead. The new feature is written to first possible 
+       position and that is the id (all dead + all alive features + 1).
+
+       If feature id is 0 the feature was not already written into
+       output map. */
+
+    n_features = Vect_get_num_lines(InMap);
+
+    G_debug(3, "Found %d line features in <%s> vector map", n_features,
+	    InMap->name);
+
+    features_id = G_malloc(sizeof(int) * n_features);
+    G_zero(features_id, sizeof(int) * n_features);
+
+    n_nodes = Vect_get_num_nodes(InMap);
+    G_debug(3, "Found %d nodes in <%s> vector map", n_nodes, InMap->name);
+
+    db_begin_transaction(driver);
+
+    /* Stores category for a next record/turn in turntable. */
+    next_ttb_cat = 1;
+
+    /* Stores number of category which will be assigned to a next feature added into tucfield. */
+    i_ucat = 1;
+
+    /* Stores number of rewritten features. If feature is rewritten, it can be sought by sum of
+       i_ucat + i_rew_lines variables values (id), when was rewritten. 
+       The value is saved into features_id array. */
+    i_rew_lines = 0;
+
+    list = G_new_ilist();
+
+    /* Every node represents one intersection. */
+    for (pivot_node = 1; pivot_node <= n_nodes; pivot_node++) {
+	n_node_lns = Vect_get_node_n_lines(InMap, pivot_node);
+
+	G_debug(3, "Found %d lines connected to node with id %d",
+		n_node_lns, pivot_node);
+
+	/*Creates record in turntable for every possible turn 
+	   in intersection defined by node and lines which meets on the node.
+
+	   It also takes into account  U-turns. */
+
+	for (i_line = 0; i_line < n_node_lns; i_line++) {
+
+	    ln_i_id = Vect_get_node_line(InMap, pivot_node, i_line);
+
+	    /*Line was not written into output map. */
+	    if (features_id[abs(ln_i_id) - 1] < 1) {
+		ltype_i =
+		    Vect_read_line(InMap, line_pnts_i, cats_i, abs(ln_i_id));
+
+		/* If line does not belong into arc layer, skip it. */
+		if (Vect_field_cat_get(cats_i, a_field, list) < 0 ||
+		    !(ltype_i & arc_type))
+		    continue;
+
+		/* Delete categories in tfield and tucfield if they are defined in input map. */
+		Vect_field_cat_del(cats_i, tfield, -1);
+		Vect_field_cat_del(cats_i, tucfield, -1);
+	    }
+
+	    /*If i line has been already written into output map, 
+	       we need to take it's categories from the output map with categories 
+	       for tlayer and tuclayer. */
+	    else {
+		ln_i_id = Vect_get_node_line(InMap, pivot_node, i_line);
+
+		ltype_i = Vect_read_line(OutMap, line_pnts_i, cats_i,
+					 features_id[abs(ln_i_id) - 1]);
+		Vect_cat_get(cats_i, tucfield, &ln_i_ucat);
+
+		/* add line direction information to ucat */
+		if (ln_i_id < 0)
+		    ln_i_ucat *= -1;
+
+	    }
+
+	    for (j_line = i_line; j_line < n_node_lns; j_line++) {
+
+		ln_j_id = Vect_get_node_line(InMap, pivot_node, j_line);
+
+		/* write line, which has not been written into new map yet. */
+		if (features_id[abs(ln_j_id) - 1] < 1) {
+		    /* Get line from input map. */
+		    ltype_j =
+			Vect_read_line(InMap, line_pnts_j, cats_j,
+				       abs(ln_j_id));
+
+		    /* If line does not belong into arc layer, skip it. */
+		    if (Vect_field_cat_get(cats_j, a_field, list) < 0 ||
+			!(ltype_i & arc_type))
+			continue;
+
+		    /* Delete categories in tfield and tucfield if  definedthey are in input map. */
+		    Vect_field_cat_del(cats_j, tfield, -1);
+		    Vect_field_cat_del(cats_j, tucfield, -1);
+
+
+		    /* Assign unique category (assigned only when feature is written). */
+		    Vect_cat_set(cats_j, tucfield, i_ucat);
+		    ln_j_ucat = i_ucat;
+		    /* add line direction information to ucat */
+		    if (ln_j_id < 0)
+			ln_j_ucat *= -1;
+
+
+		    /* Assign turn category in turntable for the U-turn. */
+		    Vect_cat_set(cats_j, tfield, next_ttb_cat);
+		    Vect_cat_set(cats_j, tfield, next_ttb_cat + 1);
+
+
+		    /* We create two nodes in turntable for every line. These nodes have 
+		       positive and negative values, with their absolute values identical.
+
+		       Every node corresponds to opposite line direction. The positive node 
+		       matches the direction of line. The negative node matches the opposite direction.
+
+		       Imagine that you are standing on some road/line before a intersection wanting to cross it.
+		       If you are going to cross intersection, which is in line direction,
+		       you are standing on the POSITIVE NODE. If you would cross the intersection from any other line 
+		       to the line, you would come into the NEGATIVE NODE. 
+
+		       These two nodes are connected with U-turns, which are two for both direction. 
+		       Every U-turn direction belongs to the another intersection. U-turn from the POSITIVE NODE 
+		       to the NEGATIVE one belongs to the intersection we are going to cross. The other U-turn belongs 
+		       to a intersection in opposite end of the line.
+
+		       Turntable columns:
+
+		       cat - category in tfield (layer with turntable), which are hold by both ln_from and ln_to lines 
+		       ln_from - unique category in tucfield assigned to the line 
+		       ln_to - unique category in tucfield assigned to the line 
+		       cost - cost for turn from ln_from to ln_to
+		       isec - point category in tucfield, which represents the intersection, where the turn belongs
+		       angle - in radians, see comments in compute_lines_angle function, it is PI for U-turns
+		     */
+
+		    /* Find second node (outside_node) of the line. */
+		    Vect_get_line_nodes(InMap, abs(ln_j_id), &node1, &node2);
+
+		    if (node1 == pivot_node)
+			outside_node = node2;
+		    else
+			outside_node = node1;
+
+		    /* Decide intersection where U-turns belong. */
+		    if (ln_j_id < 0) {
+			isec_start_ucat = outside_node + n_lines;
+			isec_end_ucat = pivot_node + n_lines;
+		    }
+		    else {
+			isec_start_ucat = pivot_node + n_lines;
+			isec_end_ucat = outside_node + n_lines;
+		    }
+
+		    /* If i and j lines are identical, write these categories also into i line,
+		       otherwise they would be forgotten during rewriting of i line. */
+		    if (ln_j_id == ln_i_id) {
+			Vect_cat_set(cats_i, tfield, next_ttb_cat);
+			Vect_cat_set(cats_i, tfield, next_ttb_cat + 1);
+			Vect_cat_set(cats_i, tucfield, i_ucat);
+		    }
+
+		    if (add_uturn
+			(driver, ttb_name, &next_ttb_cat, abs(ln_j_ucat),
+			 isec_start_ucat, isec_end_ucat) < 0) {
+
+			G_free_ilist(list);
+			G_free(features_id);
+
+			Vect_destroy_line_struct(line_pnts_i);
+			Vect_destroy_line_struct(line_pnts_j);
+			Vect_destroy_cats_struct(cats_i);
+			Vect_destroy_cats_struct(cats_j);
+
+			G_fatal_error(_
+				      ("Unable to insert data into turntable."));
+		    }
+
+		    /* increment unique category number for next line, which will be written */
+		    ++i_ucat;
+
+		    /* If i line and j line are different, we also need to insert a turn which is defined
+		       by these two edges, therefore we need to add new category to j line, which is corresponding
+		       to the turn.  
+		     */
+		    if (abs(ln_j_id) != abs(ln_i_id)) {
+			Vect_cat_set(cats_j, tfield, next_ttb_cat);
+			Vect_cat_set(cats_j, tfield, next_ttb_cat + 1);
+		    }
+
+		    /* Write new line into output map and save it's id to be possible to find it and edit it later 
+		       (when we get to intersection, which is in other end of the line.) */
+		    features_id[abs(ln_j_id) - 1] =
+			Vect_write_line(OutMap, ltype_j, line_pnts_j, cats_j);
+
+		    /* i, j lines  are equal, it consists only U-turn
+		       Absolute values are there because in case of the lines which have same start and end point, we do not want 
+		       to create redundant lines between these points. This combination has been already done by uturn method.
+		     */
+		    if (abs(ln_j_id) == abs(ln_i_id)) {
+			/* remember unique category also for i line */
+			ln_i_ucat = ln_j_ucat;
+			continue;
+		    }
+		}
+		/* skip if i, j lines are same (U-turn was already written) */
+		else if (abs(ln_j_id) == abs(ln_i_id))
+		    continue;
+		/* write new turn combination for already written i, j lines into output map */
+		else {
+		    /* Get modified cats from out map also for j line, which was already written and 
+		       cats differ from the former cats in the line in input map. */
+		    ltype_j = Vect_read_line(OutMap, line_pnts_j, cats_j,
+					     features_id[abs(ln_j_id) - 1]);
+
+		    /* set category in turntable for new turn, which will be written */
+		    Vect_cat_set(cats_j, tfield, next_ttb_cat);
+		    Vect_cat_set(cats_j, tfield, next_ttb_cat + 1);
+
+		    /* get already assigned unique category of the j line 
+		       (used for ln_from_cat or ln_to_cat in turntable) */
+		    Vect_cat_get(cats_j, tucfield, &ln_j_ucat);
+
+		    /* add line direction information to ucat */
+		    if (ln_j_id < 0)
+			ln_j_ucat *= -1;
+
+		    /* rewrite j line with the added new category for the turn */
+		    Vect_rewrite_line(OutMap, features_id[abs(ln_j_id) - 1],
+				      ltype_j, line_pnts_j, cats_j);
+
+		    /* Because of rewriting, the id of j line was changed, 
+		       therefore we have to update it in features_id. */
+		    features_id[abs(ln_j_id) - 1] = i_ucat + i_rew_lines;
+
+		    /* increment number of rewritten elements, for more see initialization of the variable */
+		    ++i_rew_lines;
+		}
+
+		/* We have to decide which nodes will be connected, which depends on lines directions.
+		   Line direction information is stored in ln_i_id/ln_j_id variables. It the variable is 
+		   negative, the line goes into the intersection. If it is positive the line goes from the 
+		   intersection. */
+
+
+		/* The turn belongs to same intersection regardless the direction. Only exception are the U-turns. */
+		isec_start_ucat = isec_end_ucat = pivot_node + n_lines;
+
+		Vect_cat_set(cats_i, tfield, next_ttb_cat);
+		Vect_cat_set(cats_i, tfield, next_ttb_cat + 1);
+
+		if (add_turns(driver, ttb_name, &next_ttb_cat,
+			      ln_i_ucat, line_pnts_i, ln_j_ucat, line_pnts_j,
+			      isec_start_ucat) < 0) {
+
+		    G_free_ilist(list);
+		    G_free(features_id);
+
+		    Vect_destroy_line_struct(line_pnts_i);
+		    Vect_destroy_line_struct(line_pnts_j);
+		    Vect_destroy_cats_struct(cats_i);
+		    Vect_destroy_cats_struct(cats_j);
+
+		    G_fatal_error(_("Unable to insert data into turntable."));
+		}
+
+	    }
+
+	    /* rewrite i line */
+	    Vect_rewrite_line(OutMap, features_id[abs(ln_i_id) - 1],
+			      ltype_i, line_pnts_i, cats_i);
+
+	    features_id[abs(ln_i_id) - 1] = i_ucat + i_rew_lines;
+	    i_rew_lines += 1;
+	}
+    }
+
+    box_List = Vect_new_boxlist(0);
+
+    /* Update point on every node (assing tuclayer cat) */
+    for (pivot_node = 1; pivot_node <= n_nodes; pivot_node++) {
+	Vect_reset_line(line_pnts_i);
+	Vect_reset_cats(cats_i);
+
+	Vect_get_node_coor(InMap, pivot_node, &x, &y, &z);
+	box.E = box.W = x;
+	box.N = box.S = y;
+	box.T = box.B = z;
+	Vect_select_lines_by_box(InMap, &box, GV_POINT, box_List);
+
+	found_pt_id = -1;
+	for (i_line = 0; i_line < box_List->n_values; i_line++) {
+	    ln_i_id = box_List->id[i_line];
+	    ltype_i = Vect_read_line(InMap, line_pnts_i, cats_i, ln_i_id);
+
+	    if (ltype_i & GV_POINT) {
+		found_pt_id = ln_i_id;
+		break;
+	    }
+	}
+
+	/* No point on the node -> new is created. */
+	/* TODO check for more points on the node Vect_new_list */
+	if (found_pt_id == -1) {
+	    Vect_reset_line(line_pnts_i);
+	    Vect_get_node_coor(InMap, pivot_node, &x, &y, &z);
+	    Vect_append_point(line_pnts_i, x, y, z);
+	    Vect_cat_set(cats_i, tucfield, i_ucat);
+	    Vect_write_line(OutMap, GV_POINT, line_pnts_i, cats_i);
+	}
+	/* Category is updated on existing point on node. */
+	else {
+	    Vect_field_cat_del(cats_i, tucfield, -1);
+	    Vect_field_cat_del(cats_i, tfield, -1);
+	    Vect_cat_set(cats_i, tucfield, i_ucat);
+	    Vect_write_line(OutMap, GV_POINT, line_pnts_i, cats_i);
+	}
+
+	i_ucat++;
+    }
+
+
+    /* copy rest of features, to output map */
+    while ((ltype_i = Vect_read_next_line(InMap, line_pnts_i, cats_i)) > 0) {
+	/* line features in alayer are already in output map */
+	if (ltype_i & arc_type &&
+	    Vect_field_cat_get(cats_i, a_field, list) != -1) {
+	    continue;
+	}
+
+	/* points with node were already written into map */
+	if (ltype_i == GV_POINT && line_pnts_i->n_points &&
+	    Vect_find_node(InMap, line_pnts_i->x[0], line_pnts_i->y[0],
+			   line_pnts_i->z[0], 0.0, WITHOUT_Z) > 0) {
+	    continue;
+	}
+
+	/* Delete categories in tfield and tucfield if they are in input map defined. */
+	Vect_field_cat_del(cats_i, tucfield, -1);
+	Vect_field_cat_del(cats_i, tfield, -1);
+
+	Vect_write_line(OutMap, ltype_i, line_pnts_i, cats_i);
+    }
+
+    G_free_ilist(list);
+    G_free(features_id);
+
+    Vect_destroy_line_struct(line_pnts_i);
+    Vect_destroy_line_struct(line_pnts_j);
+    Vect_destroy_cats_struct(cats_i);
+    Vect_destroy_cats_struct(cats_j);
+    Vect_destroy_boxlist(box_List);
+
+    db_commit_transaction(driver);
+    return;
+}
+
+void turntable(struct opt *opt)
+{
+    struct Map_info InMap, OutMap;
+    struct field_info *fi;
+
+    char *database_name, *driver_name;
+
+    int i_field_num, field_num, i_field, type;
+
+    char *ttb_name;
+    char *key_col;
+    int tfield, tucfield, afield;
+
+    char buf[DB_SQL_MAX];
+    dbDriver *driver;
+
+    dbString db_buf;
+
+    if (Vect_open_old(&InMap, opt->input->answer, "") < 2) {
+	G_fatal_error(_("Unable to open vector map <%s>."),
+		      opt->input->answer);
+    }
+    Vect_set_error_handler_io(&InMap, &OutMap);
+
+    type = Vect_option_to_types(opt->type);
+
+    afield = Vect_get_field_number(&InMap, opt->afield_opt->answer);
+    tfield = Vect_get_field_number(&InMap, opt->tfield->answer);
+    tucfield = Vect_get_field_number(&InMap, opt->tucfield->answer);
+
+    if (!Vect_get_field(&InMap, afield))
+	G_fatal_error(_("Arc layer <%s> does not exists in map <%s>."),
+		      opt->afield_opt->answer, opt->output->answer);
+
+    if (Vect_get_field(&InMap, tfield))
+	G_warning(_
+		  ("Layer <%s> already exists in map <%s>.\nIt will be overwritten by tlayer data."),
+		  opt->tfield->answer, opt->output->answer);
+
+    if (Vect_get_field(&InMap, tucfield))
+	G_warning(_
+		  ("Layer <%s> already exists in map <%s>.\nIt will be overwritten by tuclayer data."),
+		  opt->tucfield->answer, opt->output->answer);
+
+    ttb_name = NULL;
+    G_asprintf(&ttb_name, "%s_turntable_t_%s_tuc_%s_a_%s",
+	       opt->output->answer, opt->tfield->answer,
+	       opt->tucfield->answer, opt->afield_opt->answer);
+
+    if (Vect_open_new(&OutMap, opt->output->answer, WITHOUT_Z) < 1) {
+	G_fatal_error(_("Unable to create vector map <%s>."),
+		      opt->output->answer);
+    }
+
+    /*Use database and driver as layer with lowest number, 
+       if the layer is not present use def settings. */
+    field_num = -1;
+    for (i_field = 0; i_field < Vect_cidx_get_num_fields(&InMap); i_field++) {
+	i_field_num = Vect_cidx_get_field_number(&InMap, i_field);
+	if (Vect_map_check_dblink(&InMap, i_field_num, NULL) == 0)
+	    continue;
+
+	if (field_num == -1)
+	    field_num = i_field_num;
+
+	if (i_field_num != tfield && i_field_num != tucfield)
+	    Vect_copy_tables(&InMap, &OutMap, i_field_num);
+    }
+
+    if (field_num < 0) {
+	driver_name = (char *)db_get_default_driver_name();
+	database_name = (char *)db_get_default_database_name();
+    }
+    else {
+	fi = Vect_get_field(&InMap, field_num);
+	driver_name = fi->driver;
+	database_name = fi->database;
+    }
+
+    driver = db_start_driver_open_database(driver_name, database_name);
+    if (driver == NULL)
+	G_fatal_error(_("Unable to open database <%s> using driver <%s>"),
+		      database_name, driver_name);
+    G_add_error_handler(close_db, driver);
+
+    key_col = "cat";
+    sprintf(buf,
+	    "CREATE TABLE %s (%s INTEGER, ln_from INTEGER, ln_to INTEGER, "
+	    "cost DOUBLE PRECISION, isec INTEGER, angle DOUBLE PRECISION)",
+	    ttb_name, key_col);
+
+    db_init_string(&db_buf);
+    db_set_string(&db_buf, buf);
+
+    if (db_execute_immediate(driver, &db_buf) != DB_OK) {
+	db_free_string(&db_buf);
+	G_fatal_error(_("Unable to create turntable <%s>."), ttb_name);
+    }
+    db_free_string(&db_buf);
+
+    if (Vect_map_add_dblink(&OutMap, tfield,
+			    NULL, ttb_name, key_col,
+			    database_name, driver_name) == -1) {
+	G_fatal_error(_("Unable to connect table <%s> to vector map <%s>."),
+		      ttb_name, opt->input->answer);
+    }
+
+    if (db_create_index2(driver, ttb_name, key_col) != DB_OK)
+	G_warning(_("Unable to create index for column <%s> in table <%s>."),
+		  key_col, ttb_name);
+
+    Vect_build_partial(&OutMap, GV_BUILD_BASE);	/* switch to topological level */
+
+    populate_turntable(driver, &InMap, &OutMap, ttb_name, tfield,
+		       tucfield, afield, type);
+    Vect_close(&InMap);
+
+    close_db(driver);
+
+    Vect_build_partial(&OutMap, GV_BUILD_NONE);	/*must be there in order to be topology build */
+    Vect_build(&OutMap);
+
+    Vect_close(&OutMap);
+
+    return;
+}


Property changes on: grass/trunk/vector/v.net/turntable.c
___________________________________________________________________
Added: svn:mime-type
   + text/x-csrc
Added: svn:eol-style
   + native

Modified: grass/trunk/vector/v.net/v.net.html
===================================================================
--- grass/trunk/vector/v.net/v.net.html	2014-06-29 11:49:08 UTC (rev 61059)
+++ grass/trunk/vector/v.net/v.net.html	2014-06-29 11:54:33 UTC (rev 61060)
@@ -79,6 +79,29 @@
 and update the new table with cat values with <em><a href="v.to.db.html">v.to.db</a></em>. 
 
 <p>
+The <em>turntable</em> operation creates a turntable  
+with the costs for every possible 
+turn on every possible node (intersection, crossroad) in given layer (alayer). 
+U-turns are taken in account too.
+Turntable is created in <b>tlayer</b> and <b>tuclayer</b>.
+Building the turntable allows you to model e.g. trafic code, where some turns 
+may be prohibited.
+If features in analyzed network are changed, the turntable must be created again
+(e.g. it includes v.net connect operation).
+Turntable name consists of output vector map name + "_turntable_" + "t" + "_" + tlayer +
+ "_" + "tuc" + "_" + tuclayer + "_" + "a" + "_" + alayer  e. g. roads_turntable_t_3_tuc_4_a_1
+
+<p>These modules are able to work with the turntable: 
+<!--<em><a href="v.net.alloc.html">v.net.alloc</a></em>,-->
+<!--<em><a href="v.net.iso.html">v.net.iso</a></em>,-->
+<em><a href="v.net.path.html">v.net.path</a></em><!--,-->
+<!--<em><a href="v.net.salesman.html">v.net.salesman</a></em>,-->
+<!--<em><a href="v.net.steiner.html">v.net.steiner</a></em>.-->
+
+For more information about turns in the vector network analyses see 
+<a href="http://grasswiki.osgeo.org/wiki/Turns_in_the_vector_network_analysis">wiki page</a>.
+
+<p>
 Once a vector network has been created, it can be analyzed in a 
 number of powerful ways using the suite of <em>v.net</em>.* modules. 
 The shortest route between two nodes, following arcs, can be computed
@@ -153,6 +176,11 @@
 > EOF
 </pre></div>
 
+Following example generates a vector map with turntable:
+<div class="code"><pre>
+v.net operation=turntable in=railroads out=railroads_ttb
+</pre></div>
+
 <h2>SEE ALSO</h2>
 
 <em>
@@ -187,5 +215,15 @@
 Prague, Czech Republic (operation 'connect' and 'arcs')<br>
 
 Markus Metz: important fixes and improvements
+
+<h3>TURNS SUPPORT</h3>
+
+The turns support was implemnented as part of GRASS GIS turns cost project at Czech Technical University in Prague, Czech Republic.  
+Eliska Kyzlikova, Stepan Turek, Lukas Bocan and Viera Bejdova participated at the project.
+
+Implementation: Stepan Turek 
+Documentation: Lukas Bocan
+Mentor: Martin Landa
+
 <p>
 <i>Last changed: $Date$</i>



More information about the grass-commit mailing list