vnet_data.py 56 KB

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