tree.py 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869
  1. """
  2. @package datacatalog::tree
  3. @brief Data catalog tree classes
  4. Classes:
  5. - datacatalog::LocationMapTree
  6. - datacatalog::DataCatalogTree
  7. (C) 2014-2015 by Tereza Fiedlerova, and the GRASS Development Team
  8. This program is free software under the GNU General Public
  9. License (>=v2). Read the file COPYING that comes with GRASS
  10. for details.
  11. @author Tereza Fiedlerova
  12. @author Anna Petrasova (kratochanna gmail com)
  13. """
  14. import os
  15. from multiprocessing import Process, Queue, cpu_count
  16. import wx
  17. from core.gcmd import RunCommand, GError, GMessage, GWarning
  18. from core.utils import GetListOfLocations
  19. from core.debug import Debug
  20. from gui_core.dialogs import TextEntryDialog
  21. from core.giface import StandaloneGrassInterface
  22. from core.treemodel import TreeModel, DictNode
  23. from gui_core.treeview import TreeView
  24. from grass.pydispatch.signal import Signal
  25. import grass.script as gscript
  26. from grass.script import gisenv
  27. from grass.exceptions import CalledModuleError
  28. def getEnvironment(gisdbase, location, mapset):
  29. """Creates environment to be passed in run_command for example.
  30. Returns tuple with temporary file path and the environment. The user
  31. of this function is responsile for deleting the file."""
  32. tmp_gisrc_file = gscript.tempfile()
  33. with open(tmp_gisrc_file, 'w') as f:
  34. f.write('MAPSET: {mapset}\n'.format(mapset=mapset))
  35. f.write('GISDBASE: {g}\n'.format(g=gisdbase))
  36. f.write('LOCATION_NAME: {l}\n'.format(l=location))
  37. f.write('GUI: text\n')
  38. env = os.environ.copy()
  39. env['GISRC'] = tmp_gisrc_file
  40. return tmp_gisrc_file, env
  41. def getLocationTree(gisdbase, location, queue, mapsets=None):
  42. """Creates dictionary with mapsets, elements, layers for given location.
  43. Returns tuple with the dictionary and error (or None)"""
  44. tmp_gisrc_file, env = getEnvironment(gisdbase, location, 'PERMANENT')
  45. env['GRASS_SKIP_MAPSET_OWNER_CHECK'] = '1'
  46. maps_dict = {}
  47. elements = ['raster', 'raster_3d', 'vector']
  48. try:
  49. if not mapsets:
  50. mapsets = gscript.read_command(
  51. 'g.mapsets',
  52. flags='l',
  53. separator='comma',
  54. quiet=True,
  55. env=env).strip()
  56. except CalledModuleError:
  57. queue.put(
  58. (maps_dict,
  59. _("Failed to read mapsets from location <{l}>.").format(
  60. l=location)))
  61. gscript.try_remove(tmp_gisrc_file)
  62. return
  63. else:
  64. listOfMapsets = mapsets.split(',')
  65. Debug.msg(
  66. 4, "Location <{}>: {} mapsets found".format(
  67. location, len(listOfMapsets)))
  68. for each in listOfMapsets:
  69. maps_dict[each] = {}
  70. for elem in elements:
  71. maps_dict[each][elem] = []
  72. try:
  73. maplist = gscript.read_command(
  74. 'g.list', flags='mt', type=elements,
  75. mapset=','.join(listOfMapsets),
  76. quiet=True, env=env).strip()
  77. except CalledModuleError:
  78. queue.put(
  79. (maps_dict,
  80. _("Failed to read maps from location <{l}>.").format(
  81. l=location)))
  82. gscript.try_remove(tmp_gisrc_file)
  83. return
  84. else:
  85. # fill dictionary
  86. listOfMaps = maplist.splitlines()
  87. Debug.msg(
  88. 4, "Location <{}>: {} maps found".format(
  89. location, len(listOfMaps)))
  90. for each in listOfMaps:
  91. ltype, wholename = each.split('/')
  92. name, mapset = wholename.split('@')
  93. maps_dict[mapset][ltype].append(name)
  94. queue.put((maps_dict, None))
  95. gscript.try_remove(tmp_gisrc_file)
  96. def map_exists(name, element, env, mapset=None):
  97. """Check is map is present in the mapset given in the environment
  98. :param name: name of the map
  99. :param element: data type ('raster', 'raster_3d', and 'vector')
  100. :param env environment created by function getEnvironment
  101. """
  102. if not mapset:
  103. mapset = gscript.run_command('g.mapset', flags='p', env=env).strip()
  104. # change type to element used by find file
  105. if element == 'raster':
  106. element = 'cell'
  107. elif element == 'raster_3d':
  108. element = 'grid3'
  109. # g.findfile returns non-zero when file was not found
  110. # se we ignore return code and just focus on stdout
  111. process = gscript.start_command(
  112. 'g.findfile',
  113. flags='n',
  114. element=element,
  115. file=name,
  116. mapset=mapset,
  117. stdout=gscript.PIPE,
  118. stderr=gscript.PIPE,
  119. env=env)
  120. output, errors = process.communicate()
  121. info = gscript.parse_key_val(output, sep='=')
  122. # file is the key questioned in grass.script.core find_file()
  123. # return code should be equivalent to checking the output
  124. if info['file']:
  125. return True
  126. else:
  127. return False
  128. class NameEntryDialog(TextEntryDialog):
  129. def __init__(self, element, mapset, env, **kwargs):
  130. TextEntryDialog.__init__(self, **kwargs)
  131. self._element = element
  132. self._mapset = mapset
  133. self._env = env
  134. id_OK = self.GetAffirmativeId()
  135. self.Bind(wx.EVT_BUTTON, self.OnOK, self.FindWindowById(id_OK))
  136. def OnOK(self, event):
  137. new = self.GetValue()
  138. if not new:
  139. return
  140. if map_exists(new, self._element, self._env, self._mapset):
  141. dlg = wx.MessageDialog(
  142. self,
  143. message=_(
  144. "Map of type {elem} <{name}> already exists in mapset <{mapset}>. "
  145. "Do you want to overwrite it?").format(
  146. elem=self._element,
  147. name=new,
  148. mapset=self._mapset),
  149. caption=_("Overwrite?"),
  150. style=wx.YES_NO)
  151. if dlg.ShowModal() == wx.ID_YES:
  152. dlg.Destroy()
  153. self._env['GRASS_OVERWRITE'] = '1'
  154. self.EndModal(wx.ID_OK)
  155. else:
  156. dlg.Destroy()
  157. return
  158. else:
  159. self.EndModal(wx.ID_OK)
  160. class DataCatalogNode(DictNode):
  161. """Node representing item in datacatalog."""
  162. def __init__(self, label, data=None):
  163. super(DataCatalogNode, self).__init__(label=label, data=data)
  164. def match(self, **kwargs):
  165. """Method used for searching according to given parameters.
  166. :param value: dictionary value to be matched
  167. :param key: data dictionary key
  168. """
  169. if not kwargs:
  170. return False
  171. for key in kwargs:
  172. if not (key in self.data and self.data[key] == kwargs[key]):
  173. return False
  174. return True
  175. class LocationMapTree(TreeView):
  176. def __init__(
  177. self, parent, model=None, style=wx.TR_HIDE_ROOT | wx.TR_EDIT_LABELS
  178. | wx.TR_LINES_AT_ROOT | wx.TR_HAS_BUTTONS | wx.TR_FULL_ROW_HIGHLIGHT |
  179. wx.TR_SINGLE):
  180. """Location Map Tree constructor."""
  181. self._model = TreeModel(DataCatalogNode)
  182. super(
  183. LocationMapTree,
  184. self).__init__(
  185. parent=parent,
  186. model=self._model,
  187. id=wx.ID_ANY,
  188. style=style)
  189. self.showNotification = Signal('Tree.showNotification')
  190. self.parent = parent
  191. self.contextMenu.connect(self.OnRightClick)
  192. self.itemActivated.connect(self.OnDoubleClick)
  193. self._initVariables()
  194. def _initTreeItems(self, locations=None, mapsets=None):
  195. """Add locations, mapsets and layers to the tree.
  196. Runs in multiple processes. Saves resulting data and error."""
  197. # mapsets param currently unused
  198. genv = gisenv()
  199. if not locations:
  200. locations = GetListOfLocations(genv['GISDBASE'])
  201. loc_count = proc_count = 0
  202. queue_list = []
  203. proc_list = []
  204. loc_list = []
  205. nprocs = 4
  206. try:
  207. nprocs = cpu_count()
  208. except NotImplementedError:
  209. nprocs = 4
  210. results = dict()
  211. errors = []
  212. location_nodes = []
  213. nlocations = len(locations)
  214. grassdata_node = self._model.AppendNode(
  215. parent=self._model.root, label=_('GRASS locations in {}').format(
  216. genv['GISDBASE']), data=dict(
  217. type='grassdata'))
  218. for location in locations:
  219. results[location] = dict()
  220. varloc = self._model.AppendNode(
  221. parent=grassdata_node, label=location, data=dict(
  222. type='location', name=location))
  223. location_nodes.append(varloc)
  224. loc_count += 1
  225. Debug.msg(
  226. 3, "Scanning location <{}> ({}/{})".format(location, loc_count, nlocations))
  227. q = Queue()
  228. p = Process(target=getLocationTree,
  229. args=(genv['GISDBASE'], location, q))
  230. p.start()
  231. queue_list.append(q)
  232. proc_list.append(p)
  233. loc_list.append(location)
  234. proc_count += 1
  235. # Wait for all running processes
  236. if proc_count == nprocs or loc_count == nlocations:
  237. Debug.msg(4, "Process subresults")
  238. for i in range(len(loc_list)):
  239. maps, error = queue_list[i].get()
  240. proc_list[i].join()
  241. if error:
  242. errors.append(error)
  243. for key in sorted(maps.keys()):
  244. mapset_node = self._model.AppendNode(
  245. parent=location_nodes[i],
  246. label=key, data=dict(
  247. type='mapset', name=key))
  248. self._populateMapsetItem(mapset_node, maps[key])
  249. proc_count = 0
  250. proc_list = []
  251. queue_list = []
  252. loc_list = []
  253. location_nodes = []
  254. if errors:
  255. wx.CallAfter(GWarning, '\n'.join(errors))
  256. Debug.msg(1, "Tree filled")
  257. self.RefreshItems()
  258. def InitTreeItems(self):
  259. """Load locations, mapsets and layers in the tree."""
  260. raise NotImplementedError()
  261. def ReloadTreeItems(self):
  262. """Reload locations, mapsets and layers in the tree."""
  263. self._model.RemoveNode(self._model.root)
  264. self.RefreshNode(self._model.root)
  265. self.InitTreeItems()
  266. def ReloadCurrentMapset(self):
  267. """Reload current mapset tree only."""
  268. def get_first_child(node):
  269. try:
  270. child = mapsetItem.children[0]
  271. except IndexError:
  272. child = None
  273. return child
  274. genv = gisenv()
  275. locationItem, mapsetItem = self.GetCurrentLocationMapsetNode()
  276. if not locationItem or not mapsetItem:
  277. return
  278. if mapsetItem.children:
  279. node = get_first_child(mapsetItem)
  280. while node:
  281. self._model.RemoveNode(node)
  282. node = get_first_child(mapsetItem)
  283. q = Queue()
  284. p = Process(
  285. target=getLocationTree,
  286. args=(
  287. genv['GISDBASE'],
  288. locationItem.data['name'],
  289. q,
  290. mapsetItem.data['name']))
  291. p.start()
  292. maps, error = q.get()
  293. if error:
  294. raise CalledModuleError(error)
  295. self._populateMapsetItem(mapsetItem, maps[mapsetItem.data['name']])
  296. self.RefreshNode(mapsetItem)
  297. self.RefreshItems()
  298. def _populateMapsetItem(self, mapset_node, data):
  299. for elem in data:
  300. if data[elem]:
  301. element_node = self._model.AppendNode(
  302. parent=mapset_node, label=elem,
  303. data=dict(type='element', name=elem))
  304. for layer in data[elem]:
  305. self._model.AppendNode(parent=element_node, label=layer,
  306. data=dict(type=elem, name=layer))
  307. def _popupMenuLayer(self):
  308. """Create popup menu for layers"""
  309. raise NotImplementedError()
  310. def _popupMenuMapset(self):
  311. """Create popup menu for mapsets"""
  312. raise NotImplementedError()
  313. def _popupMenuElement(self):
  314. """Create popup menu for elements"""
  315. raise NotImplementedError()
  316. def _initVariables(self):
  317. """Init variables."""
  318. self.selected_layer = None
  319. self.selected_type = None
  320. self.selected_mapset = None
  321. self.selected_location = None
  322. def GetControl(self):
  323. """Returns control itself."""
  324. return self
  325. def DefineItems(self, item):
  326. """Set selected items."""
  327. self.selected_layer = None
  328. self.selected_type = None
  329. self.selected_mapset = None
  330. self.selected_location = None
  331. type = item.data['type']
  332. if type in ('raster', 'raster_3d', 'vector'):
  333. self.selected_layer = item
  334. type = 'element'
  335. item = item.parent
  336. if type == 'element':
  337. self.selected_type = item
  338. type = 'mapset'
  339. item = item.parent
  340. if type == 'mapset':
  341. self.selected_mapset = item
  342. type = 'location'
  343. item = item.parent
  344. if type == 'location':
  345. self.selected_location = item
  346. def OnSelChanged(self, event):
  347. self.selected_layer = None
  348. def OnRightClick(self, node):
  349. """Display popup menu."""
  350. self.DefineItems(node)
  351. if self.selected_layer:
  352. self._popupMenuLayer()
  353. elif self.selected_mapset and not self.selected_type:
  354. self._popupMenuMapset()
  355. elif self.selected_type:
  356. self._popupMenuElement()
  357. def OnDoubleClick(self, node):
  358. """Expand/Collapse node."""
  359. if self.IsNodeExpanded(node):
  360. self.CollapseNode(node, recursive=False)
  361. else:
  362. self.ExpandNode(node, recursive=False)
  363. def ExpandCurrentLocation(self):
  364. """Expand current location"""
  365. location = gscript.gisenv()['LOCATION_NAME']
  366. item = self._model.SearchNodes(name=location, type='location')
  367. if item:
  368. self.Select(item[0], select=True)
  369. self.ExpandNode(item[0], recursive=False)
  370. else:
  371. Debug.msg(1, "Location <%s> not found" % location)
  372. def GetCurrentLocationMapsetNode(self):
  373. """Get current mapset node"""
  374. genv = gisenv()
  375. location = genv['LOCATION_NAME']
  376. mapset = genv['MAPSET']
  377. locationItem = self._model.SearchNodes(name=location, type='location')
  378. if not locationItem:
  379. return None, None
  380. mapsetItem = self._model.SearchNodes(
  381. parent=locationItem[0],
  382. name=mapset, type='mapset')
  383. if not mapsetItem:
  384. return locationItem[0], None
  385. return locationItem[0], mapsetItem[0]
  386. def ExpandCurrentMapset(self):
  387. """Expand current mapset"""
  388. locationItem, mapsetItem = self.GetCurrentLocationMapsetNode()
  389. if mapsetItem:
  390. self.Select(mapsetItem, select=True)
  391. self.ExpandNode(mapsetItem, recursive=True)
  392. class DataCatalogTree(LocationMapTree):
  393. def __init__(self, parent, giface=None):
  394. """Data Catalog Tree constructor."""
  395. super(DataCatalogTree, self).__init__(parent)
  396. self._giface = giface
  397. self._restricted = True
  398. self._initVariablesCatalog()
  399. self.beginDrag = Signal('DataCatalogTree.beginDrag')
  400. self.endDrag = Signal('DataCatalogTree.endDrag')
  401. self.startEdit = Signal('DataCatalogTree.startEdit')
  402. self.endEdit = Signal('DataCatalogTree.endEdit')
  403. self.Bind(wx.EVT_TREE_BEGIN_DRAG, lambda evt:
  404. self._emitSignal(evt.GetItem(), self.beginDrag, event=evt))
  405. self.Bind(wx.EVT_TREE_END_DRAG, lambda evt:
  406. self._emitSignal(evt.GetItem(), self.endDrag, event=evt))
  407. self.beginDrag.connect(self.OnBeginDrag)
  408. self.endDrag.connect(self.OnEndDrag)
  409. self.Bind(wx.EVT_TREE_BEGIN_LABEL_EDIT, lambda evt:
  410. self._emitSignal(evt.GetItem(), self.startEdit, event=evt))
  411. self.Bind(wx.EVT_TREE_END_LABEL_EDIT, lambda evt:
  412. self._emitSignal(evt.GetItem(), self.endEdit, event=evt))
  413. self.startEdit.connect(self.OnStartEditLabel)
  414. self.endEdit.connect(self.OnEditLabel)
  415. def _initVariablesCatalog(self):
  416. """Init variables."""
  417. self.copy_layer = None
  418. self.copy_type = None
  419. self.copy_mapset = None
  420. self.copy_location = None
  421. def SetRestriction(self, restrict):
  422. self._restricted = restrict
  423. def _runCommand(self, prog, **kwargs):
  424. cmdString = ' '.join(gscript.make_command(prog, **kwargs))
  425. ret = RunCommand(prog, parent=self, **kwargs)
  426. return ret, cmdString
  427. def InitTreeItems(self):
  428. """Add locations, mapsets and layers to the tree."""
  429. self._initTreeItems()
  430. def OnCopyMap(self, event):
  431. """Copy layer or mapset (just save it temporarily, copying is done by paste)"""
  432. self.copy_layer = self.selected_layer
  433. self.copy_type = self.selected_type
  434. self.copy_mapset = self.selected_mapset
  435. self.copy_location = self.selected_location
  436. label = _(
  437. "Map <{layer}> marked for copying. "
  438. "You can paste it to the current mapset "
  439. "<{mapset}>.".format(
  440. layer=self.copy_layer.label,
  441. mapset=gisenv()['MAPSET']))
  442. self.showNotification.emit(message=label)
  443. def OnRenameMap(self, event):
  444. """Rename layer with dialog"""
  445. old_name = self.selected_layer.label
  446. gisrc, env = getEnvironment(
  447. gisenv()['GISDBASE'],
  448. self.selected_location.label, mapset=self.selected_mapset.label)
  449. new_name = self._getNewMapName(
  450. _('New name'),
  451. _('Rename map'),
  452. old_name,
  453. env=env,
  454. mapset=self.selected_mapset.label,
  455. element=self.selected_type.label)
  456. if new_name:
  457. self.Rename(old_name, new_name)
  458. def OnStartEditLabel(self, node, event):
  459. """Start label editing"""
  460. self.DefineItems(node)
  461. Debug.msg(1, "Start label edit {name}".format(name=node.label))
  462. label = _("Editing {name}").format(name=node.label)
  463. self.showNotification.emit(message=label)
  464. if not self.selected_layer:
  465. event.Veto()
  466. def OnEditLabel(self, node, event):
  467. """End label editing"""
  468. if self.selected_layer and not event.IsEditCancelled():
  469. old_name = node.label
  470. Debug.msg(1, "End label edit {name}".format(name=old_name))
  471. new_name = event.GetLabel()
  472. self.Rename(old_name, new_name)
  473. def Rename(self, old, new):
  474. """Rename layer"""
  475. string = old + ',' + new
  476. gisrc, env = getEnvironment(
  477. gisenv()['GISDBASE'],
  478. self.selected_location.label, self.selected_mapset.label)
  479. label = _("Renaming map <{name}>...").format(name=string)
  480. self.showNotification.emit(message=label)
  481. if self.selected_type.label == 'vector':
  482. renamed, cmd = self._runCommand('g.rename', vector=string, env=env)
  483. elif self.selected_type.label == 'raster':
  484. renamed, cmd = self._runCommand('g.rename', raster=string, env=env)
  485. else:
  486. renamed, cmd = self._runCommand(
  487. 'g.rename', raster3d=string, env=env)
  488. if renamed == 0:
  489. self.selected_layer.label = new
  490. self.selected_layer.data['name'] = new
  491. self.RefreshNode(self.selected_layer)
  492. self.showNotification.emit(
  493. message=_("{cmd} -- completed").format(cmd=cmd))
  494. Debug.msg(1, "LAYER RENAMED TO: " + new)
  495. gscript.try_remove(gisrc)
  496. def OnPasteMap(self, event):
  497. """Paste layer"""
  498. # copying between mapsets of one location
  499. if not self.copy_layer:
  500. GMessage(_("No map selected for copying."), parent=self)
  501. return
  502. if self.selected_location == self.copy_location:
  503. gisrc, env = getEnvironment(
  504. gisenv()['GISDBASE'], self.selected_location.label, mapset=self.selected_mapset.label)
  505. new_name = self._getNewMapName(
  506. _('New name'),
  507. _('Copy map'),
  508. self.copy_layer.label,
  509. env=env,
  510. mapset=self.selected_mapset.label,
  511. element=self.copy_type.label)
  512. if not new_name:
  513. return
  514. if map_exists(
  515. new_name, element=self.copy_type.label, env=env,
  516. mapset=self.selected_mapset.label):
  517. GMessage(
  518. _("Failed to copy map: new map has the same name"),
  519. parent=self)
  520. return
  521. if not self.selected_type:
  522. found = self._model.SearchNodes(
  523. parent=self.selected_mapset, type='element',
  524. name=self.copy_type.label)
  525. self.selected_type = found[0] if found else None
  526. overwrite = False
  527. if self.selected_type:
  528. found = self._model.SearchNodes(
  529. parent=self.selected_type,
  530. type=self.copy_type.label,
  531. name=new_name)
  532. if found and found[0]:
  533. dlg = wx.MessageDialog(
  534. parent=self,
  535. message=_(
  536. "Map <{map}> already exists "
  537. "in the current mapset. "
  538. "Do you want to overwrite it?").format(
  539. map=new_name),
  540. caption=_("Overwrite?"),
  541. style=wx.YES_NO | wx.YES_DEFAULT | wx.ICON_QUESTION)
  542. ret = dlg.ShowModal()
  543. dlg.Destroy()
  544. if ret == wx.ID_YES:
  545. overwrite = True
  546. string = self.copy_layer.label + '@' + self.copy_mapset.label + ',' + new_name
  547. pasted = 0
  548. label = _("Copying <{name}>...").format(name=string)
  549. self.showNotification.emit(message=label)
  550. if self.copy_type.label == 'vector':
  551. pasted, cmd = self._runCommand(
  552. 'g.copy', vector=string, overwrite=overwrite, env=env)
  553. node = 'vector'
  554. elif self.copy_type.label == 'raster':
  555. pasted, cmd = self._runCommand(
  556. 'g.copy', raster=string, overwrite=overwrite, env=env)
  557. node = 'raster'
  558. else:
  559. pasted, cmd = self._runCommand(
  560. 'g.copy', raster_3d=string, overwrite=overwrite, env=env)
  561. node = 'raster_3d'
  562. if pasted == 0:
  563. self.InsertLayer(
  564. name=new_name,
  565. mapset_node=self.selected_mapset,
  566. element_name=node)
  567. Debug.msg(1, "COPIED TO: " + new_name)
  568. self.showNotification.emit(
  569. message=_("g.copy completed").format(cmd=cmd))
  570. gscript.try_remove(gisrc)
  571. else:
  572. GError(
  573. _("Failed to copy map: action is allowed only within the same location."),
  574. parent=self)
  575. # expand selected mapset
  576. self.ExpandNode(self.selected_mapset, recursive=True)
  577. def InsertLayer(self, name, mapset_node, element_name):
  578. """Insert layer into model and refresh tree"""
  579. found_element = self._model.SearchNodes(
  580. parent=mapset_node, type='element', name=element_name)
  581. found_element = found_element[0] if found_element else None
  582. if not found_element:
  583. # add type node if not exists
  584. found_element = self._model.AppendNode(
  585. parent=mapset_node, label=element_name,
  586. data=dict(type='element', name=element_name))
  587. found = self._model.SearchNodes(parent=found_element, name=name)
  588. if len(found) == 0:
  589. self._model.AppendNode(parent=found_element, label=name,
  590. data=dict(type=element_name, name=name))
  591. self._model.SortChildren(found_element)
  592. self.RefreshNode(mapset_node, recursive=True)
  593. def OnDeleteMap(self, event):
  594. """Delete layer or mapset"""
  595. name = self.selected_layer.label
  596. gisrc, env = getEnvironment(
  597. gisenv()['GISDBASE'],
  598. self.selected_location.label, self.selected_mapset.label)
  599. if self._confirmDialog(
  600. question=_(
  601. "Do you really want to delete map <{m}> of type <{etype}> from mapset "
  602. "<{mapset}> in location <{loc}>?").format(
  603. m=name, mapset=self.selected_mapset.label,
  604. etype=self.selected_type.label,
  605. loc=self.selected_location.label),
  606. title=_('Delete map')) == wx.ID_YES:
  607. label = _("Deleting {name}...").format(name=name)
  608. self.showNotification.emit(message=label)
  609. if self.selected_type.label == 'vector':
  610. removed, cmd = self._runCommand(
  611. 'g.remove', flags='f', type='vector', name=name, env=env)
  612. elif self.selected_type.label == 'raster':
  613. removed, cmd = self._runCommand(
  614. 'g.remove', flags='f', type='raster', name=name, env=env)
  615. else:
  616. removed, cmd = self._runCommand(
  617. 'g.remove', flags='f', type='raster_3d', name=name, env=env)
  618. if removed == 0:
  619. self._model.RemoveNode(self.selected_layer)
  620. self.RefreshNode(self.selected_type, recursive=True)
  621. Debug.msg(1, "LAYER " + name + " DELETED")
  622. self.showNotification.emit(
  623. message=_("g.remove completed").format(cmd=cmd))
  624. gscript.try_remove(gisrc)
  625. def OnDisplayLayer(self, event):
  626. """Display layer in current graphics view"""
  627. layerName = []
  628. if self.selected_location.label == gisenv(
  629. )['LOCATION_NAME'] and self.selected_mapset:
  630. string = self.selected_layer.label + '@' + self.selected_mapset.label
  631. layerName.append(string)
  632. label = _("Displaying {name}...").format(name=string)
  633. self.showNotification.emit(message=label)
  634. label = "d." + self.selected_type.label[:4] + " --q map=" + string + \
  635. _(" -- completed. Go to Layers tab for further operations.")
  636. if self.selected_type.label == 'vector':
  637. self._giface.lmgr.AddMaps(layerName, 'vector', True)
  638. elif self.selected_type.label == 'raster':
  639. self._giface.lmgr.AddMaps(layerName, 'raster', True)
  640. else:
  641. self._giface.lmgr.AddMaps(layerName, 'raster_3d', True)
  642. # generate this message (command) automatically?
  643. label = "d.rast --q map=" + string + _(
  644. " -- completed. Go to Layers tab for further operations.")
  645. self.showNotification.emit(message=label)
  646. Debug.msg(1, "LAYER " + self.selected_layer.label + " DISPLAYED")
  647. else:
  648. GError(
  649. _("Failed to display layer: not in current mapset or invalid layer"),
  650. parent=self)
  651. def OnBeginDrag(self, node, event):
  652. """Just copy necessary data"""
  653. self.DefineItems(node)
  654. if self.selected_layer and not (self._restricted and gisenv()[
  655. 'LOCATION_NAME'] != self.selected_location.label):
  656. event.Allow()
  657. self.OnCopyMap(event)
  658. Debug.msg(1, "DRAG")
  659. else:
  660. event.Veto()
  661. def OnEndDrag(self, node, event):
  662. """Copy layer into target"""
  663. if not wx.GetMouseState().ControlDown():
  664. GMessage(_("Moving maps not implemented"), parent=self)
  665. event.Veto()
  666. return
  667. if node:
  668. self.DefineItems(node)
  669. if self._restricted and gisenv(
  670. )['MAPSET'] != self.selected_mapset.label:
  671. GMessage(
  672. _("Maps can be copied only to current mapset"),
  673. parent=self)
  674. event.Veto()
  675. return
  676. if self.selected_location == self.copy_location and self.selected_mapset:
  677. event.Allow()
  678. self.OnPasteMap(event)
  679. Debug.msg(1, "DROP DONE")
  680. else:
  681. event.Veto()
  682. def _getNewMapName(self, message, title, value, element, mapset, env):
  683. """Dialog for simple text entry"""
  684. dlg = NameEntryDialog(parent=self, message=message, caption=title,
  685. element=element, env=env, mapset=mapset)
  686. dlg.SetValue(value)
  687. if dlg.ShowModal() == wx.ID_OK:
  688. name = dlg.GetValue()
  689. else:
  690. name = None
  691. dlg.Destroy()
  692. return name
  693. def _confirmDialog(self, question, title):
  694. """Confirm dialog"""
  695. dlg = wx.MessageDialog(self, question, title, wx.YES_NO)
  696. res = dlg.ShowModal()
  697. dlg.Destroy()
  698. return res
  699. def _popupMenuLayer(self):
  700. """Create popup menu for layers"""
  701. menu = wx.Menu()
  702. genv = gisenv()
  703. if self._restricted:
  704. currentMapset = currentLocation = False
  705. if self.selected_location.label == genv['LOCATION_NAME']:
  706. currentLocation = True
  707. if self.selected_mapset.label == genv['MAPSET']:
  708. currentMapset = True
  709. else:
  710. currentMapset = currentLocation = True
  711. item = wx.MenuItem(menu, wx.NewId(), _("&Copy"))
  712. menu.AppendItem(item)
  713. self.Bind(wx.EVT_MENU, self.OnCopyMap, item)
  714. item.Enable(currentLocation)
  715. item = wx.MenuItem(menu, wx.NewId(), _("&Paste"))
  716. menu.AppendItem(item)
  717. self.Bind(wx.EVT_MENU, self.OnPasteMap, item)
  718. if not(
  719. currentLocation and self.copy_layer and self.selected_location ==
  720. self.copy_location):
  721. item.Enable(False)
  722. item = wx.MenuItem(menu, wx.NewId(), _("&Delete"))
  723. menu.AppendItem(item)
  724. self.Bind(wx.EVT_MENU, self.OnDeleteMap, item)
  725. item.Enable(currentMapset)
  726. item = wx.MenuItem(menu, wx.NewId(), _("&Rename"))
  727. menu.AppendItem(item)
  728. self.Bind(wx.EVT_MENU, self.OnRenameMap, item)
  729. item.Enable(currentMapset)
  730. if not isinstance(self._giface, StandaloneGrassInterface) and \
  731. self.selected_location.label == genv['LOCATION_NAME']:
  732. menu.AppendSeparator()
  733. item = wx.MenuItem(menu, wx.NewId(), _("&Display layer"))
  734. menu.AppendItem(item)
  735. self.Bind(wx.EVT_MENU, self.OnDisplayLayer, item)
  736. self.PopupMenu(menu)
  737. menu.Destroy()
  738. def _popupMenuMapset(self):
  739. """Create popup menu for mapsets"""
  740. menu = wx.Menu()
  741. item = wx.MenuItem(menu, wx.NewId(), _("&Paste"))
  742. menu.AppendItem(item)
  743. self.Bind(wx.EVT_MENU, self.OnPasteMap, item)
  744. if not (self.copy_layer and self.selected_location == self.copy_location):
  745. item.Enable(False)
  746. self.PopupMenu(menu)
  747. menu.Destroy()
  748. def _popupMenuElement(self):
  749. """Create popup menu for elements"""
  750. menu = wx.Menu()
  751. item = wx.MenuItem(menu, wx.NewId(), _("&Paste"))
  752. menu.AppendItem(item)
  753. self.Bind(wx.EVT_MENU, self.OnPasteMap, item)
  754. if not (self.copy_layer and self.selected_location == self.copy_location):
  755. item.Enable(False)
  756. self.PopupMenu(menu)
  757. menu.Destroy()