vnet_data.py 55 KB

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