vnet_data.py 55 KB

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