gselect.py 61 KB

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