gselect.py 60 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612
  1. """!
  2. @package gselect
  3. @brief Custom control that selects elements
  4. Classes:
  5. - Select
  6. - VectorSelect
  7. - TreeCrtlComboPopup
  8. - VectorDBInfo
  9. - LayerSelect
  10. - LayerNameSelect
  11. - DriverSelect
  12. - DatabaseSelect
  13. - ColumnSelect
  14. - DbaseSelect
  15. - LocationSelect
  16. - MapsetSelect
  17. - SubGroupSelect
  18. - FormatSelect
  19. - GdalSelect
  20. - ProjSelect
  21. (C) 2007-2011 by the GRASS Development Team This program is free
  22. software under the GNU General Public License (>=v2). Read the file
  23. COPYING that comes with GRASS for details.
  24. @author Michael Barton
  25. @author Martin Landa <landa.martin gmail.com>
  26. """
  27. import os
  28. import sys
  29. import glob
  30. import wx
  31. import wx.combo
  32. import wx.lib.filebrowsebutton as filebrowse
  33. from wx.lib.newevent import NewEvent
  34. import globalvar
  35. sys.path.append(os.path.join(globalvar.ETCDIR, "python"))
  36. import grass.script as grass
  37. import gcmd
  38. import utils
  39. from preferences import globalSettings as UserSettings
  40. from debug import Debug
  41. wxGdalSelect, EVT_GDALSELECT = NewEvent()
  42. class Select(wx.combo.ComboCtrl):
  43. def __init__(self, parent, id = wx.ID_ANY, size = globalvar.DIALOG_GSELECT_SIZE,
  44. type = None, multiple = False, mapsets = None,
  45. updateOnPopup = True, onPopup = None):
  46. """!Custom control to create a ComboBox with a tree control to
  47. display and select GIS elements within acessible mapsets.
  48. Elements can be selected with mouse. Can allow multiple
  49. selections, when argument multiple=True. Multiple selections
  50. are separated by commas.
  51. @param type type of GIS elements ('raster, 'vector', ...)
  52. @param multiple multiple input allowed?
  53. @param mapsets force list of mapsets (otherwise search path)
  54. @param updateOnPopup True for updating list of elements on popup
  55. @param onPopup function to be called on Popup
  56. """
  57. wx.combo.ComboCtrl.__init__(self, parent=parent, id=id, size=size)
  58. self.GetChildren()[0].SetName("Select")
  59. self.GetChildren()[0].type = type
  60. self.tcp = TreeCtrlComboPopup()
  61. self.SetPopupControl(self.tcp)
  62. self.SetPopupExtents(0, 100)
  63. if type:
  64. self.tcp.SetData(type = type, mapsets = mapsets,
  65. multiple = multiple,
  66. updateOnPopup = updateOnPopup, onPopup = onPopup)
  67. self.GetChildren()[0].Bind(wx.EVT_KEY_UP, self.OnKeyUp)
  68. def OnKeyUp(self, event):
  69. """!Shows popupwindow if down arrow key is released"""
  70. if event.GetKeyCode() == wx.WXK_DOWN:
  71. self.ShowPopup()
  72. else:
  73. event.Skip()
  74. def SetElementList(self, type, mapsets = None):
  75. """!Set element list
  76. @param type GIS element type
  77. @param mapsets list of acceptable mapsets (None for all in search path)
  78. """
  79. self.tcp.SetData(type = type, mapsets = mapsets)
  80. def GetElementList(self):
  81. """!Load elements"""
  82. self.tcp.GetElementList()
  83. class VectorSelect(Select):
  84. def __init__(self, parent, ftype, **kwargs):
  85. """!Custom to create a ComboBox with a tree control to display and
  86. select vector maps. Control allows to filter vector maps. If you
  87. don't need this feature use Select class instead
  88. @ftype filter vector maps based on feature type
  89. """
  90. Select.__init__(self, parent = parent, id = wx.ID_ANY,
  91. type = 'vector', **kwargs)
  92. self.ftype = ftype
  93. # remove vector maps which do not contain given feature type
  94. self.tcp.SetFilter(self._isElement)
  95. def _isElement(self, vectorName):
  96. """!Check if element should be filtered out"""
  97. try:
  98. if int(grass.vector_info_topo(vectorName)[self.ftype]) < 1:
  99. return False
  100. except KeyError:
  101. return False
  102. return True
  103. class TreeCtrlComboPopup(wx.combo.ComboPopup):
  104. """!Create a tree ComboBox for selecting maps and other GIS elements
  105. in accessible mapsets within the current location
  106. """
  107. # overridden ComboPopup methods
  108. def Init(self):
  109. self.value = [] # for multiple is False -> len(self.value) in [0,1]
  110. self.curitem = None
  111. self.multiple = False
  112. self.type = None
  113. self.mapsets = None
  114. self.updateOnPopup = True
  115. self.onPopup = None
  116. self.SetFilter(None)
  117. def Create(self, parent):
  118. self.seltree = wx.TreeCtrl(parent, style=wx.TR_HIDE_ROOT
  119. |wx.TR_HAS_BUTTONS
  120. |wx.TR_SINGLE
  121. |wx.TR_LINES_AT_ROOT
  122. |wx.SIMPLE_BORDER
  123. |wx.TR_FULL_ROW_HIGHLIGHT)
  124. self.seltree.Bind(wx.EVT_KEY_UP, self.OnKeyUp)
  125. self.seltree.Bind(wx.EVT_MOTION, self.OnMotion)
  126. self.seltree.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown)
  127. self.seltree.Bind(wx.EVT_TREE_ITEM_EXPANDING, self.mapsetExpanded)
  128. self.seltree.Bind(wx.EVT_TREE_ITEM_COLLAPSED, self.mapsetCollapsed)
  129. self.seltree.Bind(wx.EVT_TREE_ITEM_ACTIVATED, self.mapsetActivated)
  130. self.seltree.Bind(wx.EVT_TREE_SEL_CHANGED, self.mapsetSelected)
  131. self.seltree.Bind(wx.EVT_TREE_DELETE_ITEM, lambda x: None)
  132. # the following dummy handler are needed to keep tree events from propagating up to
  133. # the parent GIS Manager layer tree
  134. def mapsetExpanded(self, event):
  135. pass
  136. def mapsetCollapsed(self, event):
  137. pass
  138. def mapsetActivated(self, event):
  139. pass
  140. def mapsetSelected(self, event):
  141. pass
  142. # end of dummy events
  143. def GetControl(self):
  144. return self.seltree
  145. def GetStringValue(self):
  146. str = ""
  147. for value in self.value:
  148. str += value + ","
  149. str = str.rstrip(',')
  150. return str
  151. def SetFilter(self, filter):
  152. """!Set filter for GIS elements, see e.g. VectorSelect"""
  153. self.filterElements = filter
  154. def OnPopup(self, force = False):
  155. """!Limited only for first selected"""
  156. if not force and not self.updateOnPopup:
  157. return
  158. if self.onPopup:
  159. selected, exclude = self.onPopup(self.type)
  160. else:
  161. selected = None
  162. exclude = False
  163. self.GetElementList(selected, exclude)
  164. # selects map starting according to written text
  165. inputText = self.GetCombo().GetValue().strip()
  166. if inputText:
  167. root = self.seltree.GetRootItem()
  168. match = self.FindItem(root, inputText, startLetters = True)
  169. self.seltree.EnsureVisible(match)
  170. self.seltree.SelectItem(match)
  171. def GetElementList(self, elements = None, exclude = False):
  172. """!Get filtered list of GIS elements in accessible mapsets
  173. and display as tree with all relevant elements displayed
  174. beneath each mapset branch
  175. """
  176. # update list
  177. self.seltree.DeleteAllItems()
  178. self._getElementList(self.type, self.mapsets, elements, exclude)
  179. if len(self.value) > 0:
  180. root = self.seltree.GetRootItem()
  181. if not root:
  182. return
  183. item = self.FindItem(root, self.value[0])
  184. try:
  185. self.seltree.EnsureVisible(item)
  186. self.seltree.SelectItem(item)
  187. except:
  188. pass
  189. def SetStringValue(self, value):
  190. # this assumes that item strings are unique...
  191. root = self.seltree.GetRootItem()
  192. if not root:
  193. return
  194. found = self.FindItem(root, value)
  195. if found:
  196. self.value.append(found)
  197. self.seltree.SelectItem(found)
  198. def GetAdjustedSize(self, minWidth, prefHeight, maxHeight):
  199. return wx.Size(minWidth, min(200, maxHeight))
  200. def _getElementList(self, element, mapsets = None, elements = None, exclude = False):
  201. """!Get list of GIS elements in accessible mapsets and display as tree
  202. with all relevant elements displayed beneath each mapset branch
  203. @param element GIS element
  204. @param mapsets list of acceptable mapsets (None for all mapsets in search path)
  205. @param elements list of forced GIS elements
  206. @param exclude True to exclude, False for forcing the list (elements)
  207. """
  208. # get current mapset
  209. curr_mapset = grass.gisenv()['MAPSET']
  210. # list of mapsets in current location
  211. if mapsets is None:
  212. mapsets = utils.ListOfMapsets(get = 'accessible')
  213. # map element types to g.mlist types
  214. elementdict = {'cell':'rast',
  215. 'raster':'rast',
  216. 'rast':'rast',
  217. 'raster files':'rast',
  218. 'grid3':'rast3d',
  219. 'rast3d':'rast3d',
  220. 'raster3D':'rast3d',
  221. 'raster3D files':'rast3d',
  222. 'vector':'vect',
  223. 'vect':'vect',
  224. 'binary vector files':'vect',
  225. 'dig':'oldvect',
  226. 'oldvect':'oldvect',
  227. 'old vector':'oldvect',
  228. 'dig_ascii':'asciivect',
  229. 'asciivect':'asciivect',
  230. 'asciivector':'asciivect',
  231. 'ascii vector files':'asciivect',
  232. 'icons':'icon',
  233. 'icon':'icon',
  234. 'paint icon files':'icon',
  235. 'paint/labels':'labels',
  236. 'labels':'labels',
  237. 'label':'labels',
  238. 'paint label files':'labels',
  239. 'site_lists':'sites',
  240. 'sites':'sites',
  241. 'site list':'sites',
  242. 'site list files':'sites',
  243. 'windows':'region',
  244. 'region':'region',
  245. 'region definition':'region',
  246. 'region definition files':'region',
  247. 'windows3d':'region3d',
  248. 'region3d':'region3d',
  249. 'region3D definition':'region3d',
  250. 'region3D definition files':'region3d',
  251. 'group':'group',
  252. 'imagery group':'group',
  253. 'imagery group files':'group',
  254. '3d.view':'3dview',
  255. '3dview':'3dview',
  256. '3D viewing parameters':'3dview',
  257. '3D view parameters':'3dview'}
  258. if element not in elementdict:
  259. self.AddItem(_('Not selectable element'))
  260. return
  261. # get directory tree nodes
  262. # reorder mapsets based on search path (TODO)
  263. for i in range(len(mapsets)):
  264. if i > 0 and mapsets[i] == curr_mapset:
  265. mapsets[i] = mapsets[0]
  266. mapsets[0] = curr_mapset
  267. if globalvar.have_mlist:
  268. filesdict = grass.mlist_grouped(elementdict[element])
  269. else:
  270. filesdict = grass.list_grouped(elementdict[element])
  271. first_mapset = None
  272. for mapset in mapsets:
  273. mapset_node = self.AddItem('Mapset: ' + mapset)
  274. if not first_mapset:
  275. first_mapset = mapset_node
  276. self.seltree.SetItemTextColour(mapset_node, wx.Colour(50, 50, 200))
  277. if mapset not in filesdict:
  278. continue
  279. try:
  280. elem_list = filesdict[mapset]
  281. elem_list.sort()
  282. for elem in elem_list:
  283. if elem != '':
  284. fullqElem = elem + '@' + mapset
  285. if elements:
  286. if (exclude and fullqElem in elements) or \
  287. (not exclude and fullqElem not in elements):
  288. continue
  289. if self.filterElements:
  290. if self.filterElements(fullqElem):
  291. self.AddItem(elem, parent=mapset_node)
  292. else:
  293. self.AddItem(elem, parent=mapset_node)
  294. except StandardError, e:
  295. sys.stderr.write(_("GSelect: invalid item: %s") % e)
  296. continue
  297. if self.seltree.ItemHasChildren(mapset_node):
  298. sel = UserSettings.Get(group='general', key='elementListExpand',
  299. subkey='selection')
  300. collapse = True
  301. if sel == 0: # collapse all except PERMANENT and current
  302. if mapset in ('PERMANENT', curr_mapset):
  303. collapse = False
  304. elif sel == 1: # collapse all except PERMANENT
  305. if mapset == 'PERMANENT':
  306. collapse = False
  307. elif sel == 2: # collapse all except current
  308. if mapset == curr_mapset:
  309. collapse = False
  310. elif sel == 3: # collapse all
  311. pass
  312. elif sel == 4: # expand all
  313. collapse = False
  314. if collapse:
  315. self.seltree.Collapse(mapset_node)
  316. else:
  317. self.seltree.Expand(mapset_node)
  318. if first_mapset:
  319. # select first mapset (MSW hack)
  320. self.seltree.SelectItem(first_mapset)
  321. # helpers
  322. def FindItem(self, parentItem, text, startLetters = False):
  323. """!Finds item with given name or starting with given text"""
  324. startletters = startLetters
  325. item, cookie = self.seltree.GetFirstChild(parentItem)
  326. while wx.TreeItemId.IsOk(item):
  327. if self.seltree.GetItemText(item) == text:
  328. return item
  329. if self.seltree.ItemHasChildren(item):
  330. item = self.FindItem(item, text, startLetters = startletters)
  331. if wx.TreeItemId.IsOk(item):
  332. return item
  333. elif startletters and self.seltree.GetItemText(item).startswith(text.split('@')[0]):
  334. return item
  335. item, cookie = self.seltree.GetNextChild(parentItem, cookie)
  336. return wx.TreeItemId()
  337. def AddItem(self, value, parent=None):
  338. if not parent:
  339. root = self.seltree.GetRootItem()
  340. if not root:
  341. root = self.seltree.AddRoot("<hidden root>")
  342. parent = root
  343. item = self.seltree.AppendItem(parent, text=value)
  344. return item
  345. # able to recieve only wx.EVT_KEY_UP
  346. def OnKeyUp(self, event):
  347. """!Enables to select items using keyboard"""
  348. item = self.seltree.GetSelection()
  349. if event.GetKeyCode() == wx.WXK_DOWN:
  350. self.seltree.SelectItem(self.seltree.GetNextVisible(item))
  351. # problem with GetPrevVisible
  352. elif event.GetKeyCode() == wx.WXK_UP:
  353. if self.seltree.ItemHasChildren(item) and self.seltree.IsExpanded(self.seltree.GetPrevSibling(item)):
  354. itemPrev = self.seltree.GetLastChild(self.seltree.GetPrevSibling(item))
  355. else:
  356. itemPrev = self.seltree.GetPrevSibling(item)
  357. if not wx.TreeItemId.IsOk(itemPrev):
  358. itemPrev = self.seltree.GetItemParent(item)
  359. if item == self.seltree.GetFirstChild(self.seltree.GetRootItem())[0]:
  360. itemPrev = item
  361. self.seltree.SelectItem(itemPrev)
  362. # selects first item starting with the written text in next mapset
  363. elif event.GetKeyCode() == wx.WXK_TAB:
  364. selected = self.seltree.GetSelection()
  365. if self.seltree.ItemHasChildren(selected):
  366. parent = selected
  367. else:
  368. parent = self.seltree.GetItemParent(selected)
  369. nextSibling = self.seltree.GetNextSibling(parent)
  370. if wx.TreeItemId.IsOk(nextSibling):
  371. match = self.FindItem(nextSibling, self.GetCombo().GetValue().strip(), True)
  372. else:
  373. match = self.FindItem(self.seltree.GetFirstChild(self.seltree.GetItemParent(parent))[0],
  374. self.GetCombo().GetValue().strip(), True)
  375. self.seltree.SelectItem(match)
  376. elif event.GetKeyCode() == wx.WXK_RIGHT:
  377. if self.seltree.ItemHasChildren(item):
  378. self.seltree.Expand(item)
  379. elif event.GetKeyCode() == wx.WXK_LEFT:
  380. if self.seltree.ItemHasChildren(item):
  381. self.seltree.Collapse(item)
  382. elif event.GetKeyCode() == wx.WXK_ESCAPE:
  383. self.Dismiss()
  384. elif event.GetKeyCode() == wx.WXK_RETURN:
  385. if self.seltree.GetRootItem() == self.seltree.GetItemParent(item):
  386. self.value = []
  387. else:
  388. mapsetItem = self.seltree.GetItemParent(item)
  389. fullName = self.seltree.GetItemText(item) + '@' \
  390. + self.seltree.GetItemText(mapsetItem).split(' ', 1)[1]
  391. if self.multiple is True:
  392. # text item should be unique
  393. self.value.append(fullName)
  394. else:
  395. self.value = [fullName, ]
  396. self.Dismiss()
  397. def OnMotion(self, evt):
  398. """!Have the selection follow the mouse, like in a real combobox
  399. """
  400. item, flags = self.seltree.HitTest(evt.GetPosition())
  401. if item and flags & wx.TREE_HITTEST_ONITEMLABEL:
  402. self.seltree.SelectItem(item)
  403. self.curitem = item
  404. evt.Skip()
  405. def OnLeftDown(self, evt):
  406. """!Do the combobox selection
  407. """
  408. item, flags = self.seltree.HitTest(evt.GetPosition())
  409. if item and flags & wx.TREE_HITTEST_ONITEMLABEL:
  410. self.curitem = item
  411. if self.seltree.GetRootItem() == self.seltree.GetItemParent(item):
  412. self.value = [] # cannot select mapset item
  413. else:
  414. mapsetItem = self.seltree.GetItemParent(item)
  415. fullName = self.seltree.GetItemText(item) + '@' + \
  416. self.seltree.GetItemText(mapsetItem).split(' ', 1)[1]
  417. if self.multiple is True:
  418. # text item should be unique
  419. self.value.append(fullName)
  420. else:
  421. self.value = [fullName, ]
  422. self.Dismiss()
  423. evt.Skip()
  424. def SetData(self, **kargs):
  425. """!Set object properties"""
  426. if 'type' in kargs:
  427. self.type = kargs['type']
  428. if 'mapsets' in kargs:
  429. self.mapsets = kargs['mapsets']
  430. if 'multiple' in kargs:
  431. self.multiple = kargs['multiple']
  432. if 'updateOnPopup' in kargs:
  433. self.updateOnPopup = kargs['updateOnPopup']
  434. if 'onPopup' in kargs:
  435. self.onPopup = kargs['onPopup']
  436. class VectorDBInfo:
  437. """!Class providing information about attribute tables
  438. linked to a vector map"""
  439. def __init__(self, map):
  440. self.map = map
  441. # dictionary of layer number and associated (driver, database, table)
  442. self.layers = {}
  443. # dictionary of table and associated columns (type, length, values, ids)
  444. self.tables = {}
  445. if not self._CheckDBConnection(): # -> self.layers
  446. return
  447. self._DescribeTables() # -> self.tables
  448. def _CheckDBConnection(self):
  449. """!Check DB connection"""
  450. nuldev = file(os.devnull, 'w+')
  451. self.layers = grass.vector_db(map=self.map, stderr=nuldev)
  452. nuldev.close()
  453. if (len(self.layers.keys()) == 0):
  454. return False
  455. return True
  456. def _DescribeTables(self):
  457. """!Describe linked tables"""
  458. for layer in self.layers.keys():
  459. # determine column names and types
  460. table = self.layers[layer]["table"]
  461. columns = {} # {name: {type, length, [values], [ids]}}
  462. i = 0
  463. Debug.msg(1, "gselect.VectorDBInfo._DescribeTables(): table=%s driver=%s database=%s" % \
  464. (self.layers[layer]["table"], self.layers[layer]["driver"],
  465. self.layers[layer]["database"]))
  466. for item in grass.db_describe(table = self.layers[layer]["table"],
  467. driver = self.layers[layer]["driver"],
  468. database = self.layers[layer]["database"])['cols']:
  469. name, type, length = item
  470. # FIXME: support more datatypes
  471. if type.lower() == "integer":
  472. ctype = int
  473. elif type.lower() == "double precision":
  474. ctype = float
  475. else:
  476. ctype = str
  477. columns[name.strip()] = { 'index' : i,
  478. 'type' : type.lower(),
  479. 'ctype' : ctype,
  480. 'length' : int(length),
  481. 'values' : [],
  482. 'ids' : []}
  483. i += 1
  484. # check for key column
  485. # v.db.connect -g/p returns always key column name lowercase
  486. if self.layers[layer]["key"] not in columns.keys():
  487. for col in columns.keys():
  488. if col.lower() == self.layers[layer]["key"]:
  489. self.layers[layer]["key"] = col.upper()
  490. break
  491. self.tables[table] = columns
  492. return True
  493. def Reset(self):
  494. """!Reset"""
  495. for layer in self.layers:
  496. table = self.layers[layer]["table"] # get table desc
  497. columns = self.tables[table]
  498. for name in self.tables[table].keys():
  499. self.tables[table][name]['values'] = []
  500. self.tables[table][name]['ids'] = []
  501. def GetName(self):
  502. """!Get vector name"""
  503. return self.map
  504. def GetKeyColumn(self, layer):
  505. """!Get key column of given layer
  506. @param layer vector layer number
  507. """
  508. return self.layers[layer]['key']
  509. def GetTable(self, layer):
  510. """!Get table name of given layer
  511. @param layer vector layer number
  512. """
  513. return self.layers[layer]['table']
  514. def GetDbSettings(self, layer):
  515. """!Get database settins
  516. @param layer layer number
  517. @return (driver, database)
  518. """
  519. return self.layers[layer]['driver'], self.layers[layer]['database']
  520. def GetTableDesc(self, table):
  521. """!Get table columns
  522. @param table table name
  523. """
  524. return self.tables[table]
  525. class LayerSelect(wx.Choice):
  526. def __init__(self, parent, id = wx.ID_ANY,
  527. size=globalvar.DIALOG_LAYER_SIZE,
  528. vector = None, choices = [], all = False, default = None):
  529. """!Creates widget for selecting vector map layer numbers
  530. @param vector vector map name or None
  531. @param choices list of predefined choices
  532. @param all adds layer '-1' (e.g., for d.vect)
  533. @param default default layer number
  534. """
  535. super(LayerSelect, self).__init__(parent, id, size = size,
  536. choices = choices)
  537. self.all = all
  538. self.SetName("LayerSelect")
  539. # default value
  540. self.default = default
  541. if len(choices) > 1:
  542. return
  543. if vector:
  544. self.InsertLayers(vector)
  545. else:
  546. if all:
  547. self.SetItems(['-1', '1'])
  548. else:
  549. self.SetItems(['1'])
  550. self.SetStringSelection('1')
  551. def InsertLayers(self, vector):
  552. """!Insert layers for a vector into the layer combobox"""
  553. layerchoices = utils.GetVectorNumberOfLayers(self, vector)
  554. if self.all or len(layerchoices) == 0:
  555. layerchoices.insert(0, '-1')
  556. if len(layerchoices) > 1:
  557. self.SetItems(layerchoices)
  558. self.SetStringSelection('1')
  559. elif len(layerchoices) == 1:
  560. print layerchoices[0]
  561. self.SetItems(layerchoices)
  562. self.SetStringSelection(layerchoices[0])
  563. if self.default:
  564. self.SetStringSelection(str(self.default))
  565. class LayerNameSelect(wx.ComboBox):
  566. def __init__(self, parent, id = wx.ID_ANY,
  567. size = globalvar.DIALOG_COMBOBOX_SIZE,
  568. vector = None, dsn = None):
  569. """!Creates combo box for selecting vector map layer names
  570. @param vector vector map name (native or connected via v.external)
  571. @param dsn OGR data source name
  572. """
  573. super(LayerNameSelect, self).__init__(parent, id, size = size)
  574. self.SetName("LayerNameSelect")
  575. if vector:
  576. # -> native
  577. self.InsertLayers(vector = vector)
  578. elif dsn:
  579. self.InsertLayers(dsn = dsn)
  580. def InsertLayers(self, vector = None, dsn = None):
  581. """!Insert layers for a vector into the layer combobox
  582. @todo Implement native format
  583. @param vector vector map name (native or connected via v.external)
  584. @param dsn OGR data source name
  585. """
  586. layers = list()
  587. if vector:
  588. # TODO
  589. pass
  590. elif dsn:
  591. ret = gcmd.RunCommand('v.in.ogr',
  592. read = True,
  593. quiet = True,
  594. flags = 'l',
  595. dsn = dsn)
  596. if ret:
  597. layers = ret.splitlines()
  598. self.SetItems(layers)
  599. self.SetSelection(0)
  600. class DriverSelect(wx.ComboBox):
  601. """!Creates combo box for selecting database driver.
  602. """
  603. def __init__(self, parent, choices, value,
  604. id=wx.ID_ANY, pos=wx.DefaultPosition,
  605. size=globalvar.DIALOG_LAYER_SIZE, **kargs):
  606. super(DriverSelect, self).__init__(parent, id, value, pos, size,
  607. choices, style=wx.CB_READONLY)
  608. self.SetName("DriverSelect")
  609. self.SetStringSelection(value)
  610. class DatabaseSelect(wx.TextCtrl):
  611. """!Creates combo box for selecting database driver.
  612. """
  613. def __init__(self, parent, value='',
  614. id=wx.ID_ANY, pos=wx.DefaultPosition,
  615. size=globalvar.DIALOG_TEXTCTRL_SIZE, **kargs):
  616. super(DatabaseSelect, self).__init__(parent, id, value, pos, size)
  617. self.SetName("DatabaseSelect")
  618. class TableSelect(wx.ComboBox):
  619. """!Creates combo box for selecting attribute tables from the database
  620. """
  621. def __init__(self, parent,
  622. id=wx.ID_ANY, value='', pos=wx.DefaultPosition,
  623. size=globalvar.DIALOG_COMBOBOX_SIZE,
  624. choices=[]):
  625. super(TableSelect, self).__init__(parent, id, value, pos, size, choices,
  626. style=wx.CB_READONLY)
  627. self.SetName("TableSelect")
  628. if not choices:
  629. self.InsertTables()
  630. def InsertTables(self, driver=None, database=None):
  631. """!Insert attribute tables into combobox"""
  632. items = []
  633. if not driver or not database:
  634. connect = grass.db_connection()
  635. driver = connect['driver']
  636. database = connect['database']
  637. ret = gcmd.RunCommand('db.tables',
  638. flags = 'p',
  639. read = True,
  640. driver = driver,
  641. database = database)
  642. if ret:
  643. for table in ret.splitlines():
  644. items.append(table)
  645. self.SetItems(items)
  646. self.SetValue('')
  647. class ColumnSelect(wx.ComboBox):
  648. """!Creates combo box for selecting columns in the attribute table
  649. for a vector map.
  650. @param parent window parent
  651. @param id window id
  652. @param value default value
  653. @param size window size
  654. @param vector vector map name
  655. @param layer layer number
  656. @param param parameters list (see menuform.py)
  657. @param **kwags wx.ComboBox parameters
  658. """
  659. def __init__(self, parent, id = wx.ID_ANY, value = '',
  660. size=globalvar.DIALOG_COMBOBOX_SIZE,
  661. vector = None, layer = 1, param = None, **kwargs):
  662. self.defaultValue = value
  663. self.param = param
  664. super(ColumnSelect, self).__init__(parent, id, value, size = size, **kwargs)
  665. self.SetName("ColumnSelect")
  666. if vector:
  667. self.InsertColumns(vector, layer)
  668. def InsertColumns(self, vector, layer, excludeKey = False, type = None, dbInfo = None):
  669. """!Insert columns for a vector attribute table into the columns combobox
  670. @param vector vector name
  671. @param layer vector layer number
  672. @param excludeKey exclude key column from the list?
  673. @param type only columns of given type (given as list)
  674. """
  675. if not dbInfo:
  676. dbInfo = VectorDBInfo(vector)
  677. try:
  678. table = dbInfo.GetTable(int(layer))
  679. columnchoices = dbInfo.GetTableDesc(table)
  680. keyColumn = dbInfo.GetKeyColumn(int(layer))
  681. columns = len(columnchoices.keys()) * ['']
  682. for key, val in columnchoices.iteritems():
  683. columns[val['index']] = key
  684. if excludeKey: # exclude key column
  685. columns.remove(keyColumn)
  686. if type: # only selected column types
  687. for key, value in columnchoices.iteritems():
  688. if value['type'] not in type:
  689. columns.remove(key)
  690. except (KeyError, ValueError):
  691. columns = list()
  692. self.SetItems(columns)
  693. self.SetValue(self.defaultValue)
  694. if self.param:
  695. self.param['value'] = ''
  696. def InsertTableColumns(self, table, driver=None, database=None):
  697. """!Insert table columns
  698. @param table table name
  699. @param driver driver name
  700. @param database database name
  701. """
  702. columns = list()
  703. ret = gcmd.RunCommand('db.columns',
  704. read = True,
  705. driver = driver,
  706. database = database,
  707. table = table)
  708. if ret:
  709. columns = ret.splitlines()
  710. self.SetItems(columns)
  711. self.SetValue(self.defaultValue)
  712. if self.param:
  713. self.param['value'] = ''
  714. class DbaseSelect(wx.lib.filebrowsebutton.DirBrowseButton):
  715. """!Widget for selecting GRASS Database"""
  716. def __init__(self, parent, **kwargs):
  717. super(DbaseSelect, self).__init__(parent, id = wx.ID_ANY,
  718. size = globalvar.DIALOG_GSELECT_SIZE, labelText = '',
  719. dialogTitle = _('Choose GIS Data Directory'),
  720. buttonText = _('Browse'),
  721. startDirectory = grass.gisenv()['GISDBASE'],
  722. **kwargs)
  723. class LocationSelect(wx.ComboBox):
  724. """!Widget for selecting GRASS location"""
  725. def __init__(self, parent, id = wx.ID_ANY, size = globalvar.DIALOG_COMBOBOX_SIZE,
  726. gisdbase = None, **kwargs):
  727. super(LocationSelect, self).__init__(parent, id, size = size,
  728. style = wx.CB_READONLY, **kwargs)
  729. self.SetName("LocationSelect")
  730. if not gisdbase:
  731. self.gisdbase = grass.gisenv()['GISDBASE']
  732. else:
  733. self.gisdbase = gisdbase
  734. self.SetItems(utils.GetListOfLocations(self.gisdbase))
  735. def UpdateItems(self, dbase):
  736. """!Update list of locations
  737. @param dbase path to GIS database
  738. """
  739. self.gisdbase = dbase
  740. if dbase:
  741. self.SetItems(utils.GetListOfLocations(self.gisdbase))
  742. else:
  743. self.SetItems([])
  744. class MapsetSelect(wx.ComboBox):
  745. """!Widget for selecting GRASS mapset"""
  746. def __init__(self, parent, id = wx.ID_ANY, size = globalvar.DIALOG_COMBOBOX_SIZE,
  747. gisdbase = None, location = None, setItems = True, **kwargs):
  748. super(MapsetSelect, self).__init__(parent, id, size = size,
  749. style = wx.CB_READONLY, **kwargs)
  750. self.SetName("MapsetSelect")
  751. if not gisdbase:
  752. self.gisdbase = grass.gisenv()['GISDBASE']
  753. else:
  754. self.gisdbase = gisdbase
  755. if not location:
  756. self.location = grass.gisenv()['LOCATION_NAME']
  757. else:
  758. self.location = location
  759. if setItems:
  760. self.SetItems(utils.GetListOfMapsets(self.gisdbase, self.location, selectable = False)) # selectable
  761. def UpdateItems(self, location, dbase = None):
  762. """!Update list of mapsets for given location
  763. @param dbase path to GIS database (None to use currently selected)
  764. @param location name of location
  765. """
  766. if dbase:
  767. self.gisdbase = dbase
  768. self.location = location
  769. if location:
  770. self.SetItems(utils.GetListOfMapsets(self.gisdbase, self.location, selectable = False))
  771. else:
  772. self.SetItems([])
  773. class SubGroupSelect(wx.ComboBox):
  774. """!Widget for selecting subgroups"""
  775. def __init__(self, parent, id = wx.ID_ANY, size = globalvar.DIALOG_GSELECT_SIZE,
  776. **kwargs):
  777. super(SubGroupSelect, self).__init__(parent, id, size = size,
  778. style = wx.CB_READONLY, **kwargs)
  779. self.SetName("SubGroupSelect")
  780. def Insert(self, group):
  781. """!Insert subgroups for defined group"""
  782. if not group:
  783. return
  784. gisenv = grass.gisenv()
  785. try:
  786. name, mapset = group.split('@', 1)
  787. except ValueError:
  788. name = group
  789. mapset = gisenv['MAPSET']
  790. path = os.path.join(gisenv['GISDBASE'], gisenv['LOCATION_NAME'], mapset,
  791. 'group', name, 'subgroup')
  792. try:
  793. self.SetItems(os.listdir(path))
  794. except OSError:
  795. self.SetItems([])
  796. self.SetValue('')
  797. class FormatSelect(wx.Choice):
  798. def __init__(self, parent, ogr = False,
  799. sourceType = None, id = wx.ID_ANY, size = globalvar.DIALOG_COMBOBOX_SIZE,
  800. **kwargs):
  801. """!Widget for selecting external (GDAL/OGR) format
  802. @param parent parent window
  803. @param sourceType source type ('file', 'directory', 'database', 'protocol') or None
  804. @param ogr True for OGR otherwise GDAL
  805. """
  806. super(FormatSelect, self).__init__(parent, id, size = size,
  807. style = wx.CB_READONLY, **kwargs)
  808. self.SetName("FormatSelect")
  809. if ogr:
  810. ftype = 'ogr'
  811. else:
  812. ftype = 'gdal'
  813. formats = list()
  814. for f in utils.GetFormats()[ftype].values():
  815. formats += f
  816. self.SetItems(formats)
  817. def GetExtension(self, name):
  818. """!Get file extension by format name"""
  819. formatToExt = {
  820. # raster
  821. 'GeoTIFF' : 'tif',
  822. 'Erdas Imagine Images (.img)' : 'img',
  823. 'Ground-based SAR Applications Testbed File Format (.gff)' : 'gff',
  824. 'Arc/Info Binary Grid' : 'adf',
  825. 'Portable Network Graphics' : 'png',
  826. 'JPEG JFIF' : 'jpg',
  827. 'Japanese DEM (.mem)' : 'mem',
  828. 'Graphics Interchange Format (.gif)' : 'gif',
  829. 'X11 PixMap Format' : 'xpm',
  830. 'MS Windows Device Independent Bitmap' : 'bmp',
  831. 'SPOT DIMAP' : 'dim',
  832. 'RadarSat 2 XML Product' : 'xml',
  833. 'EarthWatch .TIL' : 'til',
  834. 'ERMapper .ers Labelled' : 'ers',
  835. 'ERMapper Compressed Wavelets' : 'ecw',
  836. 'GRIdded Binary (.grb)' : 'grb',
  837. 'EUMETSAT Archive native (.nat)' : 'nat',
  838. 'Idrisi Raster A.1' : 'rst',
  839. 'Golden Software ASCII Grid (.grd)' : 'grd',
  840. 'Golden Software Binary Grid (.grd)' : 'grd',
  841. 'Golden Software 7 Binary Grid (.grd)' : 'grd',
  842. 'R Object Data Store' : 'r',
  843. 'USGS DOQ (Old Style)' : 'doq',
  844. 'USGS DOQ (New Style)' : 'doq',
  845. 'ENVI .hdr Labelled' : 'hdr',
  846. 'ESRI .hdr Labelled' : 'hdr',
  847. 'Generic Binary (.hdr Labelled)' : 'hdr',
  848. 'PCI .aux Labelled' : 'aux',
  849. 'EOSAT FAST Format' : 'fst',
  850. 'VTP .bt (Binary Terrain) 1.3 Format' : 'bt',
  851. 'FARSITE v.4 Landscape File (.lcp)' : 'lcp',
  852. 'Swedish Grid RIK (.rik)' : 'rik',
  853. 'USGS Optional ASCII DEM (and CDED)' : 'dem',
  854. 'Northwood Numeric Grid Format .grd/.tab' : '',
  855. 'Northwood Classified Grid Format .grc/.tab' : '',
  856. 'ARC Digitized Raster Graphics' : 'arc',
  857. 'Magellan topo (.blx)' : 'blx',
  858. 'SAGA GIS Binary Grid (.sdat)' : 'sdat',
  859. # vector
  860. 'ESRI Shapefile' : 'shp',
  861. 'UK .NTF' : 'ntf',
  862. 'SDTS' : 'ddf',
  863. 'DGN' : 'dgn',
  864. 'VRT' : 'vrt',
  865. 'REC' : 'rec',
  866. 'BNA' : 'bna',
  867. 'CSV' : 'csv',
  868. 'GML' : 'gml',
  869. 'GPX' : 'gpx',
  870. 'KML' : 'kml',
  871. 'GMT' : 'gmt',
  872. 'PGeo' : 'mdb',
  873. 'XPlane' : 'dat',
  874. 'AVCBin' : 'adf',
  875. 'AVCE00' : 'e00',
  876. 'DXF' : 'dxf',
  877. 'Geoconcept' : 'gxt',
  878. 'GeoRSS' : 'xml',
  879. 'GPSTrackMaker' : 'gtm',
  880. 'VFK' : 'vfk'
  881. }
  882. try:
  883. return formatToExt[name]
  884. except KeyError:
  885. return ''
  886. class GdalSelect(wx.Panel):
  887. def __init__(self, parent, panel, ogr = False,
  888. default = 'file',
  889. exclude = [],
  890. envHandler = None):
  891. """!Widget for selecting GDAL/OGR datasource, format
  892. @param parent parent window
  893. @param ogr use OGR selector instead of GDAL
  894. """
  895. self.parent = parent
  896. self.ogr = ogr
  897. wx.Panel.__init__(self, parent = panel, id = wx.ID_ANY)
  898. self.settingsBox = wx.StaticBox(parent = self, id=wx.ID_ANY,
  899. label=" %s " % _("Settings"))
  900. self.inputBox = wx.StaticBox(parent = self, id=wx.ID_ANY,
  901. label=" %s " % _("Source name"))
  902. # source type
  903. sources = list()
  904. self.sourceMap = { 'file' : -1,
  905. 'dir' : -1,
  906. 'db' : -1,
  907. 'pro' : -1 }
  908. idx = 0
  909. if 'file' not in exclude:
  910. sources.append(_("File"))
  911. self.sourceMap['file'] = idx
  912. idx += 1
  913. if 'directory' not in exclude:
  914. sources.append(_("Directory"))
  915. self.sourceMap['dir'] = idx
  916. idx += 1
  917. if 'database' not in exclude:
  918. sources.append(_("Database"))
  919. self.sourceMap['db'] = idx
  920. idx += 1
  921. if 'protocol' not in exclude:
  922. sources.append(_("Protocol"))
  923. self.sourceMap['pro'] = idx
  924. if self.ogr:
  925. self.settingsFile = os.path.join(os.path.expanduser("~"), '.grass7', 'wxOGR')
  926. else:
  927. self.settingsFile = os.path.join(os.path.expanduser("~"), '.grass7', 'wxGDAL')
  928. self._settings = self._loadSettings()
  929. self.settingsChoice = wx.Choice(parent = self, id = wx.ID_ANY)
  930. self.settingsChoice.Bind(wx.EVT_CHOICE, self.OnSettingsLoad)
  931. self.settingsChoice.SetItems(self._settings.keys())
  932. self.btnSettings = wx.Button(parent = self, id = wx.ID_SAVE)
  933. self.btnSettings.Bind(wx.EVT_BUTTON, self.OnSettingsSave)
  934. self.source = wx.RadioBox(parent = self, id = wx.ID_ANY,
  935. label = _('Source type'),
  936. style = wx.RA_SPECIFY_COLS,
  937. choices = sources)
  938. self.source.SetSelection(0)
  939. self.source.Bind(wx.EVT_RADIOBOX, self.OnSetType)
  940. # dsn widgets
  941. if not ogr:
  942. filemask = 'GeoTIFF (%s)|%s|%s (*.*)|*.*' % \
  943. (self._getExtPattern('tif'), self._getExtPattern('tif'), _('All files'))
  944. else:
  945. filemask = 'ESRI Shapefile (%s)|%s|%s (*.*)|*.*' % \
  946. (self._getExtPattern('shp'), self._getExtPattern('shp'), _('All files'))
  947. dsnFile = filebrowse.FileBrowseButton(parent=self, id=wx.ID_ANY,
  948. size=globalvar.DIALOG_GSELECT_SIZE, labelText = '',
  949. dialogTitle=_('Choose file to import'),
  950. buttonText=_('Browse'),
  951. startDirectory=os.getcwd(),
  952. changeCallback=self.OnSetDsn,
  953. fileMask=filemask)
  954. dsnFile.Hide()
  955. dsnDir = filebrowse.DirBrowseButton(parent=self, id=wx.ID_ANY,
  956. size=globalvar.DIALOG_GSELECT_SIZE, labelText='',
  957. dialogTitle=_('Choose input directory'),
  958. buttonText=_('Browse'),
  959. startDirectory=os.getcwd(),
  960. changeCallback=self.OnSetDsn)
  961. dsnDir.SetName('GdalSelect')
  962. dsnDir.Hide()
  963. dsnDbFile = filebrowse.FileBrowseButton(parent=self, id=wx.ID_ANY,
  964. size=globalvar.DIALOG_GSELECT_SIZE, labelText='',
  965. dialogTitle=_('Choose file'),
  966. buttonText=_('Browse'),
  967. startDirectory=os.getcwd(),
  968. changeCallback=self.OnSetDsn)
  969. dsnDbFile.Hide()
  970. dsnDbFile.SetName('GdalSelect')
  971. dsnDbText = wx.TextCtrl(parent = self, id = wx.ID_ANY)
  972. dsnDbText.Hide()
  973. dsnDbText.Bind(wx.EVT_TEXT, self.OnSetDsn)
  974. dsnDbText.SetName('GdalSelect')
  975. dsnDbChoice = wx.Choice(parent = self, id = wx.ID_ANY)
  976. dsnDbChoice.Hide()
  977. dsnDbChoice.Bind(wx.EVT_CHOICE, self.OnSetDsn)
  978. dsnDbChoice.SetName('GdalSelect')
  979. dsnPro = wx.TextCtrl(parent = self, id = wx.ID_ANY)
  980. dsnPro.Hide()
  981. dsnPro.Bind(wx.EVT_TEXT, self.OnSetDsn)
  982. dsnPro.SetName('GdalSelect')
  983. # format
  984. self.format = FormatSelect(parent = self,
  985. ogr = ogr)
  986. self.format.Bind(wx.EVT_CHOICE, self.OnSetFormat)
  987. if ogr:
  988. fType = 'ogr'
  989. else:
  990. fType = 'gdal'
  991. self.input = { 'file' : [_("File:"),
  992. dsnFile,
  993. utils.GetFormats()[fType]['file']],
  994. 'dir' : [_("Directory:"),
  995. dsnDir,
  996. utils.GetFormats()[fType]['file']],
  997. 'db' : [_("Database:"),
  998. dsnDbFile,
  999. utils.GetFormats()[fType]['database']],
  1000. 'pro' : [_("Protocol:"),
  1001. dsnPro,
  1002. utils.GetFormats()[fType]['protocol']],
  1003. 'db-win' : { 'file' : dsnDbFile,
  1004. 'text' : dsnDbText,
  1005. 'choice' : dsnDbChoice },
  1006. }
  1007. self.dsnType = default
  1008. self.input[self.dsnType][1].Show()
  1009. self.format.SetItems(self.input[self.dsnType][2])
  1010. if not ogr:
  1011. self.format.SetStringSelection('GeoTIFF')
  1012. else:
  1013. self.format.SetStringSelection('ESRI Shapefile')
  1014. self.dsnText = wx.StaticText(parent = self, id = wx.ID_ANY,
  1015. label = self.input[self.dsnType][0],
  1016. size = (75, -1))
  1017. self.formatText = wx.StaticText(parent = self, id = wx.ID_ANY,
  1018. label = _("Format:"))
  1019. self._layout()
  1020. def _layout(self):
  1021. """!Layout"""
  1022. mainSizer = wx.BoxSizer(wx.VERTICAL)
  1023. settingsSizer = wx.StaticBoxSizer(self.settingsBox, wx.HORIZONTAL)
  1024. settingsSizer.Add(item = wx.StaticText(parent = self,
  1025. id = wx.ID_ANY,
  1026. label = _("Load settings:")),
  1027. flag = wx.ALIGN_CENTER_VERTICAL | wx.RIGHT,
  1028. border = 5)
  1029. settingsSizer.Add(item = self.settingsChoice,
  1030. proportion = 1,
  1031. flag = wx.EXPAND)
  1032. settingsSizer.Add(item = self.btnSettings,
  1033. flag = wx.LEFT,
  1034. border = 5)
  1035. inputSizer = wx.StaticBoxSizer(self.inputBox, wx.HORIZONTAL)
  1036. self.dsnSizer = wx.GridBagSizer(vgap=3, hgap=3)
  1037. self.dsnSizer.AddGrowableRow(1)
  1038. self.dsnSizer.AddGrowableCol(1)
  1039. self.dsnSizer.Add(item=self.dsnText,
  1040. flag=wx.ALIGN_CENTER_VERTICAL,
  1041. pos = (0, 0))
  1042. self.dsnSizer.Add(item=self.input[self.dsnType][1],
  1043. flag = wx.ALIGN_CENTER_VERTICAL | wx.EXPAND,
  1044. pos = (0, 1))
  1045. self.dsnSizer.Add(item=self.formatText,
  1046. flag=wx.ALIGN_CENTER_VERTICAL,
  1047. pos = (1, 0))
  1048. self.dsnSizer.Add(item=self.format,
  1049. flag = wx.ALIGN_CENTER_VERTICAL,
  1050. pos = (1, 1))
  1051. inputSizer.Add(item=self.dsnSizer, proportion=1,
  1052. flag=wx.EXPAND | wx.ALL)
  1053. mainSizer.Add(item=settingsSizer, proportion=0,
  1054. flag=wx.ALL | wx.EXPAND, border=5)
  1055. mainSizer.Add(item=self.source, proportion=0,
  1056. flag=wx.LEFT | wx.RIGHT | wx.EXPAND, border=5)
  1057. mainSizer.Add(item=inputSizer, proportion=0,
  1058. flag=wx.ALL | wx.EXPAND, border=5)
  1059. self.SetSizer(mainSizer)
  1060. mainSizer.Fit(self)
  1061. def _getExtPatternGlob(self, ext):
  1062. """!Get pattern for case-insensitive globing"""
  1063. pattern = '*.'
  1064. for c in ext:
  1065. pattern += '[%s%s]' % (c.lower(), c.upper())
  1066. return pattern
  1067. def _getExtPattern(self, ext):
  1068. """!Get pattern for case-insensitive file mask"""
  1069. return '*.%s;*.%s' % (ext.lower(), ext.upper())
  1070. def OnSettingsLoad(self, event):
  1071. """!Load named settings"""
  1072. name = event.GetString()
  1073. if name not in self._settings:
  1074. gcmd.GError(parent = self,
  1075. message = _("Settings named '%s' not found") % name)
  1076. return
  1077. data = self._settings[name]
  1078. self.OnSetType(event = None, sel = self.sourceMap[data[0]])
  1079. self.OnSetFormat(event = None, format = data[2])
  1080. self.OnSetDsn(event = None, path = data[1])
  1081. def OnSettingsSave(self, event):
  1082. """!Save settings"""
  1083. dlg = wx.TextEntryDialog(parent = self,
  1084. message = _("Name:"),
  1085. caption = _("Save settings"))
  1086. if dlg.ShowModal() != wx.ID_OK:
  1087. return
  1088. if not dlg.GetValue():
  1089. gcmd.GMessage(parent = self,
  1090. message = _("Name not given, settings is not saved."))
  1091. return
  1092. data = self._loadSettings()
  1093. try:
  1094. fd = open(self.settingsFile, 'w')
  1095. fd.write(dlg.GetValue() + ';' + self.dsnType + ';' +
  1096. self._getDsn() + ';' +
  1097. self.format.GetStringSelection())
  1098. fd.write('\n')
  1099. except IOError:
  1100. gcmd.GError(parent = self,
  1101. message = _("Unable to save settings."))
  1102. return
  1103. fd.close()
  1104. dlg.Destroy()
  1105. def _loadSettings(self):
  1106. """!Load settings from the file
  1107. The file is defined by self.SettingsFile.
  1108. @return parsed dict
  1109. @return empty dict on error
  1110. """
  1111. data = dict()
  1112. if not os.path.exists(self.settingsFile):
  1113. return data
  1114. try:
  1115. fd = open(self.settingsFile, 'r')
  1116. for line in fd.readlines():
  1117. try:
  1118. name, ftype, dsn, format = line.rstrip('\n').split(';')
  1119. data[name] = (ftype, dsn, format)
  1120. except ValueError:
  1121. pass
  1122. except IOError:
  1123. return data
  1124. fd.close()
  1125. return data
  1126. def OnSetType(self, event, sel = None):
  1127. """!Datasource type changed"""
  1128. if event:
  1129. sel = event.GetSelection()
  1130. else:
  1131. self.source.SetSelection(sel)
  1132. win = self.input[self.dsnType][1]
  1133. self.dsnSizer.Remove(win)
  1134. win.Hide()
  1135. if sel == self.sourceMap['file']: # file
  1136. self.dsnType = 'file'
  1137. format = self.input[self.dsnType][2][0]
  1138. try:
  1139. ext = self.format.GetExtension(format)
  1140. if not ext:
  1141. raise KeyError
  1142. format += ' (%s)|%s|%s (*.*)|*.*' % \
  1143. (self._getExtPattern(ext), self._getExtPattern(ext), _('All files'))
  1144. except KeyError:
  1145. format += '%s (*.*)|*.*' % _('All files')
  1146. win = filebrowse.FileBrowseButton(parent=self, id=wx.ID_ANY,
  1147. size=globalvar.DIALOG_GSELECT_SIZE, labelText='',
  1148. dialogTitle=_('Choose file to import'),
  1149. buttonText=_('Browse'),
  1150. startDirectory=os.getcwd(),
  1151. changeCallback=self.OnSetDsn,
  1152. fileMask = format)
  1153. self.input[self.dsnType][1] = win
  1154. elif sel == self.sourceMap['dir']: # directory
  1155. self.dsnType = 'dir'
  1156. elif sel == self.sourceMap['db']: # database
  1157. self.dsnType = 'db'
  1158. elif sel == self.sourceMap['pro']: # protocol
  1159. self.dsnType = 'pro'
  1160. self.dsnText.SetLabel(self.input[self.dsnType][0])
  1161. if self.parent.GetName() == 'MultiImportDialog':
  1162. self.parent.list.DeleteAllItems()
  1163. self.format.SetItems(self.input[self.dsnType][2])
  1164. if sel in (self.sourceMap['file'],
  1165. self.sourceMap['dir']):
  1166. win = self.input[self.dsnType][1]
  1167. self.dsnSizer.Add(item=self.input[self.dsnType][1],
  1168. flag = wx.ALIGN_CENTER_VERTICAL | wx.EXPAND,
  1169. pos = (0, 1))
  1170. win.SetValue('')
  1171. win.Show()
  1172. if not self.ogr:
  1173. self.OnSetFormat(event = None, format = 'GeoTIFF')
  1174. else:
  1175. self.OnSetFormat(event = None, format = 'ESRI Shapefile')
  1176. elif sel == self.sourceMap['pro']:
  1177. win = self.input[self.dsnType][1]
  1178. self.dsnSizer.Add(item=self.input[self.dsnType][1],
  1179. flag = wx.ALIGN_CENTER_VERTICAL | wx.EXPAND,
  1180. pos = (0, 1))
  1181. win.SetValue('')
  1182. win.Show()
  1183. self.dsnSizer.Layout()
  1184. def _getDsn(self):
  1185. """!Get datasource name"""
  1186. if self.format.GetStringSelection() == 'PostgreSQL':
  1187. return 'PG:dbname=%s' % self.input[self.dsnType][1].GetStringSelection()
  1188. return self.input[self.dsnType][1].GetValue()
  1189. def OnSetDsn(self, event, path = None):
  1190. """!Input DXF file/OGR dsn defined, update list of layer widget"""
  1191. if event:
  1192. path = event.GetString()
  1193. else:
  1194. if self.format.GetStringSelection() == 'PostgreSQL':
  1195. for item in path.split(':', 1)[1].split(','):
  1196. key, value = item.split('=', 1)
  1197. if key == 'dbname':
  1198. self.input[self.dsnType][1].SetStringSelection(value)
  1199. break
  1200. else:
  1201. self.input[self.dsnType][1].SetValue(path)
  1202. if not path:
  1203. return
  1204. data = list()
  1205. layerId = 1
  1206. dsn = self._getDsn()
  1207. if self.dsnType == 'file':
  1208. baseName = os.path.basename(dsn)
  1209. grassName = utils.GetValidLayerName(baseName.split('.', -1)[0])
  1210. data.append((layerId, baseName, grassName))
  1211. elif self.dsnType == 'dir':
  1212. try:
  1213. ext = self.format.GetExtension(self.format.GetStringSelection())
  1214. except KeyError:
  1215. ext = ''
  1216. for file in glob.glob(os.path.join(dsn, "%s") % self._getExtPatternGlob(ext)):
  1217. baseName = os.path.basename(file)
  1218. grassName = utils.GetValidLayerName(baseName.split('.', -1)[0])
  1219. data.append((layerId, baseName, grassName))
  1220. layerId += 1
  1221. elif self.dsnType == 'db':
  1222. ret = gcmd.RunCommand('v.in.ogr',
  1223. quiet = True,
  1224. read = True,
  1225. flags = 'l',
  1226. dsn = dsn)
  1227. if not ret:
  1228. self.parent.list.LoadData()
  1229. if hasattr(self, "btn_run"):
  1230. self.btn_run.Enable(False)
  1231. return
  1232. layerId = 1
  1233. for line in ret.splitlines():
  1234. layerName = line.strip()
  1235. grassName = utils.GetValidLayerName(layerName)
  1236. data.append((layerId, layerName.strip(), grassName.strip()))
  1237. layerId += 1
  1238. evt = wxGdalSelect(dsn = dsn + '@OGR')
  1239. evt.SetId(self.input[self.dsnType][1].GetId())
  1240. wx.PostEvent(self.parent, evt)
  1241. if self.parent.GetName() == 'MultiImportDialog':
  1242. self.parent.list.LoadData(data)
  1243. if len(data) > 0:
  1244. self.parent.btn_run.Enable(True)
  1245. else:
  1246. self.parent.btn_run.Enable(False)
  1247. if event:
  1248. event.Skip()
  1249. def OnSetFormat(self, event, format = None):
  1250. """!Format changed"""
  1251. if self.dsnType not in ['file', 'dir', 'db']:
  1252. return
  1253. win = self.input[self.dsnType][1]
  1254. self.dsnSizer.Remove(win)
  1255. if self.dsnType == 'file':
  1256. win.Destroy()
  1257. else: # database
  1258. win.Hide()
  1259. if event:
  1260. format = event.GetString()
  1261. else:
  1262. self.format.SetStringSelection(format)
  1263. if self.dsnType == 'file':
  1264. try:
  1265. ext = self.format.GetExtension(format)
  1266. if not ext:
  1267. raise KeyError
  1268. format += ' (%s)|%s|%s (*.*)|*.*' % \
  1269. (self._getExtPattern(ext), self._getExtPattern(ext), _('All files'))
  1270. except KeyError:
  1271. format += '%s (*.*)|*.*' % _('All files')
  1272. win = filebrowse.FileBrowseButton(parent=self, id=wx.ID_ANY,
  1273. size=globalvar.DIALOG_GSELECT_SIZE, labelText='',
  1274. dialogTitle=_('Choose file'),
  1275. buttonText=_('Browse'),
  1276. startDirectory=os.getcwd(),
  1277. changeCallback=self.OnSetDsn,
  1278. fileMask = format)
  1279. elif self.dsnType == 'dir':
  1280. pass
  1281. else: # database
  1282. if format == 'SQLite' or format == 'Rasterlite':
  1283. win = self.input['db-win']['file']
  1284. elif format == 'PostgreSQL' or format == 'PostGIS WKT Raster driver':
  1285. if grass.find_program('psql'):
  1286. win = self.input['db-win']['choice']
  1287. if not win.GetItems():
  1288. p = grass.Popen(['psql', '-ltA'], stdout = grass.PIPE)
  1289. ret = p.communicate()[0]
  1290. if ret:
  1291. db = list()
  1292. for line in ret.splitlines():
  1293. sline = line.split('|')
  1294. if len(sline) < 2:
  1295. continue
  1296. dbname = sline[0]
  1297. if dbname:
  1298. db.append(dbname)
  1299. win.SetItems(db)
  1300. else:
  1301. win = self.input['db-win']['text']
  1302. else:
  1303. win = self.input['db-win']['text']
  1304. self.input[self.dsnType][1] = win
  1305. if not win.IsShown():
  1306. win.Show()
  1307. self.dsnSizer.Add(item=self.input[self.dsnType][1],
  1308. flag = wx.ALIGN_CENTER_VERTICAL | wx.EXPAND,
  1309. pos = (0, 1))
  1310. self.dsnSizer.Layout()
  1311. def GetType(self):
  1312. """!Get source type"""
  1313. return self.dsnType
  1314. def GetDsn(self):
  1315. """!Get DSN"""
  1316. if self.format.GetStringSelection() == 'PostgreSQL':
  1317. return 'PG:dbname=%s' % self.input[self.dsnType][1].GetStringSelection()
  1318. return self.input[self.dsnType][1].GetValue()
  1319. def GetDsnWin(self):
  1320. """!Get list of DSN windows"""
  1321. win = list()
  1322. for stype in ('file', 'dir', 'pro'):
  1323. win.append(self.input[stype][1])
  1324. for stype in ('file', 'text', 'choice'):
  1325. win.append(self.input['db-win'][stype])
  1326. return win
  1327. def GetFormatExt(self):
  1328. """!Get format extension"""
  1329. return self.format.GetExtension(self.format.GetStringSelection())
  1330. class ProjSelect(wx.ComboBox):
  1331. """!Widget for selecting input raster/vector map used by
  1332. r.proj/v.proj modules."""
  1333. def __init__(self, parent, isRaster, id = wx.ID_ANY, size = globalvar.DIALOG_COMBOBOX_SIZE,
  1334. **kwargs):
  1335. super(ProjSelect, self).__init__(parent, id, size = size,
  1336. style = wx.CB_READONLY, **kwargs)
  1337. self.SetName("ProjSelect")
  1338. self.isRaster = isRaster
  1339. def UpdateItems(self, dbase, location, mapset):
  1340. """!Update list of maps
  1341. """
  1342. if not dbase:
  1343. dbase = grass.gisenv()['GISDBASE']
  1344. if not mapset:
  1345. mapset = grass.gisenv()['MAPSET']
  1346. if self.isRaster:
  1347. ret = gcmd.RunCommand('r.proj',
  1348. quiet = True,
  1349. read = True,
  1350. flags = 'l',
  1351. dbase = dbase,
  1352. location = location,
  1353. mapset = mapset)
  1354. else:
  1355. ret = gcmd.RunCommand('v.proj',
  1356. quiet = True,
  1357. read = True,
  1358. flags = 'l',
  1359. dbase = dbase,
  1360. location = location,
  1361. mapset = mapset)
  1362. listMaps = list()
  1363. if ret:
  1364. for line in ret.splitlines():
  1365. listMaps.append(line.strip())
  1366. utils.ListSortLower(listMaps)
  1367. self.SetItems(listMaps)
  1368. self.SetValue('')