Index: include/defs/vector.h
===================================================================
--- include/defs/vector.h	(revision 56146)
+++ include/defs/vector.h	(working copy)
@@ -379,6 +379,7 @@
 /* Graph */
 void Vect_graph_init(dglGraph_s *, int);
 void Vect_graph_build(dglGraph_s *);
+
 void Vect_graph_add_edge(dglGraph_s *, int, int, double, int);
 void Vect_graph_set_node_costs(dglGraph_s *, int, double);
 int Vect_graph_shortest_path(dglGraph_s *, int, int, struct ilist *, double *);
@@ -386,8 +387,12 @@
 /* Network (graph) */
 int Vect_net_build_graph(struct Map_info *, int, int, int, const char *,
                          const char *, const char *, int, int);
+int Vect_net_ttb_build_graph(struct Map_info *, int, int, int, int, int ,
+                            const char *, const char *, const char *, int, int);
 int Vect_net_shortest_path(struct Map_info *, int, int, struct ilist *,
                            double *);
+int Vect_net_ttb_shortest_path(struct Map_info *, int, int, int, int, int, int,
+                             struct ilist *, double *);
 dglGraph_s *Vect_net_get_graph(const struct Map_info *);
 int Vect_net_get_line_cost(const struct Map_info *, int, int, double *);
 int Vect_net_get_node_cost(const struct Map_info *, int, double *);
@@ -404,6 +409,16 @@
                                 double *, struct line_pnts *, struct ilist *, struct ilist *,
                                 struct line_pnts *, struct line_pnts *,
                                 double *, double *);
+int Vect_net_ttb_shortest_path_coor(struct Map_info *, double, double, double,
+                                double, double, double, double, double, int, int,
+                                double *, struct line_pnts *, struct ilist *,
+                                struct line_pnts *, struct line_pnts *,
+                                double *, double *);
+int Vect_net_ttb_shortest_path_coor2(struct Map_info *, double, double, double,
+                                double, double, double, double, double, int, int,
+                                double *, struct line_pnts *, struct ilist *, struct ilist *,
+                                struct line_pnts *, struct line_pnts *,
+                                double *, double *);
 
 /* Miscellaneous */
 int Vect_topo_dump(const struct Map_info *, FILE *);
Index: gui/wxpython/vnet/__init__.py
===================================================================
--- gui/wxpython/vnet/__init__.py	(revision 56146)
+++ gui/wxpython/vnet/__init__.py	(working copy)
@@ -2,4 +2,5 @@
     'widgets',
     'dialogs',
     'toolbars',
+    'vnet_core'
     ]
Index: gui/wxpython/vnet/dialogs.py
===================================================================
--- gui/wxpython/vnet/dialogs.py	(revision 56146)
+++ gui/wxpython/vnet/dialogs.py	(working copy)
@@ -7,26 +7,32 @@
  - dialogs::VNETDialog
  - dialogs::PtsList
  - dialogs::SettingsDialog
- - dialogs::AddLayerDialog
- - dialogs::VnetTmpVectMaps
- - dialogs::VectMap
- - dialogs::History
+ - dialogs::OutputVectorDialog
  - dialogs::VnetStatusbar
+ - dialogs::DefIntesectionTurnCostDialog
+ - dialogs::DefGlobalTurnsDialog
+ - dialogs::TurnAnglesList
+ - dialogs::GlobalTurnData
 
-(C) 2012 by the GRASS Development Team
+(C) 2012-2013 by the GRASS Development Team
 
 This program is free software under the GNU General Public License
 (>=v2). Read the file COPYING that comes with GRASS for details.
 
 @author Stepan Turek <stepan.turek seznam.cz> (GSoC 2012, mentor: Martin Landa)
+@author Vierka Bejdova <viera.bejdova seznam.cz> (turn costs support)
+@author Lukas Bocan <silent_bob centrum.cz> (turn costs support)
+@author Eliska Kyzlikova <eliska.kyzlikova gmail.com> (turn costs support)
 """
 
 import os
 import sys
 import types
+import  math
+
 try:
     import grass.lib.vector as vectlib
-    from ctypes import pointer, byref, c_char_p, c_int, c_double
+    from ctypes import pointer, byref, c_char_p, c_int, c_double, POINTER
     haveCtypes = True
 except ImportError:
     haveCtypes = False
@@ -38,6 +44,8 @@
 import wx.aui
 import wx.lib.flatnotebook  as FN
 import wx.lib.colourselect as csel
+import wx.lib.mixins.listctrl  as  listmix
+import wx.lib.scrolledpanel    as scrolled
 
 from core             import globalvar, utils
 from core.gcmd        import RunCommand, GMessage
@@ -45,6 +53,7 @@
 from core.settings    import UserSettings
 
 from dbmgr.base       import DbMgrBase 
+from dbmgr.vinfo      import VectorDBInfo
 
 from gui_core.widgets import GNotebook
 from gui_core.goutput import GConsoleWindow
@@ -53,6 +62,7 @@
 
 from vnet.widgets     import PointsList
 from vnet.toolbars    import MainToolbar, PointListToolbar, AnalysisToolbar
+from vnet.vnet_core   import VectMap, History, VnetTmpVectMaps
 
 #Main TODOs
 # - when layer tree of is changed, tmp result map is removed from render list 
@@ -94,6 +104,8 @@
         # setting initialization
         self._initSettings()
 
+        self.turnsData = {"global" : GlobalTurnData()}
+
         # registration graphics for drawing
         self.pointsToDraw = self.mapWin.RegisterGraphicsToDraw(graphicsType = "point", 
                                                                setStatusFunc = self.SetPointStatus)
@@ -102,6 +114,8 @@
         # information, whether mouse event handler is registered in map window
         self.handlerRegistered = False
 
+        self.defIsecTurnsHndlrReg = False
+        
         # get attribute table columns only with numbers (for cost columns in vnet analysis)
         self.columnTypes = ['integer', 'double precision'] 
         
@@ -134,7 +148,8 @@
         self.stBar = VnetStatusbar(parent = self.mainPanel, style = 0)
         self.stBar.SetFieldsCount(number = 1)
     
-        
+        self.def_isec_turns = None
+
         # Create tabs
         
         # Stores all data related to snapping
@@ -177,10 +192,10 @@
         self.list.selected = 0
         self.list.Select(self.list.selected)
 
-        dlgSize = (420, 560)
-        self.SetMinSize(dlgSize)
-        self.SetInitialSize(dlgSize)
+        self.SetSize((360, 520))
 
+        self.SetMinSize((360, 420))
+
         #fix goutput's pane size (required for Mac OSX)
         if self.gwindow:         
             self.gwindow.SetSashPosition(int(self.GetSize()[1] * .75))
@@ -347,7 +362,7 @@
 
     def _createParametersPage(self):
         """!Tab for selection of data for analysis"""
-        dataPanel = wx.Panel(parent=self)
+        dataPanel = scrolled.ScrolledPanel(parent=self)
         self.notebook.AddPage(page = dataPanel,
                               text=_('Parameters'), 
                               name = 'parameters')
@@ -356,12 +371,17 @@
                         ['input', "Choose vector map for analysis:", Select],
                         ['alayer', "Arc layer number or name:", LayerSelect],
                         ['nlayer', "Node layer number or name:", LayerSelect],
+                        ['tlayer', "Layer with turntable:", LayerSelect],
+                        ['tuclayer', "Layer with unique categories for turntable:", LayerSelect],
                         ['afcolumn', self.attrCols['afcolumn']['label'], ColumnSelect],
                         ['abcolumn', self.attrCols['abcolumn']['label'], ColumnSelect],
                         ['ncolumn', self.attrCols['ncolumn']['label'], ColumnSelect],
                       ]
 
+        self.useTurns = wx.CheckBox(parent = dataPanel, id=wx.ID_ANY,
+                                    label = _('Use turns'))
         selPanels = {}
+
         for dataSel in dataSelects:
             selPanels[dataSel[0]] = wx.Panel(parent = dataPanel)
             if dataSel[0] == 'input':
@@ -378,6 +398,11 @@
                 self.addToTreeBtn.Disable()
                 self.addToTreeBtn.Bind(wx.EVT_BUTTON, self.OnToTreeBtn)
             elif dataSel[0] != 'input':
+                if dataSel[0] == "tlayer":
+                    self.createTtbBtn = wx.Button(parent = selPanels[dataSel[0]], 
+                                                 label = _("Create turntable")) 
+                    self.createTtbBtn.Bind(wx.EVT_BUTTON, self.OnCreateTtbBtn)
+
                 self.inputData[dataSel[0]] = dataSel[2](parent = selPanels[dataSel[0]],  
                                                         size = (-1, -1))
             label[dataSel[0]] =  wx.StaticText(parent =  selPanels[dataSel[0]], 
@@ -388,6 +413,8 @@
         self.inputData['alayer'].Bind(wx.EVT_TEXT, self.OnALayerSel)
         self.inputData['nlayer'].Bind(wx.EVT_TEXT, self.OnNLayerSel)
 
+        self.useTurns.Bind(wx.EVT_CHECKBOX,
+                            lambda event: self.UseTurns())
         # Layout
         mainSizer = wx.BoxSizer(wx.VERTICAL)
         box = wx.StaticBox(dataPanel, -1, "Vector map and layers for analysis")
@@ -396,15 +423,21 @@
         mainSizer.Add(item = bsizer, proportion = 0,
                       flag = wx.EXPAND  | wx.TOP | wx.LEFT | wx.RIGHT, border = 5) 
 
-        for sel in ['input', 'alayer', 'nlayer']:
-            if sel== 'input':
+        for sel in ['input', 'alayer', 'nlayer', 'tlayer', 'tuclayer']:
+            if sel == 'input':
                 btn = self.addToTreeBtn
+            elif sel == "tlayer":
+                btn = self.createTtbBtn
             else:
                 btn = None
+            if sel == 'tlayer':
+                bsizer.Add(item = self.useTurns, proportion = 0,
+                            flag = wx.TOP | wx.LEFT | wx.RIGHT, border = 5)                       
+
             selPanels[sel].SetSizer(self._doSelLayout(title = label[sel], 
                                                       sel = self.inputData[sel], 
                                                       btn = btn))
-            bsizer.Add(item = selPanels[sel], proportion = 1,
+            bsizer.Add(item = selPanels[sel], proportion = 0,
                        flag = wx.EXPAND)
 
         box = wx.StaticBox(dataPanel, -1, "Costs")    
@@ -418,8 +451,13 @@
             bsizer.Add(item = selPanels[sel], proportion = 0,
                        flag = wx.EXPAND)
 
+        self.UseTurns()
+
         dataPanel.SetSizer(mainSizer)
+        dataPanel.SetupScrolling()
 
+        dataPanel.SetAutoLayout(1)
+
     def _doSelLayout(self, title, sel, btn = None): 
         # helper function for layout of self.inputData widgets initialized in _createParametersPage
         selSizer = wx.BoxSizer(orient = wx.VERTICAL)
@@ -614,6 +652,102 @@
             up_map_evt = gUpdateMap(render = True, renderVector = True)
             wx.PostEvent(self.mapWin, up_map_evt)
 
+    def UseTurns(self):
+        if self.useTurns.IsChecked():
+            self.turnsData["use_turns"] = True
+            self.inputData["tlayer"].GetParent().Show()
+            self.inputData["tuclayer"].GetParent().Show()
+        else:
+            self.turnsData["use_turns"] = False
+            self.inputData["tlayer"].GetParent().Hide()
+            self.inputData["tuclayer"].GetParent().Hide()
+
+        self.Layout()
+
+    def OnCreateTtbBtn(self, event):
+
+        msg = _("Missing data:\n\n")
+        try:
+            tlayer = int(self.inputData["tlayer"].GetValue())
+        except:
+            tlayer = None
+        try:
+            tuclayer = int(self.inputData["tuclayer"].GetValue())
+        except:
+            tuclayer = None
+
+        try:
+            alayer = int(self.inputData["alayer"].GetValue())
+        except:
+            err = True
+
+        nlayer = -1;
+        alayer 
+
+        dlg = OutputVectorDialog(parent = self, title = _("New vector map with turntable"))
+
+        msg = _("Vector map with analysis result does not exist.")
+        if dlg.ShowModal() == wx.ID_OK:
+
+            outputMap = dlg.vectSel.GetValue()
+            mapName, mapSet = self._parseMapStr(outputMap)
+            if mapSet !=  grass.gisenv()['MAPSET']:
+                GMessage(parent = self,
+                         message = _("Map can be created only in current mapset"))
+                return
+            existsMap = grass.find_file(name = mapName, 
+                                        element = 'vector', 
+                                        mapset = grass.gisenv()['MAPSET'])
+            dlg.Destroy()
+            if existsMap["name"]:
+                dlg = wx.MessageDialog(parent = self.parent.parent,
+                                       message = _("Vector map %s already exists. " +
+                                                "Do you want to overwrite it?") % 
+                                                (existsMap["fullname"]),
+                                       caption = _("Overwrite map layer"),
+                                       style = wx.YES_NO | wx.NO_DEFAULT |
+                                               wx.ICON_QUESTION | wx.CENTRE) 
+                ret = dlg.ShowModal()
+                if ret == wx.ID_NO:
+                    dlg.Destroy()
+                    return
+                dlg.Destroy()
+
+            cmdTtb = [ 
+                        "v.net.turntable",
+                        "input=" + self.inputData["input"].GetValue(), 
+                        "output=" + outputMap,
+                        "alayer=%d" % alayer,
+                        "nlayer=%d" % nlayer,
+                        "tlayer=%d" % tlayer,
+                        "tuclayer=%d" % tuclayer,
+                        "--overwrite", 
+                       ]
+
+            self._prepareCmd(cmdTtb)
+            self.goutput.RunCmd(command = cmdTtb, onDone = self._createTtbDone)
+
+            self.stBar.AddStatusItem(text = _('Creating turntable...'),
+                                     key = 'ttb',
+                                     priority =  self.stPriorities['important'])
+
+    def _createTtbDone(self, cmd, returncode):
+        self.stBar.RemoveStatusItem('ttb')  
+        if returncode != 0:
+            GMessage(parent = self,
+                     message = "Creation of turntable failed.")
+            return
+
+        for c in cmd:
+            spl_c = c.split("=")
+            print spl_c
+            if len(spl_c) != 2:
+                continue
+            if spl_c[0] in self.inputData.keys() and spl_c != "input":
+                self.inputData[spl_c[0]].SetValue(spl_c[1])
+            if spl_c[0] == "output":
+                self.inputData["input"].SetValue(spl_c[1])
+
     def OnVectSel(self, event):
         """!When vector map is selected it populates other comboboxes in Parameters tab (layer selects, columns selects)"""
         if self.snapData['snap_mode']:
@@ -622,11 +756,13 @@
         vectMapName, mapSet = self._parseMapStr(self.inputData['input'].GetValue())
         vectorMap = vectMapName + '@' + mapSet
 
-        self.inputData['alayer'].Clear()
-        self.inputData['nlayer'].Clear()
+        layers =  ['alayer', 'nlayer', 'tlayer', 'tuclayer']
+        for l in layers: 
+            self.inputData[l].Clear()
 
-        self.inputData['alayer'].InsertLayers(vector = vectorMap)
-        self.inputData['nlayer'].InsertLayers(vector = vectorMap)
+        for sel in layers:
+            self.inputData[sel].Clear()
+            self.inputData[sel].InsertLayers(vector = vectorMap)
 
         items = self.inputData['alayer'].GetItems()
         itemsLen = len(items)
@@ -634,22 +770,34 @@
             self.addToTreeBtn.Disable()
             if hasattr(self, 'inpDbMgrData'):
                 self._updateInputDbMgrPage(show = False)
-            self.inputData['alayer'].SetValue("")
-            self.inputData['nlayer'].SetValue("")
+            for l in layers: 
+               self.inputData[l].SetValue("")
             for sel in ['afcolumn', 'abcolumn', 'ncolumn']:
                 self.inputData[sel].SetValue("")
             return
         elif itemsLen == 1:
-            self.inputData['alayer'].SetSelection(0)
-            self.inputData['nlayer'].SetSelection(0)
+            for l in layers: 
+                self.inputData[l].SetSelection(0)
         elif itemsLen >= 1:
             if unicode("1") in items:
                 iItem = items.index(unicode("1")) 
                 self.inputData['alayer'].SetSelection(iItem)
+
             if unicode("2") in items:
                 iItem = items.index(unicode("2")) 
                 self.inputData['nlayer'].SetSelection(iItem)
+            elif unicode("1") in items:
+                iItem = items.index(unicode("1")) 
+                self.inputData['nlayer'].SetSelection(iItem)
+            
+            if unicode("3") in items:
+                iItem = items.index(unicode("3")) 
+                self.inputData['tlayer'].SetSelection(iItem)
+            if unicode("4") in items:
+                iItem = items.index(unicode("4")) 
+                self.inputData['tuclayer'].SetSelection(iItem)
 
+
         self.addToTreeBtn.Enable()
         if hasattr(self, 'inpDbMgrData'):
             self._updateInputDbMgrPage(show = True)
@@ -691,7 +839,7 @@
                 errInput['input'] = mapVal
 
         # check of arc/node layer
-        for layerSelName in ['alayer', 'nlayer'] :
+        for layerSelName in ['alayer', 'nlayer', 'tlayer', 'tuclayer'] :
             if not inpToTest or layerSelName in inpToTest:
 
                 layerItems = self.inputData[layerSelName].GetItems()
@@ -761,8 +909,12 @@
 
         errLayerStr = ""
         for layer, layerLabel in {'alayer' : _("arc layer"), 
-                                  'nlayer' : _("node layer")}.iteritems():
+                                  'nlayer' : _("node layer"),
+                                  'tlayer' : _("turntable layer"),
+                                  'tuclayer' : _("unique categories layer")}.iteritems():
 
+            if layer in ["tlayer", "tuclayer"] and not self.turnsData["use_turns"]:
+                continue
             if  errInput.has_key(layer):
                 if errInput[layer]:
                     errLayerStr += _("Chosen %s '%s' does not exist in vector map '%s'.\n") % \
@@ -915,11 +1067,145 @@
         coords[1] = n
         return True
 
+    def OnDefIsecTurnCosts(self, event):
+        """!Registers/unregisters mouse handler into map window"""
+        if self.defIsecTurnsHndlrReg == False:
+            self.mapWin.RegisterMouseEventHandler(wx.EVT_LEFT_DOWN, 
+                                                  self.OnDefIsecTurnCost,
+                                                  wx.StockCursor(wx.CURSOR_CROSS))
+            self.defIsecTurnsHndlrReg = True
+        else:
+            self.mapWin.UnregisterMouseEventHandler(wx.EVT_LEFT_DOWN, 
+                                                    self.OnDefIsecTurnCost)
+
+            self.defIsecTurnsHndlrReg = False
+
+    def OnDefGlobalTurnCosts(self, event):
+
+        dialog = DefGlobalTurnsDialog(self, self.turnsData["global"])
+        dialog.Show()
+
+    def _getNearestNodeCat(self, e, n, field, tresh, vectMap):
+
+        vectMapName, mapSet = self._parseMapStr(vectMap)
+
+        openedMap = pointer(vectlib.Map_info())
+        ret = vectlib.Vect_open_old2(openedMap, 
+                                     c_char_p(vectMapName),
+                                     c_char_p(mapSet),
+                                     c_char_p(self.inputData['alayer'].GetValue()))
+        if ret == 1:
+            vectlib.Vect_close(openedMap)
+        if ret != 2: 
+            return False
+
+        nodeNum = vectlib.Vect_find_node(openedMap,     
+                                         c_double(e), 
+                                         c_double(n), 
+                                         c_double(0), 
+                                         c_double(tresh),
+                                         vectlib.WITHOUT_Z)
+
+        if nodeNum > 0:
+            e = c_double(0)
+            n = c_double(0)
+            vectlib.Vect_get_node_coor(openedMap, 
+                                       nodeNum, 
+                                       byref(e), 
+                                       byref(n), 
+                                       None); # z
+            e = e.value
+            n = n.value
+        else:
+            vectlib.Vect_close(openedMap)
+            return False
+
+        box = vectlib.bound_box();
+        List = POINTER(vectlib.boxlist);
+        List = vectlib.Vect_new_boxlist(c_int(0));
+
+        box.E = box.W = e;
+        box.N = box.S = n;
+        box.T = box.B = 0;
+        vectlib.Vect_select_lines_by_box(openedMap, byref(box), vectlib.GV_POINT, List);
+
+        found = 0;
+        dcost = 0;
+
+        Cats = POINTER(vectlib.line_cats)
+        Cats = vectlib.Vect_new_cats_struct()
+ 
+        cat = c_int(0)
+
+        for j in range(List.contents.n_values): 
+            line = List.contents.id[j]
+            type = vectlib.Vect_read_line(openedMap, None, Cats, line)
+            if type != vectlib.GV_POINT:
+                continue
+
+            if vectlib.Vect_cat_get(Cats, field, byref(cat)): 
+                found = 1
+                break
+        if found:
+            return cat.value
+
+        return -1
+
+    def OnDefIsecTurnCost(self, event):
+        """!Take coordinates from map window"""
+        if event == 'unregistered':
+            ptListToolbar = self.toolbars['pointsList']
+            if ptListToolbar:
+                ptListToolbar.ToggleTool( id = ptListToolbar.GetToolId("isec_turn_edit"),
+                                          toggle = False)  
+            self.handlerRegistered = False
+            return
+
+        if not self.turnsData["use_turns"]:
+            GMessage(parent = self, message = _("Turns are not activated."))
+
+        e, n = self.mapWin.GetLastEN()
+
+        # compute threshold
+        snapTreshPix = int(UserSettings.Get(group ='vnet', 
+                                            key = 'other', 
+                                            subkey = 'snap_tresh'))
+        res = max(self.mapWin.Map.region['nsres'], self.mapWin.Map.region['ewres'])
+        snapTreshDist = snapTreshPix * res
+
+        vectorMap = self.inputData['input'].GetValue()
+        vectMapName, mapSet = self._parseMapStr(vectorMap)
+        inpMapExists = grass.find_file(name = vectMapName, 
+                                       element = 'vector', 
+                                       mapset = mapSet)
+        if not inpMapExists['name']:
+            return False
+
+        try:
+            tucfield = int(self.inputData['tuclayer'].GetValue())
+            tfield = int(self.inputData['tlayer'].GetValue())
+        except:
+            GMessage(parent = self, message = "Please choose turntable layer and unique categories layer in Parameters tab.")
+
+        cat = self._getNearestNodeCat(e, n, tucfield, snapTreshDist, vectorMap)
+
+        if not self.def_isec_turns:
+            self.def_isec_turns = DefIntesectionTurnCostDialog(self, self.parent)
+            self.def_isec_turns.SetSize((500, 400))
+
+        self.def_isec_turns.SetData(vectorMap, tfield)
+        self.def_isec_turns.SetIntersection(cat)
+        self.def_isec_turns.Show()
+
     def OnAnalyze(self, event):
         """!Called when network analysis is started"""
         # Check of parameters for analysis
         if not self.InputsErrorMsgs(msg = _("Analysis can not be done.")):
             return
+        
+        if not self.vnetParams[self.currAnModule]["turns_support"] and  self.turnsData["use_turns"]:
+            GMessage(parent = self, message = _("Module <%s> does not support turns costs." % self.currAnModule))
+            return
 
         if self.tmp_result:
             self.tmp_result.DeleteRenderLayer()
@@ -957,6 +1243,8 @@
 
         if self.currAnModule == "v.net.path":
             self._vnetPathRunAn(cmdParams, catPts)
+        elif self.turnsData["use_turns"]:
+            self._runTurnsAn(cmdParams, catPts)
         else:
             self._runAn(cmdParams, catPts)
 
@@ -983,15 +1271,51 @@
         coordsTmpFileOpened.write(inpPoints)
         coordsTmpFileOpened.close()
 
+        if self.turnsData["use_turns"]:
+            cmdParams.append("-t")
+
+            self.tmpTurnAn = self._addTmpMapAnalysisMsg("vnet_tunr_an_tmp")
+            if not self.tmpTurnAn:
+                return
+
+            mapName, mapSet = self._parseMapStr(self.tmpTurnAn.GetVectMapName())
+            cmdCopy = [ 
+                        "g.copy",
+                        "vect=%s,%s" % (self.inputData['input'].GetValue(), mapName), 
+                        "--overwrite",                             
+                       ]
+            cmdParams.append("input=" + self.tmpTurnAn.GetVectMapName())
+
+            ret, msg, err = RunCommand('g.copy',
+                                        getErrorMsg = True,
+                                        vect = "%s,%s" % (self.inputData['input'].GetValue(), mapName),
+                                        read = True,
+                                        overwrite = True)
+
+            self._updateTtbByGlobalCosts(self.tmpTurnAn.GetVectMapName(), 
+                                         int(self.inputData["tlayer"].GetValue()))
+            
+            #self._prepareCmd(cmdCopy)
+            #self.goutput.RunCmd(command = cmdCopy)
+        else:
+            cmdParams.append("input=" + self.inputData['input'].GetValue())
+
         cmdParams.append("file=" + self.coordsTmpFile)
 
         cmdParams.append("dmax=" + str(self.anSettings["max_dist"].GetValue()))
-        cmdParams.append("input=" + self.inputData['input'].GetValue())
 
         cmdParams.append("--overwrite")
         self._prepareCmd(cmd = cmdParams)
+        
+        if self.turnsData["use_turns"]:
+            self.goutput.RunCmd(command = cmdParams, onDone = self._vnetPathRunTurnsAnDone)
+        else:
+            self.goutput.RunCmd(command = cmdParams, onDone = self._vnetPathRunAnDone)
 
-        self.goutput.RunCmd(command = cmdParams, onDone = self._vnetPathRunAnDone)
+    def _vnetPathRunTurnsAnDone(self, cmd, returncode):
+        #TODO
+        #self.tmpMaps.DeleteTmpMap(self.tmpTurnAn)
+        self._vnetPathRunAnDone(cmd, returncode)
 
     def _vnetPathRunAnDone(self, cmd, returncode):
         """!Called when v.net.path analysis is done"""
@@ -1016,6 +1340,137 @@
 
         self.stBar.RemoveStatusItem(key = 'analyze')
 
+    def _runTurnsAn(self, cmdParams, catPts):
+
+        cats = {}
+        for cat_name, pts_coor in catPts.iteritems():
+
+            for coor in pts_coor:
+                cat_num = str(self._getNearestNodeCat(e = coor[0], 
+                                                  n = coor[1], 
+                                                  field = int(self.inputData["tuclayer"].GetValue()),
+                                                  tresh = self.anSettings["max_dist"].GetValue(), 
+                                                  vectMap = self.inputData["input"].GetValue()))
+                if cat_num < 0:
+                    continue
+                if cats.has_key(cat_name):
+                    cats[cat_name].append(cat_num)
+                else:
+                    cats[cat_name] = [cat_num]
+
+        for cat_name, cat_nums in cats.iteritems():
+            cmdParams.append(cat_name + "=" + ",".join(cat_nums))
+
+        self.tmpTurnAn = self._addTmpMapAnalysisMsg("vnet_tunr_an_tmp")
+        if not self.tmpTurnAn:
+            return
+
+        # create and run commands which goes to analysis thread
+
+        mapName, mapSet = self._parseMapStr(self.tmpTurnAn.GetVectMapName())
+        cmdCopy = [ 
+                    "g.copy",
+                    "vect=%s,%s" % (self.inputData['input'].GetValue(), mapName), 
+                    "--overwrite",                             
+                   ]
+        cmdParams.append("input=" + self.tmpTurnAn.GetVectMapName())
+
+        ret, msg, err = RunCommand('g.copy',
+                                    getErrorMsg = True,
+                                    vect = "%s,%s" % (self.inputData['input'].GetValue(), mapName),
+                                    read = True,
+                                    overwrite = True)
+
+        self._updateTtbByGlobalCosts(self.tmpTurnAn.GetVectMapName(), 
+                                     int(self.inputData["tlayer"].GetValue()))
+
+
+        self._setCmdForSpecificAn(cmdParams)
+
+        cmdParams.append("-t")
+
+        self._prepareCmd(cmdParams)
+        self.goutput.RunCmd(command = cmdParams, onDone = self._runTurnsAnDone)
+
+    def _updateTtbByGlobalCosts(self, vectMapName, tlayer):
+
+        turn_data = self.turnsData["global"].GetData()
+        intervals = turn_data["intervals"]
+
+        cmdUpdGlob = [ 
+                      "v.db.update",
+                      "map=", self.inputData["input"].GetValue(), 
+                      "layer=%d" % tlayer,
+                      "column=cost",
+                    ]
+
+        dbInfo = VectorDBInfo(vectMapName)
+        table = dbInfo.GetTable(tlayer)
+        driver,  database = dbInfo.GetDbSettings(tlayer)
+
+
+        sqlFile = grass.tempfile()
+        sqlFile_f = open(sqlFile, 'w')
+
+        for ival in intervals:
+            from_angle = ival[0]
+            to_angle = ival[1]
+            cost = ival[2]
+
+            if to_angle < from_angle:
+                to_angle = math.pi * 2  + to_angle
+            #if angle < from_angle:
+            #    angle = math.pi * 2  + angle
+
+            where = " WHERE (((angle < {0}) AND ({2} + angle >= {0} AND {2} + angle < {1})) OR \
+                            ((angle >= {0}) AND (angle >= {0} AND angle < {1}))) AND cost==0.0 ".format(str(from_angle), str(to_angle), str(math.pi * 2))
+
+            stm = ("UPDATE %s SET cost=%f " % (table, cost)) + where + ";\n";
+            print stm
+            sqlFile_f.write(stm)
+
+
+            #TODO imporve parser and run in thread
+
+        if not turn_data["use_uturns"]:
+            stm = ("UPDATE %s SET cost=-1 WHERE ln_from = -ln_to;\n" % (table));
+            print stm
+            sqlFile_f.write(stm)
+        
+        sqlFile_f.close()
+
+        ret, msg, err = RunCommand('db.execute',
+                               getErrorMsg = True,
+                               input = sqlFile,
+                               read = True,
+                               driver = driver,
+                               database = database)
+
+        grass.try_remove(sqlFile)
+
+    def _runTurnsAnDone(self, cmd, returncode):
+        """!Called when analysis is done"""
+        #self.tmpMaps.DeleteTmpMap(self.tmpTurnAn) #TODO remove earlier (OnDone lambda?)
+
+        self._saveHistStep()
+        self.tmp_result.SaveVectMapState()
+       
+        self._updateResultDbMgrPage()
+        self._updateDbMgrData()
+
+        cmd = self.GetLayerStyle()
+        self.tmp_result.AddRenderLayer(cmd)
+
+        up_map_evt = gUpdateMap(render = True, renderVector = True)
+        wx.PostEvent(self.mapWin, up_map_evt)
+
+        mainToolbar = self.toolbars['mainToolbar']
+        id = vars(mainToolbar)['showResult']
+        mainToolbar.ToggleTool(id =id,
+                               toggle = True)
+
+        self.stBar.RemoveStatusItem(key = 'analyze')
+
     def _runAn(self, cmdParams, catPts):
         """!Called for all v.net.* analysis (except v.net.path)"""
 
@@ -1070,19 +1525,9 @@
 
         cmdParams.append("input=" + self.tmpInPtsConnected.GetVectMapName())
         cmdParams.append("--overwrite")  
-
-        # append parameters needed for particular analysis
-        if self.currAnModule == "v.net.distance":
-            cmdParams.append("from_layer=1")
-            cmdParams.append("to_layer=1")
-        elif self.currAnModule == "v.net.flow":
-            self.vnetFlowTmpCut = self.NewTmpVectMapToHist('vnet_tmp_flow_cut')
-            if not self.vnetFlowTmpCut:
-                return          
-            cmdParams.append("cut=" +  self.vnetFlowTmpCut.GetVectMapName())         
-        elif self.currAnModule == "v.net.iso":
-            costs = self.anSettings["iso_lines"].GetValue()
-            cmdParams.append("costs=" + costs)          
+        
+        self._setCmdForSpecificAn(cmdParams)
+        
         for catName, catNum in catsNums.iteritems():
             if catNum[0] == catNum[1]:
                 cmdParams.append(catName + "=" + str(catNum[0]))
@@ -1160,7 +1605,13 @@
 
             inParams.append(col + '=' + self.inputData[colInptF].GetValue())
 
-        for layer in ['alayer', 'nlayer']:
+        for layer in ['alayer', 'nlayer', 'tlayer', 'tuclayer']:
+            if not self.turnsData["use_turns"] and layer in ['tlayer', 'tuclayer']:
+                continue
+            if self.turnsData["use_turns"] and layer == 'nlayer':
+                inParams.append(layer + "=" + self.inputData['tuclayer'].GetValue().strip())
+                continue
+
             inParams.append(layer + "=" + self.inputData[layer].GetValue().strip())
 
         return inParams
@@ -1213,6 +1664,20 @@
             elif not v[1].strip():
                 cmd.remove(c)
 
+    def _setCmdForSpecificAn(self, cmdParams):
+        # append parameters needed for particular analysis
+        if self.currAnModule == "v.net.distance":
+            cmdParams.append("from_layer=1")
+            cmdParams.append("to_layer=1")
+        elif self.currAnModule == "v.net.flow":
+            self.vnetFlowTmpCut = self.NewTmpVectMapToHist('vnet_tmp_flow_cut')
+            if not self.vnetFlowTmpCut:
+                return          
+            cmdParams.append("cut=" +  self.vnetFlowTmpCut.GetVectMapName())         
+        elif self.currAnModule == "v.net.iso":
+            costs = self.anSettings["iso_lines"].GetValue()
+            cmdParams.append("costs=" + costs)
+
     def GetLayerStyle(self):
         """!Returns cmd for d.vect, with set style for analysis result"""
         resProps = self.vnetParams[self.currAnModule]["resultProps"]
@@ -1280,7 +1745,7 @@
 
     def OnSaveTmpLayer(self, event):
         """!Permanently saves temporary map of analysis result"""
-        dlg = AddLayerDialog(parent = self)
+        dlg = OutputVectorDialog(parent = self)
 
         msg = _("Vector map with analysis result does not exist.")
         if dlg.ShowModal() == wx.ID_OK: 
@@ -1791,8 +2256,9 @@
                                                                    },
                                                      "resultProps" : {
                                                                       "singleColor" : None,
-                                                                      "dbMgr" : True  
-                                                                     }
+                                                                      "dbMgr" : True  #TODO delete this property, thos information can be get from result
+                                                                     },
+                                                     "turns_support" : True
                                                   },
 
                                     "v.net.salesman" : {
@@ -1807,7 +2273,9 @@
                                                         "resultProps" : {
                                                                          "singleColor" : None,
                                                                          "dbMgr" : False
-                                                                        }
+                                                                        },
+                                                     "turns_support" : True
+
                                                        },
                                     "v.net.flow" : {
                                                      "label" : _("Maximum flow %s") % "(v.net.flow)",  
@@ -1825,7 +2293,8 @@
                                                      "resultProps" : {
                                                                       "attrColColor": "flow",
                                                                       "dbMgr" : True
-                                                                     }
+                                                                     },
+                                                     "turns_support" : False
                                                    },
                                     "v.net.alloc" : {
                                                      "label" : _("Subnets for nearest centers %s") % "(v.net.alloc)",  
@@ -1840,7 +2309,8 @@
                                                      "resultProps" :  {
                                                                        "catColor" : None, 
                                                                        "dbMgr" : False
-                                                                      }
+                                                                      },
+                                                     "turns_support" : True
                                                    },
                                     "v.net.steiner" : {
                                                      "label" : _("Steiner tree for the network and given terminals %s") % "(v.net.steiner)",  
@@ -1853,7 +2323,8 @@
                                                      "resultProps" : {
                                                                       "singleColor" : None,
                                                                       "dbMgr" : False 
-                                                                     }
+                                                                     },
+                                                     "turns_support" : True
                                                    },
                                    "v.net.distance" : {
                                                        "label" : _("Shortest distance via the network %s") % "(v.net.distance)",  
@@ -1871,7 +2342,8 @@
                                                       "resultProps" : {
                                                                         "catColor" : None,
                                                                         "dbMgr" : True
-                                                                      }
+                                                                      },
+                                                     "turns_support" : False
                                                      },
                                     "v.net.iso" :  {
                                                      "label" : _("Cost isolines %s") % "(v.net.iso)",  
@@ -1886,7 +2358,8 @@
                                                      "resultProps" : {
                                                                       "catColor" : None,
                                                                       "dbMgr" : False
-                                                                     }
+                                                                     },
+                                                     "turns_support" : True
                                                    }
                                 }
         # order in combobox for choose of analysis
@@ -1927,7 +2400,6 @@
                                     value = init[2],
                                     overwrite = False)
 
-
     def SetPointDrawSettings(self):
         """!Set settings for drawing of points"""
         ptSize = int(UserSettings.Get(group='vnet', key='point_symbol', subkey = 'point_size'))
@@ -1947,6 +2419,47 @@
             else:
                 self.pointsToDraw.AddPen(colKey, wx.Pen(colour = wx.Colour(col[0], col[1], col[2], 255), width = ptWidth))
 
+
+    def ShowCats(self):
+        vectMap = self.inputData["input"].GetValue()
+        vectMapName, mapSet = self._parseMapStr(vectMap)
+        
+        for layer in self.mapWin.Map.GetListOfLayers( ltype = "vector"):
+
+            name = utils.GetLayerNameFromCmd(utils.CmdTupleToList(layer.GetCmd()), layerType = "vector")
+            if name[0] == vectMap + "@" + mapSet:
+                #cmd = utils.CmdToTuple(layer.GetCmd())
+                cmd = layer.GetCmd()
+                cmd[1]['display'] = "shape,cat"
+                cmd[1]['llayer'] = self.inputData["tuclayer"].GetValue()
+                cmd = utils.CmdTupleToList(cmd)
+                layer.SetCmd(cmd)
+
+                break
+
+        up_map_evt = gUpdateMap(render = True, renderVector = True)
+        wx.PostEvent(self.mapWin, up_map_evt)
+
+    def HideCats(self):
+        vectMap = self.inputData["input"].GetValue()
+        vectMapName, mapSet = self._parseMapStr(vectMap)
+        
+        for layer in self.mapWin.Map.GetListOfLayers( ltype = "vector"):
+
+            name = utils.GetLayerNameFromCmd(utils.CmdTupleToList(layer.GetCmd()), layerType = "vector")
+            if name[0] == vectMap + "@" + mapSet:
+                #cmd = utils.CmdToTuple(layer.GetCmd())
+                cmd = layer.GetCmd()
+                cmd[1]['display'] = "shape"
+                del cmd[1]['llayer']
+                cmd = utils.CmdTupleToList(cmd)
+                layer.SetCmd(cmd)
+
+                break
+
+        up_map_evt = gUpdateMap(render = True, renderVector = True)
+        wx.PostEvent(self.mapWin, up_map_evt)
+
 class PtsList(PointsList):
     def __init__(self, parent, dialog, cols, id=wx.ID_ANY):
         """! List with points for analysis"""
@@ -2284,7 +2797,7 @@
         """!Button 'Cancel' pressed"""
         self.Close()
 
-class AddLayerDialog(wx.Dialog):
+class OutputVectorDialog(wx.Dialog):
     def __init__(self, parent,id=wx.ID_ANY,
                  title =_("Save analysis result"), style=wx.DEFAULT_DIALOG_STYLE):
         """!Save analysis result"""
@@ -2336,524 +2849,429 @@
         self.panel.SetSizer(sizer)
         sizer.Fit(self)
 
-class VnetTmpVectMaps:
-    """!Class which creates, stores and destroys all tmp maps created during analysis"""
-    def __init__(self, parent):
-        self.tmpMaps = [] # temporary maps 
-        self.parent = parent
-        self.mapWin = self.parent.mapWin
+class VnetStatusbar(wx.StatusBar):
+    """!Extends wx.StatusBar class with functionality to show multiple messages with the highest priority"""        
+    def __init__(self, parent, style, id = wx.ID_ANY, **kwargs):
 
-    def AddTmpVectMap(self, mapName, msg):
-        """!New temporary map
+        wx.StatusBar.__init__(self, parent, id, style, **kwargs)
 
-            @return instance of VectMap representing temporary map 
-        """
-        currMapSet = grass.gisenv()['MAPSET']
-        tmpMap = grass.find_file(name = mapName, 
-                                 element = 'vector', 
-                                 mapset = currMapSet)
+        self.maxPriority = 0
+        self.statusItems = []
 
-        fullName = tmpMap["fullname"]
-        # map already exists
-        if fullName:
-            #TODO move dialog out of class, AddTmpVectMap(self, mapName, overvrite = False)
-            dlg = wx.MessageDialog(parent = self.parent, 
-                                   message = msg,
-                                   caption = _("Overwrite map layer"),
-                                   style = wx.YES_NO | wx.NO_DEFAULT |
-                                   wx.ICON_QUESTION | wx.CENTRE)
-                
-            ret = dlg.ShowModal()
-            dlg.Destroy()
-                
-            if ret == wx.ID_NO:
-                return None
-        else:
-            fullName = mapName + "@" + currMapSet
+    def AddStatusItem(self, text, key, priority):
+        """!Add new item to show
 
-        newVectMap = VectMap(self, fullName)
-        self.tmpMaps.append(newVectMap)
+            @param text - string to show
+            @param key - item identifier, if already contains 
+                         item with same identifier, overwrites this item
+            @param priority - only items with highest priority are showed 
+        """        
+        statusTextItem = {
+                            'text' : text, 
+                            'key' : key,
+                            'priority' : priority
+                         }
 
-        return newVectMap
+        for item in self.statusItems:
+            if item['key'] == statusTextItem['key']:
+                self.statusItems.remove(item)
+        self.statusItems.append(statusTextItem)
+        if self.maxPriority < statusTextItem['priority']:
+            self.maxPriority =  statusTextItem['priority']
+        self._updateStatus()
 
-    def HasTmpVectMap(self, vectMapName):
-        """ 
-            @param vectMapName name of vector map
+    def _updateStatus(self):
 
-            @return True if it contains the map
-            @return False if not 
+        currStatusText = ''
+        for item in reversed(self.statusItems):
+            if item['priority'] == self.maxPriority:
+                if currStatusText:
+                    currStatusText += '; '
+                currStatusText += item['text']
+        wx.StatusBar.SetStatusText(self, currStatusText)
+
+    def RemoveStatusItem(self, key):
+        """!Remove item 
+
+            @param key - item identifier
         """
+        update = False
+        for iItem, item in enumerate(self.statusItems):
+            if item['key'] == key:
+                if item['priority'] == self.maxPriority:
+                    update = True
+                self.statusItems.pop(iItem)
+        if update:
+            for item in self.statusItems:
+                self.maxPriority = 0
+                if self.maxPriority < item['priority']:
+                    self.maxPriority =  item['priority']
+            self._updateStatus()
 
-        mapValSpl = vectMapName.strip().split("@")
-        if len(mapValSpl) > 1:
-            mapSet = mapValSpl[1]
-        else:
-            mapSet = grass.gisenv()['MAPSET']
-        mapName = mapValSpl[0] 
-        fullName = mapName + "@" + mapSet
+class DefIntesectionTurnCostDialog(wx.Dialog):
 
-        for vectTmpMap in self.tmpMaps:
-            if vectTmpMap.GetVectMapName() == fullName:
-                return True
-        return False
+    def __init__(self, parent, mapWin, style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER, id = wx.ID_ANY, **kwargs):
+        wx.Dialog.__init__(self, parent, id, style=style, title = _("Define local turn costs"), **kwargs)
 
-    def GetTmpVectMap(self, vectMapName):
-        """ Get instance of VectMap with name vectMapName"""
-        for vectMap in self.tmpMaps:
-            if vectMap.GetVectMapName() == vectMapName.strip():
-                return vectMap
-        return None
+        self.parent = parent
+        self.dbMgr = DbMgrBase(mapdisplay = mapWin)
+        self.browsePage = self.dbMgr.CreateDbMgrPage(parent = self, pageName = 'browse')
 
-    def RemoveFromTmpMaps(self, vectMap):
-        """!Temporary map is removed from the class instance however it is not deleted
+        self.show_cats = wx.CheckBox(parent = self, id=wx.ID_ANY,
+                                    label = _('Show categories'))
 
-            @param vectMap instance of VectMap class to be removed 
+        self.show_cats.Bind(wx.EVT_CHECKBOX, self.OnShowCats)
+        self.btnClose = wx.Button(parent = self, id = wx.ID_CLOSE)
 
-            @return True if was removed
-            @return False if does not contain the map
-        """
-        try:
-            self.tmpMaps.remove(vectMap)
-            return True
-        except ValueError:
-            return False
+        self._layout()
 
-    def DeleteTmpMap(self, vectMap):
-        """!Temporary map is removed from the class and it is deleted
+    def _layout(self):
+        sizer = wx.BoxSizer(wx.VERTICAL)
+
+        sizer.Add(item = self.browsePage, proportion = 1,
+                  flag = wx.EXPAND)
         
-            @param vectMap instance of VectMap class to be deleted 
+        sizer.Add(item = self.show_cats, proportion = 0)
+        sizer.Add(item = self.btnClose, proportion = 0, flag = wx.ALIGN_RIGHT)        
 
-            @return True if was removed
-            @return False if does not contain the map
-        """
-        if vectMap:
-            vectMap.DeleteRenderLayer()
-            RunCommand('g.remove', 
-                        vect = vectMap.GetVectMapName())
-            self.RemoveFromTmpMaps(vectMap)
-            return True
-        return False
+        self.SetSizer(sizer)
 
-    def DeleteAllTmpMaps(self):
-        """Delete all temporary maps in the class"""
-        update = False
-        for tmpMap in self.tmpMaps:
-            RunCommand('g.remove', 
-                        vect = tmpMap.GetVectMapName())
-            if tmpMap.DeleteRenderLayer():
-                update = True
-        return update
+    def SetData(self, vectMapName, layer):
+        if vectMapName != self.dbMgr.GetVectorName():
+            self.dbMgr.ChangeVectorMap(vectorName = vectMapName)
+        else:
+            self.browsePage.DeleteAllPages() 
+        
+        self.browsePage.AddLayer(layer)
+        self.layer = layer;
 
-class VectMap:
-    """!Represents map 
-        It can check if it was modified or render it
-    """
-    def __init__(self, parent, fullName):
-        self.fullName = fullName
-        self.parent = parent
-        self.renderLayer = None
-        self.modifTime = None # time, for modification check
+        # TODO HACK!!!
+        self.browsePage.FindWindowById(self.browsePage.layerPage[layer]['sqlNtb']).GetParent().Hide()
 
-    def __del__(self):
+    def SetIntersection(self, isec):
 
-        self.DeleteRenderLayer()
-   
-    def AddRenderLayer(self, cmd = None):
-        """!Add map from map window layers to render """
-        existsMap = grass.find_file(name = self.fullName, 
-                                    element = 'vector', 
-                                    mapset = grass.gisenv()['MAPSET'])
+        self.browsePage.LoadData(self.layer, where = "isec = %d" % (isec))
 
-        if not existsMap["name"]:
-            self.DeleteRenderLayer()
-            return False
+    def OnShowCats(self, event):
+        if self.show_cats.GetValue():
+            self.parent.ShowCats();
+        else:
+            self.parent.HideCats();
 
-        if not cmd:
-            cmd = []    
-        cmd.insert(0, 'd.vect')
-        cmd.append('map=%s' % self.fullName)
+def DegreesToRadians(degrees):
+    return degrees * math.pi / 180
 
-        if self.renderLayer:       
-             self.DeleteRenderLayer()
 
-        self.renderLayer = self.parent.mapWin.Map.AddLayer(ltype = "vector",     command = cmd,
-                                                           name = self.fullName, active = True,
-                                                           opacity = 1.0,        render = True,       
-                                                           pos = -1)
-        return True
+def RadiansToDegrees(radians):
+    return radians * 180 / math.pi   
 
-    def DeleteRenderLayer(self):
-        """!Remove map from map window layers to render"""
-        if self.renderLayer: 
-             self.parent.mapWin.Map.DeleteLayer(self.renderLayer)
-             self.renderLayer = None
-             return True
-        return False
+class DefGlobalTurnsDialog(wx.Dialog):
+    def __init__(self, parent, data, style= wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER, 
+                 id= wx.ID_ANY, title= _("Define Global Turn Costs"), **kwargs): # v Gassu dopln preklad
 
-    def GetRenderLayer(self):
-        return self.renderLayer
+        wx.Dialog.__init__(self, parent, id, title, style = style, **kwargs)
 
-    def GetVectMapName(self):
-        return self.fullName
+        self.turn_data = data
+        self.angle_list = TurnAnglesList(parent= self, data= data) 
 
-    def SaveVectMapState(self):
-        """!Save modification time for vector map"""
-        self.modifTime = self.GetLastModified()
+        self.btnAdd = wx.Button(parent=self, id=wx.ID_ANY, label = "Add" )
+        self.btnRemove = wx.Button(parent=self, id=wx.ID_ANY, label = "Remove" )
+        self.btnClose = wx.Button(parent = self, id = wx.ID_CLOSE)
+        self.useUTurns = wx.CheckBox(parent = self, id = wx.ID_ANY, label = "Use U-turns")
 
-    def VectMapState(self):
-        """!Checks if map was modified
+        self.btnAdd.Bind(wx.EVT_BUTTON, self.OnAddButtonClick)
+        self.btnRemove.Bind(wx.EVT_BUTTON, self.OnRemoveButtonClick)
+        self.btnClose.Bind(wx.EVT_BUTTON, self.OnCloseDialog)
+        self.useUTurns.Bind(wx.EVT_CHECKBOX, self.OnCheckedUTurns)
+                
+        self.btnClose.SetDefault()
+        self.useUTurns.SetValue(data.GetData()["use_uturns"])
+        self.OnCheckedUTurns(None)
+        self.layout()
+        self.SetInitialSize((500, 200))
 
-            @return -1 - if no modification time was saved
-            @return  0 - if map was modified
-            @return  1 - if map was not modified
-        """
-        if self.modifTime is None:
-            return -1       
-        if self.modifTime != self.GetLastModified():
-            return 0  
-        return 1
+        #self.SetData(data)
 
-    def GetLastModified(self):
-        """!Get modification time 
+    def layout(self):
+        sizer = wx.BoxSizer(wx.VERTICAL)
+        labelSizer = wx.BoxSizer(wx.HORIZONTAL)
+        addRemoveSizer = wx.BoxSizer(wx.VERTICAL)
+        closeSizer = wx.BoxSizer(wx.HORIZONTAL)  
 
-            @return MAP DATE time string from vector map head file 
-        """
+        addRemoveSizer.Add(item= self.btnAdd, proportion= 0, flag= wx.ALIGN_RIGHT, border = 10)
+        addRemoveSizer.Add(item= self.btnRemove, proportion= 0, flag= wx.ALIGN_RIGHT, border = 10)
 
-        mapValSpl = self.fullName.split("@")
-        mapSet = mapValSpl[1]
-        mapName = mapValSpl[0] 
+        labelSizer.Add(item= self.angle_list, proportion= 1, flag= wx.EXPAND, border = 10)
+        labelSizer.Add(item=addRemoveSizer, proportion = 0, flag = wx.ALIGN_RIGHT, border = 10)       
 
-        headPath =  os.path.join(grass.gisenv()['GISDBASE'],
-                                 grass.gisenv()['LOCATION_NAME'],
-                                 mapSet,
-                                 "vector",
-                                 mapName,
-                                 "head")
-        try:
-            head = open(headPath, 'r')
-            for line in head.readlines():
-                i = line.find('MAP DATE:', )
-                if i == 0:
-                    head.close()
-                    return line.split(':', 1)[1].strip()
+        closeSizer.Add(item=self.useUTurns, proportion = 1, flag = wx.ALIGN_LEFT, border = 10 )
+        closeSizer.Add(item=self.btnClose, proportion = 0, flag = wx.ALIGN_RIGHT, border = 10)        
 
-            head.close()
-            return ""
-        except IOError:
-            return ""
+        sizer.Add(item=labelSizer, proportion = 1, flag = wx.EXPAND)
+        sizer.Add(item=closeSizer, proportion = 0, flag = wx.EXPAND)
 
-class History:
-    """!Class which reads and saves history data (based on gui.core.settings Settings class file save/load)"""   
-    def __init__(self, parent):
+        self.SetSizer(sizer)
+     
 
-        # max number of steps in history (zero based)
-        self.maxHistSteps = 3 
-        # current history step 
-        self.currHistStep = 0
-        # number of steps saved in history
-        self.histStepsNum = 0
+    def OnAddButtonClick(self, event):
+        """!Add new direction over selected row"""
+        selected_indices = self.angle_list.GetSelectedIndices()
+ 
+        if not selected_indices:
+            from_value = self.turn_data.GetValue(self.turn_data.GetLinesCount()-1,2)
+            to_value = self.turn_data.GetValue(0,1)
+            default_row = ["new", from_value, to_value, 0.0]
+            self.angle_list.AppendRow(default_row)
 
-        # dict contains data saved in history for current history step 
-        self.currHistStepData = {} 
+        # If no row is selected, new direction is added to the end of table
+        i_addition = 0
+        for i in selected_indices:
+            i += i_addition
+            from_value = self.turn_data.GetValue(i-1,2)
+            to_value = self.turn_data.GetValue(i,1)
+            default_row = ["new", from_value, to_value, 0.0]
+            self.angle_list.InsertRow(i,default_row)
+            i_addition += 1
 
-        # buffer for data to be saved into history 
-        self.newHistStepData = {} 
 
-        self.histFile = grass.tempfile()
+    def OnRemoveButtonClick(self, event):
+        """!Delete one or more selected directions"""
+        selected_indices = self.angle_list.GetSelectedIndices()
+  
+        i_reduction = 0
+        for i in selected_indices:
+            i -= i_reduction
+            self.angle_list.DeleteRow(i)
+            i_reduction += 1
 
-        # key/value separator
-        self.sep = ';'
+    def OnCloseDialog(self, event):
+        """!Close dialog"""
+        self.Close()
 
-    def __del__(self):
-        grass.try_remove(self.histFile)
+    def OnCheckedUTurns(self, event):
+        """!Use U-turns in analyse"""
+        self.turn_data.SetUTurns(self.useUTurns.GetValue())
+       
+    def SetData(self, data):
+        self.angle_list.SetData(data)
+        self.turns_data = data
 
-    def GetNext(self):
-        """!Go one step forward in history"""
-        self.currHistStep -= 1
-        self.currHistStepData.clear()
-        self.currHistStepData = self._getHistStepData(self.currHistStep)
 
-        return self.currHistStepData
 
-    def GetPrev(self):
-        """!Go one step back in history"""
-        self.currHistStep += 1 
-        self.currHistStepData.clear()
-        self.currHistStepData = self._getHistStepData(self.currHistStep)
+class TurnAnglesList(wx.ListCtrl, listmix.ListCtrlAutoWidthMixin, listmix.TextEditMixin):
+    """!Virtual editable table with global turns"""
+    def __init__(self, parent, data, id= wx.ID_ANY, style= wx.LC_REPORT | wx.LC_VIRTUAL, **kwargs):
+        wx.ListCtrl.__init__(self, parent, id,style= style, **kwargs)
+        listmix.ListCtrlAutoWidthMixin.__init__(self)
+        listmix.TextEditMixin.__init__(self)
+        
+        self.Populate()
+        self.data = data
+        self.SetItemCount(self.data.GetLinesCount())
+        
 
-        return self.currHistStepData
+    def Populate(self):
+        """!Columns definition"""
+        self.InsertColumn(col= 0, heading= "Direction", format= wx.LIST_FORMAT_LEFT) # v Gassu dopln preklad 
+        self.InsertColumn(col= 1, heading= "From Angle", format= wx.LIST_FORMAT_RIGHT)
+        self.InsertColumn(col= 2, heading= "To Angle", format= wx.LIST_FORMAT_RIGHT)
+        self.InsertColumn(col= 3, heading= "Cost", format= wx.LIST_FORMAT_RIGHT)
 
-    def GetStepsNum(self):
-        """!Get number of steps saved in history"""
-        return self.histStepsNum
 
-    def GetCurrHistStep(self):
-        """!Get current history step"""
-        return self.currHistStep
+    def OnGetItemText(self, item, col):
+        val = self.data.GetValue(item, col)
+        if col in [1,2]:
+            val = RadiansToDegrees(val)            
+        return str(val)
 
-    def Add(self, key, subkey, value):
-        """!Add new data into buffer"""
-        if key not in self.newHistStepData:
-            self.newHistStepData[key] = {}
 
-        if type(subkey) == types.ListType:
-            if subkey[0] not in self.newHistStepData[key]:
-                self.newHistStepData[key][subkey[0]] = {}
-            self.newHistStepData[key][subkey[0]][subkey[1]] = value
-        else:
-            self.newHistStepData[key][subkey] = value
+    def SetVirtualData(self, row, column, text):
+        """!Set data to table"""
+        if column in [1,2,3]:
+            try:
+                text = float(text)
+            except:
+                return
+        if column in [1,2]:
+            text = DegreesToRadians(text)
+            
+            # Tested allowed range of values
+            if text > math.pi:
+                text = 0.0
+            elif text < -math.pi:
+                text = 0.0
 
-    def SaveHistStep(self):
-        """!Create new history step with data in buffer"""
-        self.maxHistSteps = UserSettings.Get(group ='vnet',
-                                             key = 'other',
-                                             subkey = 'max_hist_steps')
-        self.currHistStep = 0 
+        self.data.SetValue(text, row, column)
 
-        newHistFile = grass.tempfile()
-        newHist = open(newHistFile, "w")
+        self.edited_row = row
+        self.RefreshItems(0,self.data.GetLinesCount()-1)
 
-        self._saveNewHistStep(newHist)
 
-        oldHist = open(self.histFile)
-        removedHistData = self._savePreviousHist(newHist, oldHist)
+    def AppendRow(self, values):
+        self.data.AppendRow(values)
+        self.SetItemCount(self.data.GetLinesCount())
 
-        oldHist.close()
-        newHist.close()
-        grass.try_remove(self.histFile)
-        self.histFile = newHistFile
+    def InsertRow(self, line, values):
+        self.data.InsertRow(line,values)
+        self.SetItemCount(self.data.GetLinesCount())
 
-        self.newHistStepData.clear() 
+    def DeleteRow(self, row):
+        self.data.PopRow(row)
+        self.SetItemCount(self.data.GetLinesCount())
 
-        return removedHistData
+    def SetData(self, data):
+        self.data = data
+        self.SetItemCount(self.data.GetLinesCount())
+        self.RefreshItems(0, self.data.GetLinesCount()-1)
 
-    def _savePreviousHist(self, newHist, oldHist):          
-        """!Save previous history into new file"""
-        newHistStep = False
-        removedHistData = {}
-        newHistStepsNum = self.histStepsNum
 
-        for line in oldHist.readlines():
-            if not line.strip():
-                newHistStep = True
-                newHistStepsNum += 1
-                continue
+    def GetSelectedIndices(self, state =  wx.LIST_STATE_SELECTED):
+        """!Get numbers of selected rows"""
+        indices = []
+        lastFound = -1
+        while True:
+            index = self.GetNextItem(lastFound, wx.LIST_NEXT_ALL, state)
+            if index == -1:
+                break
+            else:
+                lastFound = index
+                indices.append(index)
+        return indices
+   
 
-            if newHistStep:
-                newHistStep = False
+class GlobalTurnData:
+    """!Turn Data"""
+    def __init__(self):
+        # Definition of four basic directions
+        self.turn_data = [
+                            ["Straight", DegreesToRadians(-30), DegreesToRadians(+30), 0.0],
+                            ["Right Turn", DegreesToRadians(+30), DegreesToRadians(+150), 0.0],
+                            ["Reverse",  DegreesToRadians(+150), DegreesToRadians(-150), 0.0],
+                            ["Left Turn",  DegreesToRadians(-150), DegreesToRadians(-30), 0.0]  
+                            ] 
 
-                line = line.split("=")
-                line[1] = str(newHistStepsNum)
-                line = "=".join(line)
+        self.useUTurns = True
 
-                if newHistStepsNum >= self.maxHistSteps:
-                    removedHistStep = removedHistData[line] = {}
-                    continue
-                else:
-                    newHist.write('%s%s%s' % (os.linesep, line, os.linesep))
-                    self.histStepsNum = newHistStepsNum
-            else:
-                if newHistStepsNum >= self.maxHistSteps:
-                    self._parseLine(line, removedHistStep)
-                else:
-                    newHist.write('%s' % line)                
+    def GetData(self):
+        data = {}
+        data["intervals"] = []
+        data["use_uturns"] = self.useUTurns
 
-        return removedHistData
-            
-    def _saveNewHistStep(self, newHist):
-        """!Save buffer (new step) data into file"""
-        newHist.write('%s%s%s' % (os.linesep, "history step=0", os.linesep))  
-        for key in self.newHistStepData.keys():
-            subkeys =  self.newHistStepData[key].keys()
-            newHist.write('%s%s' % (key, self.sep))
-            for idx in range(len(subkeys)):
-                value =  self.newHistStepData[key][subkeys[idx]]
-                if type(value) == types.DictType:
-                    if idx > 0:
-                        newHist.write('%s%s%s' % (os.linesep, key, self.sep))
-                    newHist.write('%s%s' % (subkeys[idx], self.sep))
-                    kvalues =  self.newHistStepData[key][subkeys[idx]].keys()
-                    srange = range(len(kvalues))
-                    for sidx in srange:
-                        svalue = self._parseValue(self.newHistStepData[key][subkeys[idx]][kvalues[sidx]])
-                        newHist.write('%s%s%s' % (kvalues[sidx], self.sep, svalue))
-                        if sidx < len(kvalues) - 1:
-                            newHist.write('%s' % self.sep)
-                else:
-                    if idx > 0 and \
-                            type( self.newHistStepData[key][subkeys[idx - 1]]) == types.DictType:
-                        newHist.write('%s%s%s' % (os.linesep, key, self.sep))
-                    value = self._parseValue(self.newHistStepData[key][subkeys[idx]])
-                    newHist.write('%s%s%s' % (subkeys[idx], self.sep, value))
-                    if idx < len(subkeys) - 1 and \
-                            type(self.newHistStepData[key][subkeys[idx + 1]]) != types.DictType:
-                        newHist.write('%s' % self.sep)
-            newHist.write(os.linesep)
-        self.histStepsNum = 0
+        for ival in self.turn_data:
+            data["intervals"].append(ival[1:])
+        print data
+        return data
 
-    def _parseValue(self, value, read = False):
-        """!Parse value"""
-        if read: # -> read data (cast values)
+    def GetValue(self, line, col):
+        return self.turn_data[line][col]
 
-            if value:
-                if value[0] == '[' and value[-1] == ']':# TODO, possible wrong interpretation
-                    value = value[1:-1].split(',')
-                    value = map(self._castValue, value)
-                    return value
+    def GetLinesCount(self):
+        return len(self.turn_data)
 
-            if value == 'True':
-                value = True
-            elif value == 'False':
-                value = False
-            elif value == 'None':
-                value = None
-            elif ':' in value: # -> color
-                try:
-                    value = tuple(map(int, value.split(':')))
-                except ValueError: # -> string
-                    pass
-            else:
-                try:
-                    value = int(value)
-                except ValueError:
-                    try:
-                        value = float(value)
-                    except ValueError:
-                        pass
-        else: # -> write data
-            if type(value) == type(()): # -> color
-                value = str(value[0]) + ':' +\
-                    str(value[1]) + ':' + \
-                    str(value[2])
-                
-        return value
+    def SetValue(self, value, line, col):
+        self.DataValidator(line, col, value)
+        self.turn_data[line][col] = value
 
-    def _castValue(self, value):
-        """!Cast value"""
-        try:
-            value = int(value)
-        except ValueError:
-            try:
-                value = float(value)
-            except ValueError:
-                value = value[1:-1]
+    def SetUTurns(self, value):
+        """!Checked if checeBox is checed"""
+        print value
+        self.useUTurns = value
 
-        return value
+    def AppendRow(self, values):
+        self.turn_data.append(values)
 
-    def _getHistStepData(self, histStep):          
-        """!Load data saved in history step"""        
-        hist = open(self.histFile)
-        histStepData = {}
+    def InsertRow(self,line,values):
+        self.turn_data.insert(line,values)
 
-        newHistStep = False
-        isSearchedHistStep = False
-        for line in hist.readlines():
+    def PopRow(self, values):
+        self.RemoveDataValidator(values)
+        self.turn_data.pop(values)
+             
+    def DataValidator(self, row, col, value):
+        """!Angle recalculation due to value changing"""
 
-            if  not line.strip() and isSearchedHistStep:
-                 break
-            elif not line.strip():
-                newHistStep = True
-                continue
-            elif isSearchedHistStep:
-                self._parseLine(line, histStepData)
+        if col not in [1,2]:
+            return
 
-            if newHistStep:
-                line = line.split("=")
-                if int(line[1]) == histStep:
-                    isSearchedHistStep = True
-                newHistStep = False
+        if col == 1:
+            new_from_angle = value
+            old_from_angle = self.turn_data[row][1]
+            new_to_angle  = self.turn_data[row][2]
+            if self.IsInInterval(old_from_angle, new_to_angle, new_from_angle):
 
-        hist.close()
-        return histStepData
+                prev_row = row - 1
+                if  prev_row == -1:
+                    prev_row = len(self.turn_data) - 1
+                self.turn_data[prev_row][2] = new_from_angle
+                return
+        
+        if col ==2:
+            new_to_angle = value
+            old_to_angle = self.turn_data[row][2]
+            new_from_angle = self.turn_data[row][1]
+            if self.IsInInterval(new_from_angle, old_to_angle, new_to_angle):
 
-    def _parseLine(self, line, histStepData):
-        """!Parse line in file with history"""        
-        line = line.rstrip('%s' % os.linesep).split(self.sep)
-        key = line[0]
-        kv = line[1:]
-        idx = 0
-        subkeyMaster = None
-        if len(kv) % 2 != 0: # multiple (e.g. nviz)
-            subkeyMaster = kv[0]
-            del kv[0]
-        idx = 0
-        while idx < len(kv):
-            if subkeyMaster:
-                subkey = [subkeyMaster, kv[idx]]
-            else:
-                subkey = kv[idx]
-            value = kv[idx+1]
-            value = self._parseValue(value, read = True)
-            if key not in histStepData:
-                histStepData[key] = {}
+                next_row = row + 1
+                if len(self.turn_data) == next_row:
+                    next_row = 0
+                self.turn_data[next_row][1] = new_to_angle 
+                return     
+                   
 
-            if type(subkey) == types.ListType:
-                if subkey[0] not in histStepData[key]:
-                    histStepData[key][subkey[0]] = {}
-                histStepData[key][subkey[0]][subkey[1]] = value
-            else:
-                histStepData[key][subkey] = value
-            idx += 2
+        inside_new = []
+        overlap_new_from = []
+        overlap_new_to = []
 
-    def DeleteNewHistStepData(self):
-        """!Delete buffer data for new history step"""        
-        self.newHistStepData.clear() 
+        for i in range(self.GetLinesCount()):
+            if i == row:
+                continue
+            from_angle = self.turn_data[i][1]
+            is_in_from = self.IsInInterval(new_from_angle, new_to_angle, from_angle)
 
-class VnetStatusbar(wx.StatusBar):
-    """!Extends wx.StatusBar class with functionality to show multiple messages with the highest priority"""        
-    def __init__(self, parent, style, id = wx.ID_ANY, **kwargs):
+            to_angle = self.turn_data[i][2]
+            is_in_to = self.IsInInterval(new_from_angle, new_to_angle, to_angle)
 
-        wx.StatusBar.__init__(self, parent, id, style, **kwargs)
 
-        self.maxPriority = 0
-        self.statusItems = []
+            if is_in_from and is_in_to:
+                inside_new.append(i)
+            if is_in_from:
+                overlap_new_to.append(i)
+            if is_in_to:
+                overlap_new_from.append(i)
 
-    def AddStatusItem(self, text, key, priority):
-        """!Add new item to show
+        for i_row in overlap_new_from:
+            self.turn_data[i_row][2] = new_from_angle
 
-            @param text - string to show
-            @param key - item identifier, if already contains 
-                         item with same identifier, overwrites this item
-            @param priority - only items with highest priority are showed 
-        """        
-        statusTextItem = {
-                            'text' : text, 
-                            'key' : key,
-                            'priority' : priority
-                         }
+        for i_row in overlap_new_to:
+            self.turn_data[i_row][1] = new_to_angle
 
-        for item in self.statusItems:
-            if item['key'] == statusTextItem['key']:
-                self.statusItems.remove(item)
-        self.statusItems.append(statusTextItem)
-        if self.maxPriority < statusTextItem['priority']:
-            self.maxPriority =  statusTextItem['priority']
-        self._updateStatus()
+        for i_row in inside_new:
+            if col == 1:
+                angle = new_from_angle            
+            else:
+                angle = new_to_angle
 
-    def _updateStatus(self):
+            self.turn_data[i_row][1] = angle
+            self.turn_data[i_row][2] = angle
 
-        currStatusText = ''
-        for item in reversed(self.statusItems):
-            if item['priority'] == self.maxPriority:
-                if currStatusText:
-                    currStatusText += '; '
-                currStatusText += item['text']
-        wx.StatusBar.SetStatusText(self, currStatusText)
+    def RemoveDataValidator(self, row):
+        """!Angle recalculation due to direction remove"""
+        if row == 0:
+            prev_row = self.GetLinesCount() - 1
+        else:
+            prev_row = row - 1
 
-    def RemoveStatusItem(self, key):
-        """!Remove item 
+        remove_to_angle = self.turn_data[row][2]        
+        self.turn_data[prev_row][2] = remove_to_angle
 
-            @param key - item identifier
-        """
-        update = False
-        for iItem, item in enumerate(self.statusItems):
-            if item['key'] == key:
-                if item['priority'] == self.maxPriority:
-                    update = True
-                self.statusItems.pop(iItem)
-        if update:
-            for item in self.statusItems:
-                self.maxPriority = 0
-                if self.maxPriority < item['priority']:
-                    self.maxPriority =  item['priority']
-            self._updateStatus()
+
+    def IsInInterval(self, from_angle, to_angle, angle):
+        """!Test if a direction includes or not includes a value"""  
+        if to_angle < from_angle:
+            to_angle = math.pi * 2  + to_angle
+        if angle < from_angle:
+                angle = math.pi * 2  + angle
+            
+        if angle > from_angle and angle < to_angle:
+            return True
+        return False
\ No newline at end of file
Index: gui/wxpython/vnet/vnet_core.py
===================================================================
--- gui/wxpython/vnet/vnet_core.py	(revision 0)
+++ gui/wxpython/vnet/vnet_core.py	(working copy)
@@ -0,0 +1,679 @@
+"""!
+@package vnet.vnet_core
+
+@brief Vector network analysis logic.
+
+Classes:
+ - vnet_core::VnetTmpVectMaps
+ - vnet_core::VectMap
+ - vnet_core::History
+
+(C) 2013 by the GRASS Development Team
+
+This program is free software under the GNU General Public License
+(>=v2). Read the file COPYING that comes with GRASS for details.
+
+@author Stepan Turek <stepan.turek seznam.cz> (mentor: Martin Landa)
+"""
+
+import os
+import types
+try:
+    import grass.lib.vector as vectlib
+    from ctypes import pointer, byref, c_char_p, c_int, c_double
+    haveCtypes = True
+except ImportError:
+    haveCtypes = False
+
+from copy import copy
+from grass.script     import core as grass
+
+import wx
+
+
+from core             import utils
+from core.gcmd        import RunCommand
+from core.settings    import UserSettings
+
+class VnetManager():
+    def __init__(self, parent):
+        pass
+
+
+
+
+class VnetTmpVectMaps:
+    """!Class which creates, stores and destroys all tmp maps created during analysis"""
+    def __init__(self, parent):
+        self.tmpMaps = [] # temporary maps 
+        self.parent = parent
+        self.mapWin = self.parent.mapWin
+
+    def AddTmpVectMap(self, mapName, msg):
+        """!New temporary map
+
+            @return instance of VectMap representing temporary map 
+        """
+        currMapSet = grass.gisenv()['MAPSET']
+        tmpMap = grass.find_file(name = mapName, 
+                                 element = 'vector', 
+                                 mapset = currMapSet)
+
+        fullName = tmpMap["fullname"]
+        # map already exists
+        if fullName:
+            #TODO move dialog out of class, AddTmpVectMap(self, mapName, overvrite = False)
+            dlg = wx.MessageDialog(parent = self.parent, 
+                                   message = msg,
+                                   caption = _("Overwrite map layer"),
+                                   style = wx.YES_NO | wx.NO_DEFAULT |
+                                   wx.ICON_QUESTION | wx.CENTRE)
+                
+            ret = dlg.ShowModal()
+            dlg.Destroy()
+                
+            if ret == wx.ID_NO:
+                return None
+        else:
+            fullName = mapName + "@" + currMapSet
+
+        newVectMap = VectMap(self, fullName)
+        self.tmpMaps.append(newVectMap)
+
+        return newVectMap
+
+    def HasTmpVectMap(self, vectMapName):
+        """ 
+            @param vectMapName name of vector map
+
+            @return True if it contains the map
+            @return False if not 
+        """
+
+        mapValSpl = vectMapName.strip().split("@")
+        if len(mapValSpl) > 1:
+            mapSet = mapValSpl[1]
+        else:
+            mapSet = grass.gisenv()['MAPSET']
+        mapName = mapValSpl[0] 
+        fullName = mapName + "@" + mapSet
+
+        for vectTmpMap in self.tmpMaps:
+            if vectTmpMap.GetVectMapName() == fullName:
+                return True
+        return False
+
+    def GetTmpVectMap(self, vectMapName):
+        """ Get instance of VectMap with name vectMapName"""
+        for vectMap in self.tmpMaps:
+            if vectMap.GetVectMapName() == vectMapName.strip():
+                return vectMap
+        return None
+
+    def RemoveFromTmpMaps(self, vectMap):
+        """!Temporary map is removed from the class instance however it is not deleted
+
+            @param vectMap instance of VectMap class to be removed 
+
+            @return True if was removed
+            @return False if does not contain the map
+        """
+        try:
+            self.tmpMaps.remove(vectMap)
+            return True
+        except ValueError:
+            return False
+
+    def DeleteTmpMap(self, vectMap):
+        """!Temporary map is removed from the class and it is deleted
+        
+            @param vectMap instance of VectMap class to be deleted 
+
+            @return True if was removed
+            @return False if does not contain the map
+        """
+        if vectMap:
+            vectMap.DeleteRenderLayer()
+            RunCommand('g.remove', 
+                        vect = vectMap.GetVectMapName())
+            self.RemoveFromTmpMaps(vectMap)
+            return True
+        return False
+
+    def DeleteAllTmpMaps(self):
+        """Delete all temporary maps in the class"""
+        update = False
+        for tmpMap in self.tmpMaps:
+            RunCommand('g.remove', 
+                        vect = tmpMap.GetVectMapName())
+            if tmpMap.DeleteRenderLayer():
+                update = True
+        return update
+
+class VectMap:
+    """!Represents map 
+        It can check if it was modified or render it
+    """
+    def __init__(self, parent, fullName):
+        self.fullName = fullName
+        self.parent = parent
+        self.renderLayer = None
+        self.modifTime = None # time, for modification check
+
+    def __del__(self):
+
+        self.DeleteRenderLayer()
+   
+    def AddRenderLayer(self, cmd = None):
+        """!Add map from map window layers to render """
+        existsMap = grass.find_file(name = self.fullName, 
+                                    element = 'vector', 
+                                    mapset = grass.gisenv()['MAPSET'])
+
+        if not existsMap["name"]:
+            self.DeleteRenderLayer()
+            return False
+
+        if not cmd:
+            cmd = []    
+        cmd.insert(0, 'd.vect')
+        cmd.append('map=%s' % self.fullName)
+
+        if self.renderLayer:       
+             self.DeleteRenderLayer()
+
+        self.renderLayer = self.parent.mapWin.Map.AddLayer(ltype = "vector",     command = cmd,
+                                                           name = self.fullName, active = True,
+                                                           opacity = 1.0,        render = True,       
+                                                           pos = -1)
+        return True
+
+    def DeleteRenderLayer(self):
+        """!Remove map from map window layers to render"""
+        if self.renderLayer: 
+             self.parent.mapWin.Map.DeleteLayer(self.renderLayer)
+             self.renderLayer = None
+             return True
+        return False
+
+    def GetRenderLayer(self):
+        return self.renderLayer
+
+    def GetVectMapName(self):
+        return self.fullName
+
+    def SaveVectMapState(self):
+        """!Save modification time for vector map"""
+        self.modifTime = self.GetLastModified()
+
+    def VectMapState(self):
+        """!Checks if map was modified
+
+            @return -1 - if no modification time was saved
+            @return  0 - if map was modified
+            @return  1 - if map was not modified
+        """
+        if self.modifTime is None:
+            return -1       
+        if self.modifTime != self.GetLastModified():
+            return 0  
+        return 1
+
+    def GetLastModified(self):
+        """!Get modification time 
+
+            @return MAP DATE time string from vector map head file 
+        """
+
+        mapValSpl = self.fullName.split("@")
+        mapSet = mapValSpl[1]
+        mapName = mapValSpl[0] 
+
+        headPath =  os.path.join(grass.gisenv()['GISDBASE'],
+                                 grass.gisenv()['LOCATION_NAME'],
+                                 mapSet,
+                                 "vector",
+                                 mapName,
+                                 "head")
+        try:
+            head = open(headPath, 'r')
+            for line in head.readlines():
+                i = line.find('MAP DATE:', )
+                if i == 0:
+                    head.close()
+                    return line.split(':', 1)[1].strip()
+
+            head.close()
+            return ""
+        except IOError:
+            return ""
+
+class History:
+    """!Class which reads and saves history data (based on gui.core.settings Settings class file save/load)"""   
+    def __init__(self, parent):
+
+        # max number of steps in history (zero based)
+        self.maxHistSteps = 3 
+        # current history step 
+        self.currHistStep = 0
+        # number of steps saved in history
+        self.histStepsNum = 0
+
+        # dict contains data saved in history for current history step 
+        self.currHistStepData = {} 
+
+        # buffer for data to be saved into history 
+        self.newHistStepData = {} 
+
+        self.histFile = grass.tempfile()
+
+        # key/value separator
+        self.sep = ';'
+
+    def __del__(self):
+        grass.try_remove(self.histFile)
+
+    def GetNext(self):
+        """!Go one step forward in history"""
+        self.currHistStep -= 1
+        self.currHistStepData.clear()
+        self.currHistStepData = self._getHistStepData(self.currHistStep)
+
+        return self.currHistStepData
+
+    def GetPrev(self):
+        """!Go one step back in history"""
+        self.currHistStep += 1 
+        self.currHistStepData.clear()
+        self.currHistStepData = self._getHistStepData(self.currHistStep)
+
+        return self.currHistStepData
+
+    def GetStepsNum(self):
+        """!Get number of steps saved in history"""
+        return self.histStepsNum
+
+    def GetCurrHistStep(self):
+        """!Get current history step"""
+        return self.currHistStep
+
+    def Add(self, key, subkey, value):
+        """!Add new data into buffer"""
+        if key not in self.newHistStepData:
+            self.newHistStepData[key] = {}
+
+        if type(subkey) == types.ListType:
+            if subkey[0] not in self.newHistStepData[key]:
+                self.newHistStepData[key][subkey[0]] = {}
+            self.newHistStepData[key][subkey[0]][subkey[1]] = value
+        else:
+            self.newHistStepData[key][subkey] = value
+
+    def SaveHistStep(self):
+        """!Create new history step with data in buffer"""
+        self.maxHistSteps = UserSettings.Get(group ='vnet',
+                                             key = 'other',
+                                             subkey = 'max_hist_steps')
+        self.currHistStep = 0 
+
+        newHistFile = grass.tempfile()
+        newHist = open(newHistFile, "w")
+
+        self._saveNewHistStep(newHist)
+
+        oldHist = open(self.histFile)
+        removedHistData = self._savePreviousHist(newHist, oldHist)
+
+        oldHist.close()
+        newHist.close()
+        grass.try_remove(self.histFile)
+        self.histFile = newHistFile
+
+        self.newHistStepData.clear() 
+
+        return removedHistData
+
+    def _savePreviousHist(self, newHist, oldHist):          
+        """!Save previous history into new file"""
+        newHistStep = False
+        removedHistData = {}
+        newHistStepsNum = self.histStepsNum
+
+        for line in oldHist.readlines():
+            if not line.strip():
+                newHistStep = True
+                newHistStepsNum += 1
+                continue
+
+            if newHistStep:
+                newHistStep = False
+
+                line = line.split("=")
+                line[1] = str(newHistStepsNum)
+                line = "=".join(line)
+
+                if newHistStepsNum >= self.maxHistSteps:
+                    removedHistStep = removedHistData[line] = {}
+                    continue
+                else:
+                    newHist.write('%s%s%s' % (os.linesep, line, os.linesep))
+                    self.histStepsNum = newHistStepsNum
+            else:
+                if newHistStepsNum >= self.maxHistSteps:
+                    self._parseLine(line, removedHistStep)
+                else:
+                    newHist.write('%s' % line)                
+
+        return removedHistData
+            
+    def _saveNewHistStep(self, newHist):
+        """!Save buffer (new step) data into file"""
+        newHist.write('%s%s%s' % (os.linesep, "history step=0", os.linesep))  
+        for key in self.newHistStepData.keys():
+            subkeys =  self.newHistStepData[key].keys()
+            newHist.write('%s%s' % (key, self.sep))
+            for idx in range(len(subkeys)):
+                value =  self.newHistStepData[key][subkeys[idx]]
+                if type(value) == types.DictType:
+                    if idx > 0:
+                        newHist.write('%s%s%s' % (os.linesep, key, self.sep))
+                    newHist.write('%s%s' % (subkeys[idx], self.sep))
+                    kvalues =  self.newHistStepData[key][subkeys[idx]].keys()
+                    srange = range(len(kvalues))
+                    for sidx in srange:
+                        svalue = self._parseValue(self.newHistStepData[key][subkeys[idx]][kvalues[sidx]])
+                        newHist.write('%s%s%s' % (kvalues[sidx], self.sep, svalue))
+                        if sidx < len(kvalues) - 1:
+                            newHist.write('%s' % self.sep)
+                else:
+                    if idx > 0 and \
+                            type( self.newHistStepData[key][subkeys[idx - 1]]) == types.DictType:
+                        newHist.write('%s%s%s' % (os.linesep, key, self.sep))
+                    value = self._parseValue(self.newHistStepData[key][subkeys[idx]])
+                    newHist.write('%s%s%s' % (subkeys[idx], self.sep, value))
+                    if idx < len(subkeys) - 1 and \
+                            type(self.newHistStepData[key][subkeys[idx + 1]]) != types.DictType:
+                        newHist.write('%s' % self.sep)
+            newHist.write(os.linesep)
+        self.histStepsNum = 0
+
+    def _parseValue(self, value, read = False):
+        """!Parse value"""
+        if read: # -> read data (cast values)
+
+            if value:
+                if value[0] == '[' and value[-1] == ']':# TODO, possible wrong interpretation
+                    value = value[1:-1].split(',')
+                    value = map(self._castValue, value)
+                    return value
+
+            if value == 'True':
+                value = True
+            elif value == 'False':
+                value = False
+            elif value == 'None':
+                value = None
+            elif ':' in value: # -> color
+                try:
+                    value = tuple(map(int, value.split(':')))
+                except ValueError: # -> string
+                    pass
+            else:
+                try:
+                    value = int(value)
+                except ValueError:
+                    try:
+                        value = float(value)
+                    except ValueError:
+                        pass
+        else: # -> write data
+            if type(value) == type(()): # -> color
+                value = str(value[0]) + ':' +\
+                    str(value[1]) + ':' + \
+                    str(value[2])
+                
+        return value
+
+    def _castValue(self, value):
+        """!Cast value"""
+        try:
+            value = int(value)
+        except ValueError:
+            try:
+                value = float(value)
+            except ValueError:
+                value = value[1:-1]
+
+        return value
+
+    def _getHistStepData(self, histStep):          
+        """!Load data saved in history step"""        
+        hist = open(self.histFile)
+        histStepData = {}
+
+        newHistStep = False
+        isSearchedHistStep = False
+        for line in hist.readlines():
+
+            if  not line.strip() and isSearchedHistStep:
+                 break
+            elif not line.strip():
+                newHistStep = True
+                continue
+            elif isSearchedHistStep:
+                self._parseLine(line, histStepData)
+
+            if newHistStep:
+                line = line.split("=")
+                if int(line[1]) == histStep:
+                    isSearchedHistStep = True
+                newHistStep = False
+
+        hist.close()
+        return histStepData
+
+    def _parseLine(self, line, histStepData):
+        """!Parse line in file with history"""        
+        line = line.rstrip('%s' % os.linesep).split(self.sep)
+        key = line[0]
+        kv = line[1:]
+        idx = 0
+        subkeyMaster = None
+        if len(kv) % 2 != 0: # multiple (e.g. nviz)
+            subkeyMaster = kv[0]
+            del kv[0]
+        idx = 0
+        while idx < len(kv):
+            if subkeyMaster:
+                subkey = [subkeyMaster, kv[idx]]
+            else:
+                subkey = kv[idx]
+            value = kv[idx+1]
+            value = self._parseValue(value, read = True)
+            if key not in histStepData:
+                histStepData[key] = {}
+
+            if type(subkey) == types.ListType:
+                if subkey[0] not in histStepData[key]:
+                    histStepData[key][subkey[0]] = {}
+                histStepData[key][subkey[0]][subkey[1]] = value
+            else:
+                histStepData[key][subkey] = value
+            idx += 2
+
+    def DeleteNewHistStepData(self):
+        """!Delete buffer data for new history step"""        
+        self.newHistStepData.clear() 
+
+class Snapping: 
+
+    def OnSnapping(self, event):
+        """!Start/stop snapping mode"""
+        ptListToolbar = self.toolbars['pointsList']
+
+        if not haveCtypes:
+            ptListToolbar.ToggleTool(id = ptListToolbar.GetToolId("snapping"),
+                                     toggle = False)
+            GMessage(parent = self,
+                     message = _("Unable to use ctypes. \n") + \
+                               _("Snapping mode can not be activated."))
+            return
+
+        if not event or not event.IsChecked():
+            if not event: 
+                ptListToolbar.ToggleTool(id = ptListToolbar.GetToolId("snapping"),
+                                         toggle = False)
+            if self.tmpMaps.HasTmpVectMap("vnet_snap_points"):
+                self.snapPts.DeleteRenderLayer() 
+                
+                up_map_evt = gUpdateMap(render = False, renderVector = False)
+                wx.PostEvent(self.mapWin, up_map_evt)
+
+            if self.snapData.has_key('cmdThread'):
+                self.snapData['cmdThread'].abort()
+
+            self.snapData['snap_mode'] = False
+            return  
+
+        if not self.InputsErrorMsgs(msg = _("Snapping mode can not be activated."),
+                                    inpToTest = ["input", "nlayer"]):
+
+            ptListToolbar.ToggleTool(id = ptListToolbar.GetToolId("snapping"),
+                                     toggle = False)
+            return
+
+        if not self.tmpMaps.HasTmpVectMap("vnet_snap_points"):
+            endStr = _("Do you really want to activate snapping and overwrite it?")
+            self.snapPts = self.tmpMaps.AddTmpVectMap("vnet_snap_points", endStr)
+
+            if not self.snapPts:
+                ptListToolbar.ToggleTool(id = ptListToolbar.GetToolId("snapping"),
+                                         toggle = False)
+                return   
+        elif self.snapPts.VectMapState() == 0:
+                dlg = wx.MessageDialog(parent = self.parent,
+                                       message = _("Temporary map '%s' was changed outside " +
+                                                    "vector analysis tool.\n" 
+                                                    "Do you really want to activate " + 
+                                                    "snapping and overwrite it? ") % \
+                                                    self.snapPts.GetVectMapName(),
+                                        caption = _("Overwrite map"),
+                                        style = wx.YES_NO | wx.NO_DEFAULT |
+                                                wx.ICON_QUESTION | wx.CENTRE)
+
+                ret = dlg.ShowModal()
+                dlg.Destroy()
+                
+                if ret == wx.ID_NO:
+                    self.tmpMaps.DeleteTmpMap(self.snapPts)
+                    ptListToolbar.ToggleTool(id = ptListToolbar.GetToolId("snapping"),
+                                             toggle = False)
+                    return
+
+        self.snapData['snap_mode'] = True
+
+        inpName = self.inputData['input'].GetValue()
+        inpName, mapSet = self._parseMapStr(inpName)
+        inpFullName = inpName + '@' + mapSet
+        computeNodes = True
+
+        if not self.snapData.has_key("inputMap"):
+            pass
+        elif inpFullName != self.snapData["inputMap"].GetVectMapName():
+            self.snapData["inputMap"] = VectMap(self, inpFullName)
+        elif self.snapData["inputMapNlayer"] == self.inputData["nlayer"].GetValue():
+            if self.snapData["inputMap"].VectMapState() == 1:
+                computeNodes = False
+    
+        # new map need
+        if computeNodes:
+            self.stBar.AddStatusItem(text = _('Computing nodes...'),
+                                     key = 'snap',
+                                     priority = self.stPriorities['important'])
+            if not self.snapData.has_key('cmdThread'):
+                self.snapData['cmdThread'] = CmdThread(self)
+
+            cmd = ["v.to.points", "input=" + self.inputData['input'].GetValue(), 
+                                  "output=" + self.snapPts.GetVectMapName(),
+                                  "llayer=" + self.inputData["nlayer"].GetValue(),
+                                  "-n", "--overwrite"]
+            # process GRASS command with argument
+            self.snapData["inputMap"] = VectMap(self, inpFullName)
+            self.snapData["inputMap"].SaveVectMapState()
+
+            self.Bind(EVT_CMD_DONE, self._onToPointsDone)
+            self.snapData['cmdThread'].RunCmd(cmd)
+
+            self.snapData["inputMapNlayer"] = self.inputData["nlayer"].GetValue()
+        # map is already created and up to date for input data
+        else:
+            self.snapPts.AddRenderLayer()
+
+            up_map_evt = gUpdateMap(render = True, renderVector = True)
+            wx.PostEvent(self.mapWin, up_map_evt)
+
+    def _onToPointsDone(self, event):
+        """!Update map window, when map with nodes to snap is created"""
+        self.stBar.RemoveStatusItem(key = 'snap')
+        if not event.aborted:
+            self.snapPts.SaveVectMapState()
+            self.snapPts.AddRenderLayer() 
+
+            up_map_evt = gUpdateMap(render = True, renderVector = True)
+            wx.PostEvent(self.mapWin, up_map_evt)
+
+    def _snapPoint(self, coords):
+        """!Find nearest node to click coordinates (within given threshold)"""
+        e = coords[0]
+        n = coords[1]
+
+        # compute threshold
+        snapTreshPix = int(UserSettings.Get(group ='vnet', 
+                                            key = 'other', 
+                                            subkey = 'snap_tresh'))
+        res = max(self.mapWin.Map.region['nsres'], self.mapWin.Map.region['ewres'])
+        snapTreshDist = snapTreshPix * res
+
+        vectorMap = self.inputData['input'].GetValue()
+        vectMapName, mapSet = self._parseMapStr(vectorMap)
+        inpMapExists = grass.find_file(name = vectMapName, 
+                                       element = 'vector', 
+                                       mapset = mapSet)
+        if not inpMapExists['name']:
+            return False
+
+        openedMap = pointer(vectlib.Map_info())
+        ret = vectlib.Vect_open_old2(openedMap, 
+                                     c_char_p(vectMapName),
+                                     c_char_p(mapSet),
+                                     c_char_p(self.inputData['alayer'].GetValue()))
+        if ret == 1:
+            vectlib.Vect_close(openedMap)
+        if ret != 2: 
+            return False
+
+        nodeNum =  vectlib.Vect_find_node(openedMap,     
+                                          c_double(e), 
+                                          c_double(n), 
+                                          c_double(0), 
+                                          c_double(snapTreshDist),
+                                          vectlib.WITHOUT_Z)
+
+        if nodeNum > 0:
+            e = c_double(0)
+            n = c_double(0)
+            vectlib.Vect_get_node_coor(openedMap, 
+                                       nodeNum, 
+                                       byref(e), 
+                                       byref(n), 
+                                       None); # z
+            e = e.value
+            n = n.value
+        else:
+            vectlib.Vect_close(openedMap)
+            return False
+
+        coords[0] = e
+        coords[1] = n
+        return True
+
Index: gui/wxpython/vnet/toolbars.py
===================================================================
--- gui/wxpython/vnet/toolbars.py	(revision 56146)
+++ gui/wxpython/vnet/toolbars.py	(working copy)
@@ -41,6 +41,10 @@
                                     label = _('Insert points from Map Display')),
             'snapping'  : MetaIcon(img = 'move',
                                     label = _('Activate snapping to nodes')),
+            'isec_turn_edit'  : MetaIcon(img = 'line-edit',
+                                    label = _('Activate mode for turns editing')),
+            'global_turn_edit'  : MetaIcon(img = 'vector-tools',
+                                          label = _('Activate mode for global turns editing')),
             'pointAdd'     : MetaIcon(img = 'point-create',
                                     label = _('Add new point')),
             'pointDelete'  : MetaIcon(img = 'gcp-delete',
@@ -57,7 +61,13 @@
                                      ('pointAdd', icons["pointAdd"],
                                         self.list.AddItem),
                                      ('pointDelete', icons["pointDelete"],
-                                        self.list.DeleteItem)))
+                                        self.list.DeleteItem),
+                                      (None, ),
+                                      ('isec_turn_edit', icons['isec_turn_edit'],
+                                      self.list.dialog.OnDefIsecTurnCosts,
+                                      wx.ITEM_CHECK),
+                                      ('global_turn_edit', icons['global_turn_edit'],
+                                      self.list.dialog.OnDefGlobalTurnCosts)))
                                     
     def GetToolId(self, toolName): #TODO can be useful in base
 
Index: gui/wxpython/dbmgr/base.py
===================================================================
--- gui/wxpython/dbmgr/base.py	(revision 56147)
+++ gui/wxpython/dbmgr/base.py	(working copy)
@@ -118,12 +118,15 @@
         self.Bind(wx.EVT_LIST_COL_CLICK,       self.OnColumnSort)     
         self.Bind(wx.EVT_LIST_COL_RIGHT_CLICK, self.OnColumnMenu)     
         
-    def Update(self, mapDBInfo):
+    def Update(self, mapDBInfo = None):
         """!Update list according new mapDBInfo description"""
-        self.mapDBInfo = mapDBInfo
-        self.LoadData(self.layer)
+        if mapDBInfo:
+            self.mapDBInfo = mapDBInfo
+            self.LoadData(self.layer)
+        else:
+            self.LoadData(self.layer, removeSqlFilter = False)
 
-    def LoadData(self, layer, columns = None, where = None, sql = None):
+    def LoadData(self, layer, columns = None, where = None, sql = None, removeSqlFilter = True):
         """!Load data into list
 
         @param layer layer number
@@ -171,30 +174,51 @@
         # TODO: more effective way should be implemented...
         outFile = tempfile.NamedTemporaryFile(mode = 'w+b')
 
-        cmdParams = dict(quiet = True,
-                         parent = self,
-                         flags = 'c')
-        # pipe must not be given in the params for windows
-        if not (sys.platform == "win32" and fs == '|'):
-            cmdParams.update(dict(sep = fs))
+        if removeSqlFilter:
+            self.sqlFilter = None
+            self.sqlFilterType = None
 
-        if sql:
-            cmdParams.update(dict(sql = sql,
-                                  output = outFile.name,
-                                  overwrite = True))
+        if not self.sqlFilter:
+            cmdParams = dict(quiet = True,
+                             parent = self,
+                             flags = 'c')
+            # pipe must not be given in the params for windows
+            if not (sys.platform == "win32" and fs == '|'):
+                cmdParams.update(dict(sep = fs))
+        else:
+            cmdParams = self.sqlFilter
+
+
+        if  self.sqlFilterType == "sql" or (sql and not self.sqlFilterType):
+            self.sqlFilterType = "sql"
+
+            if not self.sqlFilter:
+                cmdParams.update(dict(sql = sql,
+                                      output = outFile.name,
+                                      overwrite = True))
+            else:
+                cmdParams.update(dict(output = outFile.name))
+            
             ret = RunCommand('db.select',
                              **cmdParams)
         else:
-            cmdParams.update(dict(map = self.mapDBInfo.map,
-                                  layer = layer,
-                                  where = where,
-                                  stdout = outFile))
-            if columns:
-                cmdParams.update(dict(columns = ','.join(columns)))
+            self.sqlFilterType = "where"
+            
+            if not self.sqlFilter:
+                cmdParams.update(dict(map = self.mapDBInfo.map,
+                                      layer = layer,
+                                      where = where,
+                                      stdout = outFile))
+                if columns:
+                    cmdParams.update(dict(columns = ','.join(columns)))
+            else:
+                cmdParams.update(dict(stdout = outFile))
 
             ret = RunCommand('v.db.select',
                              **cmdParams)
-        
+        print cmdParams
+        self.sqlFilter = cmdParams;
+
         # These two should probably be passed to init more cleanly
         # setting the numbers of items = number of elements in the dictionary
         self.itemDataMap  = {}
@@ -992,8 +1016,10 @@
             
         pageSizer = wx.BoxSizer(wx.VERTICAL)
 
+        sqlQueryPanel = wx.Panel(parent = panel, id = wx.ID_ANY)
+
         # attribute data            
-        sqlBox = wx.StaticBox(parent = panel, id = wx.ID_ANY,
+        sqlBox = wx.StaticBox(parent = sqlQueryPanel, id = wx.ID_ANY,
                               label = " %s " % _("SQL Query"))
 
         sqlSizer = wx.StaticBoxSizer(sqlBox, wx.VERTICAL)
@@ -1018,7 +1044,7 @@
             dbmStyle = { 'agwStyle' : FNPageStyle }
         else:
             dbmStyle = { 'style' : FNPageStyle }
-        sqlNtb = FN.FlatNotebook(parent = panel, id = wx.ID_ANY,
+        sqlNtb = FN.FlatNotebook(parent = sqlQueryPanel, id = wx.ID_ANY,
                                  **dbmStyle)
         # Simple tab
         simpleSqlPanel = wx.Panel(parent = sqlNtb, id = wx.ID_ANY)
@@ -1105,7 +1131,10 @@
                       proportion = 1,
                       flag = wx.ALL | wx.EXPAND,
                       border = 5)
-        pageSizer.Add(item = sqlSizer,
+
+        sqlQueryPanel.SetSizer(sqlSizer)
+        
+        pageSizer.Add(item = sqlQueryPanel,
                       proportion = 0,
                       flag = wx.BOTTOM | wx.LEFT | wx.RIGHT | wx.EXPAND,
                       border = 5)
@@ -1331,7 +1360,7 @@
                                                      keyColumn, cat))
                 self.ApplyCommands(self.listOfCommands, self.listOfSQLStatements)
             
-            tlist.Update(self.dbMgrData['mapDBInfo'])
+            tlist.Update()
         
     def OnDataItemAdd(self, event):
         """!Add new record to the attribute table"""
Index: vector/v.net.path/v.net.path.html
===================================================================
--- vector/v.net.path/v.net.path.html	(revision 56146)
+++ vector/v.net.path/v.net.path.html	(working copy)
@@ -52,6 +52,7 @@
     <li>fdist - the distance from first point to the network</li>
     <li>tdist - the distance from the network to second point</li>
 </ul>
+<p>There is the option of applying the <a href="v.net.turntable.html">v.net.turntable</a> module on the input layer first. This means the input layer is expanded by turntable with costs of every possible turn on any possible node (intersection) in both directions. Note that after this expansion it is required to apply the -t flag. This flag enables additional parameters tlayer and tuclayer that are otherwise ignored.
 
 <h2>NOTES</h2>
 Nodes and arcs can be closed using cost = -1. 
Index: vector/v.net.path/path.c
===================================================================
--- vector/v.net.path/path.c	(revision 56146)
+++ vector/v.net.path/path.c	(working copy)
@@ -24,7 +24,8 @@
 int cmp(const void *, const void *);
 
 int path(struct Map_info *In, struct Map_info *Out, char *filename,
-	 int nfield, double maxdist, int segments)
+	 int nfield, double maxdist, int segments, int tfield, int tucfield,
+	 int use_ttb)
 {
     FILE *in_file = NULL;
     int i, nlines, line, npoints, type, cat, id, fcat, tcat, fline, tline,
@@ -174,7 +175,9 @@
 	    else {
 		fline = Citem->line;
 		type = Vect_read_line(In, Points, NULL, fline);
-		fnode = Vect_find_node(In, Points->x[0], Points->y[0], Points->z[0], 0, 0);
+		fnode =
+		    Vect_find_node(In, Points->x[0], Points->y[0],
+				   Points->z[0], 0, 0);
 		/* Vect_get_line_nodes(In, fline, &fnode, NULL); */
 	    }
 	    G_debug(3, "from: cat = %5d point(line) = %5d node = %5d", fcat,
@@ -194,14 +197,24 @@
 	    else {
 		tline = Citem->line;
 		type = Vect_read_line(In, Points, NULL, tline);
-		tnode = Vect_find_node(In, Points->x[0], Points->y[0], Points->z[0], 0, 0);
+		tnode =
+		    Vect_find_node(In, Points->x[0], Points->y[0],
+				   Points->z[0], 0, 0);
 		/* Vect_get_line_nodes(In, tline, &tnode, NULL); */
 	    }
 	    G_debug(3, "to  : cat = %5d point(line) = %5d node = %5d", tcat,
 		    tline, tnode);
 
 	    if (sp != SP_NOPOINT) {
-		ret = Vect_net_shortest_path(In, fnode, tnode, AList, &cost);
+		if (use_ttb)
+		    ret =
+			Vect_net_ttb_shortest_path(In, fnode, 0, tnode, 0,
+						   tfield, tucfield, AList,
+						   &cost);
+		else
+		    ret =
+			Vect_net_shortest_path(In, fnode, tnode, AList,
+					       &cost);
 
 		if (ret == -1) {
 		    sp = SP_UNREACHABLE;
@@ -254,12 +267,21 @@
 	}
 	else {			/* INPUT_MODE_COOR */
 	    fcat = tcat = 0;
-	    ret =
-		Vect_net_shortest_path_coor(In, fx, fy, 0.0, tx, ty, 0.0,
-					    maxdist, maxdist, &cost, OPoints,
-					    AList, FPoints, TPoints, &fdist,
-					    &tdist);
 
+	    if (use_ttb)
+		ret =
+		    Vect_net_ttb_shortest_path_coor(In, fx, fy, 0.0, tx, ty,
+						    0.0, maxdist, maxdist,
+						    tfield, tucfield, &cost,
+						    OPoints, AList, FPoints,
+						    TPoints, &fdist, &tdist);
+	    else
+		ret =
+		    Vect_net_shortest_path_coor(In, fx, fy, 0.0, tx, ty, 0.0,
+						maxdist, maxdist, &cost,
+						OPoints, AList, FPoints,
+						TPoints, &fdist, &tdist);
+
 	    if (ret == 0) {
 		sp = SP_UNREACHABLE;
 		unreachable++;
Index: vector/v.net.path/main.c
===================================================================
--- vector/v.net.path/main.c	(revision 56146)
+++ vector/v.net.path/main.c	(working copy)
@@ -20,17 +20,18 @@
 #include <grass/vector.h>
 #include <grass/glocale.h>
 
-int path(struct Map_info *, struct Map_info *, char *, int, double, int);
+int path(struct Map_info *, struct Map_info *, char *, int, double, int, int,
+	 int, int);
 
 int main(int argc, char **argv)
 {
-    struct Option *input_opt, *output_opt, *afield_opt, *nfield_opt, *afcol,
-	*abcol, *ncol, *type_opt;
+    struct Option *input_opt, *output_opt, *afield_opt, *nfield_opt,
+	*tfield_opt, *tucfield_opt, *afcol, *abcol, *ncol, *type_opt;
     struct Option *max_dist, *file_opt;
-    struct Flag *geo_f, *segments_f;
+    struct Flag *geo_f, *segments_f, *turntable_f;
     struct GModule *module;
     struct Map_info In, Out;
-    int type, afield, nfield, geo;
+    int type, afield, nfield, tfield, tucfield, geo;
     double maxdist;
 
     /* Initialize the GIS calls */
@@ -60,6 +61,16 @@
     nfield_opt->answer = "2";
     nfield_opt->label = _("Node layer");
 
+    tfield_opt = G_define_standard_option(G_OPT_V_FIELD);
+    tfield_opt->key = "tlayer";
+    tfield_opt->answer = "3";
+    tfield_opt->label = _("Turntable layer");
+
+    tucfield_opt = G_define_standard_option(G_OPT_V_FIELD);
+    tucfield_opt->key = "tuclayer";
+    tucfield_opt->answer = "4";
+    tucfield_opt->label = _("Unique categories layer for turntable");
+
     file_opt = G_define_standard_option(G_OPT_F_INPUT);
     file_opt->key = "file";
     file_opt->required = NO;
@@ -107,12 +118,19 @@
     segments_f->description = _("Write output as original input segments, "
 				"not each path as one line.");
 
+    turntable_f = G_define_flag();
+    turntable_f->key = 't';
+    turntable_f->description = _("Use turntable"
+				 "(otherwise tuclayer and tlayer are ignored)");
+
     if (G_parser(argc, argv))
 	exit(EXIT_FAILURE);
 
     type = Vect_option_to_types(type_opt);
     afield = atoi(afield_opt->answer);
     nfield = atoi(nfield_opt->answer);
+    tfield = atoi(tfield_opt->answer);
+    tucfield = atoi(tucfield_opt->answer);
     maxdist = atof(max_dist->answer);
 
     if (geo_f->answer) {
@@ -136,10 +154,16 @@
     }
     Vect_hist_command(&Out);
 
-    Vect_net_build_graph(&In, type, afield, nfield, afcol->answer,
-			 abcol->answer, ncol->answer, geo, 0);
+    if (turntable_f->answer)
+	Vect_net_ttb_build_graph(&In, type, afield, nfield, tfield, tucfield,
+				 afcol->answer, abcol->answer, ncol->answer,
+				 geo, 0);
+    else
+	Vect_net_build_graph(&In, type, afield, nfield, afcol->answer,
+			     abcol->answer, ncol->answer, geo, 0);
 
-    path(&In, &Out, file_opt->answer, nfield, maxdist, segments_f->answer);
+    path(&In, &Out, file_opt->answer, nfield, maxdist, segments_f->answer,
+	 tfield, tucfield, turntable_f->answer);
 
     Vect_close(&In);
 
Index: vector/v.net.iso/main.c
===================================================================
--- vector/v.net.iso/main.c	(revision 56146)
+++ vector/v.net.iso/main.c	(working copy)
@@ -43,14 +43,14 @@
 
 int main(int argc, char **argv)
 {
-    int i, ret, centre, line, centre1, centre2;
+    int i, j, ret, centre, line, centre1, centre2, tfield, tucfield;
     int nlines, nnodes, type, ltype, afield, nfield, geo, cat;
     int node, node1, node2;
     double cost, e1cost, e2cost, n1cost, n2cost, s1cost, s2cost, l, l1;
     struct Option *map, *output;
     struct Option *afield_opt, *nfield_opt, *afcol, *abcol, *ncol, *type_opt,
-	*term_opt, *cost_opt;
-    struct Flag *geo_f;
+	*term_opt, *cost_opt, *tfield_opt, *tucfield_opt;
+    struct Flag *geo_f, *turntable_f;
     struct GModule *module;
     struct Map_info Map, Out;
     struct cat_list *catlist;
@@ -73,7 +73,8 @@
     G_add_keyword(_("network"));
     G_add_keyword(_("isolines"));
     module->description =
-	_("Splits net to bands between cost isolines (direction from centre). "
+	_
+	("Splits net to bands between cost isolines (direction from centre). "
 	 "Centre node must be opened (costs >= 0). "
 	 "Costs of centre node are used in calculation.");
 
@@ -94,6 +95,16 @@
     nfield_opt->answer = "2";
     nfield_opt->label = _("Node layer");
 
+    tfield_opt = G_define_standard_option(G_OPT_V_FIELD);
+    tfield_opt->key = "tlayer";
+    tfield_opt->answer = "3";
+    tfield_opt->label = _("Turntable layer");
+
+    tucfield_opt = G_define_standard_option(G_OPT_V_FIELD);
+    tucfield_opt->key = "tuclayer";
+    tucfield_opt->answer = "4";
+    tucfield_opt->label = _("Unique categories layer for turntable");
+
     afcol = G_define_standard_option(G_OPT_DB_COLUMN);
     afcol->key = "afcolumn";
     afcol->description =
@@ -127,6 +138,11 @@
     geo_f->description =
 	_("Use geodesic calculation for longitude-latitude locations");
 
+    turntable_f = G_define_flag();
+    turntable_f->key = 't';
+    turntable_f->description = _("Use turntable"
+				 "(otherwise tuclayer and tlayer are ignored)");
+
     if (G_parser(argc, argv))
 	exit(EXIT_FAILURE);
 
@@ -135,8 +151,6 @@
     SPoints = Vect_new_line_struct();
 
     type = Vect_option_to_types(type_opt);
-    afield = atoi(afield_opt->answer);
-    nfield = atoi(nfield_opt->answer);
 
     catlist = Vect_new_cat_list();
     Vect_str_to_cat_list(term_opt->answer, catlist);
@@ -169,7 +183,8 @@
 
     /* Should not happen: */
     if (niso < 2)
-	G_warning(_("Not enough costs, everything reachable falls to first band"));
+	G_warning(_
+		  ("Not enough costs, everything reachable falls to first band"));
 
     if (geo_f->answer)
 	geo = 1;
@@ -179,9 +194,19 @@
     Vect_set_open_level(2);
     Vect_open_old(&Map, map->answer, "");
 
+    afield = Vect_get_field_number(&Map, afield_opt->answer);
+    nfield = Vect_get_field_number(&Map, nfield_opt->answer);
+    tfield = Vect_get_field_number(&Map, tfield_opt->answer);
+    tucfield = Vect_get_field_number(&Map, tucfield_opt->answer);
+
     /* Build graph */
-    Vect_net_build_graph(&Map, type, afield, nfield, afcol->answer,
-			 abcol->answer, ncol->answer, geo, 0);
+    if (turntable_f->answer)
+	Vect_net_ttb_build_graph(&Map, type, afield, nfield, tfield, tucfield,
+				 afcol->answer, abcol->answer, ncol->answer,
+				 geo, 0);
+    else
+	Vect_net_build_graph(&Map, type, afield, nfield, afcol->answer,
+			     abcol->answer, ncol->answer, geo, 0);
 
     nnodes = Vect_get_num_nodes(&Map);
     nlines = Vect_get_num_lines(&Map);
@@ -193,7 +218,9 @@
 	    continue;
 
 	Vect_read_line(&Map, Points, Cats, i);
-	node = Vect_find_node(&Map, Points->x[0], Points->y[0], Points->z[0], 0, 0);
+	node =
+	    Vect_find_node(&Map, Points->x[0], Points->y[0], Points->z[0], 0,
+			   0);
 	if (!node) {
 	    G_warning(_("Point is not connected to the network"));
 	    continue;
@@ -224,13 +251,24 @@
     G_message(_("Number of centres: %d (nlayer %d)"), ncentres, nfield);
 
     if (ncentres == 0)
-	G_warning(_("Not enough centres for selected nlayer. Nothing will be allocated."));
+	G_warning(_
+		  ("Not enough centres for selected nlayer. Nothing will be allocated."));
 
-    /* alloc and reset space for all nodes */
-    Nodes = (NODE *) G_calloc((nnodes + 1), sizeof(NODE));
-    for (i = 1; i <= nnodes; i++) {
-	Nodes[i].centre = -1;
+    if (turntable_f->answer) {
+	/* alloc and reset space for all lines */
+	Nodes = (NODE *) G_calloc((nlines * 2 + 2), sizeof(NODE));
+	for (i = 2; i <= (nlines * 2 + 2); i++) {
+	    Nodes[i].centre = -1;
+	}
+
     }
+    else {
+	/* alloc and reset space for all nodes */
+	Nodes = (NODE *) G_calloc((nnodes + 1), sizeof(NODE));
+	for (i = 1; i <= nnodes; i++) {
+	    Nodes[i].centre = -1;
+	}
+    }
 
     apnts1 = 1;
     pnts1 = (ISOPOINT *) G_malloc(apnts1 * sizeof(ISOPOINT));
@@ -245,32 +283,75 @@
 	G_debug(2, "centre = %d node = %d cat = %d", centre, node1,
 		Centers[centre].cat);
 	G_message(_("Calculating costs from centre %d..."), centre + 1);
-	for (node2 = 1; node2 <= nnodes; node2++) {
-	    G_percent(node2, nnodes, 1);
-	    G_debug(5, "  node1 = %d node2 = %d", node1, node2);
-	    Vect_net_get_node_cost(&Map, node2, &n2cost);
-	    if (n2cost == -1) {
-		continue;
-	    }			/* closed, left it as not attached */
+	if (turntable_f->answer)
+	    for (line = 1; line <= nlines; line++) {
+		G_debug(5, "  node1 = %d line = %d", node1, line);
+		Vect_net_get_node_cost(&Map, line, &n2cost);
+		/* closed, left it as not attached */
 
-	    ret = Vect_net_shortest_path(&Map, node1, node2, NULL, &cost);
-	    if (ret == -1) {
-		continue;
-	    }			/* node unreachable */
+		if (Vect_read_line(&Map, Points, Cats, line) < 0)
+		    continue;
+		if (Vect_get_line_type(&Map, line) != GV_LINE)
+		    continue;
+		if (!Vect_cat_get(Cats, tucfield, &cat))
+		    continue;
 
-	    /* We must add centre node costs (not calculated by Vect_net_shortest_path() ), but
-	     *  only if centre and node are not identical, because at the end node cost is add later */
-	    if (node1 != node2)
-		cost += n1cost;
-	    G_debug(5,
-		    "Arc nodes: %d %d cost: %f (x old cent: %d old cost %f",
-		    node1, node2, cost, Nodes[node2].centre,
-		    Nodes[node2].cost);
-	    if (Nodes[node2].centre == -1 || cost < Nodes[node2].cost) {
-		Nodes[node2].cost = cost;
-		Nodes[node2].centre = centre;
+		for (j = 0; j < 2; j++) {
+			if(j == 1)
+				cat *= -1;
+			
+		    ret =
+			Vect_net_ttb_shortest_path(&Map, node1, 0, cat, 1,
+						   tfield, tucfield, NULL,
+						   &cost);
+		    if (ret == -1) {
+			continue;
+		    }		/* node unreachable */
+
+		    /* We must add centre node costs (not calculated by Vect_net_shortest_path() ), but
+		     *  only if centre and node are not identical, because at the end node cost is add later */
+		    /*TODO issue in turns support */
+		    if (ret != 1)
+			cost += n1cost;
+
+		    G_debug(5,
+			    "Arc nodes: %d %d cost: %f (x old cent: %d old cost %f",
+			    node1, line, cost, Nodes[line * 2 + j].centre,
+			    Nodes[line * 2 + j].cost);
+		    if (Nodes[line * 2 + j].centre == -1 ||
+			cost < Nodes[line * 2 + j].cost) {
+			Nodes[line * 2 + j].cost = cost;
+			Nodes[line * 2 + j].centre = centre;
+		    }
+		}
 	    }
-	}
+	else
+	    for (node2 = 1; node2 <= nnodes; node2++) {
+		G_percent(node2, nnodes, 1);
+		G_debug(5, "  node1 = %d node2 = %d", node1, node2);
+		Vect_net_get_node_cost(&Map, node2, &n2cost);
+		if (n2cost == -1) {
+		    continue;
+		}		/* closed, left it as not attached */
+
+		ret = Vect_net_shortest_path(&Map, node1, node2, NULL, &cost);
+		if (ret == -1) {
+		    continue;
+		}		/* node unreachable */
+
+		/* We must add centre node costs (not calculated by Vect_net_shortest_path() ), but
+		 *  only if centre and node are not identical, because at the end node cost is add later */
+		if (node1 != node2)
+		    cost += n1cost;
+		G_debug(5,
+			"Arc nodes: %d %d cost: %f (x old cent: %d old cost %f",
+			node1, node2, cost, Nodes[node2].centre,
+			Nodes[node2].cost);
+		if (Nodes[node2].centre == -1 || cost < Nodes[node2].cost) {
+		    Nodes[node2].cost = cost;
+		    Nodes[node2].centre = centre;
+		}
+	    }
     }
 
     /* Write arcs to new map */
@@ -281,31 +362,43 @@
     nlines = Vect_get_num_lines(&Map);
     for (line = 1; line <= nlines; line++) {
 	G_percent(line, nlines, 2);
-	
+
 	ltype = Vect_read_line(&Map, Points, NULL, line);
 	if (!(ltype & type)) {
 	    continue;
 	}
-	Vect_get_line_nodes(&Map, line, &node1, &node2);
-	centre1 = Nodes[node1].centre;
-	centre2 = Nodes[node2].centre;
-	s1cost = Nodes[node1].cost;
-	s2cost = Nodes[node2].cost;
+
 	l = Vect_line_length(Points);
-
 	if (l == 0)
 	    continue;
 
-	G_debug(3, "Line %d : length = %f", line, l);
-	G_debug(3, "Arc centres: %d %d (nodes: %d %d)", centre1, centre2,
-		node1, node2);
 
-	Vect_net_get_node_cost(&Map, node1, &n1cost);
-	Vect_net_get_node_cost(&Map, node2, &n2cost);
+	if (turntable_f->answer) {
+	    centre1 = Nodes[line * 2].centre;
+	    centre2 = Nodes[line * 2 + 1].centre;
+	    s1cost = Nodes[line * 2].cost;
+	    s2cost = Nodes[line * 2 + 1].cost;
+	    n1cost = n2cost = 0;
+	}
+	else {
+	    Vect_get_line_nodes(&Map, line, &node1, &node2);
+	    centre1 = Nodes[node1].centre;
+	    centre2 = Nodes[node2].centre;
+	    s1cost = Nodes[node1].cost;
+	    s2cost = Nodes[node2].cost;
 
+	    Vect_net_get_node_cost(&Map, node1, &n1cost);
+	    Vect_net_get_node_cost(&Map, node2, &n2cost);
+
+	}
+
 	Vect_net_get_line_cost(&Map, line, GV_FORWARD, &e1cost);
 	Vect_net_get_line_cost(&Map, line, GV_BACKWARD, &e2cost);
 
+	G_debug(3, "Line %d : length = %f", line, l);
+	G_debug(3, "Arc centres: %d %d (nodes: %d %d)", centre1, centre2,
+		node1, node2);
+
 	G_debug(3, "  s1cost = %f n1cost = %f e1cost = %f", s1cost, n1cost,
 		e1cost);
 	G_debug(3, "  s2cost = %f n2cost = %f e2cost = %f", s2cost, n2cost,
@@ -505,7 +598,8 @@
 		    Vect_line_segment(Points, pnts1[i - 1].distance,
 				      pnts1[i].distance, SPoints);
 		if (ret == 0) {
-		    G_warning(_("Cannot get line segment, segment out of line"));
+		    G_warning(_
+			      ("Cannot get line segment, segment out of line"));
 		}
 		else {
 		    Vect_reset_cats(Cats);
Index: vector/v.net.iso/v.net.iso.html
===================================================================
--- vector/v.net.iso/v.net.iso.html	(revision 56146)
+++ vector/v.net.iso/v.net.iso.html	(working copy)
@@ -15,6 +15,7 @@
 <p>
 The input vector needs to be prepared with <em>v.net operation=connect</em> 
 in order to connect points representing center nodes to the network.
+<p>There is the option of applying the <a href="v.net.turntable.html">v.net.turntable</a> module on the input layer first. This means the input layer is expanded by turntable with costs of every possible turn on any possible node (intersection) in both directions. Note that after this expansion it is required to apply the -t flag. This flag enables additional parameters tlayer and tuclayer that are otherwise ignored.
 
 <h2>NOTES</h2>
 
Index: vector/v.net.turntable/main.c
===================================================================
--- vector/v.net.turntable/main.c	(revision 0)
+++ vector/v.net.turntable/main.c	(working copy)
@@ -0,0 +1,221 @@
+
+/****************************************************************
+ *
+ * MODULE:     v.net.turntable
+ *
+ * AUTHOR(S):  GRASS Development Team
+ *             Viera Bejdová    
+ *             Luáš Bocan
+ *             Eliška Kyzlíková
+ *             Štěpán Turek
+
+ * PURPOSE:    Create sql table, which describes turns of lines 
+               intersections in vector map.
+ *
+ * COPYRIGHT:  (C) 2013 by the GRASS Development Team
+ *
+ *             This program is free software under the
+ *             GNU General Public License (>=v2).
+ *             Read the file COPYING that comes with GRASS
+ *             for details.
+ *
+ ****************************************************************/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <grass/gis.h>
+#include <grass/vector.h>
+#include <grass/dbmi.h>
+#include <grass/glocale.h>
+
+void close_db(void *p)
+{
+    dbDriver *driver = (dbDriver *) p;
+
+    db_close_database_shutdown_driver(driver);
+}
+
+int main(int argc, char *argv[])
+{
+    struct GModule *module;
+
+    struct Map_info InMap, OutMap;
+    struct field_info *fi;
+
+    struct Option *opt_in_map, *opt_out_map;
+    struct Option *opt_ttb_name, *opt_a_field, *opt_n_field, *opt_tfield,
+	*opt_tucfield;
+    char *database_name, *driver_name;
+
+    int i_field_num, field_num, i_field;
+
+    char *ttb_name;
+    char *key_col;
+    int ttb_field, tucats_field, a_field, n_field;
+
+    char buf[DB_SQL_MAX];
+    dbDriver *driver;
+
+    dbString db_buf;
+
+    /* initialize module */
+    module = G_define_module();
+    G_add_keyword(_("vector"));
+    G_add_keyword(_("network"));
+    G_add_keyword(_("turn table"));
+    module->description = _("Create table which describes turns.");
+
+    opt_in_map = G_define_standard_option(G_OPT_V_INPUT);
+    opt_out_map = G_define_standard_option(G_OPT_V_OUTPUT);
+
+    opt_a_field = G_define_standard_option(G_OPT_V_FIELD);
+    opt_a_field->description =
+	_
+	("Arcs layer which will be expanded by turntable. Format: layer number[/layer name]");
+    opt_a_field->answer = "1";
+    opt_a_field->key = "alayer";
+    opt_a_field->required = YES;
+
+
+    opt_n_field = G_define_standard_option(G_OPT_V_FIELD);
+    opt_n_field->description =
+	_
+	("Nodes (points), which will be copied into output layer. By default all points are copied. Format: layer number[/layer name]");
+    opt_n_field->answer = "-1";
+    opt_n_field->key = "nlayer";
+    opt_n_field->required = NO;
+
+    opt_tfield = G_define_standard_option(G_OPT_V_FIELD);
+    opt_tfield->description =
+	_
+	("Layer where turntable will be attached. Format: layer number[/layer name]");
+    opt_tfield->answer = "3";
+    opt_tfield->key = "tlayer";
+    opt_tfield->required = YES;
+
+    opt_tucfield = G_define_standard_option(G_OPT_V_FIELD);
+    opt_tucfield->description =
+	_
+	("Node layer with unique categories for every line and point. The points are  placed on every node. Format: layer number[/layer name]");
+    opt_tucfield->answer = "4";
+    opt_tucfield->key = "tuclayer";
+    opt_tucfield->required = YES;
+
+    opt_ttb_name = G_define_option();
+    opt_ttb_name->key = "turntable";
+    opt_ttb_name->label = _("Turntable name");
+    opt_ttb_name->description =
+	_
+	("If not given, name consists of input vector map name and \"_turntable_\" plus layer number");
+    opt_ttb_name->type = TYPE_STRING;
+    opt_ttb_name->key_desc = "name";
+
+    /* initialize GIS environment */
+    G_gisinit(argv[0]);		/* reads grass env, stores program name to G_program_name() */
+
+    /* options and flags parser */
+    if (G_parser(argc, argv))
+	exit(EXIT_FAILURE);
+
+    a_field = atoi(opt_a_field->answer);
+    n_field = atoi(opt_n_field->answer);
+    ttb_field = atoi(opt_tfield->answer);
+    tucats_field = atoi(opt_tucfield->answer);
+
+    ttb_name = NULL;
+    if (!opt_ttb_name->answer) {
+	G_asprintf(&ttb_name, "%s_%d_turntable", opt_out_map->answer,
+		   ttb_field);
+    }
+    else {
+	ttb_name = G_store(opt_out_map->answer);
+    }
+
+    if (Vect_open_old(&InMap, opt_in_map->answer, "") < 2) {
+	G_fatal_error(_("Unable to open vector map <%s>."),
+		      opt_in_map->answer);
+    }
+    Vect_set_error_handler_io(&InMap, &OutMap);
+
+    if (Vect_open_new(&OutMap, opt_out_map->answer, WITHOUT_Z) < 1) {
+	G_fatal_error(_("Unable to create vector map <%s>."),
+		      opt_out_map->answer);
+    }
+
+    Vect_copy_tables(&InMap, &OutMap, 0);
+
+    /*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;
+
+	field_num = i_field_num;
+	break;
+    }
+
+    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);
+
+    /*TODO warning?? */
+    Vect_map_del_dblink(&OutMap, ttb_field);
+    if (Vect_map_add_dblink(&OutMap, ttb_field,
+			    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_in_map->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, ttb_field,
+		       tucats_field, a_field, n_field);
+    Vect_close(&InMap);
+
+    close_db(driver);
+
+    Vect_close(&OutMap);
+
+    /*TODO WHY must be opened again????*/
+    Vect_open_old(&OutMap, opt_out_map->answer, G_mapset());
+    Vect_build(&OutMap);
+
+    Vect_close(&OutMap);
+
+    exit(EXIT_SUCCESS);
+}

Property changes on: vector/v.net.turntable/main.c
___________________________________________________________________
Added: svn:executable
## -0,0 +1 ##
+*
\ No newline at end of property
Index: vector/v.net.turntable/ttb.c
===================================================================
--- vector/v.net.turntable/ttb.c	(revision 0)
+++ vector/v.net.turntable/ttb.c	(working copy)
@@ -0,0 +1,582 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <grass/gis.h>
+#include <grass/vector.h>
+#include <grass/dbmi.h>
+#include <grass/glocale.h>
+
+int get_dir(struct line_pnts *pnts1, struct line_pnts *pnts2, int * dir1, int * dir2)
+{
+    double x_from, y_from, z;
+    double x_to, y_to;
+
+    int n_points1 = Vect_get_num_line_points(pnts1);
+    int n_points2 = Vect_get_num_line_points(pnts2);
+
+    //TODO one point in line
+    Vect_line_get_point(pnts1, 0, &x_from, &y_from, &z);
+    Vect_line_get_point(pnts2, 0, &x_to, &y_to, &z);
+
+    if (x_from == x_to && y_from == y_to)
+    {
+        * dir1 = 1;
+        * dir2 = 1;
+        return 1;
+    }
+
+    Vect_line_get_point(pnts2, n_points2 - 1, &x_to, &y_to, &z);
+    if (x_from == x_to && y_from == y_to) {
+        * dir1 = 1;
+        * dir2 = -1;
+        return 1;
+    }
+
+    Vect_line_get_point(pnts1, n_points1 - 1, &x_from, &y_from, &z);
+    if (x_from == x_to && y_from == y_to) {
+        * dir1 = -1;
+        * dir2 = -1;
+        return 1;
+    }
+
+    Vect_line_get_point(pnts2, 0, &x_to, &y_to, &z);
+    if (x_from == x_to && y_from == y_to){
+        * dir1 = -1; 
+        * dir2 = 1;
+        return 1;
+    }
+    return -1;
+}
+
+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.
+
+   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_j to is on the left from line_pnts_i 
+   the angle is negative.
+
+   \return lines angle
+   \return -9.0 if line is defined by one point
+ */
+
+double compute_lines_angle(struct line_pnts *line_pnts_from, 
+                            struct line_pnts *line_pnts_to)
+{
+    double angle_from, angle_to;
+    int from_dir, to_dir;
+
+    double angle;
+
+    get_dir(line_pnts_from, line_pnts_to, &from_dir, &to_dir);
+
+    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 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 uturns for line into turntable.
+        
+    Add two records into turntable because every line has two possible U-turns.
+*/
+int add_uturns(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);
+
+    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 turn for two lines into turntable.
+        
+    Add two records into turntable because we can take the turn from two opposite directions.
+*/
+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_to_cat = ln_j_cat;
+    ln_from_cat = ln_i_cat;
+
+    get_dir(line_pnts_i, line_pnts_j, &ln_i_dir, &ln_j_dir);
+
+    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,
+                            line_pnts_i);
+    else
+        angle =
+            compute_lines_angle(line_pnts_i,
+                                line_pnts_j);
+
+    ln_f = ln_from_cat;
+    ln_t = ln_to_cat;
+
+    for(i = 0; i < 2; ++i) {
+        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 {
+            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;
+
+}
+
+
+/*\brief Fills data into turntable.
+
+   \return lines angle
+   \return -9.0 if line is defined by one point
+ */
+
+void populate_turntable(dbDriver * driver, struct Map_info *InMap,
+                        struct Map_info *OutMap, char *ttb_name,
+                        int ttb_field, int tucats_field, int a_field,
+                        int n_field)
+{
+    char buf[DB_SQL_MAX];
+    struct ilist list;
+
+    int *features_id;
+
+    int n_node_lines, 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;
+    int ln_i_id, ln_j_id, ln_i_ucat, ln_j_ucat;
+
+    int ltype;
+
+    struct line_pnts *line_pnts_i, *line_pnts_j;
+    struct line_cats *cats_i, *cats_j;
+
+    struct line_pnts *line_pnts;
+    struct line_cats *cats;
+    double x, y, z;
+
+
+    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();
+
+    line_pnts = Vect_new_line_struct();
+    cats = Vect_new_cats_struct();
+
+
+    n_lines = Vect_get_num_primitives(InMap, GV_LINE);
+    /*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/line in turntable. */
+    next_ttb_cat = 1;
+
+    /* Stores number of category which will be assigned to a next feature added into tucats_field. */
+    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 (offset), when was rewritten. 
+       The value is saved into features_id array. */
+    i_rew_lines = 0;
+
+    G_init_ilist(&list);
+
+    /* Every node represents one intersection. */
+    for (pivot_node = 1; pivot_node <= n_nodes; pivot_node++) {
+        n_node_lines = Vect_get_node_n_lines(InMap, pivot_node);
+
+        G_debug(3, "Found %d lines connected to node with id %d",
+                n_node_lines, 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 in account  U-turns. */
+
+        for (i_line = 0; i_line < n_node_lines; 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) {
+                Vect_read_line(InMap, line_pnts_i, cats_i, abs(ln_i_id));
+                if (a_field == -1 ||
+                    Vect_field_cat_get(cats_i, a_field, &list) < 0)
+                    continue;
+            }
+
+            /*If i line was already written into output map, 
+               we need to take it's categories from the output map with added categories 
+               to tlayer and tuclayer. */
+            else {
+                ln_i_id = Vect_get_node_line(InMap, pivot_node, i_line);
+
+                Vect_read_line(OutMap, line_pnts_i, cats_i,
+                               features_id[abs(ln_i_id) - 1]);
+                Vect_cat_get(cats_i, tucats_field, &ln_i_ucat);
+            }
+
+            for (j_line = i_line; j_line < n_node_lines; j_line++) {
+
+                ln_j_id = Vect_get_node_line(InMap, pivot_node, j_line);
+
+                /* write line, which not have been written into new map yet. */
+                if (features_id[abs(ln_j_id) - 1] < 1) {
+                    /* Get line from input map. */
+                    Vect_read_line(InMap, line_pnts_j, cats_j, abs(ln_j_id));
+
+                    /* If line does not belong into arc layer, skip it. */
+                    if (a_field == -1 ||
+                        Vect_field_cat_get(cats_j, a_field, &list) < 0)
+                        continue;
+
+                    /* Write U-turn for every not yet written line. */
+
+                    /* Assign unique category (assigned only when feature is written). */
+                    Vect_cat_set(cats_j, tucats_field, i_ucat);
+
+                    /* Assign turn category in turntable for the U-turn. */
+                    Vect_cat_set(cats_j, ttb_field, next_ttb_cat);
+                    Vect_cat_set(cats_j, ttb_field, next_ttb_cat + 1);
+
+                    /* 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 (ln_j_id != ln_i_id) {
+                        Vect_cat_set(cats_j, ttb_field, next_ttb_cat);
+                        Vect_cat_set(cats_j, ttb_field, next_ttb_cat + 1);
+                    }
+
+                    ln_j_ucat = i_ucat;
+
+                    /* We create two nodes in turntable for every line. These nodes have 
+                       positive and negative values, with their absolute values same.
+
+                       Every node is corresponding to opposite line direction. The positive node 
+                       coincide with direction of line. The negative node coincide with 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 (arrows points into it),
+                       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 ttb_field (layer with turntable), which are hold by both ln_from and ln_to lines 
+                       ln_from - unique category in tucats_field assigned to the line 
+                       ln_to - unique category in tucats_field assigned to the line 
+                       cost - cost for turn from ln_from to ln_to
+                            - U-turn opposite directions are calculated as from ln_to_id to ln_from_id, 
+                              otherwise you would get same direction
+                       isec - point category in tucats_field, which represents the intersection,
+                              where the forward turn with intersections belongs into
+                       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 belongs. */
+                    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, ttb_field, next_ttb_cat);
+                        Vect_cat_set(cats_i, ttb_field, next_ttb_cat + 1);
+                        Vect_cat_set(cats_i, tucats_field, i_ucat);
+                    }
+
+                    if (add_uturns(driver, ttb_name, &next_ttb_cat, ln_j_ucat, isec_start_ucat, isec_end_ucat) < 0) {
+                        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, ttb_field, next_ttb_cat);
+                        Vect_cat_set(cats_j, ttb_field, 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 
+                       (if we get to intersection, which is in other end of the line.) */
+                    features_id[abs(ln_j_id) - 1] =
+                        Vect_write_line(OutMap, GV_LINE, line_pnts_j, cats_j);
+
+                    /* i, j lines  are equal, it consists only U-turn */
+                    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. */
+                    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, ttb_field, next_ttb_cat);
+                    Vect_cat_set(cats_j, ttb_field, 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, tucats_field, &ln_j_ucat);
+
+                    /* rewrite j line with added the new category for the turn */
+                    Vect_rewrite_line(OutMap, features_id[abs(ln_j_id) - 1],
+                                      GV_LINE, 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, ttb_field, next_ttb_cat);
+                Vect_cat_set(cats_i, ttb_field, 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(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],
+                              GV_LINE, line_pnts_i, cats_i);
+
+            features_id[abs(ln_i_id) - 1] = i_ucat + i_rew_lines;
+            i_rew_lines += 1;
+        }
+    }
+
+    /* Create point on every node. */
+    /* TODO what if nodes are not used in alayer - no line for them in turntable? 
+       TODO points are created also on intersections with created points in n_layer, check if exists?
+     */
+    for (pivot_node = 1; pivot_node <= n_nodes; pivot_node++) {
+        Vect_reset_line(line_pnts);
+
+        Vect_get_node_coor(InMap, pivot_node, &x, &y, &z);
+
+        Vect_append_point(line_pnts, x, y, z);
+
+        Vect_reset_cats(cats);
+        Vect_cat_set(cats, tucats_field, i_ucat);
+
+        Vect_write_line(OutMap, GV_POINT, line_pnts, cats);
+        i_ucat++;
+    }
+
+    /* copy points from nlayer */
+    while ((ltype = Vect_read_next_line(InMap, line_pnts, cats)) > 0) {
+
+        if (ltype != GV_POINT)
+            continue;
+
+        if (n_field == -1 || Vect_field_cat_get(cats_i, n_field, &list) > -1)
+            Vect_write_line(OutMap, GV_POINT, line_pnts, cats);
+    }
+
+
+    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);
+
+    db_commit_transaction(driver);
+    return;
+}
\ No newline at end of file
Index: vector/v.net.turntable/v.net.turntable.html
===================================================================
--- vector/v.net.turntable/v.net.turntable.html	(revision 0)
+++ vector/v.net.turntable/v.net.turntable.html	(working copy)
@@ -0,0 +1,23 @@
+<h2>DESCRIPTION</h2>
+
+<em>v.turntable</em>
+
+<h2>EXAMPLE</h2>
+
+<div class="code"><pre>
+ v.net.turntable map=roadsmajor layer=3
+</pre></div>
+
+<h2>SEE ALSO</h2>
+
+<em>
+<a href="http://grass.osgeo.org/programming7/">GRASS Programmer's Manual</a>
+</em>
+
+<h2>AUTHOR</h2>
+ 	Viera Bejdová    
+    Luáš Bocan
+    Eliška Kyzlíková
+    Štěpán Turek
+    
+<p><i>Last changed: $Date: 2012-07-19 18:56:09 +0200 (Thu, 19 Jul 2012) $</i>
\ No newline at end of file

Property changes on: vector/v.net.turntable/v.net.turntable.html
___________________________________________________________________
Added: svn:executable
## -0,0 +1 ##
+*
\ No newline at end of property
Index: vector/v.net.turntable/Makefile
===================================================================
--- vector/v.net.turntable/Makefile	(revision 0)
+++ vector/v.net.turntable/Makefile	(working copy)
@@ -0,0 +1,12 @@
+MODULE_TOPDIR = ../..
+
+PGM=v.net.turntable
+
+LIBES = $(VECTORLIB) $(GISLIB) $(DBMILIB)
+DEPENDENCIES = $(VECTORDEP) $(GISDEP) $(DBMIDEP)
+EXTRA_INC = $(VECT_INC)
+EXTRA_CFLAGS = $(VECT_CFLAGS)
+
+include $(MODULE_TOPDIR)/include/Make/Module.make
+
+default: cmd	

Property changes on: vector/v.net.turntable/Makefile
___________________________________________________________________
Added: svn:executable
## -0,0 +1 ##
+*
\ No newline at end of property
Index: vector/v.net.salesman/v.net.salesman.html
===================================================================
--- vector/v.net.salesman/v.net.salesman.html	(revision 56146)
+++ vector/v.net.salesman/v.net.salesman.html	(working copy)
@@ -18,6 +18,8 @@
 <p>Points specified by category must be exactly on network nodes, and the 
 input vector map needs to be prepared with <em>v.net operation=connect</em>.
 
+<p>There is the option of applying the <a href="v.net.turntable.html">v.net.turntable</a> module on the input layer first. This means the input layer is expanded by turntable with costs of every possible turn on any possible node (intersection) in both directions. Note that after this expansion it is required to apply the -t flag. This flag enables additional parameters tlayer and tuclayer that are otherwise ignored.
+
 <h2>NOTES</h2>
 Arcs can be closed using cost = -1. 
 
Index: vector/v.net.salesman/main.c
===================================================================
--- vector/v.net.salesman/main.c	(revision 56146)
+++ vector/v.net.salesman/main.c	(working copy)
@@ -98,12 +98,12 @@
 int main(int argc, char **argv)
 {
     int i, j, k, ret, city, city1;
-    int nlines, type, ltype, afield, tfield, geo, cat;
+    int nlines, type, ltype, afield, nfield, tfield, tucfield, geo, cat;
     int node, node1, node2, line;
-    double **cost_cache;			/* pointer to array of pointers to arrays of cached costs */
-    struct Option *map, *output, *afield_opt, *tfield_opt, *afcol, *abcol,
-	*seq, *type_opt, *term_opt;
-    struct Flag *geo_f;
+    double **cost_cache;	/* pointer to array of pointers to arrays of cached costs */
+    struct Option *map, *output, *afield_opt, *nfield_opt, *afcol, *abcol,
+	*seq, *type_opt, *term_opt, *tfield_opt, *tucfield_opt;
+    struct Flag *geo_f, *turntable_f;
     struct GModule *module;
     struct Map_info Map, Out;
     struct ilist *TList;	/* list of terminal nodes */
@@ -127,7 +127,8 @@
     G_add_keyword(_("network"));
     G_add_keyword(_("salesman"));
     module->label =
-	_("Creates a cycle connecting given nodes (Traveling salesman problem).");
+	_
+	("Creates a cycle connecting given nodes (Traveling salesman problem).");
     module->description =
 	_("Note that TSP is NP-hard, heuristic algorithm is used by "
 	  "this module and created cycle may be sub optimal");
@@ -144,11 +145,21 @@
     afield_opt->key = "alayer";
     afield_opt->label = _("Arc layer");
 
+    nfield_opt = G_define_standard_option(G_OPT_V_FIELD);
+    nfield_opt->key = "nlayer";
+    nfield_opt->answer = "2";
+    nfield_opt->label = _("Node layer (used for cities)");
+
     tfield_opt = G_define_standard_option(G_OPT_V_FIELD);
-    tfield_opt->key = "nlayer";
-    tfield_opt->answer = "2";
-    tfield_opt->label = _("Node layer (used for cities)");
+    tfield_opt->key = "tlayer";
+    tfield_opt->answer = "3";
+    tfield_opt->label = _("Turntable layer");
 
+    tucfield_opt = G_define_standard_option(G_OPT_V_FIELD);
+    tucfield_opt->key = "tuclayer";
+    tucfield_opt->answer = "4";
+    tucfield_opt->label = _("Unique categories layer for turntable");
+
     afcol = G_define_option();
     afcol->key = "afcolumn";
     afcol->type = TYPE_STRING;
@@ -160,13 +171,15 @@
     abcol->key = "abcolumn";
     abcol->type = TYPE_STRING;
     abcol->required = NO;
-    abcol->description = _("EXPERIMENTAL: Arc backward direction cost column (number)");
+    abcol->description =
+	_("EXPERIMENTAL: Arc backward direction cost column (number)");
 
     seq = G_define_standard_option(G_OPT_F_OUTPUT);
     seq->key = "sequence";
     seq->type = TYPE_STRING;
     seq->required = NO;
-    seq->description = _("Name for output file holding node sequence (\"-\" for stdout)");
+    seq->description =
+	_("Name for output file holding node sequence (\"-\" for stdout)");
 
     term_opt = G_define_standard_option(G_OPT_V_CATS);
     term_opt->key = "ccats";
@@ -179,6 +192,11 @@
     geo_f->description =
 	_("Use geodesic calculation for longitude-latitude locations");
 
+    turntable_f = G_define_flag();
+    turntable_f->key = 't';
+    turntable_f->description = _("Use turntable"
+				 "(otherwise tuclayer and tlayer are ignored)");
+
     if (G_parser(argc, argv))
 	exit(EXIT_FAILURE);
 
@@ -186,7 +204,6 @@
     Points = Vect_new_line_struct();
 
     type = Vect_option_to_types(type_opt);
-    afield = atoi(afield_opt->answer);
 
     TList = Vect_new_list();
     List = Vect_new_list();
@@ -194,9 +211,8 @@
     StNodes = Vect_new_list();
 
     Clist = Vect_new_cat_list();
-    tfield = atoi(tfield_opt->answer);
     Vect_str_to_cat_list(term_opt->answer, Clist);
-    
+
     dstr = G__getenv("DEBUG");
 
     if (dstr != NULL)
@@ -220,28 +236,36 @@
 
     Vect_set_open_level(2);
     Vect_open_old(&Map, map->answer, "");
+
+    afield = Vect_get_field_number(&Map, afield_opt->answer);
+    nfield = Vect_get_field_number(&Map, nfield_opt->answer);
+    tfield = Vect_get_field_number(&Map, tfield_opt->answer);
+    tucfield = Vect_get_field_number(&Map, tucfield_opt->answer);
+
     nnodes = Vect_get_num_nodes(&Map);
     nlines = Vect_get_num_lines(&Map);
 
     /* Create list of terminals based on list of categories */
     for (i = 1; i <= nlines; i++) {
-	
+
 	ltype = Vect_get_line_type(&Map, i);
 	if (!(ltype & GV_POINT))
 	    continue;
 
 	Vect_read_line(&Map, Points, Cats, i);
-	node = Vect_find_node(&Map, Points->x[0], Points->y[0], Points->z[0], 0, 0);
+	node =
+	    Vect_find_node(&Map, Points->x[0], Points->y[0], Points->z[0], 0,
+			   0);
 	if (!node) {
 	    G_warning(_("Point is not connected to the network"));
 	    continue;
 	}
-	if (!(Vect_cat_get(Cats, tfield, &cat)))
+	if (!(Vect_cat_get(Cats, nfield, &cat)))
 	    continue;
 	if (Vect_cat_in_cat_list(cat, Clist)) {
 	    tsp_list_append(TList, node);
 	}
-	
+
     }
 
     ncities = TList->n_values;
@@ -262,9 +286,9 @@
     for (i = 0; i < ncities; i++) {
 	costs[i] = (COST *) G_malloc(ncities * sizeof(COST));
     }
-    cost_cache = (double **) G_malloc(ncities * sizeof(double *));
+    cost_cache = (double **)G_malloc(ncities * sizeof(double *));
     for (i = 0; i < ncities; i++) {
-	cost_cache[i] = (double *) G_malloc(ncities * sizeof(double));
+	cost_cache[i] = (double *)G_malloc(ncities * sizeof(double));
     }
     if (abcol->answer) {
 	bcosts = (COST **) G_malloc(ncities * sizeof(COST *));
@@ -278,8 +302,12 @@
     cycle = (int *)G_malloc((ncities + 1) * sizeof(int));	/* + 1 is for output cycle */
 
     /* Build graph */
-    Vect_net_build_graph(&Map, type, afield, 0, afcol->answer, abcol->answer, NULL,
-			 geo, 0);
+    if (turntable_f->answer)
+	Vect_net_ttb_build_graph(&Map, type, afield, 0, tfield, tucfield,
+				 afcol->answer, abcol->answer, NULL, geo, 0);
+    else
+	Vect_net_build_graph(&Map, type, afield, 0, afcol->answer,
+			     abcol->answer, NULL, geo, 0);
 
     /* Create sorted lists of costs */
     /* for a large number of cities this will become very slow, can not be fixed */
@@ -292,33 +320,47 @@
 	    if (i == j)
 		continue;
 
-	    ret =
-		Vect_net_shortest_path(&Map, cities[i], cities[j], NULL,
-				       &cost);
+	    if (turntable_f->answer)
+		ret =
+		    Vect_net_ttb_shortest_path(&Map, cities[i], 0, cities[j],
+					       0, tfield, tucfield, NULL,
+					       &cost);
+	    else
+		ret =
+		    Vect_net_shortest_path(&Map, cities[i], cities[j], NULL,
+					   &cost);
 
 	    if (ret == -1) {
 		double coor_x, coor_y, coor_z;
 		int cat1, cat2;
-		
-		Vect_get_node_coor(&Map, cities[i], &coor_x, &coor_y, &coor_z);
-		line = Vect_find_line(&Map, coor_x, coor_y, coor_z, GV_POINT, 0, 0, 0);
-		
+
+		Vect_get_node_coor(&Map, cities[i], &coor_x, &coor_y,
+				   &coor_z);
+		line =
+		    Vect_find_line(&Map, coor_x, coor_y, coor_z, GV_POINT, 0,
+				   0, 0);
+
 		if (!line)
 		    G_fatal_error(_("No point at node %d"), cities[i]);
 
 		Vect_read_line(&Map, Points, Cats, line);
-		if (!(Vect_cat_get(Cats, tfield, &cat1)))
-		    G_fatal_error(_("No category for point at node %d"), cities[i]);
+		if (!(Vect_cat_get(Cats, nfield, &cat1)))
+		    G_fatal_error(_("No category for point at node %d"),
+				  cities[i]);
 
-		Vect_get_node_coor(&Map, cities[j], &coor_x, &coor_y, &coor_z);
-		line = Vect_find_line(&Map, coor_x, coor_y, coor_z, GV_POINT, 0, 0, 0);
-		
+		Vect_get_node_coor(&Map, cities[j], &coor_x, &coor_y,
+				   &coor_z);
+		line =
+		    Vect_find_line(&Map, coor_x, coor_y, coor_z, GV_POINT, 0,
+				   0, 0);
+
 		if (!line)
 		    G_fatal_error(_("No point at node %d"), cities[j]);
 
 		Vect_read_line(&Map, Points, Cats, line);
-		if (!(Vect_cat_get(Cats, tfield, &cat2)))
-		    G_fatal_error(_("No category for point at node %d"), cities[j]);
+		if (!(Vect_cat_get(Cats, nfield, &cat2)))
+		    G_fatal_error(_("No category for point at node %d"),
+				  cities[j]);
 
 		G_fatal_error(_("Destination node [cat %d] is unreachable "
 				"from node [cat %d]"), cat1, cat2);
@@ -334,7 +376,7 @@
 	qsort((void *)costs[i], k, sizeof(COST), cmp);
     }
     G_percent(1, 1, 2);
-    
+
     if (bcosts) {
 	for (i = 0; i < ncities; i++) {
 	    /* this should be fast, no need for G_percent() */
@@ -342,7 +384,7 @@
 	    for (j = 0; j < ncities; j++) {
 		if (i == j)
 		    continue;
-		    
+
 		bcosts[i][k].city = j;
 		bcosts[i][k].cost = cost_cache[j][i];
 
@@ -351,7 +393,7 @@
 	    qsort((void *)bcosts[i], k, sizeof(COST), cmp);
 	}
     }
-    
+
     if (debug_level >= 2) {
 	/* debug: print sorted */
 	for (i = 0; i < ncities; i++) {
@@ -373,8 +415,7 @@
 	    city = i;
 	}
     }
-    G_debug(2, "biggest costs %d - %d", city,
-	    costs[city][ncities - 2].city);
+    G_debug(2, "biggest costs %d - %d", city, costs[city][ncities - 2].city);
 
     /* add these 2 cities to array */
     add_city(city, -1);
@@ -409,7 +450,7 @@
 			continue;	/* only used */
 		    /* directional costs k -> j */
 		    tmpcost += bcosts[j][k].cost;
-		    break;		/* first nearest */
+		    break;	/* first nearest */
 		}
 	    }
 
@@ -441,7 +482,7 @@
 	    /* get cost from directional cost cache */
 	    tcost = cost_cache[city][cycle[j + 1]];
 	    tmpcost += tcost;
-	    
+
 	    /* tmpcost must always be > 0 */
 
 	    G_debug(2, "? %d - %d cost = %f x %f", node1, node2, tmpcost,
@@ -454,7 +495,7 @@
 	}
 	add_city(city, city1);
     }
-    
+
     /* TODO: optimize tour (some Lin–Kernighan method) */
 
     if (debug_level >= 2) {
@@ -466,13 +507,20 @@
     }
 
     /* Create list of arcs */
-    cycle[ncities] = cycle[0];  /* close the cycle */
+    cycle[ncities] = cycle[0];	/* close the cycle */
     cost = 0.0;
     for (i = 0; i < ncities; i++) {
 	node1 = cities[cycle[i]];
 	node2 = cities[cycle[i + 1]];
 	G_debug(2, " %d -> %d", node1, node2);
-	ret = Vect_net_shortest_path(&Map, node1, node2, List, NULL);
+
+	if (turntable_f->answer)
+	    ret =
+		Vect_net_ttb_shortest_path(&Map, node1, 0, node2, 0, tfield,
+					   tucfield, List, NULL);
+	else
+	    ret = Vect_net_shortest_path(&Map, node1, node2, List, NULL);
+
 	cost += cost_cache[cycle[i]][cycle[i + 1]];
 	for (j = 0; j < List->n_values; j++) {
 	    line = abs(List->value[j]);
@@ -500,7 +548,7 @@
 	Vect_cat_get(Cats, afield, &cat);
 	G_debug(2, "%d. arc: cat %d", i + 1, cat);
     }
-    
+
     seq2stdout = 0;
     seqname = NULL;
     if (seq->answer) {
@@ -514,8 +562,7 @@
 
 	fp = fopen(seqname, "w");
 	if (!fp)
-	    G_fatal_error(_("Unable to open file '%s' for writing"),
-			  seqname);
+	    G_fatal_error(_("Unable to open file '%s' for writing"), seqname);
 
 	fprintf(fp, "sequence;category;cost_to_next\n");
     }
@@ -524,27 +571,28 @@
 
     k = 0;
     /* this writes out only user-selected nodes, not all visited nodes */
-    G_debug(2, "Nodes' categories (layer %d, %d nodes):", tfield,
-	    ncities);
+    G_debug(2, "Nodes' categories (layer %d, %d nodes):", nfield, ncities);
     for (i = 0; i < ncities; i++) {
 	double coor_x, coor_y, coor_z;
-	
+
 	node = cities[cycle[i]];
 	Vect_get_node_coor(&Map, node, &coor_x, &coor_y, &coor_z);
-	line = Vect_find_line(&Map, coor_x, coor_y, coor_z, GV_POINT, 0, 0, 0);
-	
+	line =
+	    Vect_find_line(&Map, coor_x, coor_y, coor_z, GV_POINT, 0, 0, 0);
+
 	if (!line)
 	    continue;
 
 	ltype = Vect_read_line(&Map, Points, Cats, line);
 	if (!(ltype & GV_POINT))
 	    continue;
-	if (!(Vect_cat_get(Cats, tfield, &cat)))
+	if (!(Vect_cat_get(Cats, nfield, &cat)))
 	    continue;
 	Vect_write_line(&Out, ltype, Points, Cats);
 	k++;
 	if (fp) {
-	    fprintf(fp, "%d;%d;%.3f\n", k, cat, cost_cache[cycle[i]][cycle[i + 1]]);
+	    fprintf(fp, "%d;%d;%.3f\n", k, cat,
+		    cost_cache[cycle[i]][cycle[i + 1]]);
 	}
 
 	G_debug(2, "%d. node: cat %d", k, cat);
Index: vector/Makefile
===================================================================
--- vector/Makefile	(revision 56146)
+++ vector/Makefile	(working copy)
@@ -54,6 +54,7 @@
 	v.net.spanningtree \
 	v.net.steiner \
 	v.net.timetable \
+	v.net.turntable \
 	v.net.visibility \
 	v.normal \
 	v.out.ascii \
Index: vector/v.net.steiner/main.c
===================================================================
--- vector/v.net.steiner/main.c	(revision 56146)
+++ vector/v.net.steiner/main.c	(working copy)
@@ -50,7 +50,8 @@
 int cmp(const void *, const void *);
 
 /* Init all costs to/from given node */
-int init_node_costs(struct Map_info *Map, int from)
+int init_node_costs(struct Map_info *Map, int from, int use_uturns,
+		    int tfield, int tucfield)
 {
     int to, ret, row, col;
     double cost;
@@ -60,7 +61,14 @@
     for (to = 1; to <= nnodes; to++) {
 	if (from == to)
 	    continue;
-	ret = Vect_net_shortest_path(Map, from, to, NULL, &cost);
+
+	if (use_uturns)
+	    ret =
+		Vect_net_ttb_shortest_path(Map, from, 0, to, 0, tfield,
+					   tucfield, NULL, &cost);
+	else
+	    ret = Vect_net_shortest_path(Map, from, to, NULL, &cost);
+
 	if (ret == -1) {
 	    /* G_warning ( "Destination node %d is unreachable from node %d\n", to, from); */
 	    cost = -2;
@@ -124,7 +132,7 @@
 	double *cst, double max_cst,	/* cost, maximum cost */
 	struct ilist *AList, struct ilist *NList,	/* list of arcs/nodes in ST */
 	int sp,			/* Steiner point (node) to be tested with terminals, (0 = ignore) */
-	int rebuild)
+	int rebuild, int use_turns, int tfield, int tucfield)
 {				/* rebuild the sorted list of costs for terminals */
     int i, j, node1, node2, ret, com1, com2, t1, t2, line;
     static int k;
@@ -292,7 +300,12 @@
 	if (AList != NULL) {
 	    node1 = trms[t1];
 	    node2 = trms[t2];
-	    ret = Vect_net_shortest_path(Map, node1, node2, List, NULL);
+	    if (use_turns)
+		ret =
+		    Vect_net_ttb_shortest_path(Map, node1, 0, node2, 0,
+					       tfield, tucfield, List, NULL);
+	    else
+		ret = Vect_net_shortest_path(Map, node1, node2, List, NULL);
 	    for (j = 0; j < List->n_values; j++) {
 		Vect_list_append(AList, abs(List->value[j]));
 	    }
@@ -320,11 +333,11 @@
 int main(int argc, char **argv)
 {
     int i, j, k, ret;
-    int nlines, type, ltype, afield, tfield, geo, cat;
+    int nlines, type, ltype, afield, nfield, tfield, tucfield, geo, cat;
     int sp, nsp, nspused, node, line;
-    struct Option *map, *output, *afield_opt, *tfield_opt, *afcol, *type_opt,
-	*term_opt, *nsp_opt;
-    struct Flag *geo_f;
+    struct Option *map, *output, *afield_opt, *nfield_opt, *afcol, *type_opt,
+	*term_opt, *nsp_opt, *tfield_opt, *tucfield_opt;
+    struct Flag *geo_f, *turntable_f;
     struct GModule *module;
     struct Map_info Map, Out;
     int *testnode;		/* array all nodes: 1 - should be tested as Steiner, 
@@ -365,11 +378,21 @@
     afield_opt->answer = "1";
     afield_opt->label = _("Arc layer");
 
+    nfield_opt = G_define_standard_option(G_OPT_V_FIELD);
+    nfield_opt->key = "nlayer";
+    nfield_opt->answer = "2";
+    nfield_opt->label = _("Node layer (used for terminals)");
+
     tfield_opt = G_define_standard_option(G_OPT_V_FIELD);
-    tfield_opt->key = "nlayer";
-    tfield_opt->answer = "2";
-    tfield_opt->label = _("Node layer (used for terminals)");
+    tfield_opt->key = "tlayer";
+    tfield_opt->answer = "3";
+    tfield_opt->label = _("Turntable layer");
 
+    tucfield_opt = G_define_standard_option(G_OPT_V_FIELD);
+    tucfield_opt->key = "tuclayer";
+    tucfield_opt->answer = "4";
+    tucfield_opt->label = _("Unique categories layer for turntable");
+
     afcol = G_define_option();
     afcol->key = "acolumn";
     afcol->type = TYPE_STRING;
@@ -388,13 +411,19 @@
     nsp_opt->required = NO;
     nsp_opt->multiple = NO;
     nsp_opt->answer = "-1";
-    nsp_opt->description = _("Number of steiner points (-1 for all possible)");
+    nsp_opt->description =
+	_("Number of steiner points (-1 for all possible)");
 
     geo_f = G_define_flag();
     geo_f->key = 'g';
     geo_f->description =
 	_("Use geodesic calculation for longitude-latitude locations");
 
+    turntable_f = G_define_flag();
+    turntable_f->key = 't';
+    turntable_f->description = _("Use turntable"
+				 "(otherwise tuclayer and tlayer are ignored)");
+
     if (G_parser(argc, argv))
 	exit(EXIT_FAILURE);
 
@@ -402,14 +431,12 @@
     Points = Vect_new_line_struct();
 
     type = Vect_option_to_types(type_opt);
-    afield = atoi(afield_opt->answer);
 
     TList = Vect_new_list();
     StArcs = Vect_new_list();
     StNodes = Vect_new_list();
 
     Clist = Vect_new_cat_list();
-    tfield = atoi(tfield_opt->answer);
     Vect_str_to_cat_list(term_opt->answer, Clist);
 
     G_debug(1, "Imput categories:\n");
@@ -429,6 +456,12 @@
     nnodes = Vect_get_num_nodes(&Map);
     nlines = Vect_get_num_lines(&Map);
 
+    afield = Vect_get_field_number(&Map, afield_opt->answer);
+    nfield = Vect_get_field_number(&Map, nfield_opt->answer);
+    tfield = Vect_get_field_number(&Map, tfield_opt->answer);
+    tucfield = Vect_get_field_number(&Map, tucfield_opt->answer);
+
+
     /* Create list of terminals based on list of categories */
     for (i = 1; i <= nlines; i++) {
 	ltype = Vect_get_line_type(&Map, i);
@@ -436,12 +469,14 @@
 	    continue;
 
 	Vect_read_line(&Map, Points, Cats, i);
-	node = Vect_find_node(&Map, Points->x[0], Points->y[0], Points->z[0], 0, 0);
+	node =
+	    Vect_find_node(&Map, Points->x[0], Points->y[0], Points->z[0], 0,
+			   0);
 	if (!node) {
 	    G_warning(_("Point is not connected to the network"));
 	    continue;
 	}
-	if (!(Vect_cat_get(Cats, tfield, &cat)))
+	if (!(Vect_cat_get(Cats, nfield, &cat)))
 	    continue;
 	if (Vect_cat_in_cat_list(cat, Clist)) {
 	    Vect_list_append(TList, i);
@@ -499,12 +534,17 @@
     }
 
     /* Build graph */
-    Vect_net_build_graph(&Map, type, afield, 0, afcol->answer, NULL, NULL,
-			 geo, 0);
+    if (turntable_f->answer)
+	Vect_net_ttb_build_graph(&Map, type, afield, 0, tfield, tucfield,
+				 afcol->answer, NULL, NULL, geo, 0);
+    else
+	Vect_net_build_graph(&Map, type, afield, 0, afcol->answer,
+			     NULL, NULL, geo, 0);
 
     /* Init costs for all terminals */
     for (i = 0; i < nterms; i++)
-	init_node_costs(&Map, terms[i]);
+	init_node_costs(&Map, terms[i], turntable_f->answer, tfield,
+			tucfield);
 
     /* Test if all terminal may be connected */
     for (i = 1; i < nterms; i++) {
@@ -530,7 +570,7 @@
 		"of Steiner point candidates"), j);
 
     /* calc costs for terminals MST */
-    ret = mst(&Map, terms, nterms, &cost, PORT_DOUBLE_MAX, NULL, NULL, 0, 1);	/* no StP, rebuild */
+    ret = mst(&Map, terms, nterms, &cost, PORT_DOUBLE_MAX, NULL, NULL, 0, 1, turntable_f->answer, tfield, tucfield);	/* no StP, rebuild */
     G_message(_("MST costs = %f"), cost);
 
     /* Go through all nodes and try to use as steiner points -> find that which saves most costs */
@@ -547,7 +587,7 @@
 	    }
 	    ret =
 		mst(&Map, terms, nterms + j, &tmpcost, cost, NULL, NULL, i,
-		    0);
+		    0, turntable_f->answer, tfield, tucfield);
 	    G_debug(2, "cost = %f x %f\n", tmpcost, cost);
 	    if (tmpcost < cost) {	/* sp candidate */
 		G_debug(3,
@@ -561,13 +601,13 @@
 	    G_message(_("Steiner point at node [%d] was added "
 			"to terminals (MST costs = %f)"), sp, cost);
 	    terms[nterms + j] = sp;
-	    init_node_costs(&Map, sp);
+	    init_node_costs(&Map, sp, turntable_f->answer, tfield, tucfield);
 	    testnode[sp] = 0;
 	    nspused++;
 	    /* rebuild for nex cycle */
 	    ret =
 		mst(&Map, terms, nterms + nspused, &tmpcost, PORT_DOUBLE_MAX,
-		    NULL, NULL, 0, 1);
+		    NULL, NULL, 0, 1, turntable_f->answer, tfield, tucfield);
 	}
 	else {			/* no steiner found */
 	    G_message(_("No Steiner point found -> leaving cycle"));
@@ -581,7 +621,7 @@
     /* Build lists of arcs and nodes for final version */
     ret =
 	mst(&Map, terms, nterms + nspused, &cost, PORT_DOUBLE_MAX, StArcs,
-	    StNodes, 0, 0);
+	    StNodes, 0, 0, turntable_f->answer, tfield, tucfield);
 
     /* Calculate true costs, which may be lower than MST if steiner points were not used */
 
@@ -613,7 +653,7 @@
 
     fprintf(stdout, "\n\n");
 
-    fprintf(stdout, "Nodes' categories (layer %d, %d nodes):\n", tfield,
+    fprintf(stdout, "Nodes' categories (layer %d, %d nodes):\n", nfield,
 	    StNodes->n_values);
 
     k = 0;
@@ -621,22 +661,22 @@
     for (i = 0; i < StNodes->n_values; i++) {
 	double x, y, z;
 	struct bound_box box;
-	
+
 	node = StNodes->value[i];
-	
+
 	Vect_get_node_coor(&Map, node, &x, &y, &z);
 	box.E = box.W = x;
 	box.N = box.S = y;
 	box.T = box.B = z;
 	Vect_select_lines_by_box(&Map, &box, GV_POINT, pointlist);
-	
+
 	nlines = Vect_get_node_n_lines(&Map, node);
 	for (j = 0; j < pointlist->n_values; j++) {
 	    line = pointlist->id[j];
 	    ltype = Vect_read_line(&Map, Points, Cats, line);
 	    if (!(ltype & GV_POINT))
 		continue;
-	    if (!(Vect_cat_get(Cats, tfield, &cat)))
+	    if (!(Vect_cat_get(Cats, nfield, &cat)))
 		continue;
 	    Vect_write_line(&Out, ltype, Points, Cats);
 	    if (k > 0)
Index: vector/v.net.steiner/v.net.steiner.html
===================================================================
--- vector/v.net.steiner/v.net.steiner.html	(revision 56146)
+++ vector/v.net.steiner/v.net.steiner.html	(working copy)
@@ -21,6 +21,7 @@
 <p>Points representing nodes must be exactly on network nodes, and the 
 input vector map needs to be prepared with <em>v.net operation=connect</em>.
 
+<p>There is the option of applying the <a href="v.net.turntable.html">v.net.turntable</a> module on the input layer first. This means the input layer is expanded by turntable with costs of every possible turn on any possible node (intersection) in both directions. Note that after this expansion it is required to apply the -t flag. This flag enables additional parameters tlayer and tuclayer that are otherwise ignored.
 
 <h2>EXAMPLE</h2>
 
Index: vector/v.net.alloc/main.c
===================================================================
--- vector/v.net.alloc/main.c	(revision 56146)
+++ vector/v.net.alloc/main.c	(working copy)
@@ -37,14 +37,15 @@
 
 int main(int argc, char **argv)
 {
-    int i, ret, center, line, center1, center2;
-    int nlines, nnodes, type, ltype, afield, nfield, geo, cat;
+    int i, j, ret, center, line, center1, center2;
+    int nlines, nnodes, type, ltype, afield, nfield, geo, cat, tfield,
+	tucfield;
     int node1, node2;
     double cost, e1cost, e2cost, n1cost, n2cost, s1cost, s2cost, l, l1, l2;
     struct Option *map, *output;
     struct Option *afield_opt, *nfield_opt, *afcol, *abcol, *ncol, *type_opt,
-	*term_opt;
-    struct Flag *geo_f;
+	*term_opt, *tfield_opt, *tucfield_opt;
+    struct Flag *geo_f, *turntable_f;
     struct GModule *module;
     struct Map_info Map, Out;
     struct cat_list *catlist;
@@ -85,6 +86,16 @@
     nfield_opt->answer = "2";
     nfield_opt->label = _("Node layer");
 
+    tfield_opt = G_define_standard_option(G_OPT_V_FIELD);
+    tfield_opt->key = "tlayer";
+    tfield_opt->answer = "3";
+    tfield_opt->label = _("Turntable layer");
+
+    tucfield_opt = G_define_standard_option(G_OPT_V_FIELD);
+    tucfield_opt->key = "tuclayer";
+    tucfield_opt->answer = "4";
+    tucfield_opt->label = _("Unique categories layer for turntable");
+
     afcol = G_define_option();
     afcol->key = "afcolumn";
     afcol->type = TYPE_STRING;
@@ -117,6 +128,11 @@
     geo_f->description =
 	_("Use geodesic calculation for longitude-latitude locations");
 
+    turntable_f = G_define_flag();
+    turntable_f->key = 't';
+    turntable_f->description = _("Use turntable"
+				 "(otherwise tuclayer and tlayer are ignored)");
+
     if (G_parser(argc, argv))
 	exit(EXIT_FAILURE);
 
@@ -141,24 +157,34 @@
 
     afield = Vect_get_field_number(&Map, afield_opt->answer);
     nfield = Vect_get_field_number(&Map, nfield_opt->answer);
+    tfield = Vect_get_field_number(&Map, tfield_opt->answer);
+    tucfield = Vect_get_field_number(&Map, tucfield_opt->answer);
 
     /* Build graph */
-    Vect_net_build_graph(&Map, type, afield, nfield, afcol->answer,
-			 abcol->answer, ncol->answer, geo, 0);
+    if (turntable_f->answer)
+	Vect_net_ttb_build_graph(&Map, type, afield, nfield, tfield, tucfield,
+				 afcol->answer, abcol->answer, ncol->answer,
+				 geo, 0);
+    else
+	Vect_net_build_graph(&Map, type, afield, nfield, afcol->answer,
+			     abcol->answer, ncol->answer, geo, 0);
 
+
     nnodes = Vect_get_num_nodes(&Map);
     nlines = Vect_get_num_lines(&Map);
 
     /* Create list of centers based on list of categories */
     for (i = 1; i <= nlines; i++) {
 	int node;
-	
+
 	ltype = Vect_get_line_type(&Map, i);
 	if (!(ltype & GV_POINT))
 	    continue;
 
 	Vect_read_line(&Map, Points, Cats, i);
-	node = Vect_find_node(&Map, Points->x[0], Points->y[0], Points->z[0], 0, 0);
+	node =
+	    Vect_find_node(&Map, Points->x[0], Points->y[0], Points->z[0], 0,
+			   0);
 	if (!node) {
 	    G_warning(_("Point is not connected to the network"));
 	    continue;
@@ -192,13 +218,22 @@
 	G_warning(_("Not enough centers for selected nlayer. "
 		    "Nothing will be allocated."));
 
-    /* alloc and reset space for all nodes */
-    Nodes = (NODE *) G_calloc((nnodes + 1), sizeof(NODE));
-    for (i = 1; i <= nnodes; i++) {
-	Nodes[i].center = -1;
+    if (turntable_f->answer) {
+	/* alloc and reset space for all lines */
+	Nodes = (NODE *) G_calloc((nlines * 2 + 2), sizeof(NODE));
+	for (i = 2; i <= (nlines * 2 + 2); i++) {
+	    Nodes[i].center = -1;
+	}
+
     }
+    else {
+	/* alloc and reset space for all nodes */
+	Nodes = (NODE *) G_calloc((nnodes + 1), sizeof(NODE));
+	for (i = 1; i <= nnodes; i++) {
+	    Nodes[i].center = -1;
+	}
+    }
 
-
     /* Fill Nodes by nearest center and costs from that center */
     G_message(_("Calculating costs from centers ..."));
 
@@ -208,32 +243,76 @@
 	Vect_net_get_node_cost(&Map, node1, &n1cost);
 	G_debug(2, "center = %d node = %d cat = %d", center, node1,
 		Centers[center].cat);
-	for (node2 = 1; node2 <= nnodes; node2++) {
-	    G_debug(5, "  node1 = %d node2 = %d", node1, node2);
-	    Vect_net_get_node_cost(&Map, node2, &n2cost);
-	    if (n2cost == -1) {
-		continue;
-	    }			/* closed, left it as not attached */
 
-	    ret = Vect_net_shortest_path(&Map, node1, node2, NULL, &cost);
-	    if (ret == -1) {
-		continue;
-	    }			/* node unreachable */
+	if (turntable_f->answer)
+	    for (line = 1; line <= nlines; line++) {
+		G_debug(5, "  node1 = %d line = %d", node1, line);
+		Vect_net_get_node_cost(&Map, line, &n2cost);
+		/* closed, left it as not attached */
 
-	    /* We must add center node costs (not calculated by Vect_net_shortest_path() ), but
-	     *  only if center and node are not identical, because at the end node cost is add later */
-	    if (node1 != node2)
-		cost += n1cost;
+		if (Vect_read_line(&Map, Points, Cats, line) < 0)
+		    continue;
+		if (Vect_get_line_type(&Map, line) != GV_LINE)
+		    continue;
+		if (!Vect_cat_get(Cats, tucfield, &cat))
+		    continue;
 
-	    G_debug(5,
-		    "Arc nodes: %d %d cost: %f (x old cent: %d old cost %f",
-		    node1, node2, cost, Nodes[node2].center,
-		    Nodes[node2].cost);
-	    if (Nodes[node2].center == -1 || cost < Nodes[node2].cost) {
-		Nodes[node2].cost = cost;
-		Nodes[node2].center = center;
+		for (j = 0; j < 2; j++) {
+			if(j == 1)
+				cat *= -1;
+			
+		    ret =
+			Vect_net_ttb_shortest_path(&Map, node1, 0, cat, 1,
+						   tfield, tucfield, NULL,
+						   &cost);
+		    if (ret == -1) {
+			continue;
+		    }		/* node unreachable */
+
+		    /* We must add center node costs (not calculated by Vect_net_shortest_path() ), but
+		     *  only if center and node are not identical, because at the end node cost is add later */
+		    /*TODO issue in turns support */
+		    if (ret != 1)
+			cost += n1cost;
+
+		    G_debug(5,
+			    "Arc nodes: %d %d cost: %f (x old cent: %d old cost %f",
+			    node1, line, cost, Nodes[line * 2 + j].center,
+			    Nodes[line * 2 + j].cost);
+		    if (Nodes[line * 2 + j].center == -1 ||
+			cost < Nodes[line * 2 + j].cost) {
+			Nodes[line * 2 + j].cost = cost;
+			Nodes[line * 2 + j].center = center;
+		    }
+		}
 	    }
-	}
+	else
+	    for (node2 = 1; node2 <= nnodes; node2++) {
+		G_debug(5, "  node1 = %d node2 = %d", node1, node2);
+		Vect_net_get_node_cost(&Map, node2, &n2cost);
+		if (n2cost == -1) {
+		    continue;
+		}		/* closed, left it as not attached */
+
+		ret = Vect_net_shortest_path(&Map, node1, node2, NULL, &cost);
+		if (ret == -1) {
+		    continue;
+		}		/* node unreachable */
+
+		/* We must add center node costs (not calculated by Vect_net_shortest_path() ), but
+		 *  only if center and node are not identical, because at the end node cost is add later */
+		if (node1 != node2)
+		    cost += n1cost;
+
+		G_debug(5,
+			"Arc nodes: %d %d cost: %f (x old cent: %d old cost %f",
+			node1, node2, cost, Nodes[node2].center,
+			Nodes[node2].cost);
+		if (Nodes[node2].center == -1 || cost < Nodes[node2].cost) {
+		    Nodes[node2].cost = cost;
+		    Nodes[node2].center = center;
+		}
+	    }
     }
     G_percent(1, 1, 1);
 
@@ -247,21 +326,32 @@
 	if (!(ltype & type)) {
 	    continue;
 	}
-	Vect_get_line_nodes(&Map, line, &node1, &node2);
-	center1 = Nodes[node1].center;
-	center2 = Nodes[node2].center;
-	s1cost = Nodes[node1].cost;
-	s2cost = Nodes[node2].cost;
-	G_debug(3, "Line %d:", line);
-	G_debug(3, "Arc centers: %d %d (nodes: %d %d)", center1, center2,
-		node1, node2);
 
-	Vect_net_get_node_cost(&Map, node1, &n1cost);
-	Vect_net_get_node_cost(&Map, node2, &n2cost);
+	if (turntable_f->answer) {
+	    center1 = Nodes[line * 2].center;
+	    center2 = Nodes[line * 2 + 1].center;
+	    s1cost = Nodes[line * 2].cost;
+	    s2cost = Nodes[line * 2 + 1].cost;
+	    n1cost = n2cost = 0;
+	}
+	else {
+	    Vect_get_line_nodes(&Map, line, &node1, &node2);
+	    center1 = Nodes[node1].center;
+	    center2 = Nodes[node2].center;
+	    s1cost = Nodes[node1].cost;
+	    s2cost = Nodes[node2].cost;
 
+	    Vect_net_get_node_cost(&Map, node1, &n1cost);
+	    Vect_net_get_node_cost(&Map, node2, &n2cost);
+	}
+
 	Vect_net_get_line_cost(&Map, line, GV_FORWARD, &e1cost);
 	Vect_net_get_line_cost(&Map, line, GV_BACKWARD, &e2cost);
 
+	G_debug(3, "Line %d:", line);
+	G_debug(3, "Arc centers: %d %d (nodes: %d %d)", center1, center2,
+		node1, node2);
+
 	G_debug(3, "  s1cost = %f n1cost = %f e1cost = %f", s1cost, n1cost,
 		e1cost);
 	G_debug(3, "  s2cost = %f n2cost = %f e2cost = %f", s2cost, n2cost,
@@ -340,7 +430,8 @@
 		    /* First segment */
 		    ret = Vect_line_segment(Points, 0, l1, SPoints);
 		    if (ret == 0) {
-			G_warning(_("Cannot get line segment, segment out of line"));
+			G_warning(_
+				  ("Cannot get line segment, segment out of line"));
 		    }
 		    else {
 			cat = Centers[center1].cat;
@@ -351,7 +442,8 @@
 		    /* Second segment */
 		    ret = Vect_line_segment(Points, l1, l, SPoints);
 		    if (ret == 0) {
-			G_warning(_("Cannot get line segment, segment out of line"));
+			G_warning(_
+				  ("Cannot get line segment, segment out of line"));
 		    }
 		    else {
 			Vect_reset_cats(Cats);
Index: vector/v.net.alloc/v.net.alloc.html
===================================================================
--- vector/v.net.alloc/v.net.alloc.html	(revision 56146)
+++ vector/v.net.alloc/v.net.alloc.html	(working copy)
@@ -14,6 +14,7 @@
 For areas, costs will be calculated along boundary lines.
 <p>The input vector needs to be prepared with <em>v.net operation=connect</em> 
 in order to connect points representing center nodes to the network.
+<p>There is the option of applying the <a href="v.net.turntable.html">v.net.turntable</a> module on the input layer first. This means the input layer is expanded by turntable with costs of every possible turn on any possible node (intersection) in both directions. Note that after this expansion it is required to apply the -t flag. This flag enables additional parameters tlayer and tuclayer that are otherwise ignored.
 
 <h2>NOTES</h2>
 
Index: lib/vector/Vlib/net.c
===================================================================
--- lib/vector/Vlib/net.c	(revision 56146)
+++ lib/vector/Vlib/net.c	(working copy)
@@ -5,7 +5,7 @@
  *
  * Higher level functions for reading/writing/manipulating vectors.
  *
- * (C) 2001-2009 by the GRASS Development Team
+ * (C) 2001-2009, 2013 by the GRASS Development Team
  *
  * This program is free software under the GNU General Public License
  * (>=v2).  Read the file COPYING that comes with GRASS for details.
@@ -22,6 +22,13 @@
 
 static int From_node;		/* from node set in SP and used by clipper for first arc */
 
+static int next_virtual_ln = -1; /* TODO VERY UGLY ugly, must be changed!!!
+									This variable is used when we are adding new virtual 
+									nodes and lines during shortest path analysis using turntable.
+									Whe have to now which number to assign to virtual features.
+									This number is stored in this variable. We are unable to delete virtual 
+									features, because GRASS GIS uses DGLib in V1 mode which does not allow such a kind of operation. */
+
 static int clipper(dglGraph_s * pgraph,
 		   dglSPClipInput_s * pargIn,
 		   dglSPClipOutput_s * pargOut, void *pvarg)
@@ -60,6 +67,562 @@
 }
 
 /*!
+   \brief Build network graph extended with turntable.
+
+   Internal format for edge costs is integer, costs are multiplied
+   before conversion to int by 1000 and for lenghts LL without geo flag by 1000000.
+   The same multiplication factor is used for nodes.
+   Costs in database column may be 'integer' or 'double precision' number >= 0
+   or -1 for infinity i.e. arc or node is closed and cannot be traversed
+   If record in table is not found for arcs, arc is skip.
+   If record in table is not found for node, costs for node are set to 0.
+
+   \param Map vector map
+   \param ltype line type for arcs
+   \param afield arc costs field (if 0, use length)
+   \param nfield node costs field (if 0, do not use node costs)
+   \param tfield number of layer where turntable is attached 
+   \param tucfield number of layer with unique categories used in turntable 
+   \param afcol column with forward costs for arc
+   \param abcol column with backward costs for arc (if NULL, back costs = forward costs), 
+   \param ncol column with costs for nodes (if NULL, do not use node costs), 
+   \param geo use geodesic calculation for length (LL), 
+   \param algorithm not used (in future code for algorithm)
+
+   \return 0 on success, 1 on error
+ */
+
+int
+Vect_net_ttb_build_graph(struct Map_info *Map,
+			 int ltype,
+			 int afield,
+			 int nfield,
+			 int tfield,
+			 int tucfield,
+			 const char *afcol,
+			 const char *abcol,
+			 const char *ncol, int geo, int algorithm)
+{
+    int i, j, from, to, line, nlines, nnodes, ret, type, cat, skipped, cfound;
+    int dofw, dobw;
+    struct line_pnts *Points;
+    struct line_cats *Cats;
+    double dcost, bdcost, ll;
+    int cost, bcost;
+    dglGraph_s *gr;
+    dglInt32_t opaqueset[16] =
+	{ 360000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+    struct field_info *Fi;
+    dbDriver *driver = NULL;
+    dbDriver *ttbdriver = NULL;
+    dbHandle handle;
+    dbString stmt;
+    dbColumn *Column;
+    dbCatValArray fvarr, bvarr;
+    int fctype = 0, bctype = 0, nrec, nturns;
+
+
+    double x, y, z;
+    struct bound_box box;
+    struct boxlist *List;
+
+    /*TODO attributes of turntable shoud be in stored one place */
+    const char *tcols[] =
+	{ "cat", "ln_from", "ln_to", "cost", "isec", NULL
+    };
+    dbCatValArray tvarrs[5] = { };
+    int tctype[5] = { 0 };
+    int tucfield_idx;
+
+    int node_pt_id, turn_cat;
+    int isec;
+
+    /* TODO int costs -> double (waiting for dglib) */
+    G_debug(1, "Vect_build_graph(): ltype = %d, afield = %d, nfield = %d",
+	    ltype, afield, nfield);
+    G_debug(1, "    afcol = %s, abcol = %s, ncol = %s", afcol, abcol, ncol);
+
+    G_message(_("Building graph..."));
+
+    Map->dgraph.line_type = ltype;
+
+    Points = Vect_new_line_struct();
+    Cats = Vect_new_cats_struct();
+
+    ll = 0;
+    if (G_projection() == 3)
+	ll = 1;			/* LL */
+
+    if (afcol == NULL && ll && !geo)
+	Map->dgraph.cost_multip = 1000000;
+    else
+	Map->dgraph.cost_multip = 1000;
+
+    nlines = Vect_get_num_lines(Map);
+    nnodes = Vect_get_num_nodes(Map);
+
+    gr = &(Map->dgraph.graph_s);
+
+    /* Allocate space for costs, later replace by functions reading costs from graph */
+    Map->dgraph.edge_fcosts =
+	(double *)G_malloc((nlines + 1) * sizeof(double));
+    Map->dgraph.edge_bcosts =
+	(double *)G_malloc((nlines + 1) * sizeof(double));
+    Map->dgraph.node_costs =
+	(double *)G_malloc((nnodes + 1) * sizeof(double));
+    /* Set to -1 initially */
+    for (i = 1; i <= nlines; i++) {
+	Map->dgraph.edge_fcosts[i] = -1;	/* forward */
+	Map->dgraph.edge_bcosts[i] = -1;	/* backward */
+    }
+    for (i = 1; i <= nnodes; i++) {
+	Map->dgraph.node_costs[i] = 0;
+    }
+
+
+    dglInitialize(gr, (dglByte_t) 1, sizeof(dglInt32_t), (dglInt32_t) 0,
+		  opaqueset);
+
+    if (gr == NULL)
+	G_fatal_error(_("Unable to build network graph"));
+
+    db_init_handle(&handle);
+    db_init_string(&stmt);
+
+    /* TODO shold work without abcol and afcol (use length)? */
+    if (abcol != NULL && afcol == NULL)
+	G_fatal_error(_("Forward costs column not specified"));
+
+    /* --- Add arcs --- */
+    /* Open db connection */
+
+    /* Get field info */
+    if (tfield < 1)
+	G_fatal_error(_("Turntable field < 1"));
+    Fi = Vect_get_field(Map, tfield);
+    if (Fi == NULL)
+	G_fatal_error(_("Database connection not defined for layer %d"),
+		      tfield);
+
+    /* Open database */
+    ttbdriver = db_start_driver_open_database(Fi->driver, Fi->database);
+    if (ttbdriver == NULL)
+	G_fatal_error(_("Unable to open database <%s> by driver <%s>"),
+		      Fi->database, Fi->driver);
+
+    i = 0;
+    while (tcols[i]) {
+	/* Load costs to array */
+	if (db_get_column(ttbdriver, Fi->table, tcols[i], &Column) != DB_OK)
+	    G_fatal_error(_("Column <%s> not found in table <%s>"),
+			  tcols[i], Fi->table);
+
+	tctype[i] = db_sqltype_to_Ctype(db_get_column_sqltype(Column));
+
+	if ((tctype[i] == DB_C_TYPE_INT || tctype[i] == DB_C_TYPE_DOUBLE) &&
+	    tcols[i] == "cost") ;
+	else if (tctype[i] == DB_C_TYPE_INT) ;
+	else
+	    G_fatal_error(_
+			  ("Data type of column <%s> not supported (must be numeric)"),
+			  tcols[i]);
+
+	db_CatValArray_init(&tvarrs[i]);
+	nturns =
+	    db_select_CatValArray(ttbdriver, Fi->table, Fi->key, tcols[i],
+				  NULL, &tvarrs[i]);
+	++i;
+    }
+
+    G_debug(1, "forward costs: nrec = %d", nrec);
+
+    /* Set node attributes */
+    G_message("Register nodes");
+    if (ncol != NULL) {
+
+	G_debug(2, "Set nodes' costs");
+	if (nfield < 1)
+	    G_fatal_error("Node field < 1");
+
+	G_message(_("Setting node costs..."));
+
+	Fi = Vect_get_field(Map, nfield);
+	if (Fi == NULL)
+	    G_fatal_error(_("Database connection not defined for layer %d"),
+			  nfield);
+
+	driver = db_start_driver_open_database(Fi->driver, Fi->database);
+	if (driver == NULL)
+	    G_fatal_error(_("Unable to open database <%s> by driver <%s>"),
+			  Fi->database, Fi->driver);
+
+	/* Load costs to array */
+	if (db_get_column(driver, Fi->table, ncol, &Column) != DB_OK)
+	    G_fatal_error(_("Column <%s> not found in table <%s>"),
+			  ncol, Fi->table);
+
+	fctype = db_sqltype_to_Ctype(db_get_column_sqltype(Column));
+
+	if (fctype != DB_C_TYPE_INT && fctype != DB_C_TYPE_DOUBLE)
+	    G_fatal_error(_
+			  ("Data type of column <%s> not supported (must be numeric)"),
+			  ncol);
+
+	db_CatValArray_init(&fvarr);
+
+	nrec =
+	    db_select_CatValArray(driver, Fi->table, Fi->key, ncol, NULL,
+				  &fvarr);
+	G_debug(1, "node costs: nrec = %d", nrec);
+
+	tucfield_idx = Vect_cidx_get_field_index(Map, tucfield);
+    }
+
+    List = Vect_new_boxlist(0);
+
+    if (ncol != NULL)
+	for (i = 1; i <= nnodes; i++) {
+	    /* TODO: what happens if we set attributes of not existing node (skipped lines,
+	     *       nodes without lines) */
+
+	    /* select points at node */
+	    Vect_get_node_coor(Map, i, &x, &y, &z);
+	    box.E = box.W = x;
+	    box.N = box.S = y;
+	    box.T = box.B = z;
+	    Vect_select_lines_by_box(Map, &box, GV_POINT, List);
+
+	    G_debug(2, "  node = %d nlines = %d", i, List->n_values);
+	    cfound = 0;
+	    dcost = 0;
+
+	    for (j = 0; j < List->n_values; j++) {
+		line = List->id[j];
+		G_debug(2, "  line (%d) = %d", j, line);
+		type = Vect_read_line(Map, NULL, Cats, line);
+		if (!(type & GV_POINT))
+		    continue;
+		if (Vect_cat_get(Cats, nfield, &cat)) {	/* point with category of field found */
+		    /* Set costs */
+		    if (fctype == DB_C_TYPE_INT) {
+			ret =
+			    db_CatValArray_get_value_int(&fvarr, cat, &cost);
+			dcost = cost;
+		    }
+		    else {	/* DB_C_TYPE_DOUBLE */
+			ret =
+			    db_CatValArray_get_value_double(&fvarr, cat,
+							    &dcost);
+		    }
+		    if (ret != DB_OK) {
+			G_warning(_
+				  ("Database record for node %d (cat = %d) not found "
+				   "(cost set to 0)"), i, cat);
+		    }
+		    cfound = 1;
+		    break;
+		}
+	    }
+	    Map->dgraph.node_costs[i] = dcost;
+	}
+
+    G_message("Building turns graph...");
+
+    for (i = 1; i <= nturns; i++) {
+	/* select points at node */
+
+	/* TODO use cursors? */
+	db_CatValArray_get_value_int(&tvarrs[0], i, &turn_cat);
+
+	db_CatValArray_get_value_int(&tvarrs[1], i, &from);
+	db_CatValArray_get_value_int(&tvarrs[2], i, &to);
+
+	db_CatValArray_get_value_int(&tvarrs[4], i, &isec);
+
+	    //TODO optimization same for two values
+	    if (ncol != NULL) {
+		/*TODO check if it is only point, which belongs to the cat. */
+		if (Vect_cidx_find_next
+		    (Map, tucfield_idx, isec, GV_POINT, 0, &type,
+		     &node_pt_id) == -1) {
+		    G_warning(_
+			      ("Turn with cat <%d> was skipped. "
+			       "Unable to find point in vector map <%s> "
+			       "with category <%d> in field <%d>"), turn_cat,
+			      Map->name, isec, tucfield);
+		    continue;
+		}
+		Vect_read_line(Map, Points, Cats, node_pt_id);
+
+		node_pt_id =
+		    Vect_find_node(Map, *Points->x, *Points->y, *Points->z,
+				   0.0, WITHOUT_Z);
+		if (node_pt_id == 0) {
+		    G_warning(_("Turn with cat <%d> was skipped. "
+				"Unable to find node in vector map <%s> "
+				"for point with category <%d> in field <%d>"),
+			      turn_cat, Map->name, isec, tucfield);
+		    continue;
+		}
+
+		G_debug(2, "  node = %d", node_pt_id);
+		dcost = Map->dgraph.node_costs[node_pt_id];
+	    }
+	    else
+		dcost = 0.0;
+
+	    G_debug(2, "Set node's cost to %f", dcost);
+
+
+	    if (dcost >= 0) {
+		/* Set costs from turntable */
+		if (tctype[3] == DB_C_TYPE_INT) {
+		    ret =
+			db_CatValArray_get_value_int(&tvarrs[3], i,
+						     &cost);
+		    dcost = cost;
+		}
+		else		/* DB_C_TYPE_DOUBLE */
+		    ret =
+			db_CatValArray_get_value_double(&tvarrs[3], i,
+							&dcost);
+
+		if (dcost >= 0) {
+
+		    if (ncol != NULL)
+			cost =
+			    (Map->dgraph.node_costs[node_pt_id] +
+			     dcost) * (dglInt32_t) Map->dgraph.cost_multip;
+		    else
+			cost = dcost * (dglInt32_t) Map->dgraph.cost_multip;
+
+		    int t, f;
+
+			if (from < 0)
+			    f = from * -2 + 1;
+			else
+			    f = from * 2;
+
+			if (to < 0)
+			    t = to * -2 + 1;
+			else
+			    t = to * 2;
+
+			ret = dglAddEdge(gr, (dglInt32_t) f, (dglInt32_t) t,
+					 (dglInt32_t) cost,
+					 (dglInt32_t) (turn_cat));
+
+		    if (ret < 0)
+			G_fatal_error(_("Cannot add network arc"));
+		}
+	    }
+    }
+
+    Vect_destroy_boxlist(List);
+
+    i = 0;
+    while (tcols[i]) {
+	db_CatValArray_free(&tvarrs[i]);
+	++i;
+    }
+
+    if (ncol != NULL) {
+
+	/*TODO difference between  db_close_database_shutdown_driver and driver */
+	db_close_database_shutdown_driver(driver);
+	db_CatValArray_free(&fvarr);
+    }
+
+    /* Open db connection */
+    if (afcol != NULL) {
+	/* Get field info */
+	if (afield < 1)
+	    G_fatal_error(_("Arc field < 1"));
+	Fi = Vect_get_field(Map, afield);
+	if (Fi == NULL)
+	    G_fatal_error(_("Database connection not defined for layer %d"),
+			  afield);
+
+	/* Open database */
+	driver = db_start_driver_open_database(Fi->driver, Fi->database);
+	if (driver == NULL)
+	    G_fatal_error(_("Unable to open database <%s> by driver <%s>"),
+			  Fi->database, Fi->driver);
+
+	/* Load costs to array */
+	if (db_get_column(driver, Fi->table, afcol, &Column) != DB_OK)
+	    G_fatal_error(_("Column <%s> not found in table <%s>"),
+			  afcol, Fi->table);
+
+	fctype = db_sqltype_to_Ctype(db_get_column_sqltype(Column));
+
+	if (fctype != DB_C_TYPE_INT && fctype != DB_C_TYPE_DOUBLE)
+	    G_fatal_error(_
+			  ("Data type of column <%s> not supported (must be numeric)"),
+			  afcol);
+
+	db_CatValArray_init(&fvarr);
+	nrec =
+	    db_select_CatValArray(driver, Fi->table, Fi->key, afcol, NULL,
+				  &fvarr);
+	G_debug(1, "forward costs: nrec = %d", nrec);
+
+	if (abcol != NULL) {
+	    if (db_get_column(driver, Fi->table, abcol, &Column) != DB_OK)
+		G_fatal_error(_("Column <%s> not found in table <%s>"),
+			      abcol, Fi->table);
+
+	    bctype = db_sqltype_to_Ctype(db_get_column_sqltype(Column));
+
+	    if (bctype != DB_C_TYPE_INT && bctype != DB_C_TYPE_DOUBLE)
+		G_fatal_error(_
+			      ("Data type of column <%s> not supported (must be numeric)"),
+			      abcol);
+
+	    db_CatValArray_init(&bvarr);
+	    nrec =
+		db_select_CatValArray(driver, Fi->table, Fi->key, abcol, NULL,
+				      &bvarr);
+	    G_debug(1, "backward costs: nrec = %d", nrec);
+	}
+    }
+
+    skipped = 0;
+
+    G_message(_("Registering arcs..."));
+
+    for (i = 1; i <= nlines; i++) {
+	G_percent(i, nlines, 1);	/* must be before any continue */
+
+	type = Vect_read_line(Map, Points, Cats, i);
+	if (!(type & ltype & (GV_LINE | GV_BOUNDARY)))
+	    continue;
+
+	Vect_get_line_nodes(Map, i, &from, &to);
+
+	dcost = bdcost = 0;
+
+	if (afcol != NULL) {
+	    if (!(Vect_cat_get(Cats, afield, &cat))) {
+		G_debug(2,
+			"Category of field %d not attached to the line %d -> line skipped",
+			afield, i);
+		skipped += 2;	/* Both directions */
+		continue;
+	    }
+	    else {
+		if (fctype == DB_C_TYPE_INT) {
+		    ret = db_CatValArray_get_value_int(&fvarr, cat, &cost);
+		    dcost = cost;
+		}
+		else {		/* DB_C_TYPE_DOUBLE */
+		    ret =
+			db_CatValArray_get_value_double(&fvarr, cat, &dcost);
+		}
+		if (ret != DB_OK) {
+		    G_warning(_("Database record for line %d (cat = %d, "
+				"forward/both direction(s)) not found "
+				"(cost was set to 0)"), i, cat);
+		}
+
+		if (abcol != NULL) {
+		    if (bctype == DB_C_TYPE_INT) {
+			ret =
+			    db_CatValArray_get_value_int(&bvarr, cat, &bcost);
+			bdcost = bcost;
+		    }
+		    else {	/* DB_C_TYPE_DOUBLE */
+			ret =
+			    db_CatValArray_get_value_double(&bvarr, cat,
+							    &bdcost);
+		    }
+		    if (ret != DB_OK) {
+			G_warning(_("Database record for line %d (cat = %d, "
+				    "backword direction) not found"
+				    "(cost was set to 0)"), i, cat);
+		    }
+		}
+		else
+		    bdcost = dcost;
+	    }
+	}
+	else {
+	    if (ll) {
+		if (geo)
+		    dcost = Vect_line_geodesic_length(Points);
+		else
+		    dcost = Vect_line_length(Points);
+	    }
+	    else
+		dcost = Vect_line_length(Points);
+
+	    bdcost = dcost;
+	}
+
+	/*TODO warning for more cats */
+	if (!Vect_cat_get(Cats, tucfield, &cat)) {
+	    G_warning(_("Database record for line %d (cat = %d in layer %d, "
+			"backword direction) not found"
+			"(node in line graph was not set)"), i, cat,
+		      tucfield);
+	    continue;
+	}
+
+	cost = (dglInt32_t) Map->dgraph.cost_multip * dcost;
+
+
+	//G_message("cost = %d edge_fcosts = %f cat %d", cost,
+	//       Map->edge_fcosts[i], cat);
+
+	cat = cat * 2;
+	dglNodeSet_Attr(gr, dglGetNode(gr, (dglInt32_t) cat),
+			(dglInt32_t *) (dglInt32_t) & cost);
+
+	Map->dgraph.edge_fcosts[i] = dcost;
+
+	cost = (dglInt32_t) Map->dgraph.cost_multip * bdcost;
+
+	//G_message("cost = %d edge_fcosts = %f cat %d", cost,
+	//     Map->edge_fcosts[i], cat);
+
+	++cat;
+	/*TODO depends on direciton */
+	dglNodeSet_Attr(gr, dglGetNode(gr, (dglInt32_t) cat),
+			(dglInt32_t *) (dglInt32_t) & cost);
+
+	Map->dgraph.edge_bcosts[i] = bdcost;
+    }
+
+    if (afcol != NULL && skipped > 0)
+	G_debug(2, "%d lines missing category of field %d skipped", skipped,
+		afield);
+
+    if (afcol != NULL) {
+	db_close_database_shutdown_driver(driver);
+	db_CatValArray_free(&fvarr);
+
+	if (abcol != NULL) {
+	    db_CatValArray_free(&bvarr);
+	}
+    }
+    db_close_database_shutdown_driver(ttbdriver);
+
+    /* init SP cache */
+    /* disable to debug dglib cache */
+    dglInitializeSPCache(gr, &(Map->dgraph.spCache));
+
+    Vect_destroy_line_struct(Points);
+    Vect_destroy_cats_struct(Cats);
+
+    G_message(_("Graph was built"));
+
+    next_virtual_ln = -1;
+    return 0;
+
+
+}
+
+/*!
    \brief Build network graph.
 
    Internal format for edge costs is integer, costs are multiplied
@@ -135,9 +698,12 @@
     gr = &(Map->dgraph.graph_s);
 
     /* Allocate space for costs, later replace by functions reading costs from graph */
-    Map->dgraph.edge_fcosts = (double *)G_malloc((nlines + 1) * sizeof(double));
-    Map->dgraph.edge_bcosts = (double *)G_malloc((nlines + 1) * sizeof(double));
-    Map->dgraph.node_costs = (double *)G_malloc((nnodes + 1) * sizeof(double));
+    Map->dgraph.edge_fcosts =
+	(double *)G_malloc((nlines + 1) * sizeof(double));
+    Map->dgraph.edge_bcosts =
+	(double *)G_malloc((nlines + 1) * sizeof(double));
+    Map->dgraph.node_costs =
+	(double *)G_malloc((nnodes + 1) * sizeof(double));
     /* Set to -1 initially */
     for (i = 1; i <= nlines; i++) {
 	Map->dgraph.edge_fcosts[i] = -1;	/* forward */
@@ -188,7 +754,8 @@
 	fctype = db_sqltype_to_Ctype(db_get_column_sqltype(Column));
 
 	if (fctype != DB_C_TYPE_INT && fctype != DB_C_TYPE_DOUBLE)
-	    G_fatal_error(_("Data type of column <%s> not supported (must be numeric)"),
+	    G_fatal_error(_
+			  ("Data type of column <%s> not supported (must be numeric)"),
 			  afcol);
 
 	db_CatValArray_init(&fvarr);
@@ -205,7 +772,8 @@
 	    bctype = db_sqltype_to_Ctype(db_get_column_sqltype(Column));
 
 	    if (bctype != DB_C_TYPE_INT && bctype != DB_C_TYPE_DOUBLE)
-		G_fatal_error(_("Data type of column <%s> not supported (must be numeric)"),
+		G_fatal_error(_
+			      ("Data type of column <%s> not supported (must be numeric)"),
 			      abcol);
 
 	    db_CatValArray_init(&bvarr);
@@ -338,7 +906,7 @@
 	double x, y, z;
 	struct bound_box box;
 	struct boxlist *List;
-	
+
 	List = Vect_new_boxlist(0);
 
 	G_debug(2, "Set nodes' costs");
@@ -365,7 +933,8 @@
 	fctype = db_sqltype_to_Ctype(db_get_column_sqltype(Column));
 
 	if (fctype != DB_C_TYPE_INT && fctype != DB_C_TYPE_DOUBLE)
-	    G_fatal_error(_("Data type of column <%s> not supported (must be numeric)"),
+	    G_fatal_error(_
+			  ("Data type of column <%s> not supported (must be numeric)"),
 			  ncol);
 
 	db_CatValArray_init(&fvarr);
@@ -408,7 +977,8 @@
 							    &dcost);
 		    }
 		    if (ret != DB_OK) {
-			G_warning(_("Database record for node %d (cat = %d) not found "
+			G_warning(_
+				  ("Database record for node %d (cat = %d) not found "
 				   "(cost set to 0)"), i, cat);
 		    }
 		    cfound = 1;
@@ -433,7 +1003,7 @@
 	}
 	db_close_database_shutdown_driver(driver);
 	db_CatValArray_free(&fvarr);
-	
+
 	Vect_destroy_boxlist(List);
     }
 
@@ -451,30 +1021,110 @@
     return 0;
 }
 
-
 /*!
-   \brief Find shortest path.
+   \brief Converts result path of DGLib into vector format.
+ */
+static int convert_dgl_shortest_path_result(struct Map_info *Map,
+					    dglSPReport_s * pSPReport,
+					    struct ilist *List)
+{
+    int i, line, cArc;
 
-   Costs for 'from' and 'to' nodes are not considered (SP found even if
-   'from' or 'to' are 'closed' (costs = -1) and costs of these
-   nodes are not added to SP costs result.
+    if (List != NULL)
+	Vect_reset_list(List);
 
-   \param Map vector map
-   \param from from node
-   \param to to node
-   \param[out] List list of line ids (path)
-   \param[out] cost costs value
+    if (List != NULL) {
+	for (i = 0; i < pSPReport->cArc; i++) {
+	    line =
+		dglEdgeGet_Id(&(Map->dgraph.graph_s),
+			      pSPReport->pArc[i].pnEdge);
+	    G_debug(2, "From %ld to %ld - cost %ld user %d distance %ld", pSPReport->pArc[i].nFrom, pSPReport->pArc[i].nTo, dglEdgeGet_Cost(&(Map->dgraph.graph_s), pSPReport->pArc[i].pnEdge) / Map->dgraph.cost_multip,	/* this is the cost from clip() */
+		    line, pSPReport->pArc[i].nDistance);
+		G_message("edge1: %d", line);
 
-   \return number of segments
-   \return 0 is correct for from = to, or List == NULL ? sum of costs is better return value,
-   \return -1 : destination unreachable
+	    Vect_list_append(List, line);
+	}
+    }
 
- */
-int
-Vect_net_shortest_path(struct Map_info *Map, int from, int to,
-		       struct ilist *List, double *cost)
+    if (List != NULL)
+	cArc = pSPReport->cArc;
+    else
+	cArc = 0;
+
+    return cArc;
+}
+
+static int ttb_convert_dgl_shortest_path_result(struct Map_info *Map,
+						dglSPReport_s * pSPReport,
+						int tucfield,
+						struct ilist *List)
 {
-    int i, line, *pclip, cArc, nRet;
+    int i, j, dgl_edge, cArc, line_id, type, tucfield_idx;
+    int lines[2];
+
+    if (List != NULL)
+	Vect_reset_list(List);
+
+    tucfield_idx = Vect_cidx_get_field_index(Map, tucfield);
+
+    if (List != NULL) {
+	/* skip virtual nodes */
+	for (i = 0; i < pSPReport->cArc; i++) {
+	    dgl_edge =
+		dglEdgeGet_Id(&(Map->dgraph.graph_s),
+			      pSPReport->pArc[i].pnEdge);
+	    lines[0] =
+		dglNodeGet_Id(&(Map->dgraph.graph_s),
+			      dglEdgeGet_Head(&(Map->dgraph.graph_s),
+					      pSPReport->pArc[i].pnEdge));
+		//TODO 
+	    lines[1] =
+		dglNodeGet_Id(&(Map->dgraph.graph_s),
+			      dglEdgeGet_Tail(&(Map->dgraph.graph_s),
+					      pSPReport->pArc[i].pnEdge));
+
+
+	    for (j = 0; j < sizeof(lines) / sizeof(int); j++) {
+		if (lines[j] % 2 == 1)
+		    lines[j] = ((lines[j] - 1) / -2);
+		else
+		    lines[j] = (lines[j]) / 2;
+
+		G_message("edge%d: %d", j, lines[j]);
+
+
+		if (Vect_cidx_find_next
+		    (Map, tucfield_idx, abs(lines[j]), GV_LINE, 0, &type,
+		     &line_id) == -1)
+		    continue;
+
+		if(lines[j] < 0)
+			line_id *= -1;
+
+		if(j == 1) {
+			continue;
+		}
+
+		G_debug(2, "From %ld to %ld - cost %ld user %d distance %ld", pSPReport->pArc[i].nFrom, pSPReport->pArc[i].nTo, dglEdgeGet_Cost(&(Map->dgraph.graph_s), pSPReport->pArc[i].pnEdge) / Map->dgraph.cost_multip,	/* this is the cost from clip() */
+			lines[j], pSPReport->pArc[i].nDistance);
+		Vect_list_append(List, line_id);
+	    }
+	}
+    }
+
+    /*TODO check value of cArc */
+    if (List != NULL)
+	cArc = pSPReport->cArc;
+    else
+	cArc = 0;
+
+    return cArc;
+}
+
+static int find_shortest_path(struct Map_info *Map, int from, int to,
+			      struct ilist *List, double *cost, int tucfield)
+{
+    int *pclip, cArc, nRet;
     dglSPReport_s *pSPReport;
     dglInt32_t nDistance;
     int use_cache = 1;		/* set to 0 to disable dglib cache */
@@ -484,9 +1134,6 @@
     /* Note : if from == to dgl goes to nearest node and returns back (dgl feature) => 
      *         check here for from == to */
 
-    if (List != NULL)
-	Vect_reset_list(List);
-
     /* Check if from and to are identical, otherwise dglib returns path to neares node and back! */
     if (from == to) {
 	if (cost != NULL)
@@ -495,33 +1142,36 @@
     }
 
     From_node = from;
-
     pclip = NULL;
     if (List != NULL) {
 	if (use_cache) {
 	    nRet =
-		dglShortestPath(&(Map->dgraph.graph_s), &pSPReport, (dglInt32_t) from,
-				(dglInt32_t) to, clipper, pclip, &(Map->dgraph.spCache));
+		dglShortestPath(&(Map->dgraph.graph_s), &pSPReport,
+				(dglInt32_t) from, (dglInt32_t) to, clipper,
+				pclip, &(Map->dgraph.spCache));
 	}
 	else {
 	    nRet =
-		dglShortestPath(&(Map->dgraph.graph_s), &pSPReport, (dglInt32_t) from,
-				(dglInt32_t) to, clipper, pclip, NULL);
+		dglShortestPath(&(Map->dgraph.graph_s), &pSPReport,
+				(dglInt32_t) from, (dglInt32_t) to, clipper,
+				pclip, NULL);
 	}
     }
     else {
 	if (use_cache) {
 	    nRet =
-		dglShortestDistance(&(Map->dgraph.graph_s), &nDistance, (dglInt32_t) from,
-				    (dglInt32_t) to, clipper, pclip, &(Map->dgraph.spCache));
+		dglShortestDistance(&(Map->dgraph.graph_s), &nDistance,
+				    (dglInt32_t) from, (dglInt32_t) to,
+				    clipper, pclip, &(Map->dgraph.spCache));
 	}
 	else {
 	    nRet =
-		dglShortestDistance(&(Map->dgraph.graph_s), &nDistance, (dglInt32_t) from,
-				    (dglInt32_t) to, clipper, pclip, NULL);
+		dglShortestDistance(&(Map->dgraph.graph_s), &nDistance,
+				    (dglInt32_t) from, (dglInt32_t) to,
+				    clipper, pclip, NULL);
 	}
     }
-
+    G_message("return: %d", nRet);
     if (nRet == 0) {
 	/* G_warning("Destination node %d is unreachable from node %d\n" , to , from); */
 	if (cost != NULL)
@@ -529,17 +1179,17 @@
 	return -1;
     }
     else if (nRet < 0) {
-	G_warning(_("dglShortestPath error: %s"), dglStrerror(&(Map->dgraph.graph_s)));
+	G_warning(_("dglShortestPath error: %s"),
+		  dglStrerror(&(Map->dgraph.graph_s)));
 	return -1;
     }
 
-    if (List != NULL) {
-	for (i = 0; i < pSPReport->cArc; i++) {
-	    line = dglEdgeGet_Id(&(Map->dgraph.graph_s), pSPReport->pArc[i].pnEdge);
-	    G_debug(2, "From %ld to %ld - cost %ld user %d distance %ld", pSPReport->pArc[i].nFrom, pSPReport->pArc[i].nTo, dglEdgeGet_Cost(&(Map->dgraph.graph_s), pSPReport->pArc[i].pnEdge) / Map->dgraph.cost_multip,	/* this is the cost from clip() */
-		    line, pSPReport->pArc[i].nDistance);
-	    Vect_list_append(List, line);
-	}
+    if (tucfield <= 0)
+	cArc = convert_dgl_shortest_path_result(Map, pSPReport, List);
+    else {
+	cArc =
+	    ttb_convert_dgl_shortest_path_result(Map, pSPReport, tucfield,
+						 List);
     }
 
     if (cost != NULL) {
@@ -548,30 +1198,216 @@
 	else
 	    *cost = (double)nDistance / Map->dgraph.cost_multip;
     }
+    //G_message("CENA: %f", *cost);
+    if (List != NULL)
+	dglFreeSPReport(&(Map->dgraph.graph_s), pSPReport);
 
-    if (List != NULL) {
-	cArc = pSPReport->cArc;
-	dglFreeSPReport(&(Map->dgraph.graph_s), pSPReport);
+    return cArc;
+}
+
+static int ttb_get_interesction_edges(struct Map_info *Map, int tucfield, int node, int dir,
+				      				  struct ilist *lns_list)
+{
+	int i_line, cat, line_id;
+ 	int n_lines = Vect_get_node_n_lines(Map, node);
+ 	
+ 	struct line_cats *Cats;
+	
+	G_message("node: %d, n_lines: %d", node, n_lines);
+
+    Cats = Vect_new_cats_struct();
+
+ 	for(i_line = 0; i_line < n_lines; i_line++) {
+ 		line_id = Vect_get_node_line(Map, node, i_line);
+		Vect_read_line(Map, NULL, Cats, abs(line_id));
+		Vect_cat_get(Cats, tucfield, &cat);
+
+
+		if(line_id < 0)
+			cat *= -1;
+
+		if(dir == 1)
+			cat *= -1;
+
+		G_message("ucat %d, tf: %d", cat);
+
+		G_ilist_add(lns_list, cat);
+ 	}
+
+    Vect_destroy_cats_struct(Cats);
+
+}
+
+static int ttb_add_virtual_node(dglGraph_s * graph, struct ilist *lns_list,
+				int dir, int node_id, int min_edge_id)
+{
+    int i_line;
+    int from, to;
+
+    for (i_line = 0; i_line < lns_list->n_values; i_line++) {
+	if (dir == 0) {
+	    from = node_id;
+	    to = lns_list->value[i_line];
+	}
+	else if (dir == 1) {
+	    to = node_id;
+	    from = lns_list->value[i_line];
+	}
+	else
+	    return -1;
+
+	if (from < 0)
+	    from = from * -2 + 1;
+	else
+	    from = from * 2;
+
+	if (to < 0)
+	    to = to * -2 + 1;
+	else
+	    to = to * 2;
+    G_message("node_id: %d from:%d, to:%d", node_id, from, to);
+
+	dglAddEdge(graph, (dglInt32_t) from, (dglInt32_t) to,
+		   (dglInt32_t) 0, (dglInt32_t) min_edge_id + i_line);
     }
-    else
-	cArc = 0;
 
-    return (cArc);
+    return i_line;
 }
 
+static int ttb_remove_virtual_node(dglGraph_s * graph, struct ilist *lns_list,
+				   int node_id, int min_edge_id)
+{
+    int i_line;
+	if (node_id < 0)
+	    node_id = node_id * -2 + 1;
+	else
+	    node_id = node_id * 2;
+
+    for (i_line = 0; i_line < lns_list->n_values; i_line++)
+	G_message("%d", dglDelEdge(graph, min_edge_id + i_line));
+
+    G_message("%d", dglDelNode(graph, node_id));
+
+    return i_line;
+}
+
+
 /*!
-  \brief Get graph structure
-  
-  Graph is built by Vect_net_build_graph().
-  
-  Returns NULL when graph is not built.
-  
-  \param Map pointer to Map_info struct
+   \brief Find shortest path on network.
 
-  \return pointer to dglGraph_s struct or NULL
-*/
-dglGraph_s *Vect_net_get_graph(const struct Map_info *Map)
+   Costs for 'from' and 'to' nodes are not considered (SP found even if
+   'from' or 'to' are 'closed' (costs = -1) and costs of these
+   nodes are not added to SP costs result.
+
+   \param Map vector map
+   \param from from node
+   \param from type if 0 - point, if 1 edge
+   \param to to node
+   \param to type if 0 - point, if 1 edge
+   \param layer with turntable
+   \param layer with unique cats for turntable
+   \param[out] List list of line ids (path)
+   \param[out] cost costs value
+
+   \return number of segments
+   \return 0 is correct for from = to, or List == NULL ? sum of costs is better return value,
+   \return -1 : destination unreachable
+
+ */
+
+int
+Vect_net_ttb_shortest_path(struct Map_info *Map, int from, int from_type, int to, int to_type, int tfield,
+			   				 int tucfield, struct ilist *List, double *cost)
 {
+    struct ilist lns_list_from;
+    struct ilist lns_list_to;
+    char buf[1000];
+
+    struct field_info *Fi;
+    int max_ln_cat, nrows, f, t;
+
+
+    //TODO now right soltion
+    if (next_virtual_ln < 0)
+    	next_virtual_ln =  100000;
+
+
+    if(from_type == 0) {
+	    G_init_ilist(&lns_list_from);
+	    ttb_get_interesction_edges(Map, tucfield, from, 0, &lns_list_from);
+	    ttb_add_virtual_node(&(Map->dgraph.graph_s), &lns_list_from, 0,
+				 next_virtual_ln, next_virtual_ln + 1);
+	    f = next_virtual_ln * 2;
+	    next_virtual_ln +=  1 + lns_list_from.n_values + 1;
+	}
+	else {
+		if (from < 0)
+		    f = from * -2 + 1;
+		else
+		    f = from * 2;
+	}
+
+    if(to_type == 0) {
+	    G_init_ilist(&lns_list_to);
+	    ttb_get_interesction_edges(Map, tucfield, to, 1, &lns_list_to);
+	    ttb_add_virtual_node(&(Map->dgraph.graph_s), &lns_list_to, 1,
+				 next_virtual_ln, next_virtual_ln + 1);
+	    t = next_virtual_ln * 2;
+	    next_virtual_ln +=  1 + lns_list_to.n_values + 1;
+	}
+	else {
+		if (to < 0)
+		    t = to * -2 + 1;
+		else
+		    t = to * 2;
+	}
+
+    //G_message("flattening graph");
+    //dglFlatten(&(Map->graph));
+    G_message("f:%d, t:%d", f, t);
+    return find_shortest_path(Map, f, t, List,
+		                      cost, tucfield);
+
+}
+
+/*!
+   \brief Find shortest path.
+
+   Costs for 'from' and 'to' nodes are not considered (SP found even if
+   'from' or 'to' are 'closed' (costs = -1) and costs of these
+   nodes are not added to SP costs result.
+
+   \param Map vector map
+   \param from from node
+   \param to to node
+   \param[out] List list of line ids (path)
+   \param[out] cost costs value
+
+   \return number of segments
+   \return 0 is correct for from = to, or List == NULL ? sum of costs is better return value,
+   \return -1 : destination unreachable
+
+ */
+int
+Vect_net_shortest_path(struct Map_info *Map, int from, int to,
+		       struct ilist *List, double *cost)
+{
+    return find_shortest_path(Map, from, to, List, cost, -1);
+}
+
+/*!
+   \brief Get graph structure
+
+   Graph is built by Vect_net_build_graph().
+
+   Returns NULL when graph is not built.
+
+   \param Map pointer to Map_info struct
+
+   \return pointer to dglGraph_s struct or NULL
+ */
+dglGraph_s *Vect_net_get_graph(const struct Map_info * Map)
+{
     return &(Map->dgraph.graph_s);
 }
 
@@ -602,7 +1438,7 @@
 	/*
 	   pEdge = dglGetEdge(&(Map->dgraph.graph_s), line);
 	   if (pEdge == NULL)
-		return 0;
+	   return 0;
 	   *cost = (double) dglEdgeGet_Cost(&(Map->dgraph.graph_s), pEdge);
 	 */
 	if (Map->dgraph.edge_fcosts[line] == -1) {
@@ -610,13 +1446,13 @@
 	    return 0;
 	}
 	else
-            *cost = Map->dgraph.edge_fcosts[line];
+	    *cost = Map->dgraph.edge_fcosts[line];
     }
     else if (direction == GV_BACKWARD) {
 	/*
 	   pEdge = dglGetEdge(&(Map->dgraph.graph_s), -line);
 	   if (pEdge == NULL) 
-	    	return 0;
+	   return 0;
 	   *cost = (double) dglEdgeGet_Cost(&(Map->dgraph.graph_s), pEdge);
 	 */
 	if (Map->dgraph.edge_bcosts[line] == -1) {
@@ -882,38 +1718,10 @@
    \param tx,ty,tz to point x coordinate (z ignored)
    \param fmax maximum distance to the network from 'from'
    \param tmax maximum distance to the network from 'to'
-   \param[out] costs pointer where to store costs on the network (or NULL)
-   \param[out] Points pointer to the structure where to store vertices of shortest path (or NULL)
-   \param[out] List pointer to the structure where list of lines on the network is stored (or NULL)
-   \param[out] FPoints pointer to the structure where to store line from 'from' to first network node (or NULL)
-   \param[out] TPoints pointer to the structure where to store line from last network node to 'to' (or NULL)
-   \param[out] fdist distance from 'from' to the net (or NULL)
-   \param[out] tdist distance from 'to' to the net (or NULL)
-
-   \return 1 OK
-   \return 0 not reachable
- */
-int
-Vect_net_shortest_path_coor(struct Map_info *Map,
-			    double fx, double fy, double fz, double tx,
-			    double ty, double tz, double fmax, double tmax,
-			    double *costs, struct line_pnts *Points,
-			    struct ilist *List, struct line_pnts *FPoints,
-			    struct line_pnts *TPoints, double *fdist,
-			    double *tdist)
-{
-  return Vect_net_shortest_path_coor2(Map, fx, fy, fz, tx, ty, tz, fmax, tmax, 
-            costs, Points, List, NULL, FPoints, TPoints, fdist, tdist);
-}
-
-/*!
-   \brief Find shortest path on network between 2 points given by coordinates. 
-
-   \param Map vector map
-   \param fx,fy,fz from point x coordinate (z ignored)
-   \param tx,ty,tz to point x coordinate (z ignored)
-   \param fmax maximum distance to the network from 'from'
-   \param tmax maximum distance to the network from 'to'
+   \param tfield number of layer where turntable is attached 
+   (if it has 0 value - network is without turntable)
+   \param tucfield number of layer with unique categories used in turntable 
+   (if it has 0 value - network is without turntable)
    \param costs pointer where to store costs on the network (or NULL)
    \param Points pointer to the structure where to store vertices of shortest path (or NULL)
    \param List pointer to the structure where list of lines on the network is stored (or NULL)
@@ -925,15 +1733,16 @@
 
    \return 1 OK, 0 not reachable
  */
-int
-Vect_net_shortest_path_coor2(struct Map_info *Map,
-			    double fx, double fy, double fz, double tx,
-			    double ty, double tz, double fmax, double tmax,
-			    double *costs, struct line_pnts *Points,
-			    struct ilist *List, struct ilist *NodesList,
-                            struct line_pnts *FPoints,
-			    struct line_pnts *TPoints, double *fdist,
-			    double *tdist)
+static int
+find_shortest_path_coor(struct Map_info *Map,
+			double fx, double fy, double fz, double tx,
+			double ty, double tz, double fmax, double tmax,
+			int tfield, int tucfield,
+			double *costs, struct line_pnts *Points,
+			struct ilist *List, struct ilist *NodesList,
+			struct line_pnts *FPoints,
+			struct line_pnts *TPoints, double *fdist,
+			double *tdist)
 {
     int fnode[2], tnode[2];	/* nearest nodes, *node[1] is 0 if only one was found */
     double fcosts[2], tcosts[2], cur_cst;	/* costs to nearest nodes on the network */
@@ -991,8 +1800,8 @@
 	return 0;
 
     if (nfnodes == 1 && fPoints[0]->n_points < 3) {
-        from_point_node = fnode[0];
-    } 
+	from_point_node = fnode[0];
+    }
 
     ntnodes =
 	Vect_net_nearest_nodes(Map, tx, ty, tz, GV_BACKWARD, tmax,
@@ -1002,8 +1811,8 @@
 	return 0;
 
     if (ntnodes == 1 && tPoints[0]->n_points < 3) {
-        to_point_node = tnode[0];
-    } 
+	to_point_node = tnode[0];
+    }
 
 
     G_debug(3, "fline = %d tline = %d", fline, tline);
@@ -1081,6 +1890,10 @@
 	    G_debug(3, "i = %d fnode = %d j = %d tnode = %d", i, fnode[i], j,
 		    tnode[j]);
 
+	    if (tfield && tucfield)
+		ret = Vect_net_ttb_shortest_path(Map, fnode[fn], 0, tnode[tn], 0, tfield,
+					                  tucfield, NULL, &ncst);
+		else
 	    ret =
 		Vect_net_shortest_path(Map, fnode[i], tnode[j], NULL, &ncst);
 	    if (ret == -1)
@@ -1103,30 +1916,36 @@
 	if (shortcut) {
 	    if (Points)
 		Vect_append_points(Points, SPoints, GV_FORWARD);
-            if (NodesList) {
-                /* Check if from/to point projected to line falls on node and 
-                 *add it to the list */
-                if (from_point_node > 0)
+	    if (NodesList) {
+		/* Check if from/to point projected to line falls on node and 
+		 *add it to the list */
+		if (from_point_node > 0)
 		    Vect_list_append(NodesList, from_point_node);
 
-                if (to_point_node > 0)
+		if (to_point_node > 0)
 		    Vect_list_append(NodesList, to_point_node);
-            }
+	    }
 	}
 	else {
-            if (NodesList) {
-                /* it can happen that starting point falls on node but SP starts 
-                 * form the other node, add it in that case, 
-                 * similarly for to point below */
-                if (from_point_node > 0 && from_point_node != fnode[fn]) {
-                    Vect_list_append(NodesList, from_point_node);
-                }
+	    if (NodesList) {
+		/* it can happen that starting point falls on node but SP starts 
+		 * form the other node, add it in that case, 
+		 * similarly for to point below */
+		if (from_point_node > 0 && from_point_node != fnode[fn]) {
+		    Vect_list_append(NodesList, from_point_node);
+		}
 
-                /* add starting net SP search node */
-                Vect_list_append(NodesList, fnode[fn]);
-            }
-	    Vect_net_shortest_path(Map, fnode[fn], tnode[tn], LList,
-				   NULL);
+		/* add starting net SP search node */
+		Vect_list_append(NodesList, fnode[fn]);
+	    }
+
+	    if (tfield && tucfield)
+		Vect_net_ttb_shortest_path(Map, fnode[fn], 0, tnode[tn], 0, tfield,
+					   tucfield, LList, NULL);
+	    else
+		Vect_net_shortest_path(Map, fnode[fn], tnode[tn], LList,
+				       NULL);
+
 	    G_debug(3, "Number of lines %d", LList->n_values);
 
 	    if (Points)
@@ -1149,18 +1968,18 @@
 		    else
 			Vect_append_points(Points, APoints, GV_BACKWARD);
 		}
-                if (NodesList) {
-                    int node, node1, node2;
+		if (NodesList) {
+		    int node, node1, node2;
 
-                    Vect_get_line_nodes(Map, abs(line), &node1, &node2);
-                    /* add the second node, the first of first segmet was alread added */
-                    if (line > 0)
+		    Vect_get_line_nodes(Map, abs(line), &node1, &node2);
+		    /* add the second node, the first of first segmet was alread added */
+		    if (line > 0)
 			node = node2;
-                    else
+		    else
 			node = node1;
 
-                    Vect_list_append(NodesList, node);
-                }
+		    Vect_list_append(NodesList, node);
+		}
 
 		if (List)
 		    Vect_list_append(List, line);
@@ -1172,11 +1991,11 @@
 	    if (TPoints)
 		Vect_append_points(TPoints, tPoints[tn], GV_FORWARD);
 
-            if (NodesList) {
-                if (to_point_node > 0 && to_point_node != tnode[tn]) {
-                    Vect_list_append(NodesList, to_point_node);
-                }
-            }
+	    if (NodesList) {
+		if (to_point_node > 0 && to_point_node != tnode[tn]) {
+		    Vect_list_append(NodesList, to_point_node);
+		}
+	    }
 	}
 
 	if (costs)
@@ -1185,3 +2004,143 @@
 
     return reachable;
 }
+
+/*!
+   \brief Find shortest path on network between 2 points given by coordinates. 
+
+   \param Map vector map
+   \param fx,fy,fz from point x coordinate (z ignored)
+   \param tx,ty,tz to point x coordinate (z ignored)
+   \param fmax maximum distance to the network from 'from'
+   \param tmax maximum distance to the network from 'to'
+   \param[out] costs pointer where to store costs on the network (or NULL)
+   \param[out] Points pointer to the structure where to store vertices of shortest path (or NULL)
+   \param[out] List pointer to the structure where list of lines on the network is stored (or NULL)
+   \param[out] FPoints pointer to the structure where to store line from 'from' to first network node (or NULL)
+   \param[out] TPoints pointer to the structure where to store line from last network node to 'to' (or NULL)
+   \param[out] fdist distance from 'from' to the net (or NULL)
+   \param[out] tdist distance from 'to' to the net (or NULL)
+
+   \return 1 OK
+   \return 0 not reachable
+ */
+int
+Vect_net_shortest_path_coor(struct Map_info *Map,
+			    double fx, double fy, double fz, double tx,
+			    double ty, double tz, double fmax, double tmax,
+			    double *costs, struct line_pnts *Points,
+			    struct ilist *List, struct line_pnts *FPoints,
+			    struct line_pnts *TPoints, double *fdist,
+			    double *tdist)
+{
+    return find_shortest_path_coor(Map, fx, fy, fz, tx, ty, tz, fmax, tmax, 0,
+				   0, costs, Points, List, NULL, FPoints,
+				   TPoints, fdist, tdist);
+}
+
+/*!
+   \brief Find shortest path on network with turntable between 2 points given by coordinates. 
+
+   \param Map vector map
+   \param fx,fy,fz from point x coordinate (z ignored)
+   \param tx,ty,tz to point x coordinate (z ignored)
+   \param fmax maximum distance to the network from 'from'
+   \param tmax maximum distance to the network from 'to'
+   \param tfield number of layer where turntable is attached
+   \param tucfield number of layer with unique categories used in turntable 
+   \param[out] costs pointer where to store costs on the network (or NULL)
+   \param[out] Points pointer to the structure where to store vertices of shortest path (or NULL)
+   \param[out] List pointer to the structure where list of lines on the network is stored (or NULL)
+   \param[out] FPoints pointer to the structure where to store line from 'from' to first network node (or NULL)
+   \param[out] TPoints pointer to the structure where to store line from last network node to 'to' (or NULL)
+   \param[out] fdist distance from 'from' to the net (or NULL)
+   \param[out] tdist distance from 'to' to the net (or NULL)
+
+   \return 1 OK
+   \return 0 not reachable
+ */
+int
+Vect_net_ttb_shortest_path_coor(struct Map_info *Map,
+				double fx, double fy, double fz, double tx,
+				double ty, double tz, double fmax,
+				double tmax, int tfield, int tucfield,
+				double *costs, struct line_pnts *Points,
+				struct ilist *List, struct line_pnts *FPoints,
+				struct line_pnts *TPoints, double *fdist,
+				double *tdist)
+{
+    return find_shortest_path_coor(Map, fx, fy, fz, tx, ty, tz, fmax, tmax,
+				   tfield, tucfield, costs, Points, List,
+				   NULL, FPoints, TPoints, fdist, tdist);
+}
+
+/*!
+   \brief Find shortest path on network between 2 points given by coordinates. 
+
+   \param Map vector map
+   \param fx,fy,fz from point x coordinate (z ignored)
+   \param tx,ty,tz to point x coordinate (z ignored)
+   \param fmax maximum distance to the network from 'from'
+   \param tmax maximum distance to the network from 'to'
+   \param costs pointer where to store costs on the network (or NULL)
+   \param Points pointer to the structure where to store vertices of shortest path (or NULL)
+   \param List pointer to the structure where list of lines on the network is stored (or NULL)
+   \param NodesList pointer to the structure where list of nodes on the network is stored (or NULL)
+   \param FPoints pointer to the structure where to store line from 'from' to first network node (or NULL)
+   \param TPoints pointer to the structure where to store line from last network node to 'to' (or NULL)
+   \param fdist distance from 'from' to the net (or NULL)
+   \param tdist distance from 'to' to the net (or NULL)
+
+   \return 1 OK, 0 not reachable
+ */
+int
+Vect_net_shortest_path_coor2(struct Map_info *Map,
+			     double fx, double fy, double fz, double tx,
+			     double ty, double tz, double fmax, double tmax,
+			     double *costs, struct line_pnts *Points,
+			     struct ilist *List, struct ilist *NodesList,
+			     struct line_pnts *FPoints,
+			     struct line_pnts *TPoints, double *fdist,
+			     double *tdist)
+{
+    return find_shortest_path_coor(Map, fx, fy, fz, tx, ty, tz, fmax, tmax, 0,
+				   0, costs, Points, List, NodesList, FPoints,
+				   TPoints, fdist, tdist);
+}
+
+/*!
+   \brief Find shortest path on network with turntable between 2 points given by coordinates. 
+
+   \param Map vector map
+   \param fx,fy,fz from point x coordinate (z ignored)
+   \param tx,ty,tz to point x coordinate (z ignored)
+   \param fmax maximum distance to the network from 'from'
+   \param tmax maximum distance to the network from 'to'
+   \param tfield number of layer where turntable is attached
+   \param tucfield number of layer with unique categories used in turntable 
+   \param costs pointer where to store costs on the network (or NULL)
+   \param Points pointer to the structure where to store vertices of shortest path (or NULL)
+   \param List pointer to the structure where list of lines on the network is stored (or NULL)
+   \param NodesList pointer to the structure where list of nodes on the network is stored (or NULL)
+   \param FPoints pointer to the structure where to store line from 'from' to first network node (or NULL)
+   \param TPoints pointer to the structure where to store line from last network node to 'to' (or NULL)
+   \param fdist distance from 'from' to the net (or NULL)
+   \param tdist distance from 'to' to the net (or NULL)
+
+   \return 1 OK, 0 not reachable
+ */
+int
+Vect_net_ttb_shortest_path_coor2(struct Map_info *Map,
+				 double fx, double fy, double fz, double tx,
+				 double ty, double tz, double fmax,
+				 double tmax, int tfield, int tucfield,
+				 double *costs, struct line_pnts *Points,
+				 struct ilist *List, struct ilist *NodesList,
+				 struct line_pnts *FPoints,
+				 struct line_pnts *TPoints, double *fdist,
+				 double *tdist)
+{
+    return find_shortest_path_coor(Map, fx, fy, fz, tx, ty, tz, fmax, tmax,
+				   tfield, tucfield, costs, Points, List,
+				   NodesList, FPoints, TPoints, fdist, tdist);
+}
