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