tree.py 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886
  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.changeMapset = Signal('Tree.changeMapset')
  191. self.changeLocation = Signal('Tree.changeLocation')
  192. self.parent = parent
  193. self.contextMenu.connect(self.OnRightClick)
  194. self.itemActivated.connect(self.OnDoubleClick)
  195. self._initVariables()
  196. def _initTreeItems(self, locations=None, mapsets=None):
  197. """Add locations, mapsets and layers to the tree.
  198. Runs in multiple processes. Saves resulting data and error."""
  199. # mapsets param currently unused
  200. genv = gisenv()
  201. if not locations:
  202. locations = GetListOfLocations(genv['GISDBASE'])
  203. loc_count = proc_count = 0
  204. queue_list = []
  205. proc_list = []
  206. loc_list = []
  207. nprocs = 4
  208. try:
  209. nprocs = cpu_count()
  210. except NotImplementedError:
  211. nprocs = 4
  212. results = dict()
  213. errors = []
  214. location_nodes = []
  215. nlocations = len(locations)
  216. grassdata_node = self._model.AppendNode(
  217. parent=self._model.root, label=_('GRASS locations in {}').format(
  218. genv['GISDBASE']), data=dict(
  219. type='grassdata'))
  220. for location in locations:
  221. results[location] = dict()
  222. varloc = self._model.AppendNode(
  223. parent=grassdata_node, label=location, data=dict(
  224. type='location', name=location))
  225. location_nodes.append(varloc)
  226. loc_count += 1
  227. Debug.msg(
  228. 3, "Scanning location <{}> ({}/{})".format(location, loc_count, nlocations))
  229. q = Queue()
  230. p = Process(target=getLocationTree,
  231. args=(genv['GISDBASE'], location, q))
  232. p.start()
  233. queue_list.append(q)
  234. proc_list.append(p)
  235. loc_list.append(location)
  236. proc_count += 1
  237. # Wait for all running processes
  238. if proc_count == nprocs or loc_count == nlocations:
  239. Debug.msg(4, "Process subresults")
  240. for i in range(len(loc_list)):
  241. maps, error = queue_list[i].get()
  242. proc_list[i].join()
  243. if error:
  244. errors.append(error)
  245. for key in sorted(maps.keys()):
  246. mapset_node = self._model.AppendNode(
  247. parent=location_nodes[i],
  248. label=key, data=dict(
  249. type='mapset', name=key))
  250. self._populateMapsetItem(mapset_node, maps[key])
  251. proc_count = 0
  252. proc_list = []
  253. queue_list = []
  254. loc_list = []
  255. location_nodes = []
  256. if errors:
  257. wx.CallAfter(GWarning, '\n'.join(errors))
  258. Debug.msg(1, "Tree filled")
  259. self.RefreshItems()
  260. def InitTreeItems(self):
  261. """Load locations, mapsets and layers in the tree."""
  262. raise NotImplementedError()
  263. def ReloadTreeItems(self):
  264. """Reload locations, mapsets and layers in the tree."""
  265. self._model.RemoveNode(self._model.root)
  266. self.RefreshNode(self._model.root)
  267. self.InitTreeItems()
  268. def ReloadCurrentMapset(self):
  269. """Reload current mapset tree only."""
  270. def get_first_child(node):
  271. try:
  272. child = mapsetItem.children[0]
  273. except IndexError:
  274. child = None
  275. return child
  276. genv = gisenv()
  277. locationItem, mapsetItem = self.GetCurrentLocationMapsetNode()
  278. if not locationItem or not mapsetItem:
  279. return
  280. if mapsetItem.children:
  281. node = get_first_child(mapsetItem)
  282. while node:
  283. self._model.RemoveNode(node)
  284. node = get_first_child(mapsetItem)
  285. q = Queue()
  286. p = Process(
  287. target=getLocationTree,
  288. args=(
  289. genv['GISDBASE'],
  290. locationItem.data['name'],
  291. q,
  292. mapsetItem.data['name']))
  293. p.start()
  294. maps, error = q.get()
  295. if error:
  296. raise CalledModuleError(error)
  297. self._populateMapsetItem(mapsetItem, maps[mapsetItem.data['name']])
  298. self.RefreshNode(mapsetItem)
  299. self.RefreshItems()
  300. def _populateMapsetItem(self, mapset_node, data):
  301. for elem in data:
  302. if data[elem]:
  303. element_node = self._model.AppendNode(
  304. parent=mapset_node, label=elem,
  305. data=dict(type='element', name=elem))
  306. for layer in data[elem]:
  307. self._model.AppendNode(parent=element_node, label=layer,
  308. data=dict(type=elem, name=layer))
  309. def _popupMenuLayer(self):
  310. """Create popup menu for layers"""
  311. raise NotImplementedError()
  312. def _popupMenuMapset(self):
  313. """Create popup menu for mapsets"""
  314. raise NotImplementedError()
  315. def _popupMenuElement(self):
  316. """Create popup menu for elements"""
  317. raise NotImplementedError()
  318. def _initVariables(self):
  319. """Init variables."""
  320. self.selected_layer = None
  321. self.selected_type = None
  322. self.selected_mapset = None
  323. self.selected_location = None
  324. def GetControl(self):
  325. """Returns control itself."""
  326. return self
  327. def DefineItems(self, item):
  328. """Set selected items."""
  329. self.selected_layer = None
  330. self.selected_type = None
  331. self.selected_mapset = None
  332. self.selected_location = None
  333. type = item.data['type']
  334. if type in ('raster', 'raster_3d', 'vector'):
  335. self.selected_layer = item
  336. type = 'element'
  337. item = item.parent
  338. if type == 'element':
  339. self.selected_type = item
  340. type = 'mapset'
  341. item = item.parent
  342. if type == 'mapset':
  343. self.selected_mapset = item
  344. type = 'location'
  345. item = item.parent
  346. if type == 'location':
  347. self.selected_location = item
  348. def OnSelChanged(self, event):
  349. self.selected_layer = None
  350. def OnRightClick(self, node):
  351. """Display popup menu."""
  352. self.DefineItems(node)
  353. if self.selected_layer:
  354. self._popupMenuLayer()
  355. elif self.selected_mapset and not self.selected_type:
  356. self._popupMenuMapset()
  357. elif self.selected_type:
  358. self._popupMenuElement()
  359. def OnDoubleClick(self, node):
  360. """Expand/Collapse node."""
  361. if self.IsNodeExpanded(node):
  362. self.CollapseNode(node, recursive=False)
  363. else:
  364. self.ExpandNode(node, recursive=False)
  365. def ExpandCurrentLocation(self):
  366. """Expand current location"""
  367. location = gscript.gisenv()['LOCATION_NAME']
  368. item = self._model.SearchNodes(name=location, type='location')
  369. if item:
  370. self.Select(item[0], select=True)
  371. self.ExpandNode(item[0], recursive=False)
  372. else:
  373. Debug.msg(1, "Location <%s> not found" % location)
  374. def GetCurrentLocationMapsetNode(self):
  375. """Get current mapset node"""
  376. genv = gisenv()
  377. location = genv['LOCATION_NAME']
  378. mapset = genv['MAPSET']
  379. locationItem = self._model.SearchNodes(name=location, type='location')
  380. if not locationItem:
  381. return None, None
  382. mapsetItem = self._model.SearchNodes(
  383. parent=locationItem[0],
  384. name=mapset, type='mapset')
  385. if not mapsetItem:
  386. return locationItem[0], None
  387. return locationItem[0], mapsetItem[0]
  388. def ExpandCurrentMapset(self):
  389. """Expand current mapset"""
  390. locationItem, mapsetItem = self.GetCurrentLocationMapsetNode()
  391. if mapsetItem:
  392. self.Select(mapsetItem, select=True)
  393. self.ExpandNode(mapsetItem, recursive=True)
  394. class DataCatalogTree(LocationMapTree):
  395. def __init__(self, parent, giface=None):
  396. """Data Catalog Tree constructor."""
  397. super(DataCatalogTree, self).__init__(parent)
  398. self._giface = giface
  399. self._restricted = True
  400. self._initVariablesCatalog()
  401. self.beginDrag = Signal('DataCatalogTree.beginDrag')
  402. self.endDrag = Signal('DataCatalogTree.endDrag')
  403. self.startEdit = Signal('DataCatalogTree.startEdit')
  404. self.endEdit = Signal('DataCatalogTree.endEdit')
  405. self.Bind(wx.EVT_TREE_BEGIN_DRAG, lambda evt:
  406. self._emitSignal(evt.GetItem(), self.beginDrag, event=evt))
  407. self.Bind(wx.EVT_TREE_END_DRAG, lambda evt:
  408. self._emitSignal(evt.GetItem(), self.endDrag, event=evt))
  409. self.beginDrag.connect(self.OnBeginDrag)
  410. self.endDrag.connect(self.OnEndDrag)
  411. self.Bind(wx.EVT_TREE_BEGIN_LABEL_EDIT, lambda evt:
  412. self._emitSignal(evt.GetItem(), self.startEdit, event=evt))
  413. self.Bind(wx.EVT_TREE_END_LABEL_EDIT, lambda evt:
  414. self._emitSignal(evt.GetItem(), self.endEdit, event=evt))
  415. self.startEdit.connect(self.OnStartEditLabel)
  416. self.endEdit.connect(self.OnEditLabel)
  417. def _initVariablesCatalog(self):
  418. """Init variables."""
  419. self.copy_layer = None
  420. self.copy_type = None
  421. self.copy_mapset = None
  422. self.copy_location = None
  423. def SetRestriction(self, restrict):
  424. self._restricted = restrict
  425. def _runCommand(self, prog, **kwargs):
  426. cmdString = ' '.join(gscript.make_command(prog, **kwargs))
  427. ret = RunCommand(prog, parent=self, **kwargs)
  428. return ret, cmdString
  429. def InitTreeItems(self):
  430. """Add locations, mapsets and layers to the tree."""
  431. self._initTreeItems()
  432. def OnCopyMap(self, event):
  433. """Copy layer or mapset (just save it temporarily, copying is done by paste)"""
  434. self.copy_layer = self.selected_layer
  435. self.copy_type = self.selected_type
  436. self.copy_mapset = self.selected_mapset
  437. self.copy_location = self.selected_location
  438. label = _(
  439. "Map <{layer}> marked for copying. "
  440. "You can paste it to the current mapset "
  441. "<{mapset}>.".format(
  442. layer=self.copy_layer.label,
  443. mapset=gisenv()['MAPSET']))
  444. self.showNotification.emit(message=label)
  445. def OnRenameMap(self, event):
  446. """Rename layer with dialog"""
  447. old_name = self.selected_layer.label
  448. gisrc, env = getEnvironment(
  449. gisenv()['GISDBASE'],
  450. self.selected_location.label, mapset=self.selected_mapset.label)
  451. new_name = self._getNewMapName(
  452. _('New name'),
  453. _('Rename map'),
  454. old_name,
  455. env=env,
  456. mapset=self.selected_mapset.label,
  457. element=self.selected_type.label)
  458. if new_name:
  459. self.Rename(old_name, new_name)
  460. def OnStartEditLabel(self, node, event):
  461. """Start label editing"""
  462. self.DefineItems(node)
  463. Debug.msg(1, "Start label edit {name}".format(name=node.label))
  464. label = _("Editing {name}").format(name=node.label)
  465. self.showNotification.emit(message=label)
  466. if not self.selected_layer:
  467. event.Veto()
  468. def OnEditLabel(self, node, event):
  469. """End label editing"""
  470. if self.selected_layer and not event.IsEditCancelled():
  471. old_name = node.label
  472. Debug.msg(1, "End label edit {name}".format(name=old_name))
  473. new_name = event.GetLabel()
  474. self.Rename(old_name, new_name)
  475. def Rename(self, old, new):
  476. """Rename layer"""
  477. string = old + ',' + new
  478. gisrc, env = getEnvironment(
  479. gisenv()['GISDBASE'],
  480. self.selected_location.label, self.selected_mapset.label)
  481. label = _("Renaming map <{name}>...").format(name=string)
  482. self.showNotification.emit(message=label)
  483. if self.selected_type.label == 'vector':
  484. renamed, cmd = self._runCommand('g.rename', vector=string, env=env)
  485. elif self.selected_type.label == 'raster':
  486. renamed, cmd = self._runCommand('g.rename', raster=string, env=env)
  487. else:
  488. renamed, cmd = self._runCommand(
  489. 'g.rename', raster3d=string, env=env)
  490. if renamed == 0:
  491. self.selected_layer.label = new
  492. self.selected_layer.data['name'] = new
  493. self.RefreshNode(self.selected_layer)
  494. self.showNotification.emit(
  495. message=_("{cmd} -- completed").format(cmd=cmd))
  496. Debug.msg(1, "LAYER RENAMED TO: " + new)
  497. gscript.try_remove(gisrc)
  498. def OnPasteMap(self, event):
  499. """Paste layer"""
  500. # copying between mapsets of one location
  501. if not self.copy_layer:
  502. GMessage(_("No map selected for copying."), parent=self)
  503. return
  504. if self.selected_location == self.copy_location:
  505. gisrc, env = getEnvironment(
  506. gisenv()['GISDBASE'], self.selected_location.label, mapset=self.selected_mapset.label)
  507. new_name = self._getNewMapName(
  508. _('New name'),
  509. _('Copy map'),
  510. self.copy_layer.label,
  511. env=env,
  512. mapset=self.selected_mapset.label,
  513. element=self.copy_type.label)
  514. if not new_name:
  515. return
  516. if map_exists(
  517. new_name, element=self.copy_type.label, env=env,
  518. mapset=self.selected_mapset.label):
  519. GMessage(
  520. _("Failed to copy map: new map has the same name"),
  521. parent=self)
  522. return
  523. if not self.selected_type:
  524. found = self._model.SearchNodes(
  525. parent=self.selected_mapset, type='element',
  526. name=self.copy_type.label)
  527. self.selected_type = found[0] if found else None
  528. overwrite = False
  529. if self.selected_type:
  530. found = self._model.SearchNodes(
  531. parent=self.selected_type,
  532. type=self.copy_type.label,
  533. name=new_name)
  534. if found and found[0]:
  535. dlg = wx.MessageDialog(
  536. parent=self,
  537. message=_(
  538. "Map <{map}> already exists "
  539. "in the current mapset. "
  540. "Do you want to overwrite it?").format(
  541. map=new_name),
  542. caption=_("Overwrite?"),
  543. style=wx.YES_NO | wx.YES_DEFAULT | wx.ICON_QUESTION)
  544. ret = dlg.ShowModal()
  545. dlg.Destroy()
  546. if ret == wx.ID_YES:
  547. overwrite = True
  548. string = self.copy_layer.label + '@' + self.copy_mapset.label + ',' + new_name
  549. pasted = 0
  550. label = _("Copying <{name}>...").format(name=string)
  551. self.showNotification.emit(message=label)
  552. if self.copy_type.label == 'vector':
  553. pasted, cmd = self._runCommand(
  554. 'g.copy', vector=string, overwrite=overwrite, env=env)
  555. node = 'vector'
  556. elif self.copy_type.label == 'raster':
  557. pasted, cmd = self._runCommand(
  558. 'g.copy', raster=string, overwrite=overwrite, env=env)
  559. node = 'raster'
  560. else:
  561. pasted, cmd = self._runCommand(
  562. 'g.copy', raster_3d=string, overwrite=overwrite, env=env)
  563. node = 'raster_3d'
  564. if pasted == 0:
  565. self.InsertLayer(
  566. name=new_name,
  567. mapset_node=self.selected_mapset,
  568. element_name=node)
  569. Debug.msg(1, "COPIED TO: " + new_name)
  570. self.showNotification.emit(
  571. message=_("g.copy completed").format(cmd=cmd))
  572. gscript.try_remove(gisrc)
  573. else:
  574. GError(
  575. _("Failed to copy map: action is allowed only within the same location."),
  576. parent=self)
  577. # expand selected mapset
  578. self.ExpandNode(self.selected_mapset, recursive=True)
  579. def InsertLayer(self, name, mapset_node, element_name):
  580. """Insert layer into model and refresh tree"""
  581. found_element = self._model.SearchNodes(
  582. parent=mapset_node, type='element', name=element_name)
  583. found_element = found_element[0] if found_element else None
  584. if not found_element:
  585. # add type node if not exists
  586. found_element = self._model.AppendNode(
  587. parent=mapset_node, label=element_name,
  588. data=dict(type='element', name=element_name))
  589. found = self._model.SearchNodes(parent=found_element, name=name)
  590. if len(found) == 0:
  591. self._model.AppendNode(parent=found_element, label=name,
  592. data=dict(type=element_name, name=name))
  593. self._model.SortChildren(found_element)
  594. self.RefreshNode(mapset_node, recursive=True)
  595. def OnDeleteMap(self, event):
  596. """Delete layer or mapset"""
  597. name = self.selected_layer.label
  598. gisrc, env = getEnvironment(
  599. gisenv()['GISDBASE'],
  600. self.selected_location.label, self.selected_mapset.label)
  601. if self._confirmDialog(
  602. question=_(
  603. "Do you really want to delete map <{m}> of type <{etype}> from mapset "
  604. "<{mapset}> in location <{loc}>?").format(
  605. m=name, mapset=self.selected_mapset.label,
  606. etype=self.selected_type.label,
  607. loc=self.selected_location.label),
  608. title=_('Delete map')) == wx.ID_YES:
  609. label = _("Deleting {name}...").format(name=name)
  610. self.showNotification.emit(message=label)
  611. if self.selected_type.label == 'vector':
  612. removed, cmd = self._runCommand(
  613. 'g.remove', flags='f', type='vector', name=name, env=env)
  614. elif self.selected_type.label == 'raster':
  615. removed, cmd = self._runCommand(
  616. 'g.remove', flags='f', type='raster', name=name, env=env)
  617. else:
  618. removed, cmd = self._runCommand(
  619. 'g.remove', flags='f', type='raster_3d', name=name, env=env)
  620. if removed == 0:
  621. self._model.RemoveNode(self.selected_layer)
  622. self.RefreshNode(self.selected_type, recursive=True)
  623. Debug.msg(1, "LAYER " + name + " DELETED")
  624. self.showNotification.emit(
  625. message=_("g.remove completed").format(cmd=cmd))
  626. gscript.try_remove(gisrc)
  627. def OnDisplayLayer(self, event):
  628. """Display layer in current graphics view"""
  629. layerName = []
  630. if self.selected_location.label == gisenv(
  631. )['LOCATION_NAME'] and self.selected_mapset:
  632. string = self.selected_layer.label + '@' + self.selected_mapset.label
  633. layerName.append(string)
  634. label = _("Displaying {name}...").format(name=string)
  635. self.showNotification.emit(message=label)
  636. label = "d." + self.selected_type.label[:4] + " --q map=" + string + \
  637. _(" -- completed. Go to Layers tab for further operations.")
  638. if self.selected_type.label == 'vector':
  639. self._giface.lmgr.AddMaps(layerName, 'vector', True)
  640. elif self.selected_type.label == 'raster':
  641. self._giface.lmgr.AddMaps(layerName, 'raster', True)
  642. else:
  643. self._giface.lmgr.AddMaps(layerName, 'raster_3d', True)
  644. # generate this message (command) automatically?
  645. label = "d.rast --q map=" + string + _(
  646. " -- completed. Go to Layers tab for further operations.")
  647. self.showNotification.emit(message=label)
  648. Debug.msg(1, "LAYER " + self.selected_layer.label + " DISPLAYED")
  649. else:
  650. GError(
  651. _("Failed to display layer: not in current mapset or invalid layer"),
  652. parent=self)
  653. def OnBeginDrag(self, node, event):
  654. """Just copy necessary data"""
  655. self.DefineItems(node)
  656. if self.selected_layer and not (self._restricted and gisenv()[
  657. 'LOCATION_NAME'] != self.selected_location.label):
  658. event.Allow()
  659. self.OnCopyMap(event)
  660. Debug.msg(1, "DRAG")
  661. else:
  662. event.Veto()
  663. def OnEndDrag(self, node, event):
  664. """Copy layer into target"""
  665. if not wx.GetMouseState().ControlDown():
  666. GMessage(_("Moving maps not implemented"), parent=self)
  667. event.Veto()
  668. return
  669. if node:
  670. self.DefineItems(node)
  671. if self._restricted and gisenv(
  672. )['MAPSET'] != self.selected_mapset.label:
  673. GMessage(
  674. _("Maps can be copied only to current mapset"),
  675. parent=self)
  676. event.Veto()
  677. return
  678. if self.selected_location == self.copy_location and self.selected_mapset:
  679. event.Allow()
  680. self.OnPasteMap(event)
  681. Debug.msg(1, "DROP DONE")
  682. else:
  683. event.Veto()
  684. def OnSwitchLocationMapset(self, event):
  685. genv = gisenv()
  686. if self.selected_location.label == genv['LOCATION_NAME']:
  687. self.changeMapset.emit(mapset=self.selected_mapset.label)
  688. else:
  689. self.changeLocation.emit(mapset=self.selected_mapset.label, location=self.selected_location.label)
  690. self.ExpandCurrentMapset()
  691. def _getNewMapName(self, message, title, value, element, mapset, env):
  692. """Dialog for simple text entry"""
  693. dlg = NameEntryDialog(parent=self, message=message, caption=title,
  694. element=element, env=env, mapset=mapset)
  695. dlg.SetValue(value)
  696. if dlg.ShowModal() == wx.ID_OK:
  697. name = dlg.GetValue()
  698. else:
  699. name = None
  700. dlg.Destroy()
  701. return name
  702. def _confirmDialog(self, question, title):
  703. """Confirm dialog"""
  704. dlg = wx.MessageDialog(self, question, title, wx.YES_NO)
  705. res = dlg.ShowModal()
  706. dlg.Destroy()
  707. return res
  708. def _popupMenuLayer(self):
  709. """Create popup menu for layers"""
  710. menu = wx.Menu()
  711. genv = gisenv()
  712. if self._restricted:
  713. currentMapset = currentLocation = False
  714. if self.selected_location.label == genv['LOCATION_NAME']:
  715. currentLocation = True
  716. if self.selected_mapset.label == genv['MAPSET']:
  717. currentMapset = True
  718. else:
  719. currentMapset = currentLocation = True
  720. item = wx.MenuItem(menu, wx.NewId(), _("&Copy"))
  721. menu.AppendItem(item)
  722. self.Bind(wx.EVT_MENU, self.OnCopyMap, item)
  723. item.Enable(currentLocation)
  724. item = wx.MenuItem(menu, wx.NewId(), _("&Paste"))
  725. menu.AppendItem(item)
  726. self.Bind(wx.EVT_MENU, self.OnPasteMap, item)
  727. if not(
  728. currentLocation and self.copy_layer and self.selected_location ==
  729. self.copy_location):
  730. item.Enable(False)
  731. item = wx.MenuItem(menu, wx.NewId(), _("&Delete"))
  732. menu.AppendItem(item)
  733. self.Bind(wx.EVT_MENU, self.OnDeleteMap, item)
  734. item.Enable(currentMapset)
  735. item = wx.MenuItem(menu, wx.NewId(), _("&Rename"))
  736. menu.AppendItem(item)
  737. self.Bind(wx.EVT_MENU, self.OnRenameMap, item)
  738. item.Enable(currentMapset)
  739. if not isinstance(self._giface, StandaloneGrassInterface) and \
  740. self.selected_location.label == genv['LOCATION_NAME']:
  741. menu.AppendSeparator()
  742. item = wx.MenuItem(menu, wx.NewId(), _("&Display layer"))
  743. menu.AppendItem(item)
  744. self.Bind(wx.EVT_MENU, self.OnDisplayLayer, item)
  745. self.PopupMenu(menu)
  746. menu.Destroy()
  747. def _popupMenuMapset(self):
  748. """Create popup menu for mapsets"""
  749. menu = wx.Menu()
  750. genv = gisenv()
  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. item = wx.MenuItem(menu, wx.NewId(), _("&Switch mapset"))
  757. menu.AppendItem(item)
  758. self.Bind(wx.EVT_MENU, self.OnSwitchLocationMapset, item)
  759. if (self.selected_location.label == genv['LOCATION_NAME']
  760. and self.selected_mapset.label == genv['MAPSET']):
  761. item.Enable(False)
  762. self.PopupMenu(menu)
  763. menu.Destroy()
  764. def _popupMenuElement(self):
  765. """Create popup menu for elements"""
  766. menu = wx.Menu()
  767. item = wx.MenuItem(menu, wx.NewId(), _("&Paste"))
  768. menu.AppendItem(item)
  769. self.Bind(wx.EVT_MENU, self.OnPasteMap, item)
  770. if not (self.copy_layer and self.selected_location == self.copy_location):
  771. item.Enable(False)
  772. self.PopupMenu(menu)
  773. menu.Destroy()