Skip to content

Commit

Permalink
wxGUI/datacatalog: Add new location action to database node in Data t…
Browse files Browse the repository at this point in the history
…ab (#790)

* After clicking on database node, two options for creating new location are available (new empty and download).
* Adds standalone functions download_location_interactively(), create_location_interactively(), and import_file().
* Modernizes and clarifies new location name validation in location wizard.
  • Loading branch information
lindakarlovska committed Jul 24, 2020
1 parent 2b477bd commit bccf537
Show file tree
Hide file tree
Showing 4 changed files with 214 additions and 121 deletions.
45 changes: 41 additions & 4 deletions gui/wxpython/datacatalog/tree.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,12 @@
from datacatalog.dialogs import CatalogReprojectionDialog
from icons.icon import MetaIcon
from startup.guiutils import (create_mapset_interactively,
create_location_interactively,
rename_mapset_interactively,
rename_location_interactively,
delete_mapset_interactively,
delete_location_interactively)
delete_location_interactively,
download_location_interactively)

from grass.pydispatch.signal import Signal

Expand Down Expand Up @@ -675,8 +677,8 @@ def OnCreateMapset(self, event):
location = self.selected_location[0]
try:
mapset = create_mapset_interactively(self,
gisdbase.data['name'],
location.data['name'],)
gisdbase.data['name'],
location.data['name'])
if mapset:
self.InsertMapset(name=mapset,
location_node=location)
Expand All @@ -686,6 +688,20 @@ def OnCreateMapset(self, event):
message=_("Unable to create new mapset: %s") % e,
showTraceback=False)

def OnCreateLocation(self, event):
"""
Location wizard started
"""
grassdatabase, location, mapset = (
create_location_interactively(self,
self.selected_grassdb[0].data['name'])
)
if location is not None:
item = self._model.SearchNodes(name=grassdatabase, type='grassdb')
if not item:
self.InsertGrassDb(name=grassdatabase)
self.ReloadTreeItems()

def OnRenameMapset(self, event):
"""
Rename selected mapset
Expand Down Expand Up @@ -972,8 +988,20 @@ def OnDeleteLocation(self, event):
message=_("Unable to delete location: %s") % e,
showTraceback=False)

def OnDownloadLocation(self, event):
"""
Download location online
"""
grassdatabase, location, mapset = download_location_interactively(
self, self.selected_grassdb[0].data['name']
)
if location:
self.ReloadTreeItems()

def OnDisplayLayer(self, event):
"""Display layer in current graphics view"""
"""
Display layer in current graphics view
"""
self.DisplayLayer()

def DisplayLayer(self):
Expand Down Expand Up @@ -1299,6 +1327,15 @@ def _popupMenuLocation(self):
def _popupMenuGrassDb(self):
"""Create popup menu for grass db"""
menu = Menu()

item = wx.MenuItem(menu, wx.ID_ANY, _("&Create new location"))
menu.AppendItem(item)
self.Bind(wx.EVT_MENU, self.OnCreateLocation, item)

item = wx.MenuItem(menu, wx.ID_ANY, _("&Download sample location"))
menu.AppendItem(item)
self.Bind(wx.EVT_MENU, self.OnDownloadLocation, item)

self.PopupMenu(menu)
menu.Destroy()

Expand Down
109 changes: 28 additions & 81 deletions gui/wxpython/gis_set.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,19 +32,20 @@
import wx
import wx.lib.mixins.listctrl as listmix

from core.gcmd import GMessage, GError, RunCommand
from core.gcmd import GError, RunCommand
from core.utils import GetListOfLocations, GetListOfMapsets
from startup.utils import (
get_lockfile_if_present, get_possible_database_path,
create_database_directory)
from startup.guiutils import (SetSessionMapset,
create_mapset_interactively,
create_location_interactively,
rename_mapset_interactively,
rename_location_interactively,
delete_mapset_interactively,
delete_location_interactively)
delete_location_interactively,
download_location_interactively)
import startup.guiutils as sgui
from location_wizard.dialogs import RegionDef
from gui_core.widgets import StaticWrapText
from gui_core.wrap import Button, ListCtrl, StaticText, StaticBox, \
TextCtrl, BitmapFromImage
Expand Down Expand Up @@ -231,7 +232,7 @@ def __init__(self, parent=None, id=wx.ID_ANY,
self.bexit.Bind(wx.EVT_BUTTON, self.OnExit)
self.bhelp.Bind(wx.EVT_BUTTON, self.OnHelp)
self.bmapset.Bind(wx.EVT_BUTTON, self.OnCreateMapset)
self.bwizard.Bind(wx.EVT_BUTTON, self.OnWizard)
self.bwizard.Bind(wx.EVT_BUTTON, self.OnCreateLocation)

self.rename_location_button.Bind(wx.EVT_BUTTON, self.OnRenameLocation)
self.delete_location_button.Bind(wx.EVT_BUTTON, self.OnDeleteLocation)
Expand Down Expand Up @@ -535,76 +536,23 @@ def SuggestDatabase(self):
'your home directory. '
'Press Browse button to select the directory.'))

def OnWizard(self, event):
def OnCreateLocation(self, event):
"""Location wizard started"""
from location_wizard.wizard import LocationWizard
gWizard = LocationWizard(parent=self,
grassdatabase=self.tgisdbase.GetValue())
if gWizard.location is not None:
self.tgisdbase.SetValue(gWizard.grassdatabase)
grassdatabase, location, mapset = (
create_location_interactively(self, self.gisdbase)
)
if location is not None:
self.OnSelectLocation(None)
self.lbmapsets.SetSelection(self.listOfMapsets.index(mapset))
self.bstart.SetFocus()
self.tgisdbase.SetValue(grassdatabase)
self.OnSetDatabase(None)
self.UpdateMapsets(os.path.join(self.gisdbase, gWizard.location))
self.UpdateMapsets(os.path.join(grassdatabase, location))
self.lblocations.SetSelection(
self.listOfLocations.index(
gWizard.location))
self.listOfLocations.index(location))
self.lbmapsets.SetSelection(0)
self.SetLocation(self.gisdbase, gWizard.location, 'PERMANENT')
if gWizard.georeffile:
message = _("Do you want to import <%(name)s> to the newly created location?") % {
'name': gWizard.georeffile}
dlg = wx.MessageDialog(parent=self, message=message, caption=_(
"Import data?"), style=wx.YES_NO | wx.YES_DEFAULT | wx.ICON_QUESTION)
dlg.CenterOnParent()
if dlg.ShowModal() == wx.ID_YES:
self.ImportFile(gWizard.georeffile)
dlg.Destroy()
if gWizard.default_region:
defineRegion = RegionDef(self, location=gWizard.location)
defineRegion.CenterOnParent()
defineRegion.ShowModal()
defineRegion.Destroy()

if gWizard.user_mapset:
self.OnCreateMapset(event)

def ImportFile(self, filePath):
"""Tries to import file as vector or raster.
If successfull sets default region from imported map.
"""
RunCommand('db.connect', flags='c')
mapName = os.path.splitext(os.path.basename(filePath))[0]
vectors = RunCommand('v.in.ogr', input=filePath, flags='l',
read=True)

wx.BeginBusyCursor()
wx.GetApp().Yield()
if vectors:
# vector detected
returncode, error = RunCommand(
'v.in.ogr', input=filePath, output=mapName, flags='e',
getErrorMsg=True)
else:
returncode, error = RunCommand(
'r.in.gdal', input=filePath, output=mapName, flags='e',
getErrorMsg=True)
wx.EndBusyCursor()
self.SetLocation(grassdatabase, location, mapset)

if returncode != 0:
GError(
parent=self,
message=_(
"Import of <%(name)s> failed.\n"
"Reason: %(msg)s") % ({
'name': filePath,
'msg': error}))
else:
GMessage(
message=_(
"Data file <%(name)s> imported successfully. "
"The location's default region was set from this imported map.") % {
'name': filePath},
parent=self)

# the event can be refactored out by using lambda in bind
def OnRenameMapset(self, event):
Expand All @@ -618,7 +566,7 @@ def OnRenameMapset(self, event):
if newmapset:
self.OnSelectLocation(None)
self.lbmapsets.SetSelection(
self.listOfMapsets.index(newmapset))
self.listOfMapsets.index(newmapset))
except Exception as e:
GError(parent=self,
message=_("Unable to rename mapset: %s") % e,
Expand All @@ -634,7 +582,7 @@ def OnRenameLocation(self, event):
if newlocation:
self.UpdateLocations(self.gisdbase)
self.lblocations.SetSelection(
self.listOfLocations.index(newlocation))
self.listOfLocations.index(newlocation))
self.UpdateMapsets(newlocation)
except Exception as e:
GError(parent=self,
Expand Down Expand Up @@ -674,24 +622,23 @@ def OnDeleteLocation(self, event):
showTraceback=False)

def OnDownloadLocation(self, event):
"""Download location online"""
from startup.locdownload import LocationDownloadDialog

loc_download = LocationDownloadDialog(parent=self, database=self.gisdbase)
loc_download.ShowModal()
location = loc_download.GetLocation()
"""
Download location online
"""
grassdatabase, location, mapset = download_location_interactively(
self, self.gisdbase
)
if location:
# get the new location to the list
self.UpdateLocations(self.gisdbase)
self.UpdateLocations(grassdatabase)
# seems to be used in similar context
self.UpdateMapsets(os.path.join(self.gisdbase, location))
self.UpdateMapsets(os.path.join(grassdatabase, location))
self.lblocations.SetSelection(
self.listOfLocations.index(location))
# wizard does this as well, not sure if needed
self.SetLocation(self.gisdbase, location, 'PERMANENT')
self.SetLocation(grassdatabase, location, mapset)
# seems to be used in similar context
self.OnSelectLocation(None)
loc_download.Destroy()

def UpdateLocations(self, dbase):
"""Update list of locations"""
Expand Down
63 changes: 27 additions & 36 deletions gui/wxpython/location_wizard/wizard.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,11 @@
from core import utils
from core.utils import cmp
from core.gcmd import RunCommand, GError, GMessage, GWarning
from gui_core.widgets import GenericValidator
from gui_core.widgets import GenericMultiValidator
from gui_core.wrap import SpinCtrl, SearchCtrl, StaticText, \
TextCtrl, Button, CheckBox, StaticBox, NewId, ListCtrl, HyperlinkCtrl
from location_wizard.dialogs import SelectTransformDialog
from startup.utils import location_exists

from grass.script import decode
from grass.script import core as grass
Expand Down Expand Up @@ -171,10 +172,11 @@ def __init__(self, wizard, parent, grassdatabase):
self.tgisdbase = self.MakeLabel(grassdatabase)
self.tlocation = self.MakeTextCtrl("newLocation", size=(400, -1))
self.tlocation.SetFocus()

checks = [(grass.legal_name, self._nameValidationFailed),
(self._checkLocationNotExists, self._locationAlreadyExists)]
self.tlocation.SetValidator(
GenericValidator(
grass.legal_name,
self._nameValidationFailed))
GenericMultiValidator(checks))
self.tlocTitle = self.MakeTextCtrl(size=(400, -1))

# text for required options
Expand Down Expand Up @@ -255,15 +257,23 @@ def __init__(self, wizard, parent, grassdatabase):

def _nameValidationFailed(self, ctrl):
message = _(
"Name <%(name)s> is not a valid name for location. "
"Please use only ASCII characters excluding %(chars)s "
"and space.") % {
'name': ctrl.GetValue(),
'chars': '/"\'@,=*~'}
GError(
parent=self,
message=message,
caption=_("Invalid location name"))
"Name '{}' is not a valid name for location. "
"Please use only ASCII characters excluding characters {} "
"and space.").format(ctrl.GetValue(), '/"\'@,=*~')
GError(parent=self, message=message, caption=_("Invalid name"))

def _checkLocationNotExists(self, text):
"""Check whether user's input location exists or not."""
if location_exists(self.tgisdbase.GetLabel(), text):
return False
return True

def _locationAlreadyExists(self, ctrl):
message = _(
"Location '{}' already exists. Please consider using "
"another name for your location.").format(ctrl.GetValue())
GError(parent=self, message=message,
caption=_("Existing location path"))

def OnChangeName(self, event):
"""Name for new location was changed"""
Expand All @@ -287,21 +297,6 @@ def OnBrowse(self, event):
dlg.Destroy()

def OnPageChanging(self, event=None):
error = None
if os.path.isdir(
os.path.join(
self.tgisdbase.GetLabel(),
self.tlocation.GetValue())):
error = _("Location already exists in GRASS Database.")

if error:
GError(parent=self,
message="%s <%s>.%s%s" % (_("Unable to create location"),
str(self.tlocation.GetValue()),
os.linesep,
error))
event.Veto()
return

self.location = self.tlocation.GetValue()
self.grassdatabase = self.tgisdbase.GetLabel()
Expand Down Expand Up @@ -600,7 +595,7 @@ def __init__(self, parent, columns, data=None):
for column in columns:
self.InsertColumn(i, column)
i += 1

self.EnableAlternateRowColours()

if self.sourceData:
Expand Down Expand Up @@ -1482,14 +1477,15 @@ def OnText(self, event):
if not nextButton.IsEnabled():
nextButton.Enable()


class EPSGPage(TitledPage):
"""Wizard page for selecting EPSG code for
setting coordinate system parameters"""

def __init__(self, wizard, parent):
TitledPage.__init__(self, wizard, _("Select CRS from a list"))

self.sizer = wx.BoxSizer(wx.VERTICAL)
self.sizer = wx.BoxSizer(wx.VERTICAL)
searchBoxSizer = wx.BoxSizer(wx.HORIZONTAL)
epsglistBoxSizer = wx.BoxSizer(wx.HORIZONTAL)
informationBoxSizer = wx.BoxSizer(wx.HORIZONTAL)
Expand Down Expand Up @@ -1982,7 +1978,7 @@ def OnPageChanging(self, event):
return

# check for datum tranforms
# FIXME: -t flag is a hack-around for trac bug #1849
# FIXME: -t flag is a hack-around for trac bug #1849
ret, out, err = RunCommand('g.proj',
read=True, getErrorMsg=True,
proj4=self.customstring,
Expand Down Expand Up @@ -2550,11 +2546,6 @@ def OnWizFinished(self):
database))
return None

# change to new GISDbase directory
RunCommand('g.gisenv',
parent=self.wizard,
set='GISDBASE=%s' % database)

wx.MessageBox(
parent=self.wizard,
message=_(
Expand Down

0 comments on commit bccf537

Please sign in to comment.