1"""
2@package vnet.dialogs
3
4@brief Dialogs for vector network analysis front-end
5
6Classes:
7 - dialogs::VNETDialog
8 - dialogs::PtsList
9 - dialogs::SettingsDialog
10 - dialogs::CreateTtbDialog
11 - dialogs::OutputVectorDialog
12 - dialogs::VnetStatusbar
13 - dialogs::DefIntesectionTurnCostDialog
14 - dialogs::DefGlobalTurnsDialog
15 - dialogs::TurnAnglesList
16
17(C) 2012-2013 by the GRASS Development Team
18
19This program is free software under the GNU General Public License
20(>=v2). Read the file COPYING that comes with GRASS for details.
21
22@author Stepan Turek <stepan.turek seznam.cz> (GSoC 2012, mentor: Martin Landa)
23@author Lukas Bocan <silent_bob centrum.cz> (turn costs support)
24@author Eliska Kyzlikova <eliska.kyzlikova gmail.com> (turn costs support)
25"""
26
27import os
28import sys
29import types
30import six
31
32if sys.version_info.major >= 3:
33    unicode = str
34
35from copy import copy
36from grass.script import core as grass
37
38import wx
39import wx.aui
40try:
41    import wx.lib.agw.flatnotebook as FN
42except ImportError:
43    import wx.lib.flatnotebook as FN
44import wx.lib.colourselect as csel
45import wx.lib.mixins.listctrl as listmix
46import wx.lib.scrolledpanel as scrolled
47
48from core import globalvar, utils
49from core.gcmd import RunCommand, GMessage
50from core.settings import UserSettings
51
52from dbmgr.base import DbMgrBase
53from dbmgr.vinfo import VectorDBInfo
54
55from gui_core.widgets import GNotebook
56from gui_core.goutput import GConsoleWindow
57from gui_core.gselect import Select, LayerSelect, ColumnSelect
58from gui_core.wrap import (
59    BitmapButton, BitmapFromImage, Button, CheckBox, ComboBox, ListCtrl,
60    Panel, ScrolledPanel, SpinCtrl, StaticBox, StaticText, TextCtrl
61)
62
63from vnet.widgets import PointsList
64from vnet.toolbars import MainToolbar, PointListToolbar, AnalysisToolbar
65from vnet.vnet_core import VNETManager
66from vnet.vnet_utils import DegreesToRadians, RadiansToDegrees, GetNearestNodeCat, ParseMapStr
67
68# Main TODOs
69# - when layer tree of is changed, tmp result map is removed from render list
70# - optimization of map drawing
71# - tmp maps - add number of process
72
73
74class VNETDialog(wx.Dialog):
75
76    def __init__(self, parent, giface, id=wx.ID_ANY,
77                 title=_("GRASS GIS Vector Network Analysis Tool"),
78                 style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER, **kwargs):
79        """Dialog for vector network analysis"""
80
81        wx.Dialog.__init__(
82            self,
83            parent,
84            id,
85            style=style,
86            title=title,
87            **kwargs)
88        self.SetIcon(
89            wx.Icon(
90                os.path.join(
91                    globalvar.ICONDIR,
92                    'grass.ico'),
93                wx.BITMAP_TYPE_ICO))
94
95        self.parent = parent
96        self.mapWin = giface.GetMapWindow()
97        self.giface = giface
98
99        # contains current analysis result (do not have to be last one, when history is browsed),
100        # it is instance of VectMap class
101        self.tmp_result = None
102
103        self.defIsecTurnsHndlrReg = False
104
105        # get attribute table columns only with numbers (for cost columns in
106        # vnet analysis)
107        self.columnTypes = ['integer', 'double precision']
108
109        self.vnet_mgr = VNETManager(self, giface)
110
111        self.vnet_mgr.analysisDone.connect(self.AnalysisDone)
112        self.vnet_mgr.ttbCreated.connect(self.TtbCreated)
113        self.vnet_mgr.snapping.connect(self.Snapping)
114        self.vnet_mgr.pointsChanged.connect(self.PointsChanged)
115
116        self.currAnModule, valid = self.vnet_mgr.GetParam("analysis")
117
118        # toobars
119        self.toolbars = {}
120        self.toolbars['mainToolbar'] = MainToolbar(
121            parent=self, vnet_mgr=self.vnet_mgr)
122        self.toolbars['analysisToolbar'] = AnalysisToolbar(
123            parent=self, vnet_mgr=self.vnet_mgr)
124        #
125        # Fancy gui
126        #
127        self._mgr = wx.aui.AuiManager(self)
128
129        self.mainPanel = Panel(parent=self)
130        self.notebook = GNotebook(parent=self.mainPanel,
131                                  style=globalvar.FNPageDStyle)
132
133        # statusbar
134        self.stPriorities = {'important': 5, 'iformation': 1}
135        self.stBar = VnetStatusbar(parent=self.mainPanel, style=0)
136        self.stBar.SetFieldsCount(number=1)
137
138        self.def_isec_turns = None
139
140        # Create tabs
141
142        # Stores widgets which sets some of analysis parameters (e. g. v.ne.iso
143        # -> iso lines)
144        self.anSettings = {}
145
146        self._createPointsPage()
147
148        # stores widgets which are shown on parameters page
149        # they set data, on which analysis will be done
150        self.inputData = {}
151        self._createParametersPage()
152
153        # Output console for analysis
154        self._createOutputPage()
155
156        # Stores data which are needed for attribute table browser of analysis
157        # input map layers
158        self.inpDbMgrData = {}
159        self._checkSelectedVectorMap()
160
161        # Stores data which are need for attribute table browser of analysis
162        # result map layers
163        self.resultDbMgrData = {}
164        if self.tmp_result:
165            self.resultDbMgrData['input'] = self.tmp_result.GetVectMapName()
166        else:
167            self.resultDbMgrData['input'] = None
168
169        self.notebook.Bind(
170            FN.EVT_FLATNOTEBOOK_PAGE_CHANGED,
171            self.OnPageChanged)
172        self.Bind(wx.EVT_CLOSE, self.OnCloseDialog)
173
174        self._addPanes()
175        self._doVnetDialogLayout()
176        self._mgr.Update()
177
178        self.SetSize((370, 550))
179        self.SetMinSize((370, 420))
180
181        # fix goutput's pane size (required for Mac OSX)
182        if self.gwindow:
183            self.gwindow.SetSashPosition(int(self.GetSize()[1] * .75))
184
185        self.OnAnalysisChanged(None)
186        self.notebook.SetSelectionByName("parameters")
187        self.toolbars['analysisToolbar'].SetMinSize(
188            (-1, self.toolbars['mainToolbar'].GetSize()[1]))
189
190        self.toolbars['mainToolbar'].UpdateUndoRedo(0, 0)
191
192    def _addPanes(self):
193        """Adds toolbar pane and pane with tabs"""
194        self._mgr.AddPane(self.toolbars['mainToolbar'],
195                          wx.aui.AuiPaneInfo().
196                          Name("pointlisttools").Caption(_("Point list toolbar")).
197                          ToolbarPane().Top().Row(0).
198                          Dockable(False).
199                          CloseButton(False).Layer(0))
200
201        self._mgr.AddPane(self.toolbars['analysisToolbar'],
202                          wx.aui.AuiPaneInfo().
203                          Name("analysisTools").Caption(_("Analysis toolbar")).
204                          ToolbarPane().Top().Row(1).
205                          Dockable(False).
206                          CloseButton(False).Layer(0))
207
208        self._mgr.AddPane(self.mainPanel,
209                          wx.aui.AuiPaneInfo().
210                          Name("tabs").CaptionVisible(visible=False).
211                          Center().
212                          Dockable(False).
213                          CloseButton(False).Layer(0))
214
215    def _doVnetDialogLayout(self):
216
217        sizer = wx.BoxSizer(wx.VERTICAL)
218
219        sizer.Add(self.notebook, proportion=1,
220                  flag=wx.EXPAND)
221
222        sizer.Add(self.stBar, proportion=0, flag=wx.EXPAND)
223
224        self.mainPanel.SetSizer(sizer)
225
226        sizer.Fit(self)
227        self.Layout()
228
229    def _createPointsPage(self):
230        """Tab with points list and analysis settings"""
231        pointsPanel = Panel(parent=self)
232        anSettingsPanel = Panel(parent=pointsPanel)
233        maxDistPanel = Panel(parent=anSettingsPanel)
234        maxValue = 1e8
235
236        listBox = StaticBox(parent=pointsPanel, id=wx.ID_ANY,
237                            label=" %s " % _("Points for analysis:"))
238        listSizer = wx.StaticBoxSizer(listBox, wx.VERTICAL)
239        anSettingsBox = StaticBox(parent=anSettingsPanel, id=wx.ID_ANY,
240                                  label=" %s " % _("Analysis settings:"))
241        anSettingsSizer = wx.StaticBoxSizer(anSettingsBox, wx.VERTICAL)
242
243        self.notebook.AddPage(page=pointsPanel,
244                              text=_('Points'),
245                              name='points')
246
247        self.list = PtsList(parent=pointsPanel, vnet_mgr=self.vnet_mgr)
248        toolSwitcher = self.giface.GetMapDisplay().GetToolSwitcher()
249        self.toolbars['pointsList'] = PointListToolbar(
250            parent=pointsPanel,
251            toolSwitcher=toolSwitcher,
252            dialog=self,
253            vnet_mgr=self.vnet_mgr)
254
255        maxDistLabel = StaticText(
256            parent=maxDistPanel, id=wx.ID_ANY,
257            label=_("Maximum distance of point to the network:"))
258        self.anSettings["max_dist"] = SpinCtrl(
259            parent=maxDistPanel, id=wx.ID_ANY, min=0, max=maxValue,
260            size=(150, -1),
261        )
262        self.anSettings["max_dist"].Bind(
263            wx.EVT_SPINCTRL, lambda event: self.MaxDist())
264        self.anSettings["max_dist"].SetValue(100000)  # TODO init val
265        self.MaxDist()
266
267        #showCutPanel =  Panel(parent = anSettingsPanel)
268        # self.anSettings["show_cut"] = CheckBox(parent = showCutPanel, id=wx.ID_ANY,
269        #                                          label = _("Show minimal cut"))
270        #self.anSettings["show_cut"].Bind(wx.EVT_CHECKBOX, self.OnShowCut)
271
272        isoLinesPanel = Panel(parent=anSettingsPanel)
273        isoLineslabel = StaticText(
274            parent=isoLinesPanel,
275            id=wx.ID_ANY,
276            label=_("Iso lines:"))
277        self.anSettings["iso_lines"] = TextCtrl(
278            parent=isoLinesPanel, id=wx.ID_ANY)
279        self.anSettings["iso_lines"].Bind(
280            wx.EVT_TEXT, lambda event: self.IsoLines())
281        self.anSettings["iso_lines"].SetValue("1000,2000,3000")
282        self.IsoLines()
283
284        # Layout
285        AnalysisSizer = wx.BoxSizer(wx.VERTICAL)
286
287        listSizer.Add(self.toolbars['pointsList'], proportion=0)
288        listSizer.Add(self.list, proportion=1, flag=wx.EXPAND)
289
290        maxDistSizer = wx.BoxSizer(wx.HORIZONTAL)
291        maxDistSizer.Add(
292            maxDistLabel,
293            flag=wx.ALIGN_CENTER_VERTICAL,
294            proportion=1)
295        maxDistSizer.Add(self.anSettings["max_dist"],
296                         flag=wx.EXPAND | wx.ALL, border=5, proportion=0)
297        maxDistPanel.SetSizer(maxDistSizer)
298        anSettingsSizer.Add(maxDistPanel, proportion=1, flag=wx.EXPAND)
299
300        #showCutSizer = wx.BoxSizer(wx.HORIZONTAL)
301        # showCutPanel.SetSizer(showCutSizer)
302        # showCutSizer.Add(item = self.anSettings["show_cut"],
303        #                 flag = wx.EXPAND | wx.ALL, border = 5, proportion = 0)
304        #anSettingsSizer.Add(item = showCutPanel, proportion = 1, flag = wx.EXPAND)
305
306        isoLinesSizer = wx.BoxSizer(wx.HORIZONTAL)
307        isoLinesSizer.Add(
308            isoLineslabel,
309            flag=wx.ALIGN_CENTER_VERTICAL,
310            proportion=1)
311        isoLinesSizer.Add(self.anSettings["iso_lines"],
312                          flag=wx.EXPAND | wx.ALL, border=5, proportion=1)
313        isoLinesPanel.SetSizer(isoLinesSizer)
314        anSettingsSizer.Add(isoLinesPanel, proportion=1, flag=wx.EXPAND)
315
316        AnalysisSizer.Add(
317            listSizer,
318            proportion=1,
319            flag=wx.EXPAND | wx.ALL,
320            border=5)
321        AnalysisSizer.Add(
322            anSettingsPanel,
323            proportion=0,
324            flag=wx.EXPAND | wx.RIGHT | wx.LEFT | wx.BOTTOM,
325            border=5)
326
327        anSettingsPanel.SetSizer(anSettingsSizer)
328        pointsPanel.SetSizer(AnalysisSizer)
329
330    def MaxDist(self):
331        val = self.anSettings["max_dist"].GetValue()
332        self.vnet_mgr.SetParams({"max_dist": val}, {})
333
334    def IsoLines(self):
335        val = self.anSettings["iso_lines"].GetValue()
336        self.vnet_mgr.SetParams({"iso_lines": val}, {})
337
338    def OnShowCut(self, event):
339        """Shows vector map with minimal cut (v.net.flow) - not yet implemented"""
340        val = event.IsChecked()
341        if val and self.tmp_result:
342            self.tmp_result.DeleteRenderLayer()
343            cmd = self.GetLayerStyle()
344            self.vnetFlowTmpCut.AddRenderLayer(cmd)
345        else:
346            self.vnetFlowTmpCut.DeleteRenderLayer()
347            cmd = self.GetLayerStyle()
348            self.tmp_result.AddRenderLayer(cmd)
349
350        self.giface.updateMap.emit(render=True, renderVector=True)
351
352    def _createOutputPage(self):
353        """Tab with output console"""
354        outputPanel = Panel(parent=self)
355        self.notebook.AddPage(page=outputPanel,
356                              text=_("Output"),
357                              name='output')
358
359        goutput = self.vnet_mgr.goutput  # TODO make interface
360        self.gwindow = GConsoleWindow(parent=outputPanel, gconsole=goutput)
361
362        # Layout
363        outputSizer = wx.BoxSizer(wx.VERTICAL)
364        outputSizer.Add(self.gwindow, proportion=1, flag=wx.EXPAND)
365        self.gwindow.SetMinSize((-1, -1))
366
367        outputPanel.SetSizer(outputSizer)
368
369    def _createParametersPage(self):
370        """Tab for selection of data for analysis"""
371        dataPanel = ScrolledPanel(parent=self)
372        self.notebook.AddPage(page=dataPanel,
373                              text=_('Parameters'),
374                              name='parameters')
375        label = {}
376        dataSelects = [
377            ['input', "Choose vector map for analysis:", Select],
378            ['arc_layer', "Arc layer number or name:", LayerSelect],
379            ['node_layer', "Node layer number or name:", LayerSelect],
380            #['turn_layer', "Layer with turntable:", LayerSelect],
381            #['turn_cat_layer', "Layer with unique categories for turntable:", LayerSelect],
382            ['arc_column', "", ColumnSelect],
383            ['arc_backward_column', "", ColumnSelect],
384            ['node_column', "", ColumnSelect],
385        ]
386
387        # self.useTurns = CheckBox(parent = dataPanel, id=wx.ID_ANY,
388        #                            label = _('Use turns'))
389
390        box = StaticBox(dataPanel, -1, "Vector map and layers for analysis")
391        bsizer = wx.StaticBoxSizer(box, wx.VERTICAL)
392        box2 = StaticBox(dataPanel, -1, "Costs")
393        bsizer2 = wx.StaticBoxSizer(box2, wx.VERTICAL)
394        selPanels = {}
395
396        for dataSel in dataSelects:
397            selPanels[dataSel[0]] = Panel(parent=dataPanel)
398            if dataSel[0] == 'input':
399                self.inputData[
400                    dataSel[0]] = dataSel[2](
401                    parent=selPanels[dataSel[0]],
402                    size=(-1, -1),
403                    type='vector')
404                icon = wx.Image(
405                    os.path.join(
406                        globalvar.ICONDIR,
407                        "grass",
408                        "layer-vector-add.png"))
409                icon.Rescale(18, 18)
410                icon = BitmapFromImage(icon)
411                self.addToTreeBtn = BitmapButton(
412                    parent=selPanels[dataSel[0]],
413                    bitmap=icon, size=globalvar.DIALOG_COLOR_SIZE)
414                self.addToTreeBtn.SetToolTip(
415                    _("Add vector map into layer tree"))
416                self.addToTreeBtn.Disable()
417                self.addToTreeBtn.Bind(wx.EVT_BUTTON, self.OnToTreeBtn)
418            elif dataSel[0] != 'input':
419                # if dataSel[0] == "turn_layer":
420                #    self.createTtbBtn = wx.Button(parent = selPanels[dataSel[0]],
421                #                                 label = _("Create turntable"))
422                #    self.createTtbBtn.Bind(wx.EVT_BUTTON, self.OnCreateTtbBtn)
423
424                self.inputData[dataSel[0]] = dataSel[2](
425                    parent=selPanels[dataSel[0]], size=(-1, -1))
426            label[dataSel[0]] = StaticText(parent=selPanels[dataSel[0]],
427                                           name=dataSel[0])
428            label[dataSel[0]].SetLabel(dataSel[1])
429
430        self.inputData['input'].Bind(wx.EVT_TEXT, self.OnVectSel)
431        self.inputData['arc_layer'].Bind(wx.EVT_TEXT, self.OnALayerSel)
432        self.inputData['node_layer'].Bind(wx.EVT_TEXT, self.OnNLayerSel)
433
434        # , "turn_layer", "turn_cat_layer"]:
435        for params in ["arc_column", "arc_backward_column", "node_column"]:
436            self.inputData[params].Bind(
437                wx.EVT_TEXT, lambda event: self._setInputData())
438
439        # self.useTurns.Bind(wx.EVT_CHECKBOX,
440        #                    lambda event: self.UseTurns())
441        # self.UseTurns()
442
443        # Layout
444        mainSizer = wx.BoxSizer(wx.VERTICAL)
445
446        mainSizer.Add(bsizer, proportion=0,
447                      flag=wx.EXPAND | wx.TOP | wx.LEFT | wx.RIGHT, border=5)
448
449        # , 'turn_layer', 'turn_cat_layer']:
450        for sel in ['input', 'arc_layer', 'node_layer']:
451            if sel == 'input':
452                btn = self.addToTreeBtn
453            # elif sel == "turn_layer":
454            #    btn = self.createTtbBtn
455            else:
456                btn = None
457            # if sel == 'turn_layer':
458            #    bsizer.Add(item = self.useTurns, proportion = 0,
459            #                flag = wx.TOP | wx.LEFT | wx.RIGHT, border = 5)
460
461            selPanels[sel].SetSizer(self._doSelLayout(title=label[sel],
462                                                      sel=self.inputData[sel],
463                                                      btn=btn))
464            bsizer.Add(selPanels[sel], proportion=0,
465                       flag=wx.EXPAND)
466
467        mainSizer.Add(bsizer2, proportion=0,
468                      flag=wx.EXPAND | wx.TOP | wx.LEFT | wx.RIGHT, border=5)
469
470        for sel in ['arc_column', 'arc_backward_column', 'node_column']:
471            selPanels[sel].SetSizer(
472                self._doSelLayout(
473                    title=label[sel],
474                    sel=self.inputData[sel]))
475            bsizer2.Add(selPanels[sel], proportion=0,
476                        flag=wx.EXPAND)
477
478        dataPanel.SetSizer(mainSizer)
479        dataPanel.SetupScrolling()
480
481        dataPanel.SetAutoLayout(1)
482
483    def _doSelLayout(self, title, sel, btn=None):
484        # helper function for layout of self.inputData widgets initialized in
485        # _createParametersPage
486        selSizer = wx.BoxSizer(orient=wx.VERTICAL)
487
488        selTitleSizer = wx.BoxSizer(wx.HORIZONTAL)
489        selTitleSizer.Add(title, proportion=1,
490                          flag=wx.LEFT | wx.TOP | wx.EXPAND, border=5)
491
492        selSizer.Add(selTitleSizer, proportion=0,
493                     flag=wx.EXPAND)
494
495        if btn:
496            selFiledSizer = wx.BoxSizer(orient=wx.HORIZONTAL)
497            selFiledSizer.Add(sel, proportion=1,
498                              flag=wx.EXPAND | wx.ALL)
499
500            selFiledSizer.Add(btn, proportion=0,
501                              flag=wx.EXPAND | wx.ALL)
502
503            selSizer.Add(selFiledSizer, proportion=0,
504                         flag=wx.EXPAND | wx.ALL,
505                         border=5)
506        else:
507            selSizer.Add(sel, proportion=1,
508                         flag=wx.EXPAND | wx.ALL,
509                         border=5)
510        return selSizer
511
512    def _checkSelectedVectorMap(self):
513        """Check selected vector map"""
514        selMapName = None
515        # if selected vector map is in layer tree then set it
516        layer = self.giface.GetLayerList().GetSelectedLayer()
517        if layer is not None and layer.type == 'vector':
518            selMapName = layer.maplayer.name
519            self._createInputDbMgrPage()
520
521        self.inpDbMgrData['input'] = None
522        if selMapName:
523            self.inputData['input'].SetValue(selMapName)
524            self.OnVectSel(None)
525
526    def _createInputDbMgrPage(self):
527        """Tab with attribute tables of analysis input layers"""
528        self.inpDbMgrData['dbMgr'] = DbMgrBase()
529
530        self.inpDbMgrData['browse'] = self.inpDbMgrData[
531            'dbMgr'].CreateDbMgrPage(parent=self.notebook, pageName='browse')
532        self.inpDbMgrData['browse'].SetTabAreaColour(globalvar.FNPageColor)
533
534    def _updateInputDbMgrPage(self, show):
535        """Show or hide input tables tab"""
536        if show and self.notebook.GetPageIndexByName('inputDbMgr') == -1:
537            self.notebook.AddPage(page=self.inpDbMgrData['browse'],
538                                  text=_('Input tables'),
539                                  name='inputDbMgr')
540        elif not show:
541            self.notebook.RemovePage(page=self.notebook.GetPageIndexByName('inputDbMgr'))
542
543    def _createResultDbMgrPage(self):
544        """Tab with attribute tables of analysis result layers"""
545        self.resultDbMgrData['dbMgr'] = DbMgrBase()
546        self.resultDbMgrData['browse'] = self.resultDbMgrData[
547            'dbMgr'].CreateDbMgrPage(parent=self.notebook, pageName='browse')
548        self.resultDbMgrData['browse'].SetTabAreaColour(globalvar.FNPageColor)
549
550    def _updateResultDbMgrPage(self):
551        """Show or Hide Result tables tab"""
552        # analysis, which created result
553        analysis = self.resultDbMgrData['analysis']
554        # TODO maybe no need to store this information, just check it has
555        # attribute table, if so show it
556        haveDbMgr = self.vnet_mgr.GetAnalysisProperties(
557            analysis)["resultProps"]["dbMgr"]
558
559        if haveDbMgr and self.notebook.GetPageIndexByName('resultDbMgr') == -1:
560            self._createResultDbMgrPage()
561            self.notebook.AddPage(page=self.resultDbMgrData['browse'],
562                                  text=_('Result tables'),
563                                  name='resultDbMgr')
564        elif not haveDbMgr:
565            page = self.notebook.GetPageIndexByName("resultDbMgr")
566            if page != -1:
567                self.notebook.RemovePage(page=page)
568
569    def OnPageChanged(self, event):
570        """Tab switched"""
571        if event.GetEventObject() == self.notebook:
572            dbMgrIndxs = []
573            dbMgrIndxs.append(self.notebook.GetPageIndexByName('inputDbMgr'))
574            dbMgrIndxs.append(self.notebook.GetPageIndexByName('resultDbMgr'))
575            if self.notebook.GetSelection() in dbMgrIndxs:
576                self.stBar.AddStatusItem(
577                    text=_('Loading tables...'),
578                    key='dbMgr',
579                    priority=self.stPriorities['important'])
580                self._updateDbMgrData()
581                self.stBar.RemoveStatusItem(key='dbMgr')
582            # update columns (when some is added in input tables browser), TODO
583            # needs optimization
584            elif self.notebook.GetSelection() == self.notebook.GetPageIndexByName('parameters'):
585                self.OnALayerSel(None)
586                self.OnNLayerSel(None)
587
588        self.Layout()
589
590    def _updateDbMgrData(self):
591        """Updates input/result tables page """
592        if self.notebook.GetSelection() == self.notebook.GetPageIndexByName('inputDbMgr'):
593            self._updateInputDbMgrData()
594        elif self.notebook.GetSelection() == self.notebook.GetPageIndexByName('resultDbMgr'):
595            self._updateResultDbMgrData()
596        else:
597            self.stBar.RemoveStatusItem('manager')
598
599    def _updateInputDbMgrData(self):
600        """Loads data according to selected layers in Parameters tab"""
601        inpSel = self.inputData['input'].GetValue().strip()
602        # changed vector map
603        if self.inpDbMgrData['input'] != inpSel:
604            wx.BeginBusyCursor()
605            self.inpDbMgrData['dbMgr'].ChangeVectorMap(vectorName=inpSel)
606            self.inpDbMgrData['input'] = inpSel
607            for layerName in ['arc_layer', 'node_layer']:
608                try:
609                    layer = int(self.inputData[layerName].GetValue())
610                except ValueError:
611                    continue
612                self.inpDbMgrData['browse'].AddLayer(layer)
613            wx.EndBusyCursor()
614        # same vector map
615        else:
616            needLayers = []
617            browseLayers = self.inpDbMgrData['browse'].GetAddedLayers()
618            for layerName in ['arc_layer', 'node_layer']:
619                try:
620                    inpLayer = int(self.inputData[layerName].GetValue())
621                except ValueError:
622                    continue
623
624                if inpLayer in browseLayers:
625                    needLayers.append(inpLayer)
626                    continue
627                else:
628                    wx.BeginBusyCursor()
629                    self.inpDbMgrData['browse'].AddLayer(inpLayer)
630                    wx.EndBusyCursor()
631                    needLayers.append(inpLayer)
632
633            for layer in browseLayers:
634                if layer not in needLayers:
635                    self.inpDbMgrData['browse'].DeletePage(layer)
636
637    def _updateResultDbMgrData(self):
638        """Loads data from analysis result map"""
639        if not self.tmp_result:
640            return
641        vectName = self.tmp_result.GetVectMapName()
642
643        if self.resultDbMgrData['input'] != vectName:
644            wx.BeginBusyCursor()
645            dbMgr = self.resultDbMgrData['dbMgr']
646            dbMgr.ChangeVectorMap(vectorName=vectName)
647
648            for layer in dbMgr.GetVectorLayers():
649                self.resultDbMgrData['browse'].AddLayer(layer)
650
651            self.resultDbMgrData['input'] = vectName
652            wx.EndBusyCursor()
653
654    def OnToTreeBtn(self, event):
655        """Add vector map into layer tree"""
656        vectorMap = self.inputData['input'].GetValue()
657        vectMapName, mapSet = ParseMapStr(vectorMap)
658        vectorMap = vectMapName + '@' + mapSet
659        existsMap = grass.find_file(name=vectMapName,
660                                    element='vector',
661                                    mapset=mapSet)
662        if not existsMap["name"]:
663            return
664
665        cmd = ['d.vect',
666               'map=' + vectorMap]
667
668        if True:
669            self.giface.GetLayerList().AddLayer(ltype="vector",
670                                                cmd=cmd,
671                                                name=vectorMap,
672                                                checked=True)
673        # d.mon case is not need giface implementation should solve it for us
674
675    def UseTurns(self):
676        if self.useTurns.IsChecked():
677            self.inputData["turn_layer"].GetParent().Show()
678            self.inputData["turn_cat_layer"].GetParent().Show()
679
680            self.vnet_mgr.SetParams(params={}, flags={"t": True})
681        else:
682            self.inputData["turn_layer"].GetParent().Hide()
683            self.inputData["turn_cat_layer"].GetParent().Hide()
684
685            self.vnet_mgr.SetParams(params={}, flags={"t": False})
686
687        self.Layout()
688
689    def PointsChanged(self, method, kwargs):
690
691        if method == "EditMode" and not kwargs["activated"]:
692            ptListToolbar = self.toolbars['pointsList']
693            if ptListToolbar:
694                ptListToolbar.ToggleTool(
695                    ptListToolbar.GetToolId("insertPoint"), False)
696
697        if method == "EditMode" and kwargs["activated"]:
698            ptListToolbar = self.toolbars['pointsList']
699            if ptListToolbar:
700                ptListToolbar.ToggleTool(
701                    ptListToolbar.GetToolId("insertPoint"), True)
702
703        if method == "SetPointData" and (
704                "e" in kwargs.keys() or "n" in kwargs.keys()):
705            self.notebook.SetSelectionByName("points")
706
707    def OnCreateTtbBtn(self, event):
708
709        params, err_params, flags = self.vnet_mgr.GetParams()
710        dlg = CreateTtbDialog(parent=self, init_data=params)
711
712        if dlg.ShowModal() == wx.ID_OK:
713            self.stBar.AddStatusItem(text=_('Creating turntable...'),
714                                     key='ttb',
715                                     priority=self.stPriorities['important'])
716
717            params = dlg.GetData()
718            if not self.vnet_mgr.CreateTttb(params):
719                self.stBar.RemoveStatusItem('ttb')
720        dlg.Destroy()
721
722    def TtbCreated(self):
723
724        params, err_params, flags = self.vnet_mgr.GetParams()
725        self._updateParamsTab(params, flags)
726
727        self.stBar.RemoveStatusItem('ttb')
728
729    def OnVectSel(self, event):
730        """When vector map is selected it populates other comboboxes in Parameters tab (layer selects, columns selects)"""
731        if self.vnet_mgr.IsSnappingActive():  # TODO should be in vnet_mgr
732            self.vnet_mgr.Snapping(activate=True)
733
734        vectMapName, mapSet = self._parseMapStr(
735            self.inputData['input'].GetValue())
736        vectorMap = vectMapName + '@' + mapSet
737
738        # , 'turn_layer', 'turn_cat_layer']:
739        for sel in ['arc_layer', 'node_layer']:
740            self.inputData[sel].Clear()
741            self.inputData[sel].InsertLayers(vector=vectorMap)
742
743        items = self.inputData['arc_layer'].GetItems()
744        itemsLen = len(items)
745        if itemsLen < 1:
746            self.addToTreeBtn.Disable()
747            if hasattr(self, 'inpDbMgrData'):
748                self._updateInputDbMgrPage(show=False)
749            self.inputData['arc_layer'].SetValue("")
750            self.inputData['node_layer'].SetValue("")
751            for sel in ['arc_column', 'arc_backward_column', 'node_column']:
752                self.inputData[sel].SetValue("")
753            return
754        elif itemsLen == 1:
755            self.inputData['arc_layer'].SetSelection(0)
756            self.inputData['node_layer'].SetSelection(0)
757        elif itemsLen >= 1:
758            if unicode("1") in items:
759                iItem = items.index(unicode("1"))
760                self.inputData['arc_layer'].SetSelection(iItem)
761            if unicode("2") in items:
762                iItem = items.index(unicode("2"))
763                self.inputData['node_layer'].SetSelection(iItem)
764
765        self.addToTreeBtn.Enable()
766        if 'browse' in self.inpDbMgrData:
767            self._updateInputDbMgrPage(show=True)
768        else:
769            self._createInputDbMgrPage()
770            self._updateInputDbMgrPage(show=True)
771
772        self.OnALayerSel(event)
773        self.OnNLayerSel(event)
774
775        self._setInputData()
776
777    def _updateParamsTab(self, params, flags):
778        # TODO flag
779
780                #'turn_layer', 'turn_cat_layer',
781        for k in ['input', 'arc_layer', 'node_layer',
782                  'arc_column', 'arc_backward_column', 'node_column']:
783            self.inputData[k].SetValue(params[k])
784
785    def OnALayerSel(self, event):
786        """When arc layer from vector map is selected, populates corespondent columns selects"""
787        self.inputData['arc_column'].InsertColumns(
788            vector=self.inputData['input'].GetValue(),
789            layer=self.inputData['arc_layer'].GetValue(),
790            type=self.columnTypes)
791        self.inputData['arc_backward_column'].InsertColumns(
792            vector=self.inputData['input'].GetValue(),
793            layer=self.inputData['arc_layer'].GetValue(),
794            type=self.columnTypes)
795
796        self._setInputData()
797
798    def OnNLayerSel(self, event):
799        """When node layer from vector map is selected, populates corespondent column select"""
800        if self.vnet_mgr.IsSnappingActive():
801            self.vnet_mgr.Snapping(activate=True)
802
803        self.inputData['node_column'].InsertColumns(
804            vector=self.inputData['input'].GetValue(),
805            layer=self.inputData['node_layer'].GetValue(),
806            type=self.columnTypes)
807
808        self._setInputData()
809
810    def _setInputData(self):
811        params = {}
812        for k, v in six.iteritems(self.inputData):
813            params[k] = v.GetValue()
814        flags = {}
815        self.vnet_mgr.SetParams(params, flags)
816
817    def _parseMapStr(self, vectMapStr):
818        """Create full map name (add current mapset if it is not present in name)"""
819        mapValSpl = vectMapStr.strip().split("@")
820        if len(mapValSpl) > 1:
821            mapSet = mapValSpl[1]
822        else:
823            mapSet = grass.gisenv()['MAPSET']
824        mapName = mapValSpl[0]
825
826        return mapName, mapSet
827
828    def OnCloseDialog(self, event=None):
829        """Cancel dialog"""
830        self.vnet_mgr.CleanUp()
831        self._mgr.UnInit()
832        toolSwitcher = self.giface.GetMapDisplay().GetToolSwitcher()
833        toolSwitcher.RemoveToolbarFromGroup(
834            'mouseUse', self.toolbars['pointsList'])
835        self.parent.dialogs['vnet'] = None
836        self.Destroy()
837
838    def OnDefIsecTurnCosts(self, event):
839        """Registers/unregisters mouse handler into map window"""
840        if self.defIsecTurnsHndlrReg == False:
841            self.mapWin.RegisterMouseEventHandler(wx.EVT_LEFT_DOWN,
842                                                  self.OnDefIsecTurnCost,
843                                                  'cross')
844            self.defIsecTurnsHndlrReg = True
845        else:
846            self.mapWin.UnregisterMouseEventHandler(wx.EVT_LEFT_DOWN,
847                                                    self.OnDefIsecTurnCost)
848
849            self.defIsecTurnsHndlrReg = False
850
851    def OnDefGlobalTurnCosts(self, event):
852
853        dialog = DefGlobalTurnsDialog(
854            self, data=self.vnet_mgr.GetGlobalTurnsData())
855        dialog.Show()
856
857    def OnDefIsecTurnCost(self, event):  # TODO move to vnet mgr?
858        """Take coordinates from map window"""
859        if event == 'unregistered':
860            ptListToolbar = self.toolbars['pointsList']
861            if ptListToolbar:
862                ptListToolbar.ToggleTool(
863                    id=ptListToolbar.GetToolId("isec_turn_edit"), toggle=False)
864            self.handlerRegistered = False
865            return
866
867        e, n = self.mapWin.GetLastEN()
868
869        # compute threshold
870        snapTreshPix = int(UserSettings.Get(group='vnet',
871                                            key='other',
872                                            subkey='snap_tresh'))
873        res = max(
874            self.mapWin.Map.region['nsres'],
875            self.mapWin.Map.region['ewres'])
876        snapTreshDist = snapTreshPix * res
877
878        params, err_params, flags = self.vnet_mgr.GetParams()
879
880        if "input" in err_params:
881            GMessage(parent=self,
882                     message=_("Input vector map does not exist."))
883
884        if ["turn_layer", "turn_cat_layer"] in err_params:
885            GMessage(
886                parent=self,
887                message="Please choose existing turntable layer and unique categories layer in Parameters tab.")
888
889        cat = GetNearestNodeCat(
890            e, n, int(params['turn_cat_layer']),
891            snapTreshDist, params["input"])
892
893        if not self.def_isec_turns:
894            self.def_isec_turns = DefIntesectionTurnCostDialog(
895                self, self.parent)
896            self.def_isec_turns.SetSize((500, 400))
897
898        self.def_isec_turns.SetData(params["input"], params["turn_layer"])
899        self.def_isec_turns.SetIntersection(cat)
900        self.def_isec_turns.Show()
901
902    def OnAnalyze(self, event):
903        """Called when network analysis is started"""
904
905        self.stBar.AddStatusItem(text=_('Analysing...'),
906                                 key='analyze',
907                                 priority=self.stPriorities['important'])
908
909        ret = self.vnet_mgr.RunAnalysis()
910
911        # TODO
912        self.resultDbMgrData['analysis'] = self.currAnModule
913
914        if ret < 0:
915            self.stBar.RemoveStatusItem(key='analyze')
916            if ret == -2:
917                self.notebook.SetSelectionByName("parameters")
918
919    def AnalysisDone(self):
920
921        curr_step, steps_num = self.vnet_mgr.GetHistStep()
922        self.toolbars['mainToolbar'].UpdateUndoRedo(curr_step, steps_num)
923
924        self.tmp_result = self.vnet_mgr.GetResults()
925
926        mainToolbar = self.toolbars['mainToolbar']
927        id = vars(mainToolbar)['showResult']
928        mainToolbar.ToggleTool(id, True)
929
930        self.stBar.RemoveStatusItem(key='analyze')
931
932        self._updateResultDbMgrPage()
933        self._updateDbMgrData()
934
935        self.giface.updateMap.emit(render=True, renderVector=True)
936
937    def OnShowResult(self, event):
938        """Show/hide analysis result"""
939        mainToolbar = self.toolbars['mainToolbar']
940        id = vars(mainToolbar)['showResult']
941        toggleState = mainToolbar.GetToolState(id)
942
943        if not self.tmp_result:
944            mainToolbar.ToggleTool(id, False)
945        elif toggleState:
946            self.vnet_mgr.ShowResult(True)
947        else:
948            self.vnet_mgr.ShowResult(False)
949
950    def OnSaveTmpLayer(self, event):
951        dlg = OutputVectorDialog(parent=self)
952        dlg.ShowModal()
953        self.vnet_mgr.SaveTmpLayer(dlg.vectSel.GetValue())
954        dlg.Destroy()
955
956    def OnSettings(self, event):
957        """Displays vnet settings dialog"""
958        dlg = SettingsDialog(parent=self, id=wx.ID_ANY,
959                             title=_('Settings'), vnet_mgr=self.vnet_mgr)
960
961        dlg.ShowModal()
962        dlg.Destroy()
963
964    def OnAnalysisChanged(self, event):
965        """Updates dialog when analysis is changed"""
966        # set chosen analysis
967        iAn = self.toolbars['analysisToolbar'].anChoice.GetSelection()
968        self.currAnModule = self.vnet_mgr.GetAnalyses()[iAn]
969        self.vnet_mgr.SetParams({"analysis": self.currAnModule}, {})
970
971        # update dialog for particular analysis
972        if self.currAnModule == "v.net.iso":
973            self.anSettings['iso_lines'].GetParent().Show()
974        else:
975            self.anSettings['iso_lines'].GetParent().Hide()
976
977        # if self.currAnModule == "v.net.flow": TODO not implemented
978        #    self.anSettings['show_cut'].GetParent().Show()
979        # else:
980        #    self.anSettings['show_cut'].GetParent().Hide()
981
982        # show only corresponding selects for chosen v.net module
983        skip = []
984
985        an_props = self.vnet_mgr.GetAnalysisProperties()
986
987        used_cols = []
988        attrCols = an_props["cmdParams"]["cols"]
989
990        for col in six.iterkeys(attrCols):
991
992            if "inputField" in attrCols[col]:
993                colInptF = attrCols[col]["inputField"]
994            else:
995                colInptF = col
996
997            if col in skip:
998                continue
999
1000            inputPanel = self.inputData[colInptF].GetParent()
1001            inputPanel.Show()
1002            inputPanel.FindWindowByName(
1003                colInptF).SetLabel(attrCols[col]["label"])
1004
1005            if col != colInptF:
1006                skip.append(colInptF)
1007            used_cols.append(colInptF)
1008
1009        for col in ["arc_backward_column", "arc_column", "node_column"]:
1010            if col not in used_cols:
1011                inputPanel = self.inputData[colInptF].GetParent()
1012                inputPanel.Hide()
1013
1014        self.Layout()
1015
1016    def Snapping(self, evt):
1017        """Start/stop snapping mode"""
1018
1019        if evt == "deactivated":
1020            self.stBar.RemoveStatusItem(key='snap')
1021            ptListToolbar = self.toolbars['pointsList']
1022            ptListToolbar.ToggleTool(ptListToolbar.GetToolId("snapping"), False)
1023
1024        elif evt == "computing_points":
1025            self.stBar.AddStatusItem(text=_('Computing nodes...'),
1026                                     key='snap',
1027                                     priority=self.stPriorities['important'])
1028
1029        elif evt == "computing_points_done":
1030            self.stBar.RemoveStatusItem(key='snap')
1031
1032    def SnapPointsDone(self):
1033        """Update map window, when map with nodes to snap is created"""
1034        self.stBar.RemoveStatusItem(key='snap')
1035
1036    def OnUndo(self, event):
1037        """Step back in history"""
1038
1039        curr_step, steps_num = self.vnet_mgr.Undo()
1040        self._updateDialog()
1041        self.toolbars['mainToolbar'].UpdateUndoRedo(curr_step, steps_num)
1042
1043    def OnRedo(self, event):
1044        """Step forward in history"""
1045
1046        curr_step, steps_num = self.vnet_mgr.Redo()
1047        self._updateDialog()
1048        self.toolbars['mainToolbar'].UpdateUndoRedo(curr_step, steps_num)
1049
1050    def _updateDialog(self):
1051        params, err_params, flags = self.vnet_mgr.GetParams()
1052        self._updateParamsTab(params, flags)
1053
1054        anChoice = self.toolbars['analysisToolbar'].anChoice
1055        anChoice.SetSelection(
1056            self.vnet_mgr.GetAnalyses().index(
1057                params["analysis"]))
1058        self.currAnModule = params["analysis"]
1059        self.resultDbMgrData['analysis'] = params["analysis"]
1060
1061        # set analysis combobox
1062        anChoice = self.toolbars['analysisToolbar'].anChoice
1063        anChoice.SetSelection(
1064            self.vnet_mgr.GetAnalyses().index(
1065                params["analysis"]))
1066
1067        self._updateResultDbMgrPage()
1068        self._updateDbMgrData()
1069
1070        self.OnAnalysisChanged(None)
1071
1072
1073class PtsList(PointsList):
1074
1075    def __init__(self, parent, vnet_mgr, id=wx.ID_ANY):
1076        """ List with points for analysis"""
1077        self.updateMap = True
1078        self.vnet_mgr = vnet_mgr
1079        self.pts_data = self.vnet_mgr.GetPointsManager()
1080
1081        pts_data_cols = self.pts_data.GetColumns(only_relevant=False)
1082
1083        cols = []
1084        for i_col, name in enumerate(pts_data_cols["name"]):
1085            if i_col == 0:
1086                continue
1087            cols.append([name, pts_data_cols["label"][i_col], pts_data_cols[
1088                        "type"][i_col], pts_data_cols["def_vals"][i_col]])
1089
1090        PointsList.__init__(self, parent=parent, cols=cols, id=id)
1091
1092        self.vnet_mgr.pointsChanged.connect(self.PointsChanged)
1093        self.vnet_mgr.parametersChanged.connect(self.ParametersChanged)
1094
1095        analysis, valid = self.vnet_mgr.GetParam("analysis")
1096
1097        self.AnalysisChanged(analysis)
1098
1099        for iPt in range(self.pts_data.GetPointsCount()):
1100            self.AddItem()
1101            pt_dt = self.pts_data.GetPointData(iPt)
1102            self.SetData(iPt, pt_dt)
1103        self.Select(self.pts_data.GetSelected())
1104
1105    def AnalysisChanged(self, analysis):
1106        active_cols = self.pts_data.GetColumns()
1107        if 'type' in active_cols["name"]:
1108            if not self.IsShown('type'):
1109                self.ShowColumn('type', 1)
1110
1111            type_idx = active_cols["name"].index("type")
1112            type_labels = active_cols["type"][type_idx]
1113
1114            self.ChangeColEditable('type', type_labels)
1115            colNum = self._getColumnNum('type')
1116
1117            for iItem, item in enumerate(self.itemDataMap):
1118                self.EditCellKey(iItem, 'type', self.selIdxs[iItem][colNum])
1119
1120                if not item[1]:
1121                    self.CheckItem(iItem, False)
1122
1123        else:
1124            if self.IsShown('type'):
1125                self.HideColumn('type')
1126
1127    def ParametersChanged(self, method, kwargs):
1128        if "analysis" in kwargs["changed_params"].keys():
1129            self.AnalysisChanged(analysis=kwargs["changed_params"]["analysis"])
1130
1131    def OnItemActivated(self, event):
1132        changed, key = PointsList.OnItemActivated(self, event)
1133
1134        if not changed:
1135            return
1136
1137        dt_dict = {}
1138        active_cols = self.pts_data.GetColumns()
1139        for col in active_cols["name"]:
1140            if col == "use":
1141                continue
1142            dt_dict[col] = self.GetCellValue(key, col)
1143
1144        self.pts_data.SetPointData(key, dt_dict)
1145
1146    def PointsChanged(self, method, kwargs):
1147        if method == "AddPoint":
1148            self.AddItem()
1149
1150        elif method == "DeletePoint":
1151            self.DeleteItem()
1152
1153        elif method == "SetPointData":
1154            self.SetData(kwargs["pt_id"], kwargs["data"])
1155
1156        elif method == "SetPoints":
1157            while self.GetSelected() != wx.NOT_FOUND:
1158                self.DeleteItem()
1159
1160            for iPt, pt_dt in enumerate(kwargs["pts_data"]):
1161                self.AddItem()
1162                self.SetData(iPt, pt_dt)
1163
1164        elif method == "SetSelected":
1165            self.Select(self._findIndex(kwargs["pt_id"]))
1166
1167    def SetData(self, key, data):
1168
1169        idx = self._findIndex(key)
1170        for k, v in six.iteritems(data):
1171            if k == "use":
1172
1173                if v and not self.IsItemChecked(idx):
1174                    self.CheckItem(idx, True)
1175                elif not v and self.IsItemChecked(idx):
1176                    self.CheckItem(idx, False)
1177            else:
1178                found = 0
1179                for col in self.colsData:
1180                    if k == col[0]:
1181                        found = 1
1182                        break
1183
1184                if found:
1185                    self.EditCellKey(key, k, v)
1186
1187    def OnItemSelected(self, event):
1188        """Item selected"""
1189        PointsList.OnItemSelected(self, event)
1190        self.selectedkey = self.GetItemData(self.selected)
1191
1192        if self.selectedkey == self.pts_data.GetSelected():
1193            return
1194
1195        if self.selectedkey == wx.NOT_FOUND:
1196            self.pts_data.SetSelected(None)
1197        else:
1198            self.pts_data.SetSelected(self.selectedkey)
1199
1200    def OnCheckItem(self, index, flag):
1201        "flag is True if the item was checked, False if unchecked"
1202        key = self.GetItemData(index)
1203        if self.pts_data.GetPointData(key)["use"] != flag:
1204            self.pts_data.SetPointData(key, {"use": flag})
1205
1206
1207class SettingsDialog(wx.Dialog):
1208
1209    def __init__(
1210            self, parent, id, title, vnet_mgr, pos=wx.DefaultPosition,
1211            size=wx.DefaultSize, style=wx.DEFAULT_DIALOG_STYLE):
1212        """Settings dialog"""
1213        wx.Dialog.__init__(self, parent, id, title, pos, size, style)
1214
1215        self.vnet_mgr = vnet_mgr
1216
1217        maxValue = 1e8
1218        self.parent = parent
1219        self.settings = {}
1220
1221        # create all staticboxes before creating widgets, needed for Mac
1222        otherBox = StaticBox(parent=self, id=wx.ID_ANY,
1223                             label=" %s " % _("Other settings"))
1224        otherBoxSizer = wx.StaticBoxSizer(otherBox, wx.VERTICAL)
1225        ptsStyleBox = StaticBox(parent=self, id=wx.ID_ANY,
1226                                label=" %s " % _("Point style:"))
1227        ptsStyleBoxSizer = wx.StaticBoxSizer(ptsStyleBox, wx.VERTICAL)
1228        styleBox = StaticBox(parent=self, id=wx.ID_ANY,
1229                             label=" %s " % _("Analysis result style:"))
1230        styleBoxSizer = wx.StaticBoxSizer(styleBox, wx.VERTICAL)
1231
1232        rules = RunCommand('v.colors',
1233                           read=True,
1234                           flags='l')
1235
1236        settsLabels = {}
1237
1238        settsLabels['color_table'] = StaticText(
1239            parent=self,
1240            id=wx.ID_ANY,
1241            label=_('Color table style %s:') %
1242            '(v.net.flow)')
1243        self.settings['color_table'] = ComboBox(
1244            parent=self, id=wx.ID_ANY, choices=rules.split(),
1245            style=wx.CB_READONLY, size=(180, -1))
1246
1247        setStyle = UserSettings.Get(
1248            group='vnet',
1249            key="res_style",
1250            subkey="color_table")
1251        i = self.settings['color_table'].FindString(setStyle)
1252        if i != wx.NOT_FOUND:
1253            self.settings['color_table'].Select(i)
1254
1255        self.settings["invert_colors"] = CheckBox(
1256            parent=self,
1257            id=wx.ID_ANY,
1258            label=_('Invert colors %s:') %
1259            '(v.net.flow)')
1260        setInvert = UserSettings.Get(
1261            group='vnet',
1262            key="res_style",
1263            subkey="invert_colors")
1264        self.settings["invert_colors"].SetValue(setInvert)
1265
1266        self.colorsSetts = {
1267            "line_color": [
1268                "res_style",
1269                _("Line color:")],
1270            "unused": [
1271                "point_colors",
1272                _("Color for unused point:")],
1273            "used1cat": [
1274                "point_colors",
1275                _("Color for Start/From/Source/Used point:")],
1276            "used2cat": [
1277                "point_colors",
1278                _("Color for End/To/Sink point:")],
1279            "selected": [
1280                "point_colors",
1281                _("Color for selected point:")]}
1282
1283        for settKey, sett in six.iteritems(self.colorsSetts):
1284            settsLabels[settKey] = StaticText(
1285                parent=self, id=wx.ID_ANY, label=sett[1])
1286            col = UserSettings.Get(group='vnet', key=sett[0], subkey=settKey)
1287            self.settings[settKey] = csel.ColourSelect(
1288                parent=self, id=wx.ID_ANY, colour=wx.Colour(
1289                    col[0], col[1], col[2], 255))
1290
1291        self.sizeSetts = {
1292            "line_width": ["res_style", _("Line width:")],
1293            "point_size": ["point_symbol", _("Point size:")],
1294            "point_width": ["point_symbol", _("Point width:")],
1295            "snap_tresh": ["other", _("Snapping threshold in pixels:")],
1296            "max_hist_steps": ["other", _("Maximum number of results in history:")]
1297        }
1298
1299        for settKey, sett in six.iteritems(self.sizeSetts):
1300            settsLabels[settKey] = StaticText(
1301                parent=self, id=wx.ID_ANY, label=sett[1])
1302            self.settings[settKey] = SpinCtrl(
1303                parent=self, id=wx.ID_ANY, min=1, max=50)
1304            size = int(
1305                UserSettings.Get(
1306                    group='vnet',
1307                    key=sett[0],
1308                    subkey=settKey))
1309            self.settings[settKey].SetValue(size)
1310
1311        # buttons
1312        self.btnSave = Button(self, wx.ID_SAVE)
1313        self.btnApply = Button(self, wx.ID_APPLY)
1314        self.btnClose = Button(self, wx.ID_CLOSE)
1315        self.btnApply.SetDefault()
1316
1317        # bindings
1318        self.btnApply.Bind(wx.EVT_BUTTON, self.OnApply)
1319        self.btnApply.SetToolTip(
1320            _("Apply changes for the current session"))
1321        self.btnSave.Bind(wx.EVT_BUTTON, self.OnSave)
1322        self.btnSave.SetToolTip(
1323            _("Apply and save changes to user settings file (default for next sessions)"))
1324        self.btnClose.Bind(wx.EVT_BUTTON, self.OnClose)
1325        self.btnClose.SetToolTip(_("Close dialog"))
1326
1327        # Layout
1328
1329        # Analysis result style layout
1330        self.SetMinSize(self.GetBestSize())
1331
1332        sizer = wx.BoxSizer(wx.VERTICAL)
1333
1334        gridSizer = wx.GridBagSizer(vgap=1, hgap=1)
1335
1336        row = 0
1337        gridSizer.Add(
1338            settsLabels["line_color"],
1339            flag=wx.ALIGN_CENTER_VERTICAL,
1340            pos=(
1341                row,
1342                0))
1343        gridSizer.Add(self.settings["line_color"],
1344                      flag=wx.ALIGN_RIGHT | wx.ALL, border=5,
1345                      pos=(row, 1))
1346
1347        row += 1
1348        gridSizer.Add(
1349            settsLabels["line_width"],
1350            flag=wx.ALIGN_CENTER_VERTICAL,
1351            pos=(
1352                row,
1353                0))
1354        gridSizer.Add(self.settings["line_width"],
1355                      flag=wx.ALIGN_RIGHT | wx.ALL, border=5,
1356                      pos=(row, 1))
1357        row += 1
1358        gridSizer.Add(
1359            settsLabels['color_table'],
1360            flag=wx.ALIGN_CENTER_VERTICAL,
1361            pos=(
1362                row,
1363                0))
1364        gridSizer.Add(self.settings['color_table'],
1365                      flag=wx.ALIGN_RIGHT | wx.ALL, border=5,
1366                      pos=(row, 1))
1367
1368        row += 1
1369        gridSizer.Add(
1370            self.settings["invert_colors"],
1371            flag=wx.ALIGN_CENTER_VERTICAL,
1372            pos=(
1373                row,
1374                0))
1375
1376        gridSizer.AddGrowableCol(1)
1377        styleBoxSizer.Add(gridSizer, flag=wx.EXPAND)
1378
1379        # Point style layout
1380        gridSizer = wx.GridBagSizer(vgap=1, hgap=1)
1381
1382        row = 0
1383        setts = {**self.colorsSetts, **self.sizeSetts}
1384
1385        settsOrder = [
1386            "selected",
1387            "used1cat",
1388            "used2cat",
1389            "unused",
1390            "point_size",
1391            "point_width"]
1392        for settKey in settsOrder:
1393            sett = setts[settKey]
1394            gridSizer.Add(
1395                settsLabels[settKey],
1396                flag=wx.ALIGN_CENTER_VERTICAL,
1397                pos=(
1398                    row,
1399                    0))
1400            gridSizer.Add(self.settings[settKey],
1401                          flag=wx.ALIGN_RIGHT | wx.ALL, border=5,
1402                          pos=(row, 1))
1403            row += 1
1404
1405        gridSizer.AddGrowableCol(1)
1406        ptsStyleBoxSizer.Add(gridSizer, flag=wx.EXPAND)
1407
1408        # Other settings layout
1409        gridSizer = wx.GridBagSizer(vgap=1, hgap=1)
1410
1411        row = 0
1412        for otherSettName in ["snap_tresh", "max_hist_steps"]:
1413            gridSizer.Add(
1414                settsLabels[otherSettName],
1415                flag=wx.ALIGN_CENTER_VERTICAL,
1416                pos=(
1417                    row,
1418                    0))
1419            gridSizer.Add(self.settings[otherSettName],
1420                          flag=wx.ALIGN_RIGHT | wx.ALL, border=5,
1421                          pos=(row, 1))
1422            row += 1
1423
1424        gridSizer.AddGrowableCol(1)
1425        otherBoxSizer.Add(gridSizer, flag=wx.EXPAND)
1426
1427        btnSizer = wx.BoxSizer(wx.HORIZONTAL)
1428        btnSizer.Add(self.btnApply, flag=wx.LEFT | wx.RIGHT, border=5)
1429        btnSizer.Add(self.btnSave, flag=wx.LEFT | wx.RIGHT, border=5)
1430        btnSizer.Add(self.btnClose, flag=wx.LEFT | wx.RIGHT, border=5)
1431
1432        sizer.Add(styleBoxSizer, flag=wx.EXPAND | wx.ALL, border=5)
1433        sizer.Add(ptsStyleBoxSizer, flag=wx.EXPAND | wx.ALL, border=5)
1434        sizer.Add(otherBoxSizer, flag=wx.EXPAND | wx.ALL, border=5)
1435        sizer.Add(
1436            btnSizer,
1437            flag=wx.EXPAND | wx.ALL,
1438            border=5,
1439            proportion=0)
1440
1441        self.SetSizer(sizer)
1442        sizer.Fit(self)
1443
1444    def OnSave(self, event):
1445        """Button 'Save' pressed"""
1446        self.UpdateSettings()
1447
1448        fileSettings = {}
1449        UserSettings.ReadSettingsFile(settings=fileSettings)
1450        fileSettings['vnet'] = UserSettings.Get(group='vnet')
1451        UserSettings.SaveToFile(fileSettings)
1452
1453        self.Close()
1454
1455    def UpdateSettings(self):
1456        UserSettings.Set(group='vnet', key="res_style", subkey='line_width',
1457                         value=self.settings["line_width"].GetValue())
1458
1459        for settKey, sett in six.iteritems(self.colorsSetts):
1460            col = tuple(self.settings[settKey].GetColour())
1461            UserSettings.Set(group='vnet',
1462                             key=sett[0],
1463                             subkey=settKey,
1464                             value=col)
1465
1466        for settKey, sett in six.iteritems(self.sizeSetts):
1467            UserSettings.Set(group='vnet', key=sett[0], subkey=settKey,
1468                             value=self.settings[settKey].GetValue())
1469
1470        UserSettings.Set(
1471            group='vnet',
1472            key='res_style',
1473            subkey='color_table',
1474            value=self.settings['color_table'].GetStringSelection())
1475
1476        UserSettings.Set(group='vnet', key='res_style', subkey='invert_colors',
1477                         value=self.settings['invert_colors'].IsChecked())
1478
1479        self.vnet_mgr.SettingsUpdated()
1480
1481    def OnApply(self, event):
1482        """Button 'Apply' pressed"""
1483        self.UpdateSettings()
1484        # self.Close()
1485
1486    def OnClose(self, event):
1487        """Button 'Cancel' pressed"""
1488        self.Close()
1489
1490
1491class CreateTtbDialog(wx.Dialog):
1492
1493    def __init__(self, parent, init_data, id=wx.ID_ANY,
1494                 title=_("New vector map with turntable"),
1495                 style=wx.DEFAULT_DIALOG_STYLE):
1496        """Create turntable dialog."""
1497        wx.Dialog.__init__(self, parent, id, title=_(title), style=style)
1498
1499        box = StaticBox(self, -1, "Vector map and layers for analysis")
1500        bsizer = wx.StaticBoxSizer(box, wx.VERTICAL)
1501        label = {}
1502        dataSelects = [
1503            ['input', "Choose vector map for analysis:", Select],
1504            ['output', "Name of vector map with turntable:", Select],
1505            ['arc_layer', "Arc layer which will be expanded by turntable:", LayerSelect],
1506            ['turn_layer', "Layer with turntable:", LayerSelect],
1507            ['turn_cat_layer', "Layer with unique categories for turntable:", LayerSelect],
1508        ]
1509
1510        self.inputData = {}
1511
1512        selPanels = {}
1513
1514        for dataSel in dataSelects:
1515            selPanels[dataSel[0]] = Panel(parent=self)
1516            if dataSel[0] in ['input', 'output']:
1517                self.inputData[
1518                    dataSel[0]] = dataSel[2](
1519                    parent=selPanels[dataSel[0]],
1520                    size=(-1, -1),
1521                    type='vector')
1522            elif dataSel[0] != 'input':
1523                self.inputData[dataSel[0]] = dataSel[2](
1524                    parent=selPanels[dataSel[0]], size=(-1, -1))
1525
1526            label[dataSel[0]] = StaticText(parent=selPanels[dataSel[0]],
1527                                           name=dataSel[0])
1528            label[dataSel[0]].SetLabel(dataSel[1])
1529
1530        self.inputData['input'].Bind(wx.EVT_TEXT, lambda event: self.InputSel)
1531
1532        # buttons
1533        self.btnCancel = Button(self, wx.ID_CANCEL)
1534        self.btnOk = Button(self, wx.ID_OK)
1535        self.btnOk.SetDefault()
1536
1537        # Layout
1538        mainSizer = wx.BoxSizer(wx.VERTICAL)
1539
1540        mainSizer.Add(bsizer, proportion=0,
1541                      flag=wx.EXPAND | wx.TOP | wx.LEFT | wx.RIGHT, border=5)
1542
1543        btn = None
1544        for sel in ['input', 'output', 'arc_layer',
1545                    'turn_layer', 'turn_cat_layer']:
1546
1547            selPanels[sel].SetSizer(self._doSelLayout(title=label[sel],
1548                                                      sel=self.inputData[sel],
1549                                                      btn=btn))
1550            bsizer.Add(selPanels[sel], proportion=0,
1551                       flag=wx.EXPAND)
1552
1553        for k, v in six.iteritems(init_data):
1554            if k in self.inputData:
1555                self.inputData[k].SetValue(v)
1556
1557        inp_vect_map = self.inputData["input"].GetValue().split("@")[0]
1558        self.inputData['output'].SetValue(inp_vect_map + "_ttb")
1559
1560        btnSizer = wx.StdDialogButtonSizer()
1561        btnSizer.AddButton(self.btnCancel)
1562        btnSizer.AddButton(self.btnOk)
1563        btnSizer.Realize()
1564
1565        mainSizer.Add(btnSizer, proportion=0,
1566                      flag=wx.ALIGN_RIGHT | wx.ALL, border=5)
1567
1568        self.SetSizer(mainSizer)
1569        self.Fit()
1570
1571    def _doSelLayout(self, title, sel, btn=None):
1572        # helper function for layout of self.inputData widgets initialized in
1573        # _createParametersPage
1574        selSizer = wx.BoxSizer(orient=wx.VERTICAL)
1575
1576        selTitleSizer = wx.BoxSizer(wx.HORIZONTAL)
1577        selTitleSizer.Add(title, proportion=1,
1578                          flag=wx.LEFT | wx.TOP | wx.EXPAND, border=5)
1579
1580        selSizer.Add(selTitleSizer, proportion=0,
1581                     flag=wx.EXPAND)
1582
1583        if btn:
1584            selFiledSizer = wx.BoxSizer(orient=wx.HORIZONTAL)
1585            selFiledSizer.Add(sel, proportion=1,
1586                              flag=wx.EXPAND | wx.ALL)
1587
1588            selFiledSizer.Add(btn, proportion=0,
1589                              flag=wx.EXPAND | wx.ALL)
1590
1591            selSizer.Add(selFiledSizer, proportion=0,
1592                         flag=wx.EXPAND | wx.ALL | wx.ALIGN_CENTER_VERTICAL,
1593                         border=5)
1594        else:
1595            selSizer.Add(sel, proportion=1,
1596                         flag=wx.EXPAND | wx.ALL | wx.ALIGN_CENTER_VERTICAL,
1597                         border=5)
1598        return selSizer
1599
1600    def InputSel(self):
1601        """When vector map is selected it populates other comboboxes in Parameters tab (layer selects, columns selects)"""
1602        vectMapName, mapSet = self._parseMapStr(
1603            self.inputData['input'].GetValue())
1604        vectorMap = vectMapName + '@' + mapSet
1605
1606        for sel in ['arc_layer', 'turn_layer', 'turn_cat_layer']:
1607            self.inputData[sel].Clear()
1608            self.inputData[sel].InsertLayers(vector=vectorMap)
1609
1610        items = self.inputData['arc_layer'].GetItems()
1611        itemsLen = len(items)
1612        if itemsLen < 1:
1613            self.addToTreeBtn.Disable()
1614            if hasattr(self, 'inpDbMgrData'):
1615                self._updateInputDbMgrPage(show=False)
1616            self.inputData['arc_layer'].SetValue("")
1617            return
1618        elif itemsLen == 1:
1619            self.inputData['arc_layer'].SetSelection(0)
1620        elif itemsLen >= 1:
1621            if unicode("1") in items:
1622                iItem = items.index(unicode("1"))
1623                self.inputData['arc_layer'].SetSelection(iItem)
1624        self.addToTreeBtn.Enable()
1625        if hasattr(self, 'inpDbMgrData'):
1626            self._updateInputDbMgrPage(show=True)
1627
1628    def GetData(self):
1629
1630        params = {}
1631        for param, sel in six.iteritems(self.inputData):
1632            params[param] = sel.GetValue()
1633
1634        return params
1635
1636
1637class OutputVectorDialog(wx.Dialog):
1638
1639    def __init__(self, parent, id=wx.ID_ANY, title=_(
1640            "Save analysis result"), style=wx.DEFAULT_DIALOG_STYLE):
1641        """Save analysis result"""
1642        wx.Dialog.__init__(self, parent, id, title=_(title), style=style)
1643
1644        self.panel = Panel(parent=self)
1645        box = StaticBox(parent=self.panel, id=wx.ID_ANY,
1646                        label="Vector map")
1647
1648        self.boxSizer = wx.StaticBoxSizer(box, wx.HORIZONTAL)
1649
1650        # text fields and it's captions
1651        self.vectSel = Select(
1652            parent=self.panel, type='vector',
1653            mapsets=[grass.gisenv()['MAPSET']],
1654            size=(-1, -1))
1655        self.vectSellabel = StaticText(parent=self.panel, id=wx.ID_ANY,
1656                                       label=_("Name:"))
1657
1658        # buttons
1659        self.btnCancel = Button(self.panel, wx.ID_CANCEL)
1660        self.btnOk = Button(self.panel, wx.ID_OK)
1661        self.btnOk.SetDefault()
1662
1663        self.SetInitialSize((400, -1))
1664        self._layout()
1665
1666    def _layout(self):
1667
1668        sizer = wx.BoxSizer(wx.VERTICAL)
1669
1670        self.boxSizer.Add(self.vectSellabel,
1671                          flag=wx.ALIGN_CENTER_VERTICAL,
1672                          proportion=0)
1673
1674        self.boxSizer.Add(self.vectSel, proportion=1,
1675                          flag=wx.EXPAND | wx.ALL, border=5)
1676
1677        sizer.Add(self.boxSizer, proportion=1,
1678                  flag=wx.EXPAND | wx.ALL, border=5)
1679
1680        btnSizer = wx.StdDialogButtonSizer()
1681        btnSizer.AddButton(self.btnCancel)
1682        btnSizer.AddButton(self.btnOk)
1683        btnSizer.Realize()
1684
1685        sizer.Add(btnSizer, proportion=0,
1686                  flag=wx.ALIGN_RIGHT | wx.ALL, border=5)
1687
1688        self.panel.SetSizer(sizer)
1689        sizer.Fit(self)
1690
1691
1692class VnetStatusbar(wx.StatusBar):
1693    """Extends wx.StatusBar class with functionality to show multiple messages with the highest priority"""
1694
1695    def __init__(self, parent, style, id=wx.ID_ANY, **kwargs):
1696
1697        wx.StatusBar.__init__(self, parent, id, style, **kwargs)
1698
1699        self.maxPriority = 0
1700        self.statusItems = []
1701
1702    def AddStatusItem(self, text, key, priority):
1703        """Add new item to show
1704
1705        :param text: string to show
1706        :param key: item identifier, if already contains
1707                    item with same identifier, overwrites this item
1708        :param priority: only items with highest priority are showed
1709        """
1710        statusTextItem = {
1711            'text': text,
1712            'key': key,
1713            'priority': priority
1714        }
1715
1716        for item in self.statusItems:
1717            if item['key'] == statusTextItem['key']:
1718                self.statusItems.remove(item)
1719        self.statusItems.append(statusTextItem)
1720        if self.maxPriority < statusTextItem['priority']:
1721            self.maxPriority = statusTextItem['priority']
1722        self._updateStatus()
1723
1724    def _updateStatus(self):
1725
1726        currStatusText = ''
1727        for item in reversed(self.statusItems):
1728            if item['priority'] == self.maxPriority:
1729                if currStatusText:
1730                    currStatusText += '; '
1731                currStatusText += item['text']
1732        wx.StatusBar.SetStatusText(self, currStatusText)
1733
1734    def RemoveStatusItem(self, key):
1735        """Remove item
1736
1737        :param key: item identifier
1738        """
1739        update = False
1740        for iItem, item in enumerate(self.statusItems):
1741            if item['key'] == key:
1742                if item['priority'] == self.maxPriority:
1743                    update = True
1744                self.statusItems.pop(iItem)
1745        if update:
1746            for item in self.statusItems:
1747                self.maxPriority = 0
1748                if self.maxPriority < item['priority']:
1749                    self.maxPriority = item['priority']
1750            self._updateStatus()
1751
1752
1753class DefIntesectionTurnCostDialog(wx.Dialog):
1754
1755    def __init__(self, parent, mapWin, style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER,
1756                 id=wx.ID_ANY, title=_("Edit intersection turns costs"), **kwargs):
1757        wx.Dialog.__init__(
1758            self,
1759            parent,
1760            id,
1761            style=style,
1762            title=title,
1763            **kwargs)
1764
1765        self.dbMgr = DbMgrBase(mapdisplay=mapWin)
1766        self.browsePage = self.dbMgr.CreateDbMgrPage(
1767            parent=self, pageName='browse')
1768
1769    def layout(self):
1770        sizer = wx.BoxSizer(wx.VERTICAL)
1771
1772        sizer.Add(self.browsePage, proportion=1,
1773                  flag=wx.EXPAND)
1774
1775        self.SetSizer(sizer)
1776
1777    def SetData(self, vectMapName, layer):
1778        if vectMapName != self.dbMgr.GetVectorName():
1779            self.dbMgr.ChangeVectorMap(vectorName=vectMapName)
1780        else:
1781            self.browsePage.DeleteAllPages()
1782
1783        self.browsePage.AddLayer(int(layer))
1784        self.layer = int(layer)
1785
1786        # TODO HACK!!!
1787        self.browsePage.FindWindowById(
1788            self.browsePage.layerPage[int(layer)]
1789            ['sqlNtb']).GetParent().Hide()
1790
1791    def SetIntersection(self, isec):
1792
1793        self.browsePage.LoadData(self.layer, where="isec = %d" % (isec))
1794
1795
1796class DefGlobalTurnsDialog(wx.Dialog):
1797
1798    def __init__(self, parent, data, style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER,
1799                 id=wx.ID_ANY, title=_("Define Global Turn Costs"), **kwargs):  # v Gassu dopln preklad
1800
1801        wx.Dialog.__init__(self, parent, id, title, style=style, **kwargs)
1802
1803        self.data = data
1804
1805        self.angle_list = TurnAnglesList(parent=self, data=self.data)
1806
1807        self.btnAdd = Button(parent=self, id=wx.ID_ANY, label="Add")
1808        self.btnRemove = Button(parent=self, id=wx.ID_ANY, label="Remove")
1809        self.btnClose = Button(parent=self, id=wx.ID_CLOSE)
1810        self.useUTurns = CheckBox(
1811            parent=self, id=wx.ID_ANY, label="Use U-turns")
1812
1813        self.btnAdd.Bind(wx.EVT_BUTTON, self.OnAddButtonClick)
1814        self.btnRemove.Bind(wx.EVT_BUTTON, self.OnRemoveButtonClick)
1815        self.Bind(wx.EVT_CLOSE, self.OnCloseDialog)
1816        self.useUTurns.Bind(wx.EVT_CHECKBOX, self.OnCheckedUTurns)
1817
1818        self.btnClose.SetDefault()
1819        self.useUTurns.SetValue(True)
1820        self.OnCheckedUTurns(None)
1821        self.layout()
1822        self.SetInitialSize((500, 200))
1823
1824    def layout(self):
1825        sizer = wx.BoxSizer(wx.VERTICAL)
1826        labelSizer = wx.BoxSizer(wx.HORIZONTAL)
1827        addRemoveSizer = wx.BoxSizer(wx.VERTICAL)
1828        closeSizer = wx.BoxSizer(wx.HORIZONTAL)
1829
1830        addRemoveSizer.Add(
1831            self.btnAdd,
1832            proportion=0,
1833            flag=wx.ALIGN_RIGHT,
1834            border=10)
1835        addRemoveSizer.Add(
1836            self.btnRemove,
1837            proportion=0,
1838            flag=wx.ALIGN_RIGHT,
1839            border=10)
1840
1841        labelSizer.Add(
1842            self.angle_list,
1843            proportion=1,
1844            flag=wx.EXPAND,
1845            border=10)
1846        labelSizer.Add(
1847            addRemoveSizer,
1848            proportion=0,
1849            flag=wx.ALIGN_RIGHT,
1850            border=10)
1851
1852        closeSizer.Add(
1853            self.useUTurns,
1854            proportion=1,
1855            flag=wx.ALIGN_LEFT,
1856            border=10)
1857        closeSizer.Add(
1858            self.btnClose,
1859            proportion=0,
1860            flag=wx.ALIGN_RIGHT,
1861            border=10)
1862
1863        sizer.Add(labelSizer, proportion=1, flag=wx.EXPAND)
1864        sizer.Add(closeSizer, proportion=0, flag=wx.EXPAND)
1865
1866        self.SetSizer(sizer)
1867
1868    def OnAddButtonClick(self, event):
1869        """Add new direction over selected row"""
1870        selected_indices = self.angle_list.GetSelectedIndices()
1871
1872        if not selected_indices:
1873            from_value = self.turn_data.GetValue(
1874                self.turn_data.GetLinesCount() - 1, 2)
1875            to_value = self.turn_data.GetValue(0, 1)
1876            default_row = ["new", from_value, to_value, 0.0]
1877            self.angle_list.AppendRow(default_row)
1878
1879        # If no row is selected, new direction is added to the end of table
1880        i_addition = 0
1881        for i in selected_indices:
1882            i += i_addition
1883            from_value = self.turn_data.GetValue(i - 1, 2)
1884            to_value = self.turn_data.GetValue(i, 1)
1885            default_row = ["new", from_value, to_value, 0.0]
1886            self.angle_list.InsertRow(i, default_row)
1887            i_addition += 1
1888
1889    def OnRemoveButtonClick(self, event):
1890        """Delete one or more selected directions"""
1891        selected_indices = self.angle_list.GetSelectedIndices()
1892
1893        i_reduction = 0
1894        for i in selected_indices:
1895            i -= i_reduction
1896            self.angle_list.DeleteRow(i)
1897            i_reduction += 1
1898
1899    def OnCloseDialog(self, event):
1900        """Close dialog"""
1901        self.Close()
1902
1903    def OnCheckedUTurns(self, event):
1904        """Use U-turns in analyse"""
1905        self.data.SetUTurns(self.useUTurns.GetValue())
1906
1907    def SetData(self, data):
1908        self.angle_list.SetData(data)
1909        self.data = data
1910
1911
1912class TurnAnglesList(
1913        ListCtrl, listmix.ListCtrlAutoWidthMixin, listmix.TextEditMixin):
1914    """Virtual editable table with global turns"""
1915
1916    def __init__(self, parent, data, id=wx.ID_ANY,
1917                 style=wx.LC_REPORT | wx.LC_VIRTUAL, **kwargs):
1918        ListCtrl.__init__(self, parent, id, style=style, **kwargs)
1919        listmix.ListCtrlAutoWidthMixin.__init__(self)
1920        listmix.TextEditMixin.__init__(self)
1921
1922        self.Populate()
1923        self.data = data
1924        self.SetItemCount(self.data.GetLinesCount())
1925
1926    def Populate(self):
1927        """Columns definition"""
1928        self.InsertColumn(
1929            col=0,
1930            heading="Direction",
1931            format=wx.LIST_FORMAT_LEFT)  # v Gassu dopln preklad
1932        self.InsertColumn(
1933            col=1,
1934            heading="From Angle",
1935            format=wx.LIST_FORMAT_RIGHT)
1936        self.InsertColumn(
1937            col=2,
1938            heading="To Angle",
1939            format=wx.LIST_FORMAT_RIGHT)
1940        self.InsertColumn(col=3, heading="Cost", format=wx.LIST_FORMAT_RIGHT)
1941
1942    def OnGetItemText(self, item, col):
1943        val = self.data.GetValue(item, col)
1944        if col in [1, 2]:
1945            val = RadiansToDegrees(val)
1946        return str(val)
1947
1948    def SetVirtualData(self, row, column, text):
1949        """Set data to table"""
1950        if column in [1, 2, 3]:
1951            try:
1952                text = float(text)
1953            except:
1954                return
1955        if column in [1, 2]:
1956            text = DegreesToRadians(text)
1957
1958            # Tested allowed range of values
1959            if text > math.pi:
1960                text = 0.0
1961            elif text < -math.pi:
1962                text = 0.0
1963
1964        self.data.SetValue(text, row, column)
1965
1966        self.edited_row = row
1967        self.RefreshItems(0, self.data.GetLinesCount() - 1)
1968
1969    def AppendRow(self, values):
1970        self.data.AppendRow(values)
1971        self.SetItemCount(self.data.GetLinesCount())
1972
1973    def InsertRow(self, line, values):
1974        self.data.InsertRow(line, values)
1975        self.SetItemCount(self.data.GetLinesCount())
1976
1977    def DeleteRow(self, row):
1978        self.data.PopRow(row)
1979        self.SetItemCount(self.data.GetLinesCount())
1980
1981    def SetData(self, data):
1982        self.data = data
1983        self.SetItemCount(self.data.GetLinesCount())
1984        self.RefreshItems(0, self.data.GetLinesCount() - 1)
1985
1986    def GetSelectedIndices(self, state=wx.LIST_STATE_SELECTED):
1987        """Get numbers of selected rows"""
1988        indices = []
1989        lastFound = -1
1990        while True:
1991            index = self.GetNextItem(lastFound, wx.LIST_NEXT_ALL, state)
1992            if index == -1:
1993                break
1994            else:
1995                lastFound = index
1996                indices.append(index)
1997        return indices
1998