vnet_data.py 50 KB

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