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