vnet_data.py 50 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562
  1. """
  2. @package vnet.vnet_data
  3. @brief Vector network analysis classes for data managment.
  4. Classes:
  5. - vnet_data::VNETData
  6. - vnet_data::VNETPointsData
  7. - vnet_data::VNETAnalysisParameters
  8. - vnet_data::VNETAnalysesProperties
  9. - vnet_data::VNETTmpVectMaps
  10. - vnet_data::VectMap
  11. - vnet_data::History
  12. - vnet_data::VNETGlobalTurnsData
  13. (C) 2013-2014 by the GRASS Development Team
  14. This program is free software under the GNU General Public License
  15. (>=v2). Read the file COPYING that comes with GRASS for details.
  16. @author Stepan Turek <stepan.turek seznam.cz> (GSoC 2012, mentor: Martin Landa)
  17. @author Lukas Bocan <silent_bob centrum.cz> (turn costs support)
  18. @author Eliska Kyzlikova <eliska.kyzlikova gmail.com> (turn costs support)
  19. """
  20. import os
  21. import six
  22. from copy import deepcopy
  23. from grass.script.utils import try_remove
  24. from grass.script import core as grass
  25. from grass.script.task import cmdlist_to_tuple
  26. import wx
  27. from core import utils
  28. from core.gcmd import RunCommand, GMessage
  29. from core.settings import UserSettings
  30. from vnet.vnet_utils import ParseMapStr, SnapToNode
  31. from gui_core.gselect import VectorDBInfo
  32. from grass.pydispatch.signal import Signal
  33. from vnet.vnet_utils import DegreesToRadians, RadiansToDegrees
  34. class VNETData:
  35. def __init__(self, guiparent, mapWin):
  36. # setting initialization
  37. self._initSettings()
  38. self.guiparent = guiparent
  39. self.an_props = VNETAnalysesProperties()
  40. self.an_params = VNETAnalysisParameters(self.an_props)
  41. self.an_points = VNETPointsData(mapWin, self.an_props, self.an_params)
  42. self.global_turns = VNETGlobalTurnsData()
  43. self.pointsChanged = self.an_points.pointsChanged
  44. self.parametersChanged = self.an_params.parametersChanged
  45. def CleanUp(self):
  46. self.an_points.CleanUp()
  47. def GetAnalyses(self):
  48. return self.an_props.used_an
  49. def GetPointsData(self):
  50. return self.an_points
  51. def GetGlobalTurnsData(self):
  52. return self.global_turns
  53. def GetRelevantParams(self, analysis=None):
  54. if analysis:
  55. return self.an_props.GetRelevantParams(analysis)
  56. else:
  57. analysis, valid = self.an_params.GetParam("analysis")
  58. return self.an_props.GetRelevantParams(analysis)
  59. def GetAnalysisProperties(self, analysis=None):
  60. if analysis:
  61. return self.an_props[analysis]
  62. else:
  63. analysis, valid = self.an_params.GetParam("analysis")
  64. return self.an_props[analysis]
  65. def GetParam(self, param):
  66. return self.an_params.GetParam(param)
  67. def GetParams(self):
  68. return self.an_params.GetParams()
  69. def SetParams(self, params, flags):
  70. return self.an_params.SetParams(params, flags)
  71. def SetSnapping(self, activate):
  72. self.an_points.SetSnapping(activate)
  73. def GetSnapping(self):
  74. return self.an_points.GetSnapping()
  75. def GetLayerStyle(self):
  76. """Returns cmd for d.vect, with set style for analysis result"""
  77. analysis, valid = self.an_params.GetParam("analysis")
  78. resProps = self.an_props[analysis]["resultProps"]
  79. width = UserSettings.Get(
  80. group='vnet',
  81. key='res_style',
  82. subkey="line_width")
  83. layerStyleCmd = ["layer=1", 'width=' + str(width)]
  84. if "catColor" in resProps:
  85. layerStyleCmd.append('flags=c')
  86. elif "singleColor" in resProps:
  87. col = UserSettings.Get(
  88. group='vnet',
  89. key='res_style',
  90. subkey="line_color")
  91. layerStyleCmd.append(
  92. 'color=' + str(col[0]) + ':' + str(col[1]) + ':' + str(col[2]))
  93. layerStyleVnetColors = []
  94. if "attrColColor" in resProps:
  95. colorStyle = UserSettings.Get(
  96. group='vnet', key='res_style', subkey="color_table")
  97. invert = UserSettings.Get(
  98. group='vnet',
  99. key='res_style',
  100. subkey="invert_colors")
  101. layerStyleVnetColors = [
  102. "v.colors",
  103. "color=" + colorStyle,
  104. "column=" + resProps["attrColColor"],
  105. ]
  106. if invert:
  107. layerStyleVnetColors.append("-n")
  108. return layerStyleCmd, layerStyleVnetColors
  109. def InputsErrorMsgs(self, msg, analysis, params, flags,
  110. inv_params, relevant_params):
  111. """Checks input data in Parameters tab and shows messages if some value is not valid
  112. :param str msg: message added to start of message string
  113. :return: True if checked inputs are OK
  114. :return: False if some of checked inputs is not ok
  115. """
  116. if flags["t"] and "turn_layer" not in relevant_params:
  117. GMessage(
  118. parent=self.guiparent, message=_(
  119. "Module <%s> does not support turns costs." %
  120. analysis))
  121. return False
  122. errMapStr = ""
  123. if 'input' in inv_params:
  124. if params['input']:
  125. errMapStr = _("Vector map '%s' does not exist.") % (
  126. params['input'])
  127. else:
  128. errMapStr = _("Vector map was not chosen.")
  129. if errMapStr:
  130. GMessage(parent=self.guiparent,
  131. message=msg + "\n" + errMapStr)
  132. return False
  133. errLayerStr = ""
  134. vals = {
  135. 'arc_layer': _("arc layer"),
  136. 'node_layer': _("node layer"),
  137. 'turn_layer': _("turntable layer"),
  138. 'turn_cat_layer': _("unique categories layer")
  139. }
  140. for layer, layerLabel in six.iteritems(vals):
  141. if layer in ["turn_layer", "turn_cat_layer"] and not flags["t"]:
  142. continue
  143. if layer in inv_params:
  144. if params[layer]:
  145. errLayerStr += _("Chosen %s '%s' does not exist in vector map '%s'.\n") % (
  146. layerLabel, params[layer], params['input'])
  147. else:
  148. errLayerStr += _("Choose existing %s.\n") % \
  149. (layerLabel)
  150. if errLayerStr:
  151. GMessage(parent=self.guiparent,
  152. message=msg + "\n" + errLayerStr)
  153. return False
  154. errColStr = ""
  155. for col in ["arc_column", "arc_backward_column", "node_column"]:
  156. if params[col] and col in inv_params and col in relevant_params:
  157. errColStr += _("Chosen column '%s' does not exist in attribute table of layer '%s' of vector map '%s'.\n") % (
  158. params[col], params[layer], params['input'])
  159. if errColStr:
  160. GMessage(parent=self.guiparent,
  161. message=msg + "\n" + errColStr)
  162. return False
  163. return True
  164. def _initSettings(self):
  165. """Initialization of settings (if not already defined)"""
  166. # initializes default settings
  167. initSettings = [
  168. ['res_style', 'line_width', 5],
  169. ['res_style', 'line_color', (192, 0, 0)],
  170. ['res_style', 'color_table', 'byr'],
  171. ['res_style', 'invert_colors', False],
  172. ['point_symbol', 'point_size', 10],
  173. ['point_symbol', 'point_width', 2],
  174. ['point_colors', "unused", (131, 139, 139)],
  175. ['point_colors', "used1cat", (192, 0, 0)],
  176. ['point_colors', "used2cat", (0, 0, 255)],
  177. ['point_colors', "selected", (9, 249, 17)],
  178. ['other', "snap_tresh", 10],
  179. ['other', "max_hist_steps", 5]
  180. ]
  181. for init in initSettings:
  182. UserSettings.ReadSettingsFile()
  183. UserSettings.Append(dict=UserSettings.userSettings,
  184. group='vnet',
  185. key=init[0],
  186. subkey=init[1],
  187. value=init[2],
  188. overwrite=False)
  189. class VNETPointsData:
  190. def __init__(self, mapWin, an_data, an_params):
  191. self.mapWin = mapWin
  192. self.an_data = an_data
  193. self.an_params = an_params
  194. # information, whether mouse event handler is registered in map window
  195. self.handlerRegistered = False
  196. self.pointsChanged = Signal('VNETPointsData.pointsChanged')
  197. self.an_params.parametersChanged.connect(self.ParametersChanged)
  198. self.snapping = False
  199. self.data = []
  200. self.cols = {"name": ['use', 'type', 'topology', 'e', 'n'],
  201. "label": [_('use'), _('type'), _('topology'), 'e', 'n'],
  202. # TDO
  203. "type": [None, ["", _("Start point"), _("End Point")], None, float, float],
  204. "def_vals": [False, 0, "new point", 0, 0]
  205. }
  206. # registration graphics for drawing
  207. self.pointsToDraw = self.mapWin.RegisterGraphicsToDraw(
  208. graphicsType="point", setStatusFunc=self.SetPointStatus)
  209. self.SetPointDrawSettings()
  210. self.AddPoint()
  211. self.AddPoint()
  212. self.SetPointData(0, {'use': True, 'type': 1})
  213. self.SetPointData(1, {'use': True, 'type': 2})
  214. self.selected = 0
  215. def __del__(self):
  216. self.CleanUp()
  217. def CleanUp(self):
  218. self.mapWin.UnregisterGraphicsToDraw(self.pointsToDraw)
  219. if self.handlerRegistered:
  220. self.mapWin.UnregisterMouseEventHandler(wx.EVT_LEFT_DOWN,
  221. self.OnMapClickHandler)
  222. def SetSnapping(self, activate):
  223. self.snapping = activate
  224. def GetSnapping(self):
  225. return self.snapping
  226. def AddPoint(self):
  227. self.pointsToDraw.AddItem(
  228. coords=(
  229. self.cols["def_vals"][3],
  230. self.cols["def_vals"][4]))
  231. self.data.append(self.cols["def_vals"][:])
  232. self.pointsChanged.emit(method="AddPoint", kwargs={})
  233. def DeletePoint(self, pt_id):
  234. item = self.pointsToDraw.GetItem(pt_id)
  235. if item:
  236. self.pointsToDraw.DeleteItem(item)
  237. self.data.pop(pt_id)
  238. self.pointsChanged.emit(method="DeletePoint", kwargs={"pt_id": pt_id})
  239. def SetPoints(self, pts_data):
  240. for item in self.pointsToDraw.GetAllItems():
  241. self.pointsToDraw.DeleteItem(item)
  242. self.data = []
  243. for pt_data in pts_data:
  244. pt_data_list = self._ptDataToList(pt_data)
  245. self.data.append(pt_data_list)
  246. self.pointsToDraw.AddItem(
  247. coords=(pt_data_list[3], pt_data_list[4]))
  248. self.pointsChanged.emit(
  249. method="SetPoints", kwargs={
  250. "pts_data": pts_data})
  251. def SetPointData(self, pt_id, data):
  252. for col, v in six.iteritems(data):
  253. if col == 'use':
  254. continue
  255. idx = self.cols["name"].index(col)
  256. self.data[pt_id][idx] = v
  257. # if type is changed checked columns must be recalculated by _usePoint
  258. if 'type' in data and 'use' not in data:
  259. data["use"] = self.GetPointData(pt_id)['use']
  260. if 'use' in data:
  261. if self._usePoint(pt_id, data["use"]) == -1:
  262. data["use"] = False
  263. idx = self.cols["name"].index("use")
  264. self.data[pt_id][idx] = data["use"]
  265. self.pointsChanged.emit(
  266. method="SetPointData", kwargs={
  267. "pt_id": pt_id, "data": data})
  268. def GetPointData(self, pt_id):
  269. return self._ptListDataToPtData(self.data[pt_id])
  270. def GetPointsCount(self):
  271. return len(self.data)
  272. def SetPointStatus(self, item, itemIndex):
  273. """Before point is drawn, decides properties of drawing style"""
  274. analysis, valid = self.an_params.GetParam("analysis")
  275. cats = self.an_data[analysis]["cmdParams"]["cats"]
  276. if itemIndex == self.selected:
  277. wxPen = "selected"
  278. elif not self.data[itemIndex][0]:
  279. wxPen = "unused"
  280. item.hide = False
  281. elif len(cats) > 1:
  282. idx = self.data[itemIndex][1]
  283. if idx == 2: # End/To/Sink point
  284. wxPen = "used2cat"
  285. else:
  286. wxPen = "used1cat"
  287. else:
  288. wxPen = "used1cat"
  289. item.SetPropertyVal('label', str(itemIndex + 1))
  290. item.SetPropertyVal('penName', wxPen)
  291. def SetSelected(self, pt_id):
  292. self.selected = pt_id
  293. self.pointsChanged.emit(method="SetSelected", kwargs={"pt_id": pt_id})
  294. def GetSelected(self):
  295. return self.selected
  296. def SetPointDrawSettings(self):
  297. """Set settings for drawing of points"""
  298. ptSize = int(
  299. UserSettings.Get(
  300. group='vnet',
  301. key='point_symbol',
  302. subkey='point_size'))
  303. self.pointsToDraw.SetPropertyVal("size", ptSize)
  304. colors = UserSettings.Get(group='vnet', key='point_colors')
  305. ptWidth = int(
  306. UserSettings.Get(
  307. group='vnet',
  308. key='point_symbol',
  309. subkey='point_width'))
  310. textProp = self.pointsToDraw.GetPropertyVal("text")
  311. textProp["font"].SetPointSize(ptSize + 2)
  312. for colKey, col in six.iteritems(colors):
  313. pen = self.pointsToDraw.GetPen(colKey)
  314. if pen:
  315. pen.SetColour(wx.Colour(col[0], col[1], col[2], 255))
  316. pen.SetWidth(ptWidth)
  317. else:
  318. self.pointsToDraw.AddPen(
  319. colKey,
  320. wx.Pen(
  321. colour=wx.Colour(
  322. col[0],
  323. col[1],
  324. col[2],
  325. 255),
  326. width=ptWidth))
  327. def ParametersChanged(self, method, kwargs):
  328. if "analysis" in list(kwargs["changed_params"].keys()):
  329. self._updateTypeCol()
  330. if self.an_params.GetParam("analysis")[0] == "v.net.path":
  331. self._vnetPathUpdateUsePoints(None)
  332. def _updateTypeCol(self):
  333. """Rename category values when module is changed. Expample: Start point -> Sink point"""
  334. colValues = [""]
  335. analysis, valid = self.an_params.GetParam("analysis")
  336. anParamsCats = self.an_data[analysis]["cmdParams"]["cats"]
  337. for ptCat in anParamsCats:
  338. colValues.append(ptCat[1])
  339. type_idx = self.cols["name"].index("type")
  340. self.cols['type'][type_idx] = colValues
  341. def _ptDataToList(self, pt_data):
  342. pt_list_data = [None] * len(self.cols['name'])
  343. for k, val in six.iteritems(pt_data):
  344. pt_list_data[self.cols["name"].index(k)] = val
  345. return pt_list_data
  346. def _ptListDataToPtData(self, pt_list_data):
  347. pt_data = {}
  348. for i, val in enumerate(pt_list_data):
  349. pt_data[self.cols["name"][i]] = val
  350. return pt_data
  351. def _usePoint(self, pt_id, use):
  352. """Item is checked/unchecked"""
  353. analysis, valid = self.an_params.GetParam("analysis")
  354. cats = self.an_data[analysis]["cmdParams"]["cats"]
  355. # TODO move
  356. # if self.updateMap:
  357. # up_map_evt = gUpdateMap(render = False, renderVector = False)
  358. # wx.PostEvent(self.dialog.mapWin, up_map_evt)
  359. if len(cats) <= 1:
  360. return 0
  361. use_idx = self.cols["name"].index("use")
  362. checkedVal = self.data[pt_id][1]
  363. # point without given type cannot be selected
  364. if checkedVal == 0:
  365. self.data[pt_id][use_idx] = False
  366. self.pointsChanged.emit(
  367. method="SetPointData", kwargs={
  368. "pt_id": pt_id, "data": {
  369. "use": False}})
  370. return -1
  371. if analysis == "v.net.path" and use:
  372. self._vnetPathUpdateUsePoints(pt_id)
  373. def _vnetPathUpdateUsePoints(self, checked_pt_id):
  374. alreadyChecked = []
  375. type_idx = self.cols["name"].index("type")
  376. use_idx = self.cols["name"].index("use")
  377. if checked_pt_id is not None:
  378. checkedKey = checked_pt_id
  379. alreadyChecked.append(self.data[checked_pt_id][type_idx])
  380. else:
  381. checkedKey = -1
  382. for iKey, dt in enumerate(self.data):
  383. pt_type = dt[type_idx]
  384. if ((pt_type in alreadyChecked and checkedKey != iKey)
  385. or pt_type == 0) and self.data[iKey][use_idx]:
  386. self.data[iKey][use_idx] = False
  387. self.pointsChanged.emit(
  388. method="SetPointData", kwargs={
  389. "pt_id": iKey, "data": {
  390. "use": False}})
  391. elif self.data[iKey][use_idx]:
  392. alreadyChecked.append(pt_type)
  393. def EditPointMode(self, activate):
  394. """Registers/unregisters mouse handler into map window"""
  395. if activate == self.handlerRegistered:
  396. return
  397. if activate:
  398. self.mapWin.RegisterMouseEventHandler(wx.EVT_LEFT_DOWN,
  399. self.OnMapClickHandler,
  400. 'cross')
  401. self.handlerRegistered = True
  402. else:
  403. self.mapWin.UnregisterMouseEventHandler(wx.EVT_LEFT_DOWN,
  404. self.OnMapClickHandler)
  405. self.handlerRegistered = False
  406. self.pointsChanged.emit(
  407. method="EditMode", kwargs={
  408. "activated": activate})
  409. def IsEditPointModeActive(self):
  410. return self.handlerRegistered
  411. def OnMapClickHandler(self, event):
  412. """Take coordinates from map window"""
  413. # TODO update snapping after input change
  414. if event == 'unregistered':
  415. self.handlerRegistered = False
  416. return
  417. if not self.data:
  418. self.AddPoint()
  419. e, n = self.mapWin.GetLastEN()
  420. if self.snapping:
  421. # compute threshold
  422. snapTreshPix = int(UserSettings.Get(group='vnet',
  423. key='other',
  424. subkey='snap_tresh'))
  425. res = max(
  426. self.mapWin.Map.region['nsres'],
  427. self.mapWin.Map.region['ewres'])
  428. snapTreshDist = snapTreshPix * res
  429. params, err_params, flags = self.an_params.GetParams()
  430. vectMap = params["input"]
  431. if "input" in err_params:
  432. msg = _("new point")
  433. coords = SnapToNode(e, n, snapTreshDist, vectMap)
  434. if coords:
  435. e = coords[0]
  436. n = coords[1]
  437. msg = ("snapped to node")
  438. else:
  439. msg = _("new point")
  440. else:
  441. msg = _("new point")
  442. self.SetPointData(self.selected,
  443. {'topology': msg,
  444. 'e': e,
  445. 'n': n})
  446. self.pointsToDraw.GetItem(self.selected).SetCoords([e, n])
  447. if self.selected == len(self.data) - 1:
  448. self.SetSelected(0)
  449. else:
  450. self.SetSelected(self.GetSelected() + 1)
  451. def GetColumns(self, only_relevant=True):
  452. cols_data = deepcopy(self.cols)
  453. hidden_cols = []
  454. hidden_cols.append(self.cols["name"].index("e"))
  455. hidden_cols.append(self.cols["name"].index("n"))
  456. analysis, valid = self.an_params.GetParam("analysis")
  457. if only_relevant and len(self.an_data[analysis][
  458. "cmdParams"]["cats"]) <= 1:
  459. hidden_cols.append(self.cols["name"].index("type"))
  460. i_red = 0
  461. hidden_cols.sort()
  462. for idx in hidden_cols:
  463. for dt in six.itervalues(cols_data):
  464. dt.pop(idx - i_red)
  465. i_red += 1
  466. return cols_data
  467. class VNETAnalysisParameters:
  468. def __init__(self, an_props):
  469. self.an_props = an_props
  470. self.params = {"analysis": self.an_props.used_an[0],
  471. "input": "",
  472. "arc_layer": "",
  473. "node_layer": "",
  474. "arc_column": "",
  475. "arc_backward_column": "",
  476. "node_column": "",
  477. "turn_layer": "",
  478. "turn_cat_layer": "",
  479. "iso_lines": "", # TODO check validity
  480. "max_dist": 0} # TODO check validity
  481. self.flags = {"t": False}
  482. self.parametersChanged = Signal(
  483. 'VNETAnalysisParameters.parametersChanged')
  484. def SetParams(self, params, flags):
  485. changed_params = {}
  486. for p, v in six.iteritems(params):
  487. if p == "analysis" and v not in self.an_props.used_an:
  488. continue
  489. if p == "input":
  490. mapName, mapSet = ParseMapStr(v)
  491. v = mapName + "@" + mapSet
  492. if p in self.params:
  493. if isinstance(v, str):
  494. v = v.strip()
  495. self.params[p] = v
  496. changed_params[p] = v
  497. changed_flags = {}
  498. for p, v in six.iteritems(flags):
  499. if p in self.flags:
  500. self.flags[p] = v
  501. changed_flags[p] = v
  502. self.parametersChanged.emit(
  503. method="SetParams",
  504. kwargs={
  505. "changed_params": changed_params,
  506. "changed_flags": changed_flags})
  507. return changed_params, changed_flags
  508. def GetParam(self, param):
  509. invParams = []
  510. if param in [
  511. "input", "arc_layer", "node_layer", "arc_column",
  512. "arc_backward_column", "node_column", "turn_layer",
  513. "turn_cat_layer"]:
  514. invParams = self._getInvalidParams(self.params)
  515. if invParams:
  516. return self.params[param], False
  517. return self.params[param], True
  518. def GetParams(self):
  519. invParams = self._getInvalidParams(self.params)
  520. return self.params, invParams, self.flags
  521. def _getInvalidParams(self, params):
  522. """Check of analysis input data for invalid values (Parameters tab)"""
  523. # dict of invalid values {key from self.itemData (comboboxes from
  524. # Parameters tab) : invalid value}
  525. invParams = []
  526. # check vector map
  527. if params["input"]:
  528. mapName, mapSet = params["input"].split("@")
  529. if mapSet in grass.list_grouped('vector'):
  530. vectMaps = grass.list_grouped('vector')[mapSet]
  531. if not params["input"] or mapName not in vectMaps:
  532. invParams = params.keys()[:]
  533. return invParams
  534. # check arc/node layer
  535. layers = utils.GetVectorNumberOfLayers(params["input"])
  536. for l in ['arc_layer', 'node_layer', 'turn_layer', 'turn_cat_layer']:
  537. if not layers or params[l] not in layers:
  538. invParams.append(l)
  539. dbInfo = VectorDBInfo(params["input"])
  540. try:
  541. table = dbInfo.GetTable(int(params["arc_layer"]))
  542. columnchoices = dbInfo.GetTableDesc(table)
  543. except (KeyError, ValueError):
  544. table = None
  545. # check costs columns
  546. for col in ["arc_column", "arc_backward_column", "node_column"]:
  547. if col == "node_column":
  548. try:
  549. table = dbInfo.GetTable(int(params["node_layer"]))
  550. columnchoices = dbInfo.GetTableDesc(table)
  551. except (KeyError, ValueError):
  552. table = None
  553. if not table or not params[col] in list(columnchoices.keys()):
  554. invParams.append(col)
  555. continue
  556. if columnchoices[
  557. params[col]]['type'] not in [
  558. 'integer', 'double precision']:
  559. invParams.append(col)
  560. continue
  561. return invParams
  562. class VNETAnalysesProperties:
  563. def __init__(self):
  564. """Initializes parameters for different v.net.* modules """
  565. # initialization of v.net.* analysis parameters (data which
  566. # characterizes particular analysis)
  567. self.attrCols = {
  568. 'arc_column': {
  569. "label": _("Arc forward/both direction(s) cost column:"),
  570. "name": _("arc forward/both")
  571. },
  572. 'arc_backward_column': {
  573. "label": _("Arc backward direction cost column:"),
  574. "name": _("arc backward")
  575. },
  576. 'acolumn': {
  577. "label": _("Arcs' cost column (for both directions):"),
  578. "name": _("arc"),
  579. "inputField": 'arc_column',
  580. },
  581. 'node_column': {
  582. "label": _("Node cost column:"),
  583. "name": _("node")
  584. }
  585. }
  586. self.vnetProperties = {
  587. "v.net.path": {
  588. "label": _("Shortest path %s") % "(v.net.path)",
  589. "cmdParams": {
  590. "cats": [
  591. ["st_pt", _("Start point")],
  592. ["end_pt", _("End point")]
  593. ],
  594. "cols": [
  595. 'arc_column',
  596. 'arc_backward_column',
  597. 'node_column'
  598. ],
  599. },
  600. "resultProps": {
  601. "singleColor": None,
  602. "dbMgr": True # TODO delete this property, this information can be get from result
  603. },
  604. "turns_support": True
  605. },
  606. "v.net.salesman": {
  607. "label": _("Traveling salesman %s") % "(v.net.salesman)",
  608. "cmdParams": {
  609. "cats": [["center_cats", None]],
  610. "cols": [
  611. 'arc_column',
  612. 'arc_backward_column'
  613. ],
  614. },
  615. "resultProps": {
  616. "singleColor": None,
  617. "dbMgr": False
  618. },
  619. "turns_support": True
  620. },
  621. "v.net.flow": {
  622. "label": _("Maximum flow %s") % "(v.net.flow)",
  623. "cmdParams": {
  624. "cats": [
  625. ["source_cats", _("Source point")],
  626. ["sink_cats", _("Sink point")]
  627. ],
  628. "cols": [
  629. 'arc_column',
  630. 'arc_backward_column',
  631. 'node_column'
  632. ]
  633. },
  634. "resultProps": {
  635. "attrColColor": "flow",
  636. "dbMgr": True
  637. },
  638. "turns_support": False
  639. },
  640. "v.net.alloc": {
  641. "label": _("Subnets for nearest centers %s") % "(v.net.alloc)",
  642. "cmdParams": {
  643. "cats": [["center_cats", None]],
  644. "cols": [
  645. 'arc_column',
  646. 'arc_backward_column',
  647. 'node_column'
  648. ]
  649. },
  650. "resultProps": {
  651. "catColor": None,
  652. "dbMgr": False
  653. },
  654. "turns_support": True
  655. },
  656. "v.net.steiner": {
  657. "label": _("Steiner tree for the network and given terminals %s") % "(v.net.steiner)",
  658. "cmdParams": {
  659. "cats": [["terminal_cats", None]],
  660. "cols": [
  661. 'acolumn',
  662. ]
  663. },
  664. "resultProps": {
  665. "singleColor": None,
  666. "dbMgr": False
  667. },
  668. "turns_support": True
  669. },
  670. "v.net.distance": {
  671. "label": _("Shortest distance via the network %s") % "(v.net.distance)",
  672. "cmdParams": {
  673. "cats": [
  674. ["from_cats", "From point"],
  675. ["to_cats", "To point"]
  676. ],
  677. "cols": [
  678. 'arc_column',
  679. 'arc_backward_column',
  680. 'node_column'
  681. ],
  682. },
  683. "resultProps": {
  684. "catColor": None,
  685. "dbMgr": True
  686. },
  687. "turns_support": False
  688. },
  689. "v.net.iso": {
  690. "label": _("Cost isolines %s") % "(v.net.iso)",
  691. "cmdParams": {
  692. "cats": [["center_cats", None]],
  693. "cols": [
  694. 'arc_column',
  695. 'arc_backward_column',
  696. 'node_column'
  697. ]
  698. },
  699. "resultProps": {
  700. "catColor": None,
  701. "dbMgr": False
  702. },
  703. "turns_support": True
  704. }
  705. }
  706. self.used_an = ["v.net.path",
  707. "v.net.salesman",
  708. "v.net.flow",
  709. "v.net.alloc",
  710. "v.net.distance",
  711. "v.net.iso",
  712. #"v.net.steiner"
  713. ]
  714. for an in list(self.vnetProperties.keys()):
  715. if an not in self.used_an:
  716. del self.vnetProperties[an]
  717. continue
  718. cols = self.vnetProperties[an]["cmdParams"]["cols"]
  719. self.vnetProperties[an]["cmdParams"]["cols"] = {}
  720. for c in cols:
  721. self.vnetProperties[an]["cmdParams"][
  722. "cols"][c] = self.attrCols[c]
  723. def has_key(self, key):
  724. return key in self.vnetProperties
  725. def __getitem__(self, key):
  726. return self.vnetProperties[key]
  727. def GetRelevantParams(self, analysis):
  728. if analysis not in self.vnetProperties:
  729. return None
  730. relevant_params = ["input", "arc_layer", "node_layer"]
  731. if self.vnetProperties[analysis]["turns_support"]:
  732. relevant_params += ["turn_layer", "turn_cat_layer"]
  733. cols = self.vnetProperties[analysis]["cmdParams"]["cols"]
  734. for col, v in six.iteritems(cols):
  735. if "inputField" in col:
  736. colInptF = v["inputField"]
  737. else:
  738. colInptF = col
  739. relevant_params.append(colInptF)
  740. return relevant_params
  741. class VNETTmpVectMaps:
  742. """Class which creates, stores and destroys all tmp maps created during analysis"""
  743. def __init__(self, parent, mapWin):
  744. self.tmpMaps = [] # temporary maps
  745. self.parent = parent
  746. self.mapWin = mapWin
  747. def AddTmpVectMap(self, mapName, msg):
  748. """New temporary map
  749. :return: instance of VectMap representing temporary map
  750. """
  751. currMapSet = grass.gisenv()['MAPSET']
  752. tmpMap = grass.find_file(name=mapName,
  753. element='vector',
  754. mapset=currMapSet)
  755. fullName = tmpMap["fullname"]
  756. # map already exists
  757. if fullName:
  758. # TODO move dialog out of class, AddTmpVectMap(self, mapName,
  759. # overvrite = False)
  760. dlg = wx.MessageDialog(parent=self.parent,
  761. message=msg,
  762. caption=_("Overwrite map layer"),
  763. style=wx.YES_NO | wx.NO_DEFAULT |
  764. wx.ICON_QUESTION | wx.CENTRE)
  765. ret = dlg.ShowModal()
  766. dlg.Destroy()
  767. if ret == wx.ID_NO:
  768. return None
  769. else:
  770. fullName = mapName + "@" + currMapSet
  771. newVectMap = VectMap(self.mapWin, fullName)
  772. self.tmpMaps.append(newVectMap)
  773. return newVectMap
  774. def HasTmpVectMap(self, vectMapName):
  775. """
  776. :param: vectMapName name of vector map
  777. :return: True if it contains the map
  778. :return: False if not
  779. """
  780. mapValSpl = vectMapName.strip().split("@")
  781. if len(mapValSpl) > 1:
  782. mapSet = mapValSpl[1]
  783. else:
  784. mapSet = grass.gisenv()['MAPSET']
  785. mapName = mapValSpl[0]
  786. fullName = mapName + "@" + mapSet
  787. for vectTmpMap in self.tmpMaps:
  788. if vectTmpMap.GetVectMapName() == fullName:
  789. return True
  790. return False
  791. def GetTmpVectMap(self, vectMapName):
  792. """Get instance of VectMap with name vectMapName"""
  793. for vectMap in self.tmpMaps:
  794. if vectMap.GetVectMapName() == vectMapName.strip():
  795. return vectMap
  796. return None
  797. def RemoveFromTmpMaps(self, vectMap):
  798. """Temporary map is removed from the class instance however it is not deleted
  799. :param vectMap: instance of VectMap class to be removed
  800. :return: True if was removed
  801. :return: False if does not contain the map
  802. """
  803. try:
  804. self.tmpMaps.remove(vectMap)
  805. return True
  806. except ValueError:
  807. return False
  808. def DeleteTmpMap(self, vectMap):
  809. """Temporary map is removed from the class and it is deleted
  810. :param vectMap: instance of VectMap class to be deleted
  811. :return: True if was removed
  812. :return: False if does not contain the map
  813. """
  814. if vectMap:
  815. vectMap.DeleteRenderLayer()
  816. RunCommand('g.remove', flags='f', type='vector',
  817. name=vectMap.GetVectMapName())
  818. self.RemoveFromTmpMaps(vectMap)
  819. return True
  820. return False
  821. def DeleteAllTmpMaps(self):
  822. """Delete all temporary maps in the class"""
  823. update = False
  824. for tmpMap in self.tmpMaps:
  825. RunCommand('g.remove', flags='f', type='vector',
  826. name=tmpMap.GetVectMapName())
  827. if tmpMap.DeleteRenderLayer():
  828. update = True
  829. return update
  830. class VectMap:
  831. """Represents map
  832. It can check if it was modified or render it
  833. """
  834. def __init__(self, mapWin, fullName):
  835. self.fullName = fullName
  836. self.mapWin = mapWin
  837. self.renderLayer = None
  838. self.modifTime = None # time, for modification check
  839. def __del__(self):
  840. self.DeleteRenderLayer()
  841. def AddRenderLayer(self, cmd=None, colorsCmd=None):
  842. """Add map from map window layers to render """
  843. if not self.mapWin:
  844. return False
  845. existsMap = grass.find_file(name=self.fullName,
  846. element='vector',
  847. mapset=grass.gisenv()['MAPSET'])
  848. if not existsMap["name"]:
  849. self.DeleteRenderLayer()
  850. return False
  851. if not cmd:
  852. cmd = []
  853. cmd.insert(0, 'd.vect')
  854. cmd.append('map=%s' % self.fullName)
  855. if self.renderLayer:
  856. self.DeleteRenderLayer()
  857. if colorsCmd:
  858. colorsCmd.append('map=%s' % self.fullName)
  859. layerStyleVnetColors = cmdlist_to_tuple(colorsCmd)
  860. RunCommand(layerStyleVnetColors[0],
  861. **layerStyleVnetColors[1])
  862. self.renderLayer = self.mapWin.Map.AddLayer(
  863. ltype="vector", command=cmd, name=self.fullName, active=True,
  864. opacity=1.0, render=False, pos=-1)
  865. return True
  866. def DeleteRenderLayer(self):
  867. """Remove map from map window layers to render"""
  868. if not self.mapWin:
  869. return False
  870. if self.renderLayer:
  871. self.mapWin.Map.DeleteLayer(self.renderLayer)
  872. self.renderLayer = None
  873. return True
  874. return False
  875. def GetRenderLayer(self):
  876. return self.renderLayer
  877. def GetVectMapName(self):
  878. return self.fullName
  879. def SaveVectMapState(self):
  880. """Save modification time for vector map"""
  881. self.modifTime = self.GetLastModified()
  882. def VectMapState(self):
  883. """Checks if map was modified
  884. :return: -1 - if no modification time was saved
  885. :return: 0 - if map was modified
  886. :return: 1 - if map was not modified
  887. """
  888. if self.modifTime is None:
  889. return -1
  890. if self.modifTime != self.GetLastModified():
  891. return 0
  892. return 1
  893. def GetLastModified(self):
  894. """Get modification time
  895. :return: MAP DATE time string from vector map head file
  896. """
  897. mapValSpl = self.fullName.split("@")
  898. mapSet = mapValSpl[1]
  899. mapName = mapValSpl[0]
  900. headPath = os.path.join(grass.gisenv()['GISDBASE'],
  901. grass.gisenv()['LOCATION_NAME'],
  902. mapSet,
  903. "vector",
  904. mapName,
  905. "head")
  906. try:
  907. head = open(headPath, 'r')
  908. for line in head.readlines():
  909. i = line.find('MAP DATE:', )
  910. if i == 0:
  911. head.close()
  912. return line.split(':', 1)[1].strip()
  913. head.close()
  914. return ""
  915. except IOError:
  916. return ""
  917. class History:
  918. """Class which reads and saves history data (based on gui.core.settings Settings class file save/load)
  919. .. todo::
  920. Maybe it could be useful for other GRASS wxGUI tools.
  921. """
  922. def __init__(self):
  923. # max number of steps in history (zero based)
  924. self.maxHistSteps = 3
  925. # current history step
  926. self.currHistStep = 0
  927. # number of steps saved in history
  928. self.histStepsNum = 0
  929. # dict contains data saved in history for current history step
  930. self.currHistStepData = {}
  931. # buffer for data to be saved into history
  932. self.newHistStepData = {}
  933. self.histFile = grass.tempfile()
  934. # key/value separator
  935. self.sep = ';'
  936. def __del__(self):
  937. try_remove(self.histFile)
  938. def GetNext(self):
  939. """Go one step forward in history"""
  940. self.currHistStep -= 1
  941. self.currHistStepData.clear()
  942. self.currHistStepData = self._getHistStepData(self.currHistStep)
  943. return self.currHistStepData
  944. def GetPrev(self):
  945. """Go one step back in history"""
  946. self.currHistStep += 1
  947. self.currHistStepData.clear()
  948. self.currHistStepData = self._getHistStepData(self.currHistStep)
  949. return self.currHistStepData
  950. def GetStepsNum(self):
  951. """Get number of steps saved in history"""
  952. return self.histStepsNum
  953. def GetCurrHistStep(self):
  954. """Get current history step"""
  955. return self.currHistStep
  956. def Add(self, key, subkey, value):
  957. """Add new data into buffer"""
  958. if key not in self.newHistStepData:
  959. self.newHistStepData[key] = {}
  960. if isinstance(subkey, list):
  961. if subkey[0] not in self.newHistStepData[key]:
  962. self.newHistStepData[key][subkey[0]] = {}
  963. self.newHistStepData[key][subkey[0]][subkey[1]] = value
  964. else:
  965. self.newHistStepData[key][subkey] = value
  966. def SaveHistStep(self):
  967. """Create new history step with data in buffer"""
  968. self.maxHistSteps = UserSettings.Get(group='vnet',
  969. key='other',
  970. subkey='max_hist_steps')
  971. self.currHistStep = 0
  972. newHistFile = grass.tempfile()
  973. newHist = open(newHistFile, "w")
  974. self._saveNewHistStep(newHist)
  975. oldHist = open(self.histFile)
  976. removedHistData = self._savePreviousHist(newHist, oldHist)
  977. oldHist.close()
  978. newHist.close()
  979. try_remove(self.histFile)
  980. self.histFile = newHistFile
  981. self.newHistStepData.clear()
  982. return removedHistData
  983. def _savePreviousHist(self, newHist, oldHist):
  984. """Save previous history into new file"""
  985. newHistStep = False
  986. removedHistData = {}
  987. newHistStepsNum = self.histStepsNum
  988. for line in oldHist.readlines():
  989. if not line.strip():
  990. newHistStep = True
  991. newHistStepsNum += 1
  992. continue
  993. if newHistStep:
  994. newHistStep = False
  995. line = line.split("=")
  996. line[1] = str(newHistStepsNum)
  997. line = "=".join(line)
  998. if newHistStepsNum >= self.maxHistSteps:
  999. removedHistStep = removedHistData[line] = {}
  1000. continue
  1001. else:
  1002. newHist.write('%s%s%s' % (os.linesep, line, os.linesep))
  1003. self.histStepsNum = newHistStepsNum
  1004. else:
  1005. if newHistStepsNum >= self.maxHistSteps:
  1006. self._parseLine(line, removedHistStep)
  1007. else:
  1008. newHist.write('%s' % line)
  1009. return removedHistData
  1010. def _saveNewHistStep(self, newHist):
  1011. """Save buffer (new step) data into file"""
  1012. newHist.write('%s%s%s' % (os.linesep, "history step=0", os.linesep))
  1013. for key in list(self.newHistStepData.keys()):
  1014. subkeys = list(self.newHistStepData[key].keys())
  1015. newHist.write('%s%s' % (key, self.sep))
  1016. for idx in range(len(subkeys)):
  1017. value = self.newHistStepData[key][subkeys[idx]]
  1018. if isinstance(value, dict):
  1019. if idx > 0:
  1020. newHist.write('%s%s%s' % (os.linesep, key, self.sep))
  1021. newHist.write('%s%s' % (subkeys[idx], self.sep))
  1022. kvalues = list(self.newHistStepData[key][subkeys[idx]].keys())
  1023. srange = range(len(kvalues))
  1024. for sidx in srange:
  1025. svalue = self._parseValue(
  1026. self.newHistStepData[key][
  1027. subkeys[idx]][
  1028. kvalues[sidx]])
  1029. newHist.write(
  1030. '%s%s%s' %
  1031. (kvalues[sidx], self.sep, svalue))
  1032. if sidx < len(kvalues) - 1:
  1033. newHist.write('%s' % self.sep)
  1034. else:
  1035. if idx > 0 and isinstance(
  1036. self.newHistStepData[key][subkeys[idx - 1]],
  1037. dict):
  1038. newHist.write('%s%s%s' % (os.linesep, key, self.sep))
  1039. value = self._parseValue(
  1040. self.newHistStepData[key][subkeys[idx]])
  1041. newHist.write('%s%s%s' % (subkeys[idx], self.sep, value))
  1042. if idx < len(subkeys) - 1 and not isinstance(
  1043. self.newHistStepData[key][subkeys[idx + 1]],
  1044. dict):
  1045. newHist.write('%s' % self.sep)
  1046. newHist.write(os.linesep)
  1047. self.histStepsNum = 0
  1048. def _parseValue(self, value, read=False):
  1049. """Parse value"""
  1050. if read: # -> read data (cast values)
  1051. if value:
  1052. if value[
  1053. 0] == '[' and value[-1] == ']': # TODO, possible wrong interpretation
  1054. value = value[1:-1].split(',')
  1055. value = map(self._castValue, value)
  1056. return value
  1057. if value == 'True':
  1058. value = True
  1059. elif value == 'False':
  1060. value = False
  1061. elif value == 'None':
  1062. value = None
  1063. elif ':' in value: # -> color
  1064. try:
  1065. value = tuple(map(int, value.split(':')))
  1066. except ValueError: # -> string
  1067. pass
  1068. else:
  1069. try:
  1070. value = int(value)
  1071. except ValueError:
  1072. try:
  1073. value = float(value)
  1074. except ValueError:
  1075. pass
  1076. else: # -> write data
  1077. if isinstance(value, type(())): # -> color
  1078. value = str(value[0]) + ':' +\
  1079. str(value[1]) + ':' + \
  1080. str(value[2])
  1081. return value
  1082. def _castValue(self, value):
  1083. """Cast value"""
  1084. try:
  1085. value = int(value)
  1086. except ValueError:
  1087. try:
  1088. value = float(value)
  1089. except ValueError:
  1090. value = value[1:-1]
  1091. return value
  1092. def _getHistStepData(self, histStep):
  1093. """Load data saved in history step"""
  1094. hist = open(self.histFile)
  1095. histStepData = {}
  1096. newHistStep = False
  1097. isSearchedHistStep = False
  1098. for line in hist.readlines():
  1099. if not line.strip() and isSearchedHistStep:
  1100. break
  1101. elif not line.strip():
  1102. newHistStep = True
  1103. continue
  1104. elif isSearchedHistStep:
  1105. self._parseLine(line, histStepData)
  1106. if newHistStep:
  1107. line = line.split("=")
  1108. if int(line[1]) == histStep:
  1109. isSearchedHistStep = True
  1110. newHistStep = False
  1111. hist.close()
  1112. return histStepData
  1113. def _parseLine(self, line, histStepData):
  1114. """Parse line in file with history"""
  1115. line = line.rstrip('%s' % os.linesep).split(self.sep)
  1116. key = line[0]
  1117. kv = line[1:]
  1118. idx = 0
  1119. subkeyMaster = None
  1120. if len(kv) % 2 != 0: # multiple (e.g. nviz)
  1121. subkeyMaster = kv[0]
  1122. del kv[0]
  1123. idx = 0
  1124. while idx < len(kv):
  1125. if subkeyMaster:
  1126. subkey = [subkeyMaster, kv[idx]]
  1127. else:
  1128. subkey = kv[idx]
  1129. value = kv[idx + 1]
  1130. value = self._parseValue(value, read=True)
  1131. if key not in histStepData:
  1132. histStepData[key] = {}
  1133. if isinstance(subkey, list):
  1134. if subkey[0] not in histStepData[key]:
  1135. histStepData[key][subkey[0]] = {}
  1136. histStepData[key][subkey[0]][subkey[1]] = value
  1137. else:
  1138. histStepData[key][subkey] = value
  1139. idx += 2
  1140. def DeleteNewHistStepData(self):
  1141. """Delete buffer data for new history step"""
  1142. self.newHistStepData.clear()
  1143. class VNETGlobalTurnsData:
  1144. """Turn Data"""
  1145. def __init__(self):
  1146. # Definition of four basic directions
  1147. self.turn_data = [
  1148. ["Straight", DegreesToRadians(-30), DegreesToRadians(+30), 0.0],
  1149. ["Right Turn", DegreesToRadians(+30), DegreesToRadians(+150), 0.0],
  1150. ["Reverse", DegreesToRadians(+150), DegreesToRadians(-150), 0.0],
  1151. ["Left Turn", DegreesToRadians(-150), DegreesToRadians(-30), 0.0]
  1152. ]
  1153. def GetData(self):
  1154. data = []
  1155. for ival in self.turn_data:
  1156. data.append(ival[1:])
  1157. return data
  1158. def GetValue(self, line, col):
  1159. return self.turn_data[line][col]
  1160. def GetLinesCount(self):
  1161. return len(self.turn_data)
  1162. def SetValue(self, value, line, col):
  1163. self.DataValidator(line, col, value)
  1164. self.turn_data[line][col] = value
  1165. def SetUTurns(self, value):
  1166. """Checked if checeBox is checed"""
  1167. useUTurns = value
  1168. def AppendRow(self, values):
  1169. self.turn_data.append(values)
  1170. def InsertRow(self, line, values):
  1171. self.turn_data.insert(line, values)
  1172. def PopRow(self, values):
  1173. self.RemoveDataValidator(values)
  1174. self.turn_data.pop(values)
  1175. def DataValidator(self, row, col, value):
  1176. """Angle recalculation due to value changing"""
  1177. if col not in [1, 2]:
  1178. return
  1179. if col == 1:
  1180. new_from_angle = value
  1181. old_from_angle = self.turn_data[row][1]
  1182. new_to_angle = self.turn_data[row][2]
  1183. if self.IsInInterval(old_from_angle, new_to_angle, new_from_angle):
  1184. prev_row = row - 1
  1185. if prev_row == -1:
  1186. prev_row = len(self.turn_data) - 1
  1187. self.turn_data[prev_row][2] = new_from_angle
  1188. return
  1189. if col == 2:
  1190. new_to_angle = value
  1191. old_to_angle = self.turn_data[row][2]
  1192. new_from_angle = self.turn_data[row][1]
  1193. if self.IsInInterval(new_from_angle, old_to_angle, new_to_angle):
  1194. next_row = row + 1
  1195. if len(self.turn_data) == next_row:
  1196. next_row = 0
  1197. self.turn_data[next_row][1] = new_to_angle
  1198. return
  1199. inside_new = []
  1200. overlap_new_from = []
  1201. overlap_new_to = []
  1202. for i in range(self.GetLinesCount()):
  1203. if i == row:
  1204. continue
  1205. from_angle = self.turn_data[i][1]
  1206. is_in_from = self.IsInInterval(
  1207. new_from_angle, new_to_angle, from_angle)
  1208. to_angle = self.turn_data[i][2]
  1209. is_in_to = self.IsInInterval(
  1210. new_from_angle, new_to_angle, to_angle)
  1211. if is_in_from and is_in_to:
  1212. inside_new.append(i)
  1213. if is_in_from:
  1214. overlap_new_to.append(i)
  1215. if is_in_to:
  1216. overlap_new_from.append(i)
  1217. for i_row in overlap_new_from:
  1218. self.turn_data[i_row][2] = new_from_angle
  1219. for i_row in overlap_new_to:
  1220. self.turn_data[i_row][1] = new_to_angle
  1221. for i_row in inside_new:
  1222. if col == 1:
  1223. angle = new_from_angle
  1224. else:
  1225. angle = new_to_angle
  1226. self.turn_data[i_row][1] = angle
  1227. self.turn_data[i_row][2] = angle
  1228. def RemoveDataValidator(self, row):
  1229. """Angle recalculation due to direction remove"""
  1230. if row == 0:
  1231. prev_row = self.GetLinesCount() - 1
  1232. else:
  1233. prev_row = row - 1
  1234. remove_to_angle = self.turn_data[row][2]
  1235. self.turn_data[prev_row][2] = remove_to_angle
  1236. def IsInInterval(self, from_angle, to_angle, angle):
  1237. """Test if a direction includes or not includes a value"""
  1238. if to_angle < from_angle:
  1239. to_angle = math.pi * 2 + to_angle
  1240. if angle < from_angle:
  1241. angle = math.pi * 2 + angle
  1242. if angle > from_angle and angle < to_angle:
  1243. return True
  1244. return False