vnet_data.py 55 KB

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