import_export.py 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026
  1. """
  2. @package modules.import_export
  3. @brief Import/export dialogs used in wxGUI.
  4. List of classes:
  5. - :class:`ImportDialog`
  6. - :class:`GdalImportDialog`
  7. - :class:`OgrImportDialog`
  8. - :class:`GdalOutputDialog`
  9. - :class:`DxfImportDialog`
  10. - :class:`ReprojectionDialog`
  11. (C) 2008-2016 by the GRASS Development Team
  12. This program is free software under the GNU General Public License
  13. (>=v2). Read the file COPYING that comes with GRASS for details.
  14. @author Martin Landa <landa.martin gmail.com>
  15. @author Anna Kratochvilova <kratochanna gmail.com> (GroupDialog, SymbolDialog)
  16. """
  17. import os
  18. import sys
  19. import wx
  20. from core import globalvar
  21. if globalvar.wxPythonPhoenix:
  22. try:
  23. import agw.flatnotebook as FN
  24. except ImportError: # if it's not there locally, try the wxPython lib.
  25. import wx.lib.agw.flatnotebook as FN
  26. else:
  27. import wx.lib.flatnotebook as FN
  28. import wx.lib.filebrowsebutton as filebrowse
  29. from grass.script import core as grass
  30. from grass.script import task as gtask
  31. from core.gcmd import GError, GMessage, GWarning, RunCommand
  32. from gui_core.forms import CmdPanel
  33. from gui_core.gselect import OgrTypeSelect, GdalSelect, SubGroupSelect
  34. from gui_core.widgets import GListCtrl, GNotebook, LayersList, \
  35. LayersListValidator
  36. from gui_core.wrap import Button, StaticText, StaticBox
  37. from core.utils import GetValidLayerName
  38. from core.settings import UserSettings, GetDisplayVectSettings
  39. class ImportDialog(wx.Dialog):
  40. """Dialog for bulk import of various data (base class)"""
  41. def __init__(self, parent, giface, itype,
  42. id=wx.ID_ANY, title=_("Multiple import"),
  43. style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER):
  44. self.parent = parent # GMFrame
  45. self._giface = giface # used to add layers
  46. self.importType = itype
  47. self.options = dict() # list of options
  48. self.options_par = dict()
  49. self.commandId = -1 # id of running command
  50. wx.Dialog.__init__(self, parent, id, title, style=style,
  51. name="MultiImportDialog")
  52. self.panel = wx.Panel(parent=self, id=wx.ID_ANY)
  53. self.layerBox = StaticBox(parent=self.panel, id=wx.ID_ANY)
  54. if self.importType == 'gdal':
  55. label = _("List of raster layers")
  56. elif self.importType == 'ogr':
  57. label = _("List of vector layers")
  58. else:
  59. label = _("List of %s layers") % self.importType.upper()
  60. self.layerBox.SetLabel(" %s - %s " %
  61. (label, _("right click to (un)select all")))
  62. # list of layers
  63. columns = [_('Layer id'),
  64. _('Layer name'),
  65. _('Name for output GRASS map (editable)')]
  66. if itype == 'ogr':
  67. columns.insert(2, _('Feature type'))
  68. columns.insert(3, _('Projection match'))
  69. elif itype == 'gdal':
  70. columns.insert(2, _('Projection match'))
  71. self.list = LayersList(parent=self.panel, columns=columns)
  72. self.list.LoadData()
  73. self.override = wx.CheckBox(
  74. parent=self.panel, id=wx.ID_ANY,
  75. label=_("Override projection check (use current location's projection)"))
  76. self.overwrite = wx.CheckBox(
  77. parent=self.panel, id=wx.ID_ANY,
  78. label=_("Allow output files to overwrite existing files"))
  79. self.overwrite.SetValue(
  80. UserSettings.Get(
  81. group='cmd',
  82. key='overwrite',
  83. subkey='enabled'))
  84. self.overwrite.Bind(wx.EVT_CHECKBOX, self.OnCheckOverwrite)
  85. self.add = wx.CheckBox(parent=self.panel, id=wx.ID_ANY)
  86. self.closeOnFinish = wx.CheckBox(parent=self.panel, id=wx.ID_ANY,
  87. label=_("Close dialog on finish"))
  88. self.closeOnFinish.SetValue(
  89. UserSettings.Get(
  90. group='cmd',
  91. key='closeDlg',
  92. subkey='enabled'))
  93. #
  94. # buttons
  95. #
  96. # cancel
  97. self.btn_close = Button(parent=self.panel, id=wx.ID_CLOSE)
  98. self.btn_close.SetToolTip(_("Close dialog"))
  99. self.btn_close.Bind(wx.EVT_BUTTON, self.OnClose)
  100. # run
  101. self.btn_run = Button(
  102. parent=self.panel,
  103. id=wx.ID_OK,
  104. label=_("&Import"))
  105. self.btn_run.SetToolTip(_("Import selected layers"))
  106. self.btn_run.SetDefault()
  107. self.btn_run.Bind(wx.EVT_BUTTON, self.OnRun)
  108. self.Bind(wx.EVT_CLOSE, lambda evt: self.Destroy())
  109. self.notebook = GNotebook(parent=self,
  110. style=FN.FNB_FANCY_TABS | FN.FNB_BOTTOM |
  111. FN.FNB_NO_X_BUTTON)
  112. self.notebook.AddPage(page=self.panel,
  113. text=_('Source settings'),
  114. name='source')
  115. self.createSettingsPage()
  116. # Enable copying to clipboard with cmd+c from dialog on macOS
  117. # (default key binding will close the dialog), trac #3592
  118. if sys.platform == "darwin":
  119. self.Bind(wx.EVT_MENU, self.OnCopyToClipboard, id=wx.ID_COPY)
  120. self.accel_tbl = wx.AcceleratorTable([(wx.ACCEL_CTRL, ord("C"), wx.ID_COPY)])
  121. self.SetAcceleratorTable(self.accel_tbl)
  122. def createSettingsPage(self):
  123. self._blackList = {
  124. 'enabled': True,
  125. 'items': {
  126. self._getCommand(): {
  127. 'params': self._getBlackListedParameters(),
  128. 'flags': self._getBlackListedFlags()}}}
  129. grass_task = gtask.parse_interface(self._getCommand(),
  130. blackList=self._blackList)
  131. self.advancedPagePanel = CmdPanel(
  132. parent=self, giface=None, task=grass_task, frame=None)
  133. self.notebook.AddPage(page=self.advancedPagePanel,
  134. text=_('Import settings'),
  135. name='settings')
  136. def doLayout(self):
  137. """Do layout"""
  138. dialogSizer = wx.BoxSizer(wx.VERTICAL)
  139. # dsn input
  140. dialogSizer.Add(self.dsnInput, proportion=0,
  141. flag=wx.EXPAND)
  142. #
  143. # list of DXF layers
  144. #
  145. layerSizer = wx.StaticBoxSizer(self.layerBox, wx.HORIZONTAL)
  146. layerSizer.Add(self.list, proportion=1,
  147. flag=wx.ALL | wx.EXPAND, border=5)
  148. dialogSizer.Add(
  149. layerSizer,
  150. proportion=1,
  151. flag=wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND,
  152. border=5)
  153. dialogSizer.Add(self.override, proportion=0,
  154. flag=wx.LEFT | wx.RIGHT | wx.BOTTOM, border=5)
  155. dialogSizer.Add(self.overwrite, proportion=0,
  156. flag=wx.LEFT | wx.RIGHT | wx.BOTTOM, border=5)
  157. dialogSizer.Add(self.add, proportion=0,
  158. flag=wx.LEFT | wx.RIGHT | wx.BOTTOM, border=5)
  159. dialogSizer.Add(self.closeOnFinish, proportion=0,
  160. flag=wx.LEFT | wx.RIGHT | wx.BOTTOM, border=5)
  161. #
  162. # buttons
  163. #
  164. btnsizer = wx.BoxSizer(orient=wx.HORIZONTAL)
  165. btnsizer.Add(self.btn_close, proportion=0,
  166. flag=wx.LEFT | wx.RIGHT | wx.ALIGN_CENTER,
  167. border=10)
  168. btnsizer.Add(self.btn_run, proportion=0,
  169. flag=wx.RIGHT | wx.ALIGN_CENTER,
  170. border=10)
  171. dialogSizer.Add(
  172. btnsizer,
  173. proportion=0,
  174. flag=wx.BOTTOM | wx.ALIGN_RIGHT,
  175. border=10)
  176. # dialogSizer.SetSizeHints(self.panel)
  177. self.panel.SetAutoLayout(True)
  178. self.panel.SetSizer(dialogSizer)
  179. dialogSizer.Fit(self.panel)
  180. # auto-layout seems not work here - FIXME
  181. size = wx.Size(globalvar.DIALOG_GSELECT_SIZE[0] + 322, 550)
  182. self.SetMinSize(size)
  183. self.SetSize((size.width, size.height + 100))
  184. # width = self.GetSize()[0]
  185. # self.list.SetColumnWidth(col = 1, width = width / 2 - 50)
  186. self.Layout()
  187. def _getCommand(self):
  188. """Get command"""
  189. raise NotImplementedError()
  190. def _getBlackListedParameters(self):
  191. """Get parameters which will not be showed in Settings page"""
  192. raise NotImplementedError()
  193. def _getBlackListedFlags(self):
  194. """Get flags which will not be showed in Settings page"""
  195. raise NotImplementedError()
  196. def _nameValidationFailed(self, layers_list):
  197. """Output map name validation callback
  198. :param layers_list: LayersList class instance
  199. """
  200. if isinstance(layers_list.output_map, list):
  201. maps = [
  202. '<{}>'.format(m) for m in layers_list.output_map]
  203. message = _(
  204. "Output map names %(names)s exist. "
  205. ) % {
  206. 'names': ', '.join(maps)}
  207. else:
  208. message = _(
  209. "Output map name <%(name)s> exist. "
  210. ) % {
  211. 'name': layers_list.output_map}
  212. GError(parent=self, message=message, caption=_("Invalid name"))
  213. def _validateOutputMapName(self):
  214. """Enable/disable output map name validation according the
  215. overwrite state"""
  216. if not self.overwrite.IsChecked() or \
  217. UserSettings.Get(group='cmd', key='overwrite',
  218. subkey='enabled'):
  219. if not self.list.GetValidator().\
  220. Validate(win=self.list, validate_all=True):
  221. return False
  222. return True
  223. def OnClose(self, event=None):
  224. """Close dialog"""
  225. self.Close()
  226. def OnRun(self, event):
  227. """Import/Link data (each layes as separate vector map)"""
  228. pass
  229. def OnCheckOverwrite(self, event):
  230. """Check/uncheck overwrite checkbox widget"""
  231. if self.overwrite.IsChecked():
  232. self.list.validate = False
  233. else:
  234. self.list.validate = True
  235. def AddLayers(self, returncode, cmd=None, userData=None):
  236. """Add imported/linked layers into layer tree"""
  237. if not self.add.IsChecked() or returncode != 0:
  238. return
  239. # TODO: if importing map creates more map the folowing does not work
  240. # * do nothing if map does not exist or
  241. # * try to determine names using regexp or
  242. # * persuade import tools to report map names
  243. self.commandId += 1
  244. layer, output = self.list.GetLayers()[self.commandId][:2]
  245. if '@' not in output:
  246. name = output + '@' + grass.gisenv()['MAPSET']
  247. else:
  248. name = output
  249. # add imported layers into layer tree
  250. # an alternative would be emit signal (mapCreated) and (optionally)
  251. # connect to this signal
  252. llist = self._giface.GetLayerList()
  253. if self.importType == 'gdal':
  254. if userData:
  255. nBands = int(userData.get('nbands', 1))
  256. else:
  257. nBands = 1
  258. if UserSettings.Get(group='rasterLayer',
  259. key='opaque', subkey='enabled'):
  260. nFlag = True
  261. else:
  262. nFlag = False
  263. for i in range(1, nBands + 1):
  264. nameOrig = name
  265. if nBands > 1:
  266. mapName, mapsetName = name.split('@')
  267. mapName += '.%d' % i
  268. name = mapName + '@' + mapsetName
  269. cmd = ['d.rast',
  270. 'map=%s' % name]
  271. if nFlag:
  272. cmd.append('-n')
  273. llist.AddLayer(ltype='raster',
  274. name=name, checked=True,
  275. cmd=cmd)
  276. name = nameOrig
  277. else:
  278. llist.AddLayer(ltype='vector',
  279. name=name, checked=True,
  280. cmd=['d.vect',
  281. 'map=%s' % name] + GetDisplayVectSettings())
  282. self._giface.GetMapWindow().ZoomToMap()
  283. def OnAbort(self, event):
  284. """Abort running import
  285. .. todo::
  286. not yet implemented
  287. """
  288. pass
  289. def OnCmdDone(self, event):
  290. """Do what has to be done after importing"""
  291. pass
  292. def OnCopyToClipboard(self, event):
  293. """Copy selected text in dialog to the clipboard"""
  294. try:
  295. wx.Window.FindFocus().Copy()
  296. except:
  297. pass
  298. def _getLayersToReprojetion(self, projMatch_idx, grassName_idx):
  299. """If there are layers with different projection from loation projection,
  300. show dialog to user to explicitly select layers which will be reprojected..."""
  301. differentProjLayers = []
  302. data = self.list.GetData(checked=True)
  303. for itm in data:
  304. layerId = itm[-1]
  305. # select only layers with different projetion
  306. if self.layersData[layerId][projMatch_idx] == 0:
  307. dt = [itm[0], itm[grassName_idx]]
  308. differentProjLayers.append(tuple(dt))
  309. layers = self.list.GetLayers()
  310. if not self.link and \
  311. differentProjLayers and \
  312. not self.override.IsChecked(): # '-o' not in self.getSettingsPageCmd():
  313. dlg = ReprojectionDialog(
  314. parent=self,
  315. giface=self._giface,
  316. data=differentProjLayers)
  317. ret = dlg.ShowModal()
  318. if ret == wx.ID_OK:
  319. # do not import unchecked layers
  320. for itm in reversed(list(dlg.GetData(checked=False))):
  321. idx = itm[-1]
  322. layers.pop(idx)
  323. else:
  324. return None
  325. return layers
  326. def getSettingsPageCmd(self):
  327. return self.advancedPagePanel.createCmd(
  328. ignoreErrors=True, ignoreRequired=True)
  329. class GdalImportDialog(ImportDialog):
  330. def __init__(self, parent, giface, link=False):
  331. """Dialog for bulk import of various raster data
  332. .. todo::
  333. split importing logic from gui code
  334. :param parent: parent window
  335. :param link: True for linking data otherwise importing data
  336. """
  337. self._giface = giface
  338. self.link = link
  339. self.layersData = []
  340. ImportDialog.__init__(self, parent, giface=giface, itype='gdal')
  341. self.list.SetValidator(
  342. LayersListValidator(
  343. condition='raster',
  344. callback=self._nameValidationFailed))
  345. if link:
  346. self.SetTitle(_("Link external raster data"))
  347. else:
  348. self.SetTitle(_("Import raster data"))
  349. self.dsnInput = GdalSelect(parent=self, panel=self.panel,
  350. ogr=False, link=link)
  351. self.dsnInput.AttachSettings()
  352. self.dsnInput.reloadDataRequired.connect(self.reload)
  353. if link:
  354. self.add.SetLabel(_("Add linked layers into layer tree"))
  355. else:
  356. self.add.SetLabel(_("Add imported layers into layer tree"))
  357. self.add.SetValue(
  358. UserSettings.Get(
  359. group='cmd',
  360. key='addNewLayer',
  361. subkey='enabled'))
  362. if link:
  363. self.btn_run.SetLabel(_("&Link"))
  364. self.btn_run.SetToolTip(_("Link selected layers"))
  365. else:
  366. self.btn_run.SetLabel(_("&Import"))
  367. self.btn_run.SetToolTip(_("Import selected layers"))
  368. self.doLayout()
  369. def reload(self, data, listData):
  370. self.list.LoadData(listData)
  371. self.layersData = data
  372. def OnRun(self, event):
  373. """Import/Link data (each layes as separate vector map)"""
  374. self.commandId = -1
  375. data = self.list.GetLayers()
  376. data = self._getLayersToReprojetion(2, 3)
  377. if data is None:
  378. return
  379. if not data:
  380. GMessage(_("No layers selected. Operation canceled."),
  381. parent=self)
  382. return
  383. if not self._validateOutputMapName():
  384. return
  385. dsn = self.dsnInput.GetDsn()
  386. ext = self.dsnInput.GetFormatExt()
  387. for layer, output, listId in data:
  388. userData = {}
  389. if self.dsnInput.GetType() == 'dir':
  390. idsn = os.path.join(dsn, layer)
  391. else:
  392. idsn = dsn
  393. # check number of bands
  394. nBandsStr = RunCommand('r.in.gdal',
  395. flags='p',
  396. input=idsn, read=True)
  397. nBands = -1
  398. if nBandsStr:
  399. try:
  400. nBands = int(nBandsStr.rstrip('\n'))
  401. except:
  402. pass
  403. if nBands < 0:
  404. GWarning(_("Unable to determine number of raster bands"),
  405. parent=self)
  406. nBands = 1
  407. userData['nbands'] = nBands
  408. cmd = self.getSettingsPageCmd()
  409. cmd.append('input=%s' % idsn)
  410. cmd.append('output=%s' % output)
  411. if self.override.IsChecked():
  412. cmd.append('-o')
  413. if self.overwrite.IsChecked():
  414. cmd.append('--overwrite')
  415. if UserSettings.Get(group='cmd', key='overwrite',
  416. subkey='enabled') and '--overwrite' not in cmd:
  417. cmd.append('--overwrite')
  418. # run in Layer Manager
  419. self._giface.RunCmd(
  420. cmd,
  421. onDone=self.OnCmdDone,
  422. userData=userData,
  423. addLayer=False)
  424. def OnCmdDone(self, event):
  425. """Load layers and close if required"""
  426. if not hasattr(self, 'AddLayers'):
  427. return
  428. self.AddLayers(event.returncode, event.cmd, event.userData)
  429. if event.returncode == 0 and self.closeOnFinish.IsChecked():
  430. self.Close()
  431. def _getCommand(self):
  432. """Get command"""
  433. if self.link:
  434. return 'r.external'
  435. else:
  436. return 'r.import'
  437. def _getBlackListedParameters(self):
  438. """Get flags which will not be showed in Settings page"""
  439. return ['input', 'output']
  440. def _getBlackListedFlags(self):
  441. """Get flags which will not be showed in Settings page"""
  442. return ['overwrite', 'o']
  443. class OgrImportDialog(ImportDialog):
  444. def __init__(self, parent, giface, link=False):
  445. """Dialog for bulk import of various vector data
  446. .. todo::
  447. split importing logic from gui code
  448. :param parent: parent window
  449. :param link: True for linking data otherwise importing data
  450. """
  451. self._giface = giface
  452. self.link = link
  453. self.layersData = []
  454. ImportDialog.__init__(self, parent, giface=giface, itype='ogr')
  455. self.list.SetValidator(
  456. LayersListValidator(
  457. condition='vector',
  458. callback=self._nameValidationFailed))
  459. if link:
  460. self.SetTitle(_("Link external vector data"))
  461. else:
  462. self.SetTitle(_("Import vector data"))
  463. self.dsnInput = GdalSelect(parent=self, panel=self.panel,
  464. ogr=True, link=link)
  465. self.dsnInput.AttachSettings()
  466. self.dsnInput.reloadDataRequired.connect(self.reload)
  467. if link:
  468. self.add.SetLabel(_("Add linked layers into layer tree"))
  469. else:
  470. self.add.SetLabel(_("Add imported layers into layer tree"))
  471. self.add.SetValue(
  472. UserSettings.Get(
  473. group='cmd',
  474. key='addNewLayer',
  475. subkey='enabled'))
  476. if link:
  477. self.btn_run.SetLabel(_("&Link"))
  478. self.btn_run.SetToolTip(_("Link selected layers"))
  479. else:
  480. self.btn_run.SetLabel(_("&Import"))
  481. self.btn_run.SetToolTip(_("Import selected layers"))
  482. self.doLayout()
  483. def reload(self, data, listData):
  484. self.list.LoadData(listData)
  485. self.layersData = data
  486. def OnRun(self, event):
  487. """Import/Link data (each layes as separate vector map)"""
  488. self.commandId = -1
  489. data = self.list.GetLayers()
  490. data = self._getLayersToReprojetion(3, 4)
  491. if data is None:
  492. return
  493. if not data:
  494. GMessage(_("No layers selected. Operation canceled."),
  495. parent=self)
  496. return
  497. if not self._validateOutputMapName():
  498. return
  499. dsn = self.dsnInput.GetDsn()
  500. ext = self.dsnInput.GetFormatExt()
  501. # determine data driver for PostGIS links
  502. self.popOGR = False
  503. if self.dsnInput.GetType() == 'db' and \
  504. self.dsnInput.GetFormat() == 'PostgreSQL' and \
  505. 'GRASS_VECTOR_OGR' not in os.environ:
  506. self.popOGR = True
  507. os.environ['GRASS_VECTOR_OGR'] = '1'
  508. for layer, output, listId in data:
  509. userData = {}
  510. if ext and layer.rfind(ext) > -1:
  511. layer = layer.replace('.' + ext, '')
  512. if '|' in layer:
  513. layer, geometry = layer.split('|', 1)
  514. else:
  515. geometry = None
  516. # TODO: v.import has no geometry option
  517. # if geometry:
  518. # cmd.append('geometry=%s' % geometry)
  519. cmd = self.getSettingsPageCmd()
  520. cmd.append('input=%s' % dsn)
  521. cmd.append('layer=%s' % layer)
  522. cmd.append('output=%s' % output)
  523. if self.override.IsChecked():
  524. cmd.append('-o')
  525. if self.overwrite.IsChecked():
  526. cmd.append('--overwrite')
  527. # TODO options
  528. if UserSettings.Get(group='cmd', key='overwrite',
  529. subkey='enabled') and '--overwrite' not in cmd:
  530. cmd.append('--overwrite')
  531. # run in Layer Manager
  532. self._giface.RunCmd(
  533. cmd,
  534. onDone=self.OnCmdDone,
  535. userData=userData,
  536. addLayer=False)
  537. def OnCmdDone(self, event):
  538. """Load layers and close if required"""
  539. if not hasattr(self, 'AddLayers'):
  540. return
  541. self.AddLayers(event.returncode, event.cmd, event.userData)
  542. if self.popOGR:
  543. os.environ.pop('GRASS_VECTOR_OGR')
  544. if event.returncode == 0 and self.closeOnFinish.IsChecked():
  545. self.Close()
  546. def _getCommand(self):
  547. """Get command"""
  548. if self.link:
  549. return 'v.external'
  550. else:
  551. return 'v.import'
  552. def _getBlackListedParameters(self):
  553. """Get parametrs which will not be showed in Settings page"""
  554. return ['input', 'output', 'layer']
  555. def _getBlackListedFlags(self):
  556. """Get flags which will not be showed in Settings page"""
  557. return ['overwrite', 'o', 'l', 'f']
  558. class GdalOutputDialog(wx.Dialog):
  559. def __init__(self, parent, id=wx.ID_ANY, ogr=False,
  560. style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER, *kwargs):
  561. """Dialog for setting output format for rasters/vectors
  562. .. todo::
  563. Split into GdalOutputDialog and OgrOutputDialog
  564. :param parent: parent window
  565. :param id: window id
  566. :param ogr: True for OGR (vector) otherwise GDAL (raster)
  567. :param style: window style
  568. :param *kwargs: other wx.Dialog's arguments
  569. """
  570. self.parent = parent # GMFrame
  571. self.ogr = ogr
  572. wx.Dialog.__init__(self, parent, id=id, style=style, *kwargs)
  573. if self.ogr:
  574. self.SetTitle(_("Define output format for vector data"))
  575. else:
  576. self.SetTitle(_("Define output format for raster data"))
  577. self.panel = wx.Panel(parent=self, id=wx.ID_ANY)
  578. # buttons
  579. self.btnCancel = Button(parent=self.panel, id=wx.ID_CANCEL)
  580. self.btnCancel.SetToolTip(_("Close dialog"))
  581. self.btnOk = Button(parent=self.panel, id=wx.ID_OK)
  582. self.btnOk.SetToolTip(_("Set external format and close dialog"))
  583. self.btnOk.SetDefault()
  584. self.dsnInput = GdalSelect(parent=self, panel=self.panel,
  585. ogr=ogr,
  586. exclude=['file', 'protocol'], dest=True)
  587. self.dsnInput.AttachSettings()
  588. self.Bind(wx.EVT_BUTTON, self.OnCancel, self.btnCancel)
  589. self.Bind(wx.EVT_BUTTON, self.OnOK, self.btnOk)
  590. self._layout()
  591. def _layout(self):
  592. dialogSizer = wx.BoxSizer(wx.VERTICAL)
  593. dialogSizer.Add(self.dsnInput, proportion=1,
  594. flag=wx.EXPAND)
  595. btnSizer = wx.BoxSizer(orient=wx.HORIZONTAL)
  596. btnSizer.Add(self.btnCancel, proportion=0,
  597. flag=wx.LEFT | wx.RIGHT | wx.ALIGN_CENTER,
  598. border=10)
  599. btnSizer.Add(self.btnOk, proportion=0,
  600. flag=wx.RIGHT | wx.ALIGN_CENTER,
  601. border=10)
  602. dialogSizer.Add(
  603. btnSizer,
  604. proportion=0,
  605. flag=wx.ALIGN_CENTER_VERTICAL | wx.BOTTOM | wx.TOP | wx.ALIGN_RIGHT,
  606. border=10)
  607. self.panel.SetAutoLayout(True)
  608. self.panel.SetSizer(dialogSizer)
  609. dialogSizer.Fit(self.panel)
  610. size = wx.Size(
  611. globalvar.DIALOG_GSELECT_SIZE[0] + 320,
  612. self.GetBestSize()[1] + 35)
  613. self.SetMinSize(size)
  614. self.SetSize((size.width, size.height))
  615. self.Layout()
  616. def OnCancel(self, event):
  617. self.Destroy()
  618. def OnOK(self, event):
  619. if self.dsnInput.GetType() == 'native':
  620. RunCommand('v.external.out',
  621. parent=self,
  622. flags='r')
  623. else:
  624. dsn = self.dsnInput.GetDsn()
  625. frmt = self.dsnInput.GetFormat()
  626. options = self.dsnInput.GetOptions()
  627. if not dsn:
  628. GMessage(_("No data source selected."), parent=self)
  629. return
  630. RunCommand('v.external.out',
  631. parent=self,
  632. output=dsn, format=frmt,
  633. options=options)
  634. self.Close()
  635. class DxfImportDialog(ImportDialog):
  636. """Dialog for bulk import of DXF layers"""
  637. def __init__(self, parent, giface):
  638. ImportDialog.__init__(self, parent, giface=giface, itype='dxf',
  639. title=_("Import DXF layers"))
  640. self.list.SetValidator(
  641. LayersListValidator(
  642. condition='vector',
  643. callback=self._nameValidationFailed))
  644. self._giface = giface
  645. self.dsnInput = filebrowse.FileBrowseButton(
  646. parent=self.panel,
  647. id=wx.ID_ANY,
  648. size=globalvar.DIALOG_GSELECT_SIZE,
  649. labelText='',
  650. dialogTitle=_('Choose DXF file to import'),
  651. buttonText=_('Browse'),
  652. startDirectory=os.getcwd(),
  653. fileMode=0,
  654. changeCallback=self.OnSetDsn,
  655. fileMask="DXF File (*.dxf)|*.dxf")
  656. self.add.SetLabel(_("Add imported layers into layer tree"))
  657. self.add.SetValue(
  658. UserSettings.Get(
  659. group='cmd',
  660. key='addNewLayer',
  661. subkey='enabled'))
  662. self.doLayout()
  663. def OnRun(self, event):
  664. """Import/Link data (each layes as separate vector map)"""
  665. data = self.list.GetLayers()
  666. if not data:
  667. GMessage(_("No layers selected."), parent=self)
  668. return
  669. if not self._validateOutputMapName():
  670. return
  671. # hide dialog
  672. self.Hide()
  673. inputDxf = self.dsnInput.GetValue()
  674. for layer, output, itemId in data:
  675. cmd = self.getSettingsPageCmd()
  676. cmd.append('input=%s' % inputDxf)
  677. cmd.append('layer=%s' % layer)
  678. cmd.append('output=%s' % output)
  679. for key in self.options.keys():
  680. if self.options[key].IsChecked():
  681. cmd.append('-%s' % key)
  682. if self.overwrite.IsChecked() or UserSettings.Get(
  683. group='cmd', key='overwrite', subkey='enabled'):
  684. cmd.append('--overwrite')
  685. # run in Layer Manager
  686. self._giface.RunCmd(cmd, onDone=self.OnCmdDone, addLayer=False)
  687. def OnCmdDone(self, event):
  688. """Load layers and close if required"""
  689. if not hasattr(self, 'AddLayers'):
  690. return
  691. self.AddLayers(event.returncode, event.cmd)
  692. if self.closeOnFinish.IsChecked():
  693. self.Close()
  694. def OnSetDsn(self, event):
  695. """Input DXF file defined, update list of layer widget"""
  696. path = event.GetString()
  697. if not path:
  698. return
  699. data = list()
  700. ret = RunCommand('v.in.dxf',
  701. quiet=True,
  702. parent=self,
  703. read=True,
  704. flags='l',
  705. input=path)
  706. if not ret:
  707. self.list.LoadData()
  708. return
  709. for line in ret.splitlines():
  710. layerId = line.split(':')[0].split(' ')[1]
  711. layerName = line.split(':')[1].strip()
  712. grassName = GetValidLayerName(layerName)
  713. data.append((layerId, layerName.strip(), grassName.strip()))
  714. self.list.LoadData(data)
  715. def _getCommand(self):
  716. """Get command"""
  717. return 'v.in.dxf'
  718. def _getBlackListedParameters(self):
  719. """Get parametrs which will not be showed in Settings page"""
  720. return ['input', 'output', 'layers']
  721. def _getBlackListedFlags(self):
  722. """Get flags which will not be showed in Settings page"""
  723. return ['overwrite']
  724. class ReprojectionDialog(wx.Dialog):
  725. """ """
  726. def __init__(self, parent, giface, data,
  727. id=wx.ID_ANY, title=_("Reprojection"),
  728. style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER):
  729. self.parent = parent # GMFrame
  730. self._giface = giface # used to add layers
  731. wx.Dialog.__init__(self, parent, id, title, style=style,
  732. name="MultiImportDialog")
  733. self.panel = wx.Panel(parent=self, id=wx.ID_ANY)
  734. # list of layers
  735. columns = [_('Layer id'),
  736. _('Name for output GRASS map')]
  737. self.layerBox = StaticBox(parent=self.panel, id=wx.ID_ANY)
  738. self.layerSizer = wx.StaticBoxSizer(self.layerBox, wx.HORIZONTAL)
  739. self.list = GListCtrl(parent=self.panel)
  740. for i in range(len(columns)):
  741. self.list.InsertColumn(i, columns[i])
  742. width = (65, 180)
  743. for i in range(len(width)):
  744. self.list.SetColumnWidth(col=i, width=width[i])
  745. self.list.LoadData(data)
  746. self.labelText = StaticText(parent=self.panel, id=wx.ID_ANY, label=_(
  747. "Projection of following layers do not match with projection of current location. "))
  748. label = _("Layers to be reprojected")
  749. self.layerBox.SetLabel(" %s - %s " %
  750. (label, _("right click to (un)select all")))
  751. #
  752. # buttons
  753. #
  754. # cancel
  755. self.btn_close = Button(parent=self.panel, id=wx.ID_CANCEL)
  756. # run
  757. self.btn_run = Button(
  758. parent=self.panel,
  759. id=wx.ID_OK,
  760. label=_("&Import && reproject"))
  761. self.btn_run.SetToolTip(_("Reproject selected layers"))
  762. self.btn_run.SetDefault()
  763. self.doLayout()
  764. def doLayout(self):
  765. """Do layout"""
  766. dialogSizer = wx.BoxSizer(wx.VERTICAL)
  767. dialogSizer.Add(self.labelText,
  768. flag=wx.ALL | wx.EXPAND, border=5)
  769. self.layerSizer.Add(self.list, proportion=1,
  770. flag=wx.ALL | wx.EXPAND, border=5)
  771. dialogSizer.Add(
  772. self.layerSizer,
  773. proportion=1,
  774. flag=wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND,
  775. border=5)
  776. #
  777. # buttons
  778. #
  779. btnsizer = wx.BoxSizer(orient=wx.HORIZONTAL)
  780. btnsizer.Add(self.btn_close, proportion=0,
  781. flag=wx.LEFT | wx.RIGHT | wx.ALIGN_CENTER,
  782. border=10)
  783. btnsizer.Add(self.btn_run, proportion=0,
  784. flag=wx.RIGHT | wx.ALIGN_CENTER,
  785. border=10)
  786. dialogSizer.Add(
  787. btnsizer,
  788. proportion=0,
  789. flag=wx.BOTTOM | wx.ALIGN_RIGHT,
  790. border=10)
  791. self.panel.SetSizer(dialogSizer)
  792. dialogSizer.Fit(self.panel)
  793. self.Layout()
  794. def GetData(self, checked):
  795. return self.list.GetData(checked)