vnet_data.py 55 KB

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