class SQLBuilderUpdate(SQLBuilder): """Class for building UPDATE SQL statement""" def __init__(self, parent, vectmap, id=wx.ID_ANY, layer=1, column=None): self.column = column # set dialog title title = _("GRASS SQL Builder (%(type)s) - <%(map)s>") % \ { 'type' : "UPDATE", 'map' : vectmap } modeChoices = [ _("Column to set (SET clause)"), _("Constraint for query (WHERE clause)"), _("Calculate column value to set") ] SQLBuilder.__init__(self, parent, title, vectmap, id=wx.ID_ANY, modeChoices=modeChoices, layer=layer) # signals self.sqlApplied = Signal("SQLBuilder.sqlApplied") if parent: # TODO: replace by giface self.sqlApplied.connect(parent.Update) def _doLayout(self, modeChoices): """Do dialog layout""" SQLBuilder._doLayout(self, modeChoices) self.initText = "UPDATE %s SET" % self.tablename if self.column: self.initText += " %s = " % self.column self.text_sql.SetValue(self.initText) self.btn_arithmetic = { 'eq': [ '=', ], 'brac': [ '()', ], 'plus': [ '+', ], 'minus': [ '-', ], 'divide': [ '/', ], 'multiply': [ '*', ] } self.btn_arithmeticpanel = wx.Panel(parent=self.panel, id=wx.ID_ANY) for key, value in self.btn_arithmetic.iteritems(): btn = wx.Button(parent=self.btn_arithmeticpanel, id=wx.ID_ANY, label=value[0]) self.btn_arithmetic[key].append(btn.GetId()) btn_arithmeticsizer = wx.GridBagSizer(hgap=5, vgap=5) btn_arithmeticsizer.Add(item=self.FindWindowById( self.btn_arithmetic['eq'][1]), pos=(0, 0)) btn_arithmeticsizer.Add(item=self.FindWindowById( self.btn_arithmetic['brac'][1]), pos=(1, 0)) btn_arithmeticsizer.Add(item=self.FindWindowById( self.btn_arithmetic['plus'][1]), pos=(0, 1)) btn_arithmeticsizer.Add(item=self.FindWindowById( self.btn_arithmetic['minus'][1]), pos=(1, 1)) btn_arithmeticsizer.Add(item=self.FindWindowById( self.btn_arithmetic['divide'][1]), pos=(0, 2)) btn_arithmeticsizer.Add(item=self.FindWindowById( self.btn_arithmetic['multiply'][1]), pos=(1, 2)) self.btn_arithmeticpanel.SetSizer(btn_arithmeticsizer) self.pagesizer.Insert(item=self.btn_arithmeticpanel, before=3, proportion=0, flag=wx.ALIGN_CENTER_HORIZONTAL) self.funcpanel = wx.Panel(parent=self.panel, id=wx.ID_ANY) self._initSqlFunctions() funcsbox = wx.StaticBox(parent=self.funcpanel, id=wx.ID_ANY, label=" %s " % _("Functions")) funcsizer = wx.StaticBoxSizer(funcsbox, wx.VERTICAL) self.list_func = wx.ListBox(parent=self.funcpanel, id=wx.ID_ANY, choices=self.sqlFuncs['sqlite'].keys(), style=wx.LB_SORT) funcsizer.Add(item=self.list_func, proportion=1, flag=wx.EXPAND) self.funcpanel.SetSizer(funcsizer) self.hsizer.Insert(item=self.funcpanel, before=2, proportion=1, flag=wx.EXPAND) self.list_func.Bind(wx.EVT_LISTBOX, self.OnAddFunc) for key, value in self.btn_arithmetic.iteritems(): self.FindWindowById(value[1]).Bind(wx.EVT_BUTTON, self.OnAddMark) self.mode.SetSelection(0) self.OnMode(None) self.text_sql.SetInsertionPoint(self.text_sql.GetLastPosition()) def OnApply(self, event): """Apply button pressed""" ret, msg = RunCommand('db.execute', getErrorMsg=True, parent=self, stdin=self.text_sql.GetValue(), input='-', driver=self.driver, database=self.database) if ret != 0 and msg: self.statusbar.SetStatusText(_("SQL statement was not applied"), 0) else: self.statusbar.SetStatusText(_("SQL statement applied"), 0) self.sqlApplied.emit() def OnClear(self, event): """Clear button pressed""" self.text_sql.SetValue(self.initText) def OnMode(self, event): """Adjusts builder for chosen mode""" if self.mode.GetSelection() == 0: self.valuespanel.Hide() self.btn_logicpanel.Hide() self.btn_arithmeticpanel.Hide() self.funcpanel.Hide() elif self.mode.GetSelection() == 1: self.valuespanel.Show() self.btn_logicpanel.Show() self.btn_arithmeticpanel.Hide() self.funcpanel.Hide() elif self.mode.GetSelection() == 2: self.valuespanel.Hide() self.btn_logicpanel.Hide() self.btn_arithmeticpanel.Show() self.funcpanel.Show() self.pagesizer.Layout() def OnAddFunc(self, event): """Add function to the query""" if self.driver == 'dbf': GMessage( parent=self, message=_( "Dbf driver does not support usage of SQL functions.")) return idx = self.list_func.GetSelections() for i in idx: func = self.sqlFuncs['sqlite'][self.list_func.GetString(i)][0] self._add(element='func', value=func) def _add(self, element, value): """Add element to the query :param element: element to add (column, value) """ sqlstr = self.text_sql.GetValue() curspos = self.text_sql.GetInsertionPoint() newsqlstr = '' if element in ['value', 'mark', 'func'] or \ (element == 'column' and self.mode.GetSelection() == 2): addstr = ' ' + value + ' ' newsqlstr = sqlstr[:curspos] + addstr + sqlstr[curspos:] curspos += len(addstr) elif element == 'column': if self.mode.GetSelection() == 0: # -> column idx1 = sqlstr.lower().find('set') + len('set') idx2 = sqlstr.lower().find('where') if idx2 >= 0: colstr = sqlstr[idx1:idx2].strip() else: colstr = sqlstr[idx1:].strip() cols = [col.split('=')[0].strip() for col in colstr.split(',')] if unicode(value) in cols: self.text_sql.SetInsertionPoint(curspos) wx.CallAfter(self.text_sql.SetFocus) return if colstr: colstr += ',' colstr = ' ' + colstr colstr += ' ' + value + '= ' newsqlstr = sqlstr[:idx1] + colstr if idx2 >= 0: newsqlstr += sqlstr[idx2:] curspos = idx1 + len(colstr) elif self.mode.GetSelection() == 1: # -> where newsqlstr = '' if sqlstr.lower().find('where') < 0: newsqlstr += ' WHERE' newsqlstr += ' ' + value curspos = self.text_sql.GetLastPosition() + len(newsqlstr) newsqlstr = sqlstr + newsqlstr if newsqlstr: self.text_sql.SetValue(newsqlstr) wx.CallAfter(self.text_sql.SetFocus) self.text_sql.SetInsertionPoint(curspos) def _initSqlFunctions(self): self.sqlFuncs = {} # TODO add functions for other drivers self.sqlFuncs['sqlite'] = { 'ABS': ['ABS()'], 'LENGTH': ['LENGTH()'], 'LOWER': ['LOWER()'], 'LTRIM': ['LTRIM(,)'], 'MAX': ['MAX()'], 'MIN': ['MIN()'], 'RTRIM': ['RTRIM(,)'], 'SUBSTR': ['SUBSTR (,[,])'], 'TRIM': ['TRIM (,)'] }
class VDigitToolbar(BaseToolbar): """Toolbar for digitization""" def __init__(self, parent, toolSwitcher, MapWindow, digitClass, giface, tools=[]): self.MapWindow = MapWindow self.Map = MapWindow.GetMap() # Map class instance self.tools = tools self.digitClass = digitClass BaseToolbar.__init__(self, parent, toolSwitcher) self.digit = None self._giface = giface self.fType = None # feature type for simple features editing self.editingStarted = Signal("VDigitToolbar.editingStarted") self.editingStopped = Signal("VDigitToolbar.editingStopped") self.editingBgMap = Signal("VDigitToolbar.editingBgMap") self.quitDigitizer = Signal("VDigitToolbar.quitDigitizer") layerTree = self._giface.GetLayerTree() if layerTree: self.editingStarted.connect(layerTree.StartEditing) self.editingStopped.connect(layerTree.StopEditing) self.editingBgMap.connect(layerTree.SetBgMapForEditing) # bind events self.Bind(wx.EVT_SHOW, self.OnShow) # currently selected map layer for editing (reference to MapLayer # instance) self.mapLayer = None # list of vector layers from Layer Manager (only in the current mapset) self.layers = [] self.comboid = self.combo = None self.undo = -1 self.redo = -1 # only one dialog can be open self.settingsDialog = None # create toolbars (two rows optionally) self.InitToolbar(self._toolbarData()) self._default = -1 # default action (digitize new point, line, etc.) self.action = {"desc": "", "type": "", "id": -1} self._currentAreaActionType = None # list of available vector maps self.UpdateListOfLayers(updateTool=True) for tool in ( "addPoint", "addLine", "addBoundary", "addCentroid", "addArea", "addVertex", "deleteLine", "deleteArea", "displayAttr", "displayCats", "editLine", "moveLine", "moveVertex", "removeVertex", "additionalTools", ): if hasattr(self, tool): tool = getattr(self, tool) self.toolSwitcher.AddToolToGroup( group="mouseUse", toolbar=self, tool=tool ) else: Debug.msg(1, "%s skipped" % tool) # custom button for digitization of area/boundary/centroid # TODO: could this be somehow generalized? nAreaTools = 0 if self.tools and "addBoundary" in self.tools: nAreaTools += 1 if self.tools and "addCentroid" in self.tools: nAreaTools += 1 if self.tools and "addArea" in self.tools: nAreaTools += 1 if nAreaTools != 1: self.areaButton = self.CreateSelectionButton( _("Select area/boundary/centroid tool") ) self.areaButtonId = self.InsertControl(5, self.areaButton) self.areaButton.Bind(wx.EVT_BUTTON, self.OnAddAreaMenu) # realize toolbar self.Realize() # workaround for Mac bug. May be fixed by 2.8.8, but not before then. if self.combo: self.combo.Hide() self.combo.Show() # disable undo/redo if self.undo > 0: self.EnableTool(self.undo, False) if self.redo > 0: self.EnableTool(self.redo, False) self.FixSize(width=105) def _toolbarData(self): """Toolbar data""" data = [] self.icons = { "addPoint": MetaIcon( img="point-create", label=_("Digitize new point"), desc=_("Left: new point"), ), "addLine": MetaIcon( img="line-create", label=_("Digitize new line"), desc=_( "Left: new point; Ctrl+Left: undo last point; Right: close line" ), ), "addBoundary": MetaIcon( img="boundary-create", label=_("Digitize new boundary"), desc=_( "Left: new point; Ctrl+Left: undo last point; Right: close line" ), ), "addCentroid": MetaIcon( img="centroid-create", label=_("Digitize new centroid"), desc=_("Left: new point"), ), "addArea": MetaIcon( img="polygon-create", label=_("Digitize new area (boundary without category)"), desc=_("Left: new point"), ), "addVertex": MetaIcon( img="vertex-create", label=_("Add new vertex to line or boundary"), desc=_("Left: Select; Ctrl+Left: Unselect; Right: Confirm"), ), "deleteLine": MetaIcon( img="line-delete", label=_( "Delete selected point(s), line(s), boundary(ies) or centroid(s)" ), desc=_("Left: Select; Ctrl+Left: Unselect; Right: Confirm"), ), "deleteArea": MetaIcon( img="polygon-delete", label=_("Delete selected area(s)"), desc=_("Left: Select; Ctrl+Left: Unselect; Right: Confirm"), ), "displayAttr": MetaIcon( img="attributes-display", label=_("Display/update attributes"), desc=_("Left: Select"), ), "displayCats": MetaIcon( img="cats-display", label=_("Display/update categories"), desc=_("Left: Select"), ), "editLine": MetaIcon( img="line-edit", label=_("Edit selected line/boundary"), desc=_( "Left: new point; Ctrl+Left: undo last point; Right: close line" ), ), "moveLine": MetaIcon( img="line-move", label=_( "Move selected point(s), line(s), boundary(ies) or centroid(s)" ), desc=_("Left: Select; Ctrl+Left: Unselect; Right: Confirm"), ), "moveVertex": MetaIcon( img="vertex-move", label=_("Move selected vertex"), desc=_("Left: Select; Ctrl+Left: Unselect; Right: Confirm"), ), "removeVertex": MetaIcon( img="vertex-delete", label=_("Remove selected vertex"), desc=_("Left: Select; Ctrl+Left: Unselect; Right: Confirm"), ), "settings": BaseIcons["settings"].SetLabel(_("Digitization settings")), "quit": BaseIcons["quit"].SetLabel( label=_("Quit digitizer"), desc=_("Quit digitizer and save changes") ), "help": BaseIcons["help"].SetLabel( label=_("Vector Digitizer manual"), desc=_("Show Vector Digitizer manual"), ), "additionalTools": MetaIcon( img="tools", label=_("Additional tools " "(copy, flip, connect, etc.)"), desc=_("Left: Select; Ctrl+Left: Unselect; Right: Confirm"), ), "undo": MetaIcon( img="undo", label=_("Undo"), desc=_("Undo previous changes") ), "redo": MetaIcon( img="redo", label=_("Redo"), desc=_("Redo previous changes") ), } if not self.tools or "selector" in self.tools: data.append((None,)) if not self.tools or "addPoint" in self.tools: data.append( ("addPoint", self.icons["addPoint"], self.OnAddPoint, wx.ITEM_CHECK) ) if not self.tools or "addLine" in self.tools: data.append( ("addLine", self.icons["addLine"], self.OnAddLine, wx.ITEM_CHECK) ) if not self.tools or "addArea" in self.tools: data.append( ("addArea", self.icons["addArea"], self.OnAddAreaTool, wx.ITEM_CHECK) ) if not self.tools or "deleteLine" in self.tools: data.append( ( "deleteLine", self.icons["deleteLine"], self.OnDeleteLine, wx.ITEM_CHECK, ) ) if not self.tools or "deleteArea" in self.tools: data.append( ( "deleteArea", self.icons["deleteArea"], self.OnDeleteArea, wx.ITEM_CHECK, ) ) if not self.tools or "moveVertex" in self.tools: data.append( ( "moveVertex", self.icons["moveVertex"], self.OnMoveVertex, wx.ITEM_CHECK, ) ) if not self.tools or "addVertex" in self.tools: data.append( ("addVertex", self.icons["addVertex"], self.OnAddVertex, wx.ITEM_CHECK) ) if not self.tools or "removeVertex" in self.tools: data.append( ( "removeVertex", self.icons["removeVertex"], self.OnRemoveVertex, wx.ITEM_CHECK, ) ) if not self.tools or "editLine" in self.tools: data.append( ("editLine", self.icons["editLine"], self.OnEditLine, wx.ITEM_CHECK) ) if not self.tools or "moveLine" in self.tools: data.append( ("moveLine", self.icons["moveLine"], self.OnMoveLine, wx.ITEM_CHECK) ) if not self.tools or "displayCats" in self.tools: data.append( ( "displayCats", self.icons["displayCats"], self.OnDisplayCats, wx.ITEM_CHECK, ) ) if not self.tools or "displayAttr" in self.tools: data.append( ( "displayAttr", self.icons["displayAttr"], self.OnDisplayAttr, wx.ITEM_CHECK, ) ) if not self.tools or "additionalSelf.Tools" in self.tools: data.append( ( "additionalTools", self.icons["additionalTools"], self.OnAdditionalToolMenu, wx.ITEM_CHECK, ) ) if not self.tools or "undo" in self.tools or "redo" in self.tools: data.append((None,)) if not self.tools or "undo" in self.tools: data.append(("undo", self.icons["undo"], self.OnUndo)) if not self.tools or "redo" in self.tools: data.append(("redo", self.icons["redo"], self.OnRedo)) if ( not self.tools or "settings" in self.tools or "help" in self.tools or "quit" in self.tools ): data.append((None,)) if not self.tools or "settings" in self.tools: data.append(("settings", self.icons["settings"], self.OnSettings)) if not self.tools or "help" in self.tools: data.append(("help", self.icons["help"], self.OnHelp)) if not self.tools or "quit" in self.tools: data.append(("quit", self.icons["quit"], self.OnExit)) return self._getToolbarData(data) def OnTool(self, event): """Tool selected -> untoggles previusly selected tool in toolbar""" Debug.msg(3, "VDigitToolbar.OnTool(): id = %s" % event.GetId()) # set cursor self.MapWindow.SetNamedCursor("cross") self.MapWindow.mouse["box"] = "point" self.MapWindow.mouse["use"] = "pointer" aId = self.action.get("id", -1) BaseToolbar.OnTool(self, event) # clear tmp canvas if self.action["id"] != aId or aId == -1: self.MapWindow.polycoords = [] self.MapWindow.ClearLines(pdc=self.MapWindow.pdcTmp) if self.digit and len(self.MapWindow.digit.GetDisplay().GetSelected()) > 0: # cancel action self.MapWindow.OnMiddleDown(None) # set no action if self.action["id"] == -1: self.action = {"desc": "", "type": "", "id": -1} # set focus self.MapWindow.SetFocus() def OnAddPoint(self, event): """Add point to the vector map Laier""" Debug.msg(2, "VDigitToolbar.OnAddPoint()") self.action = {"desc": "addLine", "type": "point", "id": self.addPoint} self.MapWindow.mouse["box"] = "point" def OnAddLine(self, event): """Add line to the vector map layer""" Debug.msg(2, "VDigitToolbar.OnAddLine()") self.action = {"desc": "addLine", "type": "line", "id": self.addLine} self.MapWindow.mouse["box"] = "line" # self.MapWindow.polycoords = [] # reset temp line def OnAddBoundary(self, event): """Add boundary to the vector map layer""" Debug.msg(2, "VDigitToolbar.OnAddBoundary()") self._toggleAreaIfNeeded() # reset temp line if self.action["desc"] != "addLine" or self.action["type"] != "boundary": self.MapWindow.polycoords = [] # update icon and tooltip self.SetToolNormalBitmap(self.addArea, self.icons["addBoundary"].GetBitmap()) self.SetToolShortHelp(self.addArea, self.icons["addBoundary"].GetLabel()) # set action self.action = {"desc": "addLine", "type": "boundary", "id": self.addArea} self.MapWindow.mouse["box"] = "line" self._currentAreaActionType = "boundary" def OnAddCentroid(self, event): """Add centroid to the vector map layer""" Debug.msg(2, "VDigitToolbar.OnAddCentroid()") self._toggleAreaIfNeeded() # update icon and tooltip self.SetToolNormalBitmap(self.addArea, self.icons["addCentroid"].GetBitmap()) self.SetToolShortHelp(self.addArea, self.icons["addCentroid"].GetLabel()) # set action self.action = {"desc": "addLine", "type": "centroid", "id": self.addArea} self.MapWindow.mouse["box"] = "point" self._currentAreaActionType = "centroid" def OnAddArea(self, event): """Add area to the vector map layer""" Debug.msg(2, "VDigitToolbar.OnAddArea()") self._toggleAreaIfNeeded() # update icon and tooltip self.SetToolNormalBitmap(self.addArea, self.icons["addArea"].GetBitmap()) self.SetToolShortHelp(self.addArea, self.icons["addArea"].GetLabel()) # set action self.action = {"desc": "addLine", "type": "area", "id": self.addArea} self.MapWindow.mouse["box"] = "line" self._currentAreaActionType = "area" def _toggleAreaIfNeeded(self): """In some cases, the area tool is not toggled, we have to do it manually.""" if not self.GetToolState(self.addArea): self.ToggleTool(self.addArea, True) self.toolSwitcher.ToolChanged(self.addArea) def OnAddAreaTool(self, event): """Area tool activated.""" Debug.msg(2, "VDigitToolbar.OnAddAreaTool()") # we need the previous id if ( not self._currentAreaActionType or self._currentAreaActionType == "area" ): # default action self.OnAddArea(event) elif self._currentAreaActionType == "boundary": self.OnAddBoundary(event) elif self._currentAreaActionType == "centroid": self.OnAddCentroid(event) def OnAddAreaMenu(self, event): """Digitize area menu (add area/boundary/centroid)""" menuItems = [] if not self.tools or "addArea" in self.tools: menuItems.append((self.icons["addArea"], self.OnAddArea)) if not self.fType and not self.tools or "addBoundary" in self.tools: menuItems.append((self.icons["addBoundary"], self.OnAddBoundary)) if not self.fType and not self.tools or "addCentroid" in self.tools: menuItems.append((self.icons["addCentroid"], self.OnAddCentroid)) self._onMenu(menuItems) def OnExit(self, event=None): """Quit digitization tool""" # stop editing of the currently selected map layer if self.mapLayer: self.StopEditing() # close dialogs if still open if self.settingsDialog: self.settingsDialog.OnCancel(None) # set default mouse settings self.parent.GetMapToolbar().SelectDefault() self.MapWindow.polycoords = [] self.quitDigitizer.emit() def OnMoveVertex(self, event): """Move line vertex""" Debug.msg(2, "Digittoolbar.OnMoveVertex():") self.action = {"desc": "moveVertex", "id": self.moveVertex} self.MapWindow.mouse["box"] = "point" def OnAddVertex(self, event): """Add line vertex""" Debug.msg(2, "Digittoolbar.OnAddVertex():") self.action = {"desc": "addVertex", "id": self.addVertex} self.MapWindow.mouse["box"] = "point" def OnRemoveVertex(self, event): """Remove line vertex""" Debug.msg(2, "Digittoolbar.OnRemoveVertex():") self.action = {"desc": "removeVertex", "id": self.removeVertex} self.MapWindow.mouse["box"] = "point" def OnEditLine(self, event): """Edit line""" Debug.msg(2, "Digittoolbar.OnEditLine():") self.action = {"desc": "editLine", "id": self.editLine} self.MapWindow.mouse["box"] = "line" def OnMoveLine(self, event): """Move line""" Debug.msg(2, "Digittoolbar.OnMoveLine():") self.action = {"desc": "moveLine", "id": self.moveLine} self.MapWindow.mouse["box"] = "box" def OnDeleteLine(self, event): """Delete line""" Debug.msg(2, "Digittoolbar.OnDeleteLine():") self.action = {"desc": "deleteLine", "id": self.deleteLine} self.MapWindow.mouse["box"] = "box" def OnDeleteArea(self, event): """Delete Area""" Debug.msg(2, "Digittoolbar.OnDeleteArea():") self.action = {"desc": "deleteArea", "id": self.deleteArea} self.MapWindow.mouse["box"] = "box" def OnDisplayCats(self, event): """Display/update categories""" Debug.msg(2, "Digittoolbar.OnDisplayCats():") self.action = {"desc": "displayCats", "id": self.displayCats} self.MapWindow.mouse["box"] = "point" def OnDisplayAttr(self, event): """Display/update attributes""" Debug.msg(2, "Digittoolbar.OnDisplayAttr():") self.action = {"desc": "displayAttrs", "id": self.displayAttr} self.MapWindow.mouse["box"] = "point" def OnUndo(self, event): """Undo previous changes""" if self.digit: self.digit.Undo() event.Skip() def OnRedo(self, event): """Undo previous changes""" if self.digit: self.digit.Undo(level=1) event.Skip() def EnableUndo(self, enable=True): """Enable 'Undo' in toolbar :param enable: False for disable """ self._enableTool(self.undo, enable) def EnableRedo(self, enable=True): """Enable 'Redo' in toolbar :param enable: False for disable """ self._enableTool(self.redo, enable) def _enableTool(self, tool, enable): if not self.FindById(tool): return if enable: if self.GetToolEnabled(tool) is False: self.EnableTool(tool, True) else: if self.GetToolEnabled(tool) is True: self.EnableTool(tool, False) def GetAction(self, type="desc"): """Get current action info""" return self.action.get(type, "") def OnSettings(self, event): """Show settings dialog""" if self.digit is None: try: self.digit = self.MapWindow.digit = self.digitClass( mapwindow=self.MapWindow ) except SystemExit: self.digit = self.MapWindow.digit = None if not self.settingsDialog: self.settingsDialog = VDigitSettingsDialog( parent=self.parent, giface=self._giface ) self.settingsDialog.Show() def OnHelp(self, event): """Show digitizer help page in web browser""" self._giface.Help("wxGUI.vdigit") def OnAdditionalToolMenu(self, event): """Menu for additional tools""" point = wx.GetMousePosition() toolMenu = Menu() for label, itype, handler, desc in ( ( _("Break selected lines/boundaries at intersection"), wx.ITEM_CHECK, self.OnBreak, "breakLine", ), ( _("Connect selected lines/boundaries"), wx.ITEM_CHECK, self.OnConnect, "connectLine", ), (_("Copy categories"), wx.ITEM_CHECK, self.OnCopyCats, "copyCats"), ( _("Copy features from (background) vector map"), wx.ITEM_CHECK, self.OnCopy, "copyLine", ), (_("Copy attributes"), wx.ITEM_CHECK, self.OnCopyAttrb, "copyAttrs"), ( _("Feature type conversion"), wx.ITEM_CHECK, self.OnTypeConversion, "typeConv", ), ( _("Flip selected lines/boundaries"), wx.ITEM_CHECK, self.OnFlip, "flipLine", ), ( _("Merge selected lines/boundaries"), wx.ITEM_CHECK, self.OnMerge, "mergeLine", ), ( _("Snap selected lines/boundaries (only to nodes)"), wx.ITEM_CHECK, self.OnSnap, "snapLine", ), (_("Split line/boundary"), wx.ITEM_CHECK, self.OnSplitLine, "splitLine"), (_("Query features"), wx.ITEM_CHECK, self.OnQuery, "queryLine"), ( _("Z bulk-labeling of 3D lines"), wx.ITEM_CHECK, self.OnZBulk, "zbulkLine", ), ): # Add items to the menu item = wx.MenuItem( parentMenu=toolMenu, id=wx.ID_ANY, text=label, kind=itype ) toolMenu.AppendItem(item) self.MapWindow.Bind(wx.EVT_MENU, handler, item) if self.action["desc"] == desc: item.Check(True) # Popup the menu. If an item is selected then its handler # will be called before PopupMenu returns. self.MapWindow.PopupMenu(toolMenu) toolMenu.Destroy() if self.action["desc"] == "addPoint": self.ToggleTool(self.additionalTools, False) def OnCopy(self, event): """Copy selected features from (background) vector map""" if not self.digit: GError(_("No vector map open for editing."), self.parent) return # select background map dlg = VectorDialog( self.parent, title=_("Select background vector map"), layerTree=self._giface.GetLayerTree(), ) if dlg.ShowModal() != wx.ID_OK: dlg.Destroy() return mapName = dlg.GetName(full=True) dlg.Destroy() # close open background map if any bgMap = UserSettings.Get( group="vdigit", key="bgmap", subkey="value", settings_type="internal" ) if bgMap: self.digit.CloseBackgroundMap() self.editingBgMap.emit(mapName=bgMap, unset=True) # open background map for reading UserSettings.Set( group="vdigit", key="bgmap", subkey="value", value=str(mapName), settings_type="internal", ) self.digit.OpenBackgroundMap(mapName) self.editingBgMap.emit(mapName=mapName) if self.action["desc"] == "copyLine": # select previous action self.ToggleTool(self.addPoint, True) self.ToggleTool(self.additionalTools, False) self.OnAddPoint(event) return Debug.msg(2, "Digittoolbar.OnCopy():") self.action = {"desc": "copyLine", "id": self.additionalTools} self.MapWindow.mouse["box"] = "box" def OnSplitLine(self, event): """Split line""" if self.action["desc"] == "splitLine": # select previous action self.ToggleTool(self.addPoint, True) self.ToggleTool(self.additionalTools, False) self.OnAddPoint(event) return Debug.msg(2, "Digittoolbar.OnSplitLine():") self.action = {"desc": "splitLine", "id": self.additionalTools} self.MapWindow.mouse["box"] = "point" def OnCopyCats(self, event): """Copy categories""" if self.action["desc"] == "copyCats": # select previous action self.ToggleTool(self.addPoint, True) self.ToggleTool(self.copyCats, False) self.OnAddPoint(event) return Debug.msg(2, "Digittoolbar.OnCopyCats():") self.action = {"desc": "copyCats", "id": self.additionalTools} self.MapWindow.mouse["box"] = "point" def OnCopyAttrb(self, event): """Copy attributes""" if self.action["desc"] == "copyAttrs": # select previous action self.ToggleTool(self.addPoint, True) self.ToggleTool(self.copyCats, False) self.OnAddPoint(event) return Debug.msg(2, "Digittoolbar.OnCopyAttrb():") self.action = {"desc": "copyAttrs", "id": self.additionalTools} self.MapWindow.mouse["box"] = "point" def OnFlip(self, event): """Flip selected lines/boundaries""" if self.action["desc"] == "flipLine": # select previous action self.ToggleTool(self.addPoint, True) self.ToggleTool(self.additionalTools, False) self.OnAddPoint(event) return Debug.msg(2, "Digittoolbar.OnFlip():") self.action = {"desc": "flipLine", "id": self.additionalTools} self.MapWindow.mouse["box"] = "box" def OnMerge(self, event): """Merge selected lines/boundaries""" if self.action["desc"] == "mergeLine": # select previous action self.ToggleTool(self.addPoint, True) self.ToggleTool(self.additionalTools, False) self.OnAddPoint(event) return Debug.msg(2, "Digittoolbar.OnMerge():") self.action = {"desc": "mergeLine", "id": self.additionalTools} self.MapWindow.mouse["box"] = "box" def OnBreak(self, event): """Break selected lines/boundaries""" if self.action["desc"] == "breakLine": # select previous action self.ToggleTool(self.addPoint, True) self.ToggleTool(self.additionalTools, False) self.OnAddPoint(event) return Debug.msg(2, "Digittoolbar.OnBreak():") self.action = {"desc": "breakLine", "id": self.additionalTools} self.MapWindow.mouse["box"] = "box" def OnSnap(self, event): """Snap selected features""" if self.action["desc"] == "snapLine": # select previous action self.ToggleTool(self.addPoint, True) self.ToggleTool(self.additionalTools, False) self.OnAddPoint(event) return Debug.msg(2, "Digittoolbar.OnSnap():") self.action = {"desc": "snapLine", "id": self.additionalTools} self.MapWindow.mouse["box"] = "box" def OnConnect(self, event): """Connect selected lines/boundaries""" if self.action["desc"] == "connectLine": # select previous action self.ToggleTool(self.addPoint, True) self.ToggleTool(self.additionalTools, False) self.OnAddPoint(event) return Debug.msg(2, "Digittoolbar.OnConnect():") self.action = {"desc": "connectLine", "id": self.additionalTools} self.MapWindow.mouse["box"] = "box" def OnQuery(self, event): """Query selected lines/boundaries""" if self.action["desc"] == "queryLine": # select previous action self.ToggleTool(self.addPoint, True) self.ToggleTool(self.additionalTools, False) self.OnAddPoint(event) return Debug.msg( 2, "Digittoolbar.OnQuery(): %s" % UserSettings.Get(group="vdigit", key="query", subkey="selection"), ) self.action = {"desc": "queryLine", "id": self.additionalTools} self.MapWindow.mouse["box"] = "box" def OnZBulk(self, event): """Z bulk-labeling selected lines/boundaries""" if not self.digit.IsVector3D(): GError( parent=self.parent, message=_("Vector map is not 3D. Operation canceled."), ) return if self.action["desc"] == "zbulkLine": # select previous action self.ToggleTool(self.addPoint, True) self.ToggleTool(self.additionalTools, False) self.OnAddPoint(event) return Debug.msg(2, "Digittoolbar.OnZBulk():") self.action = {"desc": "zbulkLine", "id": self.additionalTools} self.MapWindow.mouse["box"] = "line" def OnTypeConversion(self, event): """Feature type conversion Supported conversions: - point <-> centroid - line <-> boundary """ if self.action["desc"] == "typeConv": # select previous action self.ToggleTool(self.addPoint, True) self.ToggleTool(self.additionalTools, False) self.OnAddPoint(event) return Debug.msg(2, "Digittoolbar.OnTypeConversion():") self.action = {"desc": "typeConv", "id": self.additionalTools} self.MapWindow.mouse["box"] = "box" def OnSelectMap(self, event): """Select vector map layer for editing If there is a vector map layer already edited, this action is firstly terminated. The map layer is closed. After this the selected map layer activated for editing. """ if event.GetSelection() == 0: # create new vector map layer if self.mapLayer: openVectorMap = self.mapLayer.GetName(fullyQualified=False)["name"] else: openVectorMap = None dlg = CreateNewVector( self.parent, exceptMap=openVectorMap, giface=self._giface, cmd=(("v.edit", {"tool": "create"}, "map")), disableAdd=True, ) if dlg and dlg.GetName(): # add layer to map layer tree/map display mapName = dlg.GetName() + "@" + grass.gisenv()["MAPSET"] self._giface.GetLayerList().AddLayer( ltype="vector", name=mapName, checked=True, cmd=["d.vect", "map=%s" % mapName], ) vectLayers = self.UpdateListOfLayers(updateTool=True) selection = vectLayers.index(mapName) # create table ? if dlg.IsChecked("table"): # TODO: replace this by signal # also note that starting of tools such as atm, iclass, # plots etc. should be handled in some better way # than starting randomly from mapdisp and lmgr lmgr = self.parent.GetLayerManager() if lmgr: lmgr.OnShowAttributeTable(None, selection="table") dlg.Destroy() else: self.combo.SetValue(_("Select vector map")) if dlg: dlg.Destroy() return else: selection = event.GetSelection() - 1 # first option is 'New vector map' # skip currently selected map if self.layers[selection] == self.mapLayer: return if self.mapLayer: # deactive map layer for editing self.StopEditing() # select the given map layer for editing self.StartEditing(self.layers[selection]) event.Skip() def StartEditing(self, mapLayer): """Start editing selected vector map layer. :param mapLayer: MapLayer to be edited """ # check if topology is available (skip for hidden - temporary # maps, see iclass for details) if ( not mapLayer.IsHidden() and grass.vector_info(mapLayer.GetName())["level"] != 2 ): dlg = wx.MessageDialog( parent=self.MapWindow, message=_( "Topology for vector map <%s> is not available. " "Topology is required by digitizer.\nDo you want to " "rebuild topology (takes some time) and open the vector map " "for editing?" ) % mapLayer.GetName(), caption=_("Digitizer error"), style=wx.YES_NO | wx.YES_DEFAULT | wx.ICON_QUESTION | wx.CENTRE, ) if dlg.ShowModal() == wx.ID_YES: RunCommand("v.build", map=mapLayer.GetName()) else: return # deactive layer self.Map.ChangeLayerActive(mapLayer, False) # clean map canvas self.MapWindow.EraseMap() # unset background map if needed if mapLayer: if ( UserSettings.Get( group="vdigit", key="bgmap", subkey="value", settings_type="internal", ) == mapLayer.GetName() ): UserSettings.Set( group="vdigit", key="bgmap", subkey="value", value="", settings_type="internal", ) self.parent.SetStatusText( _("Please wait, " "opening vector map <%s> for editing...") % mapLayer.GetName(), 0, ) self.MapWindow.pdcVector = PseudoDC() self.digit = self.MapWindow.digit = self.digitClass(mapwindow=self.MapWindow) self.mapLayer = mapLayer # open vector map (assume that 'hidden' map layer is temporary vector # map) if self.digit.OpenMap(mapLayer.GetName(), tmp=mapLayer.IsHidden()) is None: self.mapLayer = None self.StopEditing() return False # check feature type (only for OGR layers) self.fType = self.digit.GetFeatureType() self.EnableAll() self.EnableUndo(False) self.EnableRedo(False) if self.fType == "point": for tool in ( self.addLine, self.addArea, self.moveVertex, self.addVertex, self.removeVertex, self.editLine, ): self.EnableTool(tool, False) elif self.fType == "linestring": for tool in (self.addPoint, self.addArea): self.EnableTool(tool, False) elif self.fType == "polygon": for tool in (self.addPoint, self.addLine): self.EnableTool(tool, False) elif self.fType: GError( parent=self, message=_( "Unsupported feature type '%(type)s'. Unable to edit " "OGR layer <%(layer)s>." ) % {"type": self.fType, "layer": mapLayer.GetName()}, ) self.digit.CloseMap() self.mapLayer = None self.StopEditing() return False # update toolbar if self.combo: self.combo.SetValue(mapLayer.GetName()) if "map" in self.parent.toolbars: self.parent.toolbars["map"].combo.SetValue(_("Vector digitizer")) # here was dead code to enable vdigit button in toolbar # with if to ignore iclass # some signal (DigitizerStarted) can be emitted here Debug.msg(4, "VDigitToolbar.StartEditing(): layer=%s" % mapLayer.GetName()) # change cursor if self.MapWindow.mouse["use"] == "pointer": self.MapWindow.SetNamedCursor("cross") if not self.MapWindow.resize: self.MapWindow.UpdateMap(render=True) # respect opacity opacity = mapLayer.GetOpacity() if opacity < 1.0: alpha = int(opacity * 255) self.digit.GetDisplay().UpdateSettings(alpha=alpha) # emit signal layerTree = self._giface.GetLayerTree() if layerTree: item = layerTree.FindItemByData("maplayer", self.mapLayer) else: item = None self.editingStarted.emit( vectMap=mapLayer.GetName(), digit=self.digit, layerItem=item ) return True def StopEditing(self): """Stop editing of selected vector map layer. :return: True on success :return: False on failure """ item = None if self.combo: self.combo.SetValue(_("Select vector map")) # save changes if self.mapLayer: Debug.msg( 4, "VDigitToolbar.StopEditing(): layer=%s" % self.mapLayer.GetName() ) if ( UserSettings.Get(group="vdigit", key="saveOnExit", subkey="enabled") is False ): if self.digit.GetUndoLevel() > -1: dlg = wx.MessageDialog( parent=self.parent, message=_("Do you want to save changes " "in vector map <%s>?") % self.mapLayer.GetName(), caption=_("Save changes?"), style=wx.YES_NO | wx.YES_DEFAULT | wx.ICON_QUESTION, ) if dlg.ShowModal() == wx.ID_NO: # revert changes self.digit.Undo(0) dlg.Destroy() self.parent.SetStatusText( _( "Please wait, " "closing and rebuilding topology of " "vector map <%s>..." ) % self.mapLayer.GetName(), 0, ) self.digit.CloseMap() # close open background map if any bgMap = UserSettings.Get( group="vdigit", key="bgmap", subkey="value", settings_type="internal" ) if bgMap: self.digit.CloseBackgroundMap() self.editingBgMap.emit(mapName=bgMap, unset=True) self._giface.GetProgress().SetValue(0) self._giface.WriteCmdLog( _("Editing of vector map <%s> successfully finished") % self.mapLayer.GetName(), notification=Notification.HIGHLIGHT, ) # re-active layer layerTree = self._giface.GetLayerTree() if layerTree: item = layerTree.FindItemByData("maplayer", self.mapLayer) if item and layerTree.IsItemChecked(item): self.Map.ChangeLayerActive(self.mapLayer, True) # change cursor self.MapWindow.SetNamedCursor("default") self.MapWindow.pdcVector = None # close dialogs for dialog in ("attributes", "category"): if self.parent.dialogs[dialog]: self.parent.dialogs[dialog].Close() self.parent.dialogs[dialog] = None self.digit = None self.MapWindow.digit = None self.editingStopped.emit(layerItem=item) self.mapLayer = None self.MapWindow.redrawAll = True return True def UpdateListOfLayers(self, updateTool=False): """Update list of available vector map layers. This list consists only editable layers (in the current mapset) :param updateTool: True to update also toolbar :type updateTool: bool """ Debug.msg(4, "VDigitToolbar.UpdateListOfLayers(): updateTool=%d" % updateTool) layerNameSelected = None # name of currently selected layer if self.mapLayer: layerNameSelected = self.mapLayer.GetName() # select vector map layer in the current mapset layerNameList = [] self.layers = self.Map.GetListOfLayers( ltype="vector", mapset=grass.gisenv()["MAPSET"] ) for layer in self.layers: if layer.name not in layerNameList: # do not duplicate layer layerNameList.append(layer.GetName()) if updateTool: # update toolbar if not self.mapLayer: value = _("Select vector map") else: value = layerNameSelected if not self.comboid: if not self.tools or "selector" in self.tools: self.combo = wx.ComboBox( self, id=wx.ID_ANY, value=value, choices=[ _("New vector map"), ] + layerNameList, size=(80, -1), style=wx.CB_READONLY, ) self.comboid = self.InsertControl(0, self.combo) self.parent.Bind(wx.EVT_COMBOBOX, self.OnSelectMap, self.comboid) else: self.combo.SetItems( [ _("New vector map"), ] + layerNameList ) self.Realize() return layerNameList def GetLayer(self): """Get selected layer for editing -- MapLayer instance""" return self.mapLayer def OnShow(self, event): """Show frame event""" if event.IsShown(): # list of available vector maps self.UpdateListOfLayers(updateTool=True)
class DataCatalogTree(LocationMapTree): def __init__(self, parent, giface=None): """Data Catalog Tree constructor.""" super(DataCatalogTree, self).__init__(parent) self._giface = giface self._restricted = True self._initVariablesCatalog() self.beginDrag = Signal('DataCatalogTree.beginDrag') self.endDrag = Signal('DataCatalogTree.endDrag') self.startEdit = Signal('DataCatalogTree.startEdit') self.endEdit = Signal('DataCatalogTree.endEdit') self.Bind( wx.EVT_TREE_BEGIN_DRAG, lambda evt: self._emitSignal( evt.GetItem(), self.beginDrag, event=evt)) self.Bind( wx.EVT_TREE_END_DRAG, lambda evt: self._emitSignal( evt.GetItem(), self.endDrag, event=evt)) self.beginDrag.connect(self.OnBeginDrag) self.endDrag.connect(self.OnEndDrag) self.Bind( wx.EVT_TREE_BEGIN_LABEL_EDIT, lambda evt: self._emitSignal( evt.GetItem(), self.startEdit, event=evt)) self.Bind( wx.EVT_TREE_END_LABEL_EDIT, lambda evt: self._emitSignal( evt.GetItem(), self.endEdit, event=evt)) self.startEdit.connect(self.OnStartEditLabel) self.endEdit.connect(self.OnEditLabel) def _initVariablesCatalog(self): """Init variables.""" self.copy_mode = False self.copy_layer = None self.copy_type = None self.copy_mapset = None self.copy_location = None def SetRestriction(self, restrict): self._restricted = restrict def _runCommand(self, prog, **kwargs): cmdString = ' '.join(gscript.make_command(prog, **kwargs)) ret = RunCommand(prog, parent=self, **kwargs) return ret, cmdString def InitTreeItems(self): """Add locations, mapsets and layers to the tree.""" self._initTreeItems() def OnMoveMap(self, event): """Move layer or mapset (just save it temporarily, copying is done by paste)""" self.copy_mode = False self.copy_layer = self.selected_layer self.copy_type = self.selected_type self.copy_mapset = self.selected_mapset self.copy_location = self.selected_location label = _("Map <{layer}> marked for moving.").format( layer=self.copy_layer.label) self.showNotification.emit(message=label) def OnCopyMap(self, event): """Copy layer or mapset (just save it temporarily, copying is done by paste)""" self.copy_mode = True self.copy_layer = self.selected_layer self.copy_type = self.selected_type self.copy_mapset = self.selected_mapset self.copy_location = self.selected_location label = _("Map <{layer}> marked for copying.").format( layer=self.copy_layer.label) self.showNotification.emit(message=label) def OnRenameMap(self, event): """Rename layer with dialog""" old_name = self.selected_layer.label gisrc, env = gscript.create_environment( gisenv()['GISDBASE'], self.selected_location.label, mapset=self.selected_mapset.label) new_name = self._getNewMapName(_('New name'), _('Rename map'), old_name, env=env, mapset=self.selected_mapset.label, element=self.selected_type.label) if new_name: self.Rename(old_name, new_name) def OnStartEditLabel(self, node, event): """Start label editing""" self.DefineItems(node) Debug.msg(1, "Start label edit {name}".format(name=node.label)) label = _("Editing {name}").format(name=node.label) self.showNotification.emit(message=label) if not self.selected_layer: event.Veto() def OnEditLabel(self, node, event): """End label editing""" if self.selected_layer and not event.IsEditCancelled(): old_name = node.label Debug.msg(1, "End label edit {name}".format(name=old_name)) new_name = event.GetLabel() self.Rename(old_name, new_name) def Rename(self, old, new): """Rename layer""" string = old + ',' + new gisrc, env = gscript.create_environment(gisenv()['GISDBASE'], self.selected_location.label, self.selected_mapset.label) label = _("Renaming map <{name}>...").format(name=string) self.showNotification.emit(message=label) if self.selected_type.label == 'vector': renamed, cmd = self._runCommand('g.rename', vector=string, env=env) elif self.selected_type.label == 'raster': renamed, cmd = self._runCommand('g.rename', raster=string, env=env) else: renamed, cmd = self._runCommand('g.rename', raster3d=string, env=env) if renamed == 0: self.selected_layer.label = new self.selected_layer.data['name'] = new self.RefreshNode(self.selected_layer) self.showNotification.emit(message=_("{cmd} -- completed").format( cmd=cmd)) Debug.msg(1, "LAYER RENAMED TO: " + new) gscript.try_remove(gisrc) def OnPasteMap(self, event): # copying between mapsets of one location if not self.copy_layer: if self.copy_mode: GMessage(_("No map selected for copying."), parent=self) else: GMessage(_("No map selected for moving."), parent=self) return gisrc, env = gscript.create_environment( gisenv()['GISDBASE'], self.selected_location.label, mapset=self.selected_mapset.label) gisrc2, env2 = gscript.create_environment( gisenv()['GISDBASE'], self.copy_location.label, mapset=self.copy_mapset.label) new_name = self.copy_layer.label if self.selected_location == self.copy_location: # within one mapset if self.selected_mapset == self.copy_mapset: # ignore when just moves map if self.copy_mode is False: return new_name = self._getNewMapName( _('New name'), _('Select new name'), self.copy_layer.label, env=env, mapset=self.selected_mapset.label, element=self.copy_type.label) if not new_name: return # within one location, different mapsets else: if map_exists(new_name, element=self.copy_type.label, env=env, mapset=self.selected_mapset.label): new_name = self._getNewMapName( _('New name'), _('Select new name'), self.copy_layer.label, env=env, mapset=self.selected_mapset.label, element=self.copy_type.label) if not new_name: return string = self.copy_layer.label + '@' + self.copy_mapset.label + ',' + new_name pasted = 0 if self.copy_mode: label = _("Copying <{name}>...").format(name=string) else: label = _("Moving <{name}>...").format(name=string) self.showNotification.emit(message=label) if self.copy_type.label == 'vector': pasted, cmd = self._runCommand('g.copy', vector=string, env=env) node = 'vector' elif self.copy_type.label == 'raster': pasted, cmd = self._runCommand('g.copy', raster=string, env=env) node = 'raster' else: pasted, cmd = self._runCommand('g.copy', raster_3d=string, env=env) node = 'raster_3d' if pasted == 0: self.InsertLayer(name=new_name, mapset_node=self.selected_mapset, element_name=node) Debug.msg(1, "COPIED TO: " + new_name) if self.copy_mode: self.showNotification.emit( message=_("g.copy completed").format(cmd=cmd)) else: self.showNotification.emit( message=_("g.copy completed").format(cmd=cmd)) # remove old if not self.copy_mode: self._removeMapAfterCopy(env2) gscript.try_remove(gisrc) gscript.try_remove(gisrc2) # expand selected mapset self.ExpandNode(self.selected_mapset, recursive=True) self._initVariablesCatalog() else: if self.copy_type.label == 'raster_3d': GError(_("Reprojection is not implemented for 3D rasters"), parent=self) return if map_exists(new_name, element=self.copy_type.label, env=env, mapset=self.selected_mapset.label): new_name = self._getNewMapName( _('New name'), _('Select new name'), self.copy_layer.label, env=env, mapset=self.selected_mapset.label, element=self.copy_type.label) if not new_name: return gisdbase = gisenv()['GISDBASE'] callback = lambda: self._onDoneReprojection( iEnv=env2, iGisrc=gisrc2, oGisrc=gisrc) dlg = CatalogReprojectionDialog( self, self._giface, gisdbase, self.copy_location.label, self.copy_mapset.label, self.copy_layer.label, env2, gisdbase, self.selected_location.label, self.selected_mapset.label, new_name, self.copy_type.label, env, callback) dlg.ShowModal() def _onDoneReprojection(self, iEnv, iGisrc, oGisrc): self.InsertLayer(name=self.copy_layer.label, mapset_node=self.selected_mapset, element_name=self.copy_type.label) if not self.copy_mode: self._removeMapAfterCopy(iEnv) gscript.try_remove(iGisrc) gscript.try_remove(oGisrc) self.ExpandNode(self.selected_mapset, recursive=True) self._initVariablesCatalog() def _removeMapAfterCopy(self, env): removed, cmd = self._runCommand('g.remove', type=self.copy_type.label, name=self.copy_layer.label, flags='f', env=env) if removed == 0: self._model.RemoveNode(self.copy_layer) self.RefreshNode(self.copy_type, recursive=True) Debug.msg(1, "LAYER " + self.copy_layer.label + " DELETED") self.showNotification.emit(message=_("g.remove completed").format( cmd=cmd)) def InsertLayer(self, name, mapset_node, element_name): """Insert layer into model and refresh tree""" found_element = self._model.SearchNodes(parent=mapset_node, type='element', name=element_name) found_element = found_element[0] if found_element else None if not found_element: # add type node if not exists found_element = self._model.AppendNode(parent=mapset_node, label=element_name, data=dict( type='element', name=element_name)) found = self._model.SearchNodes(parent=found_element, name=name) if len(found) == 0: self._model.AppendNode(parent=found_element, label=name, data=dict(type=element_name, name=name)) self._model.SortChildren(found_element) self.RefreshNode(mapset_node, recursive=True) def OnDeleteMap(self, event): """Delete layer or mapset""" name = self.selected_layer.label gisrc, env = gscript.create_environment(gisenv()['GISDBASE'], self.selected_location.label, self.selected_mapset.label) if self._confirmDialog(question=_( "Do you really want to delete map <{m}> of type <{etype}> from mapset " "<{mapset}> in location <{loc}>?").format( m=name, mapset=self.selected_mapset.label, etype=self.selected_type.label, loc=self.selected_location.label), title=_('Delete map')) == wx.ID_YES: label = _("Deleting {name}...").format(name=name) self.showNotification.emit(message=label) if self.selected_type.label == 'vector': removed, cmd = self._runCommand('g.remove', flags='f', type='vector', name=name, env=env) elif self.selected_type.label == 'raster': removed, cmd = self._runCommand('g.remove', flags='f', type='raster', name=name, env=env) else: removed, cmd = self._runCommand('g.remove', flags='f', type='raster_3d', name=name, env=env) if removed == 0: self._model.RemoveNode(self.selected_layer) self.RefreshNode(self.selected_type, recursive=True) Debug.msg(1, "LAYER " + name + " DELETED") self.showNotification.emit( message=_("g.remove completed").format(cmd=cmd)) gscript.try_remove(gisrc) def OnDisplayLayer(self, event): """Display layer in current graphics view""" layerName = [] if self.selected_location.label == gisenv( )['LOCATION_NAME'] and self.selected_mapset: string = self.selected_layer.label + '@' + self.selected_mapset.label layerName.append(string) label = _("Displaying {name}...").format(name=string) self.showNotification.emit(message=label) label = "d." + self.selected_type.label[:4] + " --q map=" + string + \ _(" -- completed. Go to Layers tab for further operations.") if self.selected_type.label == 'vector': self._giface.lmgr.AddMaps(layerName, 'vector', True) elif self.selected_type.label == 'raster': self._giface.lmgr.AddMaps(layerName, 'raster', True) else: self._giface.lmgr.AddMaps(layerName, 'raster_3d', True) # generate this message (command) automatically? label = "d.rast --q map=" + string + _( " -- completed. Go to Layers tab for further operations.") self.showNotification.emit(message=label) Debug.msg(1, "LAYER " + self.selected_layer.label + " DISPLAYED") else: GError(_( "Failed to display layer: not in current mapset or invalid layer" ), parent=self) def OnBeginDrag(self, node, event): """Just copy necessary data""" self.DefineItems(node) if self.selected_layer and not ( self._restricted and gisenv()['LOCATION_NAME'] != self.selected_location.label): event.Allow() self.OnCopyMap(event) Debug.msg(1, "DRAG") else: event.Veto() def OnEndDrag(self, node, event): """Copy layer into target""" self.copy_mode = wx.GetMouseState().ControlDown() if node: self.DefineItems(node) if self._restricted and gisenv( )['MAPSET'] != self.selected_mapset.label: GMessage(_( "To move or copy maps to other mapsets, unlock editing of other mapsets" ), parent=self) event.Veto() return event.Allow() Debug.msg(1, "DROP DONE") self.OnPasteMap(event) def OnSwitchLocationMapset(self, event): genv = gisenv() if self.selected_location.label == genv['LOCATION_NAME']: self.changeMapset.emit(mapset=self.selected_mapset.label) else: self.changeLocation.emit(mapset=self.selected_mapset.label, location=self.selected_location.label) self.ExpandCurrentMapset() def OnMetadata(self, event): """Show metadata of any raster/vector/3draster""" def done(event): gscript.try_remove(gisrc) if self.selected_type.label == 'raster': cmd = ['r.info'] elif self.selected_type.label == 'vector': cmd = ['v.info'] elif self.selected_type.label == 'raster_3d': cmd = ['r3.info'] cmd.append('map=%s@%s' % (self.selected_layer.label, self.selected_mapset.label)) gisrc, env = gscript.create_environment(gisenv()['GISDBASE'], self.selected_location.label, self.selected_mapset.label) # print output to command log area # temp gisrc file must be deleted onDone self._giface.RunCmd(cmd, env=env, onDone=done) def OnCopyName(self, event): """Copy layer name to clipboard""" if wx.TheClipboard.Open(): do = wx.TextDataObject() do.SetText('%s@%s' % (self.selected_layer.label, self.selected_mapset.label)) wx.TheClipboard.SetData(do) wx.TheClipboard.Close() def Filter(self, text): """Filter tree based on name and type.""" text = text.strip() if len(text.split(':')) > 1: name = text.split(':')[1].strip() elem = text.split(':')[0].strip() if 'r' == elem: element = 'raster' elif 'r3' == elem: element = 'raster_3d' elif 'v' == elem: element = 'vector' else: element = None else: element = None name = text.strip() self._model = filterModel(self._orig_model, name=name, element=element) self.RefreshItems() self.ExpandCurrentMapset() def _getNewMapName(self, message, title, value, element, mapset, env): """Dialog for simple text entry""" dlg = NameEntryDialog(parent=self, message=message, caption=title, element=element, env=env, mapset=mapset) dlg.SetValue(value) if dlg.ShowModal() == wx.ID_OK: name = dlg.GetValue() else: name = None dlg.Destroy() return name def _confirmDialog(self, question, title): """Confirm dialog""" dlg = wx.MessageDialog(self, question, title, wx.YES_NO) res = dlg.ShowModal() dlg.Destroy() return res def _isCurrent(self, genv): if self._restricted: currentMapset = currentLocation = False if self.selected_location.label == genv['LOCATION_NAME']: currentLocation = True if self.selected_mapset.label == genv['MAPSET']: currentMapset = True return currentLocation, currentMapset else: return True, True def _popupMenuLayer(self): """Create popup menu for layers""" menu = Menu() genv = gisenv() currentLocation, currentMapset = self._isCurrent(genv) item = wx.MenuItem(menu, wx.NewId(), _("&Cut")) menu.AppendItem(item) self.Bind(wx.EVT_MENU, self.OnMoveMap, item) if not currentMapset: item.Enable(False) item = wx.MenuItem(menu, wx.NewId(), _("&Copy")) menu.AppendItem(item) self.Bind(wx.EVT_MENU, self.OnCopyMap, item) item = wx.MenuItem(menu, wx.NewId(), _("Copy &name")) menu.AppendItem(item) self.Bind(wx.EVT_MENU, self.OnCopyName, item) item = wx.MenuItem(menu, wx.NewId(), _("&Paste")) menu.AppendItem(item) self.Bind(wx.EVT_MENU, self.OnPasteMap, item) if not (currentMapset and self.copy_layer): item.Enable(False) item = wx.MenuItem(menu, wx.NewId(), _("&Delete")) menu.AppendItem(item) self.Bind(wx.EVT_MENU, self.OnDeleteMap, item) item.Enable(currentMapset) item = wx.MenuItem(menu, wx.NewId(), _("&Rename")) menu.AppendItem(item) self.Bind(wx.EVT_MENU, self.OnRenameMap, item) item.Enable(currentMapset) menu.AppendSeparator() if not isinstance(self._giface, StandaloneGrassInterface) and \ self.selected_location.label == genv['LOCATION_NAME']: item = wx.MenuItem(menu, wx.NewId(), _("&Display layer")) menu.AppendItem(item) self.Bind(wx.EVT_MENU, self.OnDisplayLayer, item) item = wx.MenuItem(menu, wx.NewId(), _("Show &metadata")) menu.AppendItem(item) self.Bind(wx.EVT_MENU, self.OnMetadata, item) self.PopupMenu(menu) menu.Destroy() def _popupMenuMapset(self): """Create popup menu for mapsets""" menu = wx.Menu() genv = gisenv() currentLocation, currentMapset = self._isCurrent(genv) item = wx.MenuItem(menu, wx.NewId(), _("&Paste")) menu.AppendItem(item) self.Bind(wx.EVT_MENU, self.OnPasteMap, item) if not (currentMapset and self.copy_layer): item.Enable(False) item = wx.MenuItem(menu, wx.NewId(), _("&Switch mapset")) menu.AppendItem(item) self.Bind(wx.EVT_MENU, self.OnSwitchLocationMapset, item) if (self.selected_location.label == genv['LOCATION_NAME'] and self.selected_mapset.label == genv['MAPSET']): item.Enable(False) self.PopupMenu(menu) menu.Destroy() def _popupMenuElement(self): """Create popup menu for elements""" menu = wx.Menu() item = wx.MenuItem(menu, wx.NewId(), _("&Paste")) menu.AppendItem(item) self.Bind(wx.EVT_MENU, self.OnPasteMap, item) genv = gisenv() currentLocation, currentMapset = self._isCurrent(genv) if not (currentMapset and self.copy_layer): item.Enable(False) self.PopupMenu(menu) menu.Destroy()
class DataCatalogTree(LocationMapTree): def __init__(self, parent, giface=None): """Data Catalog Tree constructor.""" super(DataCatalogTree, self).__init__(parent) self._giface = giface self._restricted = True self._initVariablesCatalog() self.beginDrag = Signal('DataCatalogTree.beginDrag') self.endDrag = Signal('DataCatalogTree.endDrag') self.startEdit = Signal('DataCatalogTree.startEdit') self.endEdit = Signal('DataCatalogTree.endEdit') self.Bind( wx.EVT_TREE_BEGIN_DRAG, lambda evt: self._emitSignal( evt.GetItem(), self.beginDrag, event=evt)) self.Bind( wx.EVT_TREE_END_DRAG, lambda evt: self._emitSignal( evt.GetItem(), self.endDrag, event=evt)) self.beginDrag.connect(self.OnBeginDrag) self.endDrag.connect(self.OnEndDrag) self.Bind( wx.EVT_TREE_BEGIN_LABEL_EDIT, lambda evt: self._emitSignal( evt.GetItem(), self.startEdit, event=evt)) self.Bind( wx.EVT_TREE_END_LABEL_EDIT, lambda evt: self._emitSignal( evt.GetItem(), self.endEdit, event=evt)) self.startEdit.connect(self.OnStartEditLabel) self.endEdit.connect(self.OnEditLabel) def _initVariablesCatalog(self): """Init variables.""" self.copy_layer = None self.copy_type = None self.copy_mapset = None self.copy_location = None def SetRestriction(self, restrict): self._restricted = restrict def _runCommand(self, prog, **kwargs): cmdString = ' '.join(gscript.make_command(prog, **kwargs)) ret = RunCommand(prog, parent=self, **kwargs) return ret, cmdString def InitTreeItems(self): """Add locations, mapsets and layers to the tree.""" self._initTreeItems() def OnCopyMap(self, event): """Copy layer or mapset (just save it temporarily, copying is done by paste)""" self.copy_layer = self.selected_layer self.copy_type = self.selected_type self.copy_mapset = self.selected_mapset self.copy_location = self.selected_location label = _("Map <{layer}> marked for copying. " "You can paste it to the current mapset " "<{mapset}>.".format(layer=self.copy_layer.label, mapset=gisenv()['MAPSET'])) self.showNotification.emit(message=label) def OnRenameMap(self, event): """Rename layer with dialog""" old_name = self.selected_layer.label gisrc, env = getEnvironment(gisenv()['GISDBASE'], self.selected_location.label, mapset=self.selected_mapset.label) new_name = self._getNewMapName(_('New name'), _('Rename map'), old_name, env=env, mapset=self.selected_mapset.label, element=self.selected_type.label) if new_name: self.Rename(old_name, new_name) def OnStartEditLabel(self, node, event): """Start label editing""" self.DefineItems(node) Debug.msg(1, "Start label edit {name}".format(name=node.label)) label = _("Editing {name}").format(name=node.label) self.showNotification.emit(message=label) if not self.selected_layer: event.Veto() def OnEditLabel(self, node, event): """End label editing""" if self.selected_layer and not event.IsEditCancelled(): old_name = node.label Debug.msg(1, "End label edit {name}".format(name=old_name)) new_name = event.GetLabel() self.Rename(old_name, new_name) def Rename(self, old, new): """Rename layer""" string = old + ',' + new gisrc, env = getEnvironment(gisenv()['GISDBASE'], self.selected_location.label, self.selected_mapset.label) label = _("Renaming map <{name}>...").format(name=string) self.showNotification.emit(message=label) if self.selected_type.label == 'vector': renamed, cmd = self._runCommand('g.rename', vector=string, env=env) elif self.selected_type.label == 'raster': renamed, cmd = self._runCommand('g.rename', raster=string, env=env) else: renamed, cmd = self._runCommand('g.rename', raster3d=string, env=env) if renamed == 0: self.selected_layer.label = new self.selected_layer.data['name'] = new self.RefreshNode(self.selected_layer) self.showNotification.emit(message=_("{cmd} -- completed").format( cmd=cmd)) Debug.msg(1, "LAYER RENAMED TO: " + new) gscript.try_remove(gisrc) def OnPasteMap(self, event): """Paste layer""" # copying between mapsets of one location if not self.copy_layer: GMessage(_("No map selected for copying."), parent=self) return if self.selected_location == self.copy_location: gisrc, env = getEnvironment(gisenv()['GISDBASE'], self.selected_location.label, mapset=self.selected_mapset.label) new_name = self._getNewMapName(_('New name'), _('Copy map'), self.copy_layer.label, env=env, mapset=self.selected_mapset.label, element=self.copy_type.label) if not new_name: return if map_exists(new_name, element=self.copy_type.label, env=env, mapset=self.selected_mapset.label): GMessage(_("Failed to copy map: new map has the same name"), parent=self) return if not self.selected_type: found = self._model.SearchNodes(parent=self.selected_mapset, type='element', name=self.copy_type.label) self.selected_type = found[0] if found else None overwrite = False if self.selected_type: found = self._model.SearchNodes(parent=self.selected_type, type=self.copy_type.label, name=new_name) if found and found[0]: dlg = wx.MessageDialog( parent=self, message=_("Map <{map}> already exists " "in the current mapset. " "Do you want to overwrite it?").format( map=new_name), caption=_("Overwrite?"), style=wx.YES_NO | wx.YES_DEFAULT | wx.ICON_QUESTION) ret = dlg.ShowModal() dlg.Destroy() if ret == wx.ID_YES: overwrite = True string = self.copy_layer.label + '@' + self.copy_mapset.label + ',' + new_name pasted = 0 label = _("Copying <{name}>...").format(name=string) self.showNotification.emit(message=label) if self.copy_type.label == 'vector': pasted, cmd = self._runCommand('g.copy', vector=string, overwrite=overwrite, env=env) node = 'vector' elif self.copy_type.label == 'raster': pasted, cmd = self._runCommand('g.copy', raster=string, overwrite=overwrite, env=env) node = 'raster' else: pasted, cmd = self._runCommand('g.copy', raster_3d=string, overwrite=overwrite, env=env) node = 'raster_3d' if pasted == 0: self.InsertLayer(name=new_name, mapset_node=self.selected_mapset, element_name=node) Debug.msg(1, "COPIED TO: " + new_name) self.showNotification.emit( message=_("g.copy completed").format(cmd=cmd)) gscript.try_remove(gisrc) else: GError(_( "Failed to copy map: action is allowed only within the same location." ), parent=self) # expand selected mapset self.ExpandNode(self.selected_mapset, recursive=True) def InsertLayer(self, name, mapset_node, element_name): """Insert layer into model and refresh tree""" found_element = self._model.SearchNodes(parent=mapset_node, type='element', name=element_name) found_element = found_element[0] if found_element else None if not found_element: # add type node if not exists found_element = self._model.AppendNode(parent=mapset_node, label=element_name, data=dict( type='element', name=element_name)) found = self._model.SearchNodes(parent=found_element, name=name) if len(found) == 0: self._model.AppendNode(parent=found_element, label=name, data=dict(type=element_name, name=name)) self._model.SortChildren(found_element) self.RefreshNode(mapset_node, recursive=True) def OnDeleteMap(self, event): """Delete layer or mapset""" name = self.selected_layer.label gisrc, env = getEnvironment(gisenv()['GISDBASE'], self.selected_location.label, self.selected_mapset.label) if self._confirmDialog(question=_( "Do you really want to delete map <{m}> of type <{etype}> from mapset " "<{mapset}> in location <{loc}>?").format( m=name, mapset=self.selected_mapset.label, etype=self.selected_type.label, loc=self.selected_location.label), title=_('Delete map')) == wx.ID_YES: label = _("Deleting {name}...").format(name=name) self.showNotification.emit(message=label) if self.selected_type.label == 'vector': removed, cmd = self._runCommand('g.remove', flags='f', type='vector', name=name, env=env) elif self.selected_type.label == 'raster': removed, cmd = self._runCommand('g.remove', flags='f', type='raster', name=name, env=env) else: removed, cmd = self._runCommand('g.remove', flags='f', type='raster_3d', name=name, env=env) if removed == 0: self._model.RemoveNode(self.selected_layer) self.RefreshNode(self.selected_type, recursive=True) Debug.msg(1, "LAYER " + name + " DELETED") self.showNotification.emit( message=_("g.remove completed").format(cmd=cmd)) gscript.try_remove(gisrc) def OnDisplayLayer(self, event): """Display layer in current graphics view""" layerName = [] if self.selected_location.label == gisenv( )['LOCATION_NAME'] and self.selected_mapset: string = self.selected_layer.label + '@' + self.selected_mapset.label layerName.append(string) label = _("Displaying {name}...").format(name=string) self.showNotification.emit(message=label) label = "d." + self.selected_type.label[:4] + " --q map=" + string + \ _(" -- completed. Go to Layers tab for further operations.") if self.selected_type.label == 'vector': self._giface.lmgr.AddMaps(layerName, 'vector', True) elif self.selected_type.label == 'raster': self._giface.lmgr.AddMaps(layerName, 'raster', True) else: self._giface.lmgr.AddMaps(layerName, 'raster_3d', True) # generate this message (command) automatically? label = "d.rast --q map=" + string + _( " -- completed. Go to Layers tab for further operations.") self.showNotification.emit(message=label) Debug.msg(1, "LAYER " + self.selected_layer.label + " DISPLAYED") else: GError(_( "Failed to display layer: not in current mapset or invalid layer" ), parent=self) def OnBeginDrag(self, node, event): """Just copy necessary data""" self.DefineItems(node) if self.selected_layer and not ( self._restricted and gisenv()['LOCATION_NAME'] != self.selected_location.label): event.Allow() self.OnCopyMap(event) Debug.msg(1, "DRAG") else: event.Veto() def OnEndDrag(self, node, event): """Copy layer into target""" if not wx.GetMouseState().ControlDown(): GMessage(_("Moving maps not implemented"), parent=self) event.Veto() return if node: self.DefineItems(node) if self._restricted and gisenv( )['MAPSET'] != self.selected_mapset.label: GMessage(_("Maps can be copied only to current mapset"), parent=self) event.Veto() return if self.selected_location == self.copy_location and self.selected_mapset: event.Allow() self.OnPasteMap(event) Debug.msg(1, "DROP DONE") else: event.Veto() def OnSwitchLocationMapset(self, event): genv = gisenv() if self.selected_location.label == genv['LOCATION_NAME']: self.changeMapset.emit(mapset=self.selected_mapset.label) else: self.changeLocation.emit(mapset=self.selected_mapset.label, location=self.selected_location.label) self.ExpandCurrentMapset() def Filter(self, text): """Filter tree based on name and type.""" text = text.strip() if len(text.split(':')) > 1: name = text.split(':')[1].strip() elem = text.split(':')[0].strip() if 'r' == elem: element = 'raster' elif 'r3' == elem: element = 'raster_3d' elif 'v' == elem: element = 'vector' else: element = None else: element = None name = text.strip() self._model = filterModel(self._orig_model, name=name, element=element) self.RefreshItems() self.ExpandCurrentMapset() def _getNewMapName(self, message, title, value, element, mapset, env): """Dialog for simple text entry""" dlg = NameEntryDialog(parent=self, message=message, caption=title, element=element, env=env, mapset=mapset) dlg.SetValue(value) if dlg.ShowModal() == wx.ID_OK: name = dlg.GetValue() else: name = None dlg.Destroy() return name def _confirmDialog(self, question, title): """Confirm dialog""" dlg = wx.MessageDialog(self, question, title, wx.YES_NO) res = dlg.ShowModal() dlg.Destroy() return res def _popupMenuLayer(self): """Create popup menu for layers""" menu = wx.Menu() genv = gisenv() if self._restricted: currentMapset = currentLocation = False if self.selected_location.label == genv['LOCATION_NAME']: currentLocation = True if self.selected_mapset.label == genv['MAPSET']: currentMapset = True else: currentMapset = currentLocation = True item = wx.MenuItem(menu, wx.NewId(), _("&Copy")) menu.AppendItem(item) self.Bind(wx.EVT_MENU, self.OnCopyMap, item) item.Enable(currentLocation) item = wx.MenuItem(menu, wx.NewId(), _("&Paste")) menu.AppendItem(item) self.Bind(wx.EVT_MENU, self.OnPasteMap, item) if not (currentLocation and self.copy_layer and self.selected_location == self.copy_location): item.Enable(False) item = wx.MenuItem(menu, wx.NewId(), _("&Delete")) menu.AppendItem(item) self.Bind(wx.EVT_MENU, self.OnDeleteMap, item) item.Enable(currentMapset) item = wx.MenuItem(menu, wx.NewId(), _("&Rename")) menu.AppendItem(item) self.Bind(wx.EVT_MENU, self.OnRenameMap, item) item.Enable(currentMapset) if not isinstance(self._giface, StandaloneGrassInterface) and \ self.selected_location.label == genv['LOCATION_NAME']: menu.AppendSeparator() item = wx.MenuItem(menu, wx.NewId(), _("&Display layer")) menu.AppendItem(item) self.Bind(wx.EVT_MENU, self.OnDisplayLayer, item) self.PopupMenu(menu) menu.Destroy() def _popupMenuMapset(self): """Create popup menu for mapsets""" menu = wx.Menu() genv = gisenv() item = wx.MenuItem(menu, wx.NewId(), _("&Paste")) menu.AppendItem(item) self.Bind(wx.EVT_MENU, self.OnPasteMap, item) if not (self.copy_layer and self.selected_location == self.copy_location): item.Enable(False) item = wx.MenuItem(menu, wx.NewId(), _("&Switch mapset")) menu.AppendItem(item) self.Bind(wx.EVT_MENU, self.OnSwitchLocationMapset, item) if (self.selected_location.label == genv['LOCATION_NAME'] and self.selected_mapset.label == genv['MAPSET']): item.Enable(False) self.PopupMenu(menu) menu.Destroy() def _popupMenuElement(self): """Create popup menu for elements""" menu = wx.Menu() item = wx.MenuItem(menu, wx.NewId(), _("&Paste")) menu.AppendItem(item) self.Bind(wx.EVT_MENU, self.OnPasteMap, item) if not (self.copy_layer and self.selected_location == self.copy_location): item.Enable(False) self.PopupMenu(menu) menu.Destroy()
class VDigitToolbar(BaseToolbar): """Toolbar for digitization """ def __init__(self, parent, toolSwitcher, MapWindow, digitClass, giface, tools=[]): self.MapWindow = MapWindow self.Map = MapWindow.GetMap() # Map class instance self.tools = tools self.digitClass = digitClass BaseToolbar.__init__(self, parent, toolSwitcher) self.digit = None self._giface = giface self.fType = None # feature type for simple features editing self.editingStarted = Signal("VDigitToolbar.editingStarted") self.editingStopped = Signal("VDigitToolbar.editingStopped") self.editingBgMap = Signal("VDigitToolbar.editingBgMap") layerTree = self._giface.GetLayerTree() if layerTree: self.editingStarted.connect(layerTree.StartEditing) self.editingStopped.connect(layerTree.StopEditing) self.editingBgMap.connect(layerTree.SetBgMapForEditing) # currently selected map layer for editing (reference to MapLayer # instance) self.mapLayer = None # list of vector layers from Layer Manager (only in the current mapset) self.layers = [] self.comboid = self.combo = None self.undo = -1 self.redo = -1 # only one dialog can be open self.settingsDialog = None # create toolbars (two rows optionally) self.InitToolbar(self._toolbarData()) self._default = -1 # default action (digitize new point, line, etc.) self.action = {'desc': '', 'type': '', 'id': -1} self._currentAreaActionType = None # list of available vector maps self.UpdateListOfLayers(updateTool=True) for tool in ( 'addPoint', 'addLine', 'addBoundary', 'addCentroid', 'addArea', 'addVertex', 'deleteLine', 'deleteArea', 'displayAttr', 'displayCats', 'editLine', 'moveLine', 'moveVertex', 'removeVertex', 'additionalTools'): if hasattr(self, tool): tool = getattr(self, tool) self.toolSwitcher.AddToolToGroup( group='mouseUse', toolbar=self, tool=tool) else: Debug.msg(1, '%s skipped' % tool) # custom button for digitization of area/boundary/centroid # TODO: could this be somehow generalized? nAreaTools = 0 if self.tools and 'addBoundary' in self.tools: nAreaTools += 1 if self.tools and 'addCentroid' in self.tools: nAreaTools += 1 if self.tools and 'addArea' in self.tools: nAreaTools += 1 if nAreaTools != 1: self.areaButton = self.CreateSelectionButton( _("Select area/boundary/centroid tool")) self.areaButtonId = self.InsertControl(5, self.areaButton) self.areaButton.Bind(wx.EVT_BUTTON, self.OnAddAreaMenu) # realize toolbar self.Realize() # workaround for Mac bug. May be fixed by 2.8.8, but not before then. if self.combo: self.combo.Hide() self.combo.Show() # disable undo/redo if self.undo > 0: self.EnableTool(self.undo, False) if self.redo > 0: self.EnableTool(self.redo, False) self.FixSize(width=105) def _toolbarData(self): """Toolbar data """ data = [] self.icons = { 'addPoint': MetaIcon(img='point-create', label=_('Digitize new point'), desc=_('Left: new point')), 'addLine': MetaIcon(img='line-create', label=_('Digitize new line'), desc=_('Left: new point; Ctrl+Left: undo last point; Right: close line')), 'addBoundary': MetaIcon(img='boundary-create', label=_('Digitize new boundary'), desc=_('Left: new point; Ctrl+Left: undo last point; Right: close line')), 'addCentroid': MetaIcon(img='centroid-create', label=_('Digitize new centroid'), desc=_('Left: new point')), 'addArea': MetaIcon(img='polygon-create', label=_('Digitize new area (boundary without category)'), desc=_('Left: new point')), 'addVertex': MetaIcon(img='vertex-create', label=_('Add new vertex to line or boundary'), desc=_('Left: Select; Ctrl+Left: Unselect; Right: Confirm')), 'deleteLine': MetaIcon(img='line-delete', label=_('Delete selected point(s), line(s), boundary(ies) or centroid(s)'), desc=_('Left: Select; Ctrl+Left: Unselect; Right: Confirm')), 'deleteArea': MetaIcon(img='polygon-delete', label=_('Delete selected area(s)'), desc=_('Left: Select; Ctrl+Left: Unselect; Right: Confirm')), 'displayAttr': MetaIcon(img='attributes-display', label=_('Display/update attributes'), desc=_('Left: Select')), 'displayCats': MetaIcon(img='cats-display', label=_('Display/update categories'), desc=_('Left: Select')), 'editLine': MetaIcon(img='line-edit', label=_('Edit selected line/boundary'), desc=_('Left: new point; Ctrl+Left: undo last point; Right: close line')), 'moveLine': MetaIcon(img='line-move', label=_('Move selected point(s), line(s), boundary(ies) or centroid(s)'), desc=_('Left: Select; Ctrl+Left: Unselect; Right: Confirm')), 'moveVertex': MetaIcon(img='vertex-move', label=_('Move selected vertex'), desc=_('Left: Select; Ctrl+Left: Unselect; Right: Confirm')), 'removeVertex': MetaIcon(img='vertex-delete', label=_('Remove selected vertex'), desc=_('Left: Select; Ctrl+Left: Unselect; Right: Confirm')), 'settings': BaseIcons['settings'].SetLabel(_('Digitization settings')), 'quit': BaseIcons['quit'].SetLabel(label=_('Quit digitizer'), desc=_('Quit digitizer and save changes')), 'help': BaseIcons['help'].SetLabel(label=_('Vector Digitizer manual'), desc=_('Show Vector Digitizer manual')), 'additionalTools': MetaIcon(img='tools', label=_('Additional tools ' '(copy, flip, connect, etc.)'), desc=_('Left: Select; Ctrl+Left: Unselect; Right: Confirm')), 'undo': MetaIcon(img='undo', label=_('Undo'), desc=_('Undo previous changes')), 'redo': MetaIcon(img='redo', label=_('Redo'), desc=_('Redo previous changes')), } if not self.tools or 'selector' in self.tools: data.append((None, )) if not self.tools or 'addPoint' in self.tools: data.append(("addPoint", self.icons["addPoint"], self.OnAddPoint, wx.ITEM_CHECK)) if not self.tools or 'addLine' in self.tools: data.append(("addLine", self.icons["addLine"], self.OnAddLine, wx.ITEM_CHECK)) if not self.tools or 'addArea' in self.tools: data.append(("addArea", self.icons["addArea"], self.OnAddAreaTool, wx.ITEM_CHECK)) if not self.tools or 'deleteLine' in self.tools: data.append(("deleteLine", self.icons["deleteLine"], self.OnDeleteLine, wx.ITEM_CHECK)) if not self.tools or 'deleteArea' in self.tools: data.append(("deleteArea", self.icons["deleteArea"], self.OnDeleteArea, wx.ITEM_CHECK)) if not self.tools or 'moveVertex' in self.tools: data.append(("moveVertex", self.icons["moveVertex"], self.OnMoveVertex, wx.ITEM_CHECK)) if not self.tools or 'addVertex' in self.tools: data.append(("addVertex", self.icons["addVertex"], self.OnAddVertex, wx.ITEM_CHECK)) if not self.tools or 'removeVertex' in self.tools: data.append(("removeVertex", self.icons["removeVertex"], self.OnRemoveVertex, wx.ITEM_CHECK)) if not self.tools or 'editLine' in self.tools: data.append(("editLine", self.icons["editLine"], self.OnEditLine, wx.ITEM_CHECK)) if not self.tools or 'moveLine' in self.tools: data.append(("moveLine", self.icons["moveLine"], self.OnMoveLine, wx.ITEM_CHECK)) if not self.tools or 'displayCats' in self.tools: data.append(("displayCats", self.icons["displayCats"], self.OnDisplayCats, wx.ITEM_CHECK)) if not self.tools or 'displayAttr' in self.tools: data.append(("displayAttr", self.icons["displayAttr"], self.OnDisplayAttr, wx.ITEM_CHECK)) if not self.tools or 'additionalSelf.Tools' in self.tools: data.append(("additionalTools", self.icons["additionalTools"], self.OnAdditionalToolMenu, wx.ITEM_CHECK)) if not self.tools or 'undo' in self.tools or \ 'redo' in self.tools: data.append((None, )) if not self.tools or 'undo' in self.tools: data.append(("undo", self.icons["undo"], self.OnUndo)) if not self.tools or 'redo' in self.tools: data.append(("redo", self.icons["redo"], self.OnRedo)) if not self.tools or 'settings' in self.tools or \ 'help' in self.tools or \ 'quit' in self.tools: data.append((None, )) if not self.tools or 'settings' in self.tools: data.append(("settings", self.icons["settings"], self.OnSettings)) if not self.tools or 'help' in self.tools: data.append(("help", self.icons["help"], self.OnHelp)) if not self.tools or 'quit' in self.tools: data.append(("quit", self.icons["quit"], self.OnExit)) return self._getToolbarData(data) def OnTool(self, event): """Tool selected -> untoggles previusly selected tool in toolbar""" Debug.msg(3, "VDigitToolbar.OnTool(): id = %s" % event.GetId()) # set cursor self.MapWindow.SetNamedCursor('cross') self.MapWindow.mouse['box'] = 'point' self.MapWindow.mouse['use'] = 'pointer' aId = self.action.get('id', -1) BaseToolbar.OnTool(self, event) # clear tmp canvas if self.action['id'] != aId or aId == -1: self.MapWindow.polycoords = [] self.MapWindow.ClearLines(pdc=self.MapWindow.pdcTmp) if self.digit and \ len(self.MapWindow.digit.GetDisplay().GetSelected()) > 0: # cancel action self.MapWindow.OnMiddleDown(None) # set no action if self.action['id'] == -1: self.action = {'desc': '', 'type': '', 'id': -1} # set focus self.MapWindow.SetFocus() def OnAddPoint(self, event): """Add point to the vector map Laier""" Debug.msg(2, "VDigitToolbar.OnAddPoint()") self.action = {'desc': "addLine", 'type': "point", 'id': self.addPoint} self.MapWindow.mouse['box'] = 'point' def OnAddLine(self, event): """Add line to the vector map layer""" Debug.msg(2, "VDigitToolbar.OnAddLine()") self.action = {'desc': "addLine", 'type': "line", 'id': self.addLine} self.MapWindow.mouse['box'] = 'line' # self.MapWindow.polycoords = [] # reset temp line def OnAddBoundary(self, event): """Add boundary to the vector map layer""" Debug.msg(2, "VDigitToolbar.OnAddBoundary()") self._toggleAreaIfNeeded() # reset temp line if self.action['desc'] != 'addLine' or \ self.action['type'] != 'boundary': self.MapWindow.polycoords = [] # update icon and tooltip self.SetToolNormalBitmap(self.addArea, self.icons[ 'addBoundary'].GetBitmap()) self.SetToolShortHelp(self.addArea, self.icons['addBoundary'].GetLabel()) # set action self.action = {'desc': "addLine", 'type': "boundary", 'id': self.addArea} self.MapWindow.mouse['box'] = 'line' self._currentAreaActionType = 'boundary' def OnAddCentroid(self, event): """Add centroid to the vector map layer""" Debug.msg(2, "VDigitToolbar.OnAddCentroid()") self._toggleAreaIfNeeded() # update icon and tooltip self.SetToolNormalBitmap(self.addArea, self.icons[ 'addCentroid'].GetBitmap()) self.SetToolShortHelp(self.addArea, self.icons['addCentroid'].GetLabel()) # set action self.action = {'desc': "addLine", 'type': "centroid", 'id': self.addArea} self.MapWindow.mouse['box'] = 'point' self._currentAreaActionType = 'centroid' def OnAddArea(self, event): """Add area to the vector map layer""" Debug.msg(2, "VDigitToolbar.OnAddArea()") self._toggleAreaIfNeeded() # update icon and tooltip self.SetToolNormalBitmap( self.addArea, self.icons['addArea'].GetBitmap()) self.SetToolShortHelp(self.addArea, self.icons['addArea'].GetLabel()) # set action self.action = {'desc': "addLine", 'type': "area", 'id': self.addArea} self.MapWindow.mouse['box'] = 'line' self._currentAreaActionType = 'area' def _toggleAreaIfNeeded(self): """In some cases, the area tool is not toggled, we have to do it manually.""" if not self.GetToolState(self.addArea): self.ToggleTool(self.addArea, True) self.toolSwitcher.ToolChanged(self.addArea) def OnAddAreaTool(self, event): """Area tool activated.""" Debug.msg(2, "VDigitToolbar.OnAddAreaTool()") # we need the previous id if not self._currentAreaActionType or self._currentAreaActionType == 'area': # default action self.OnAddArea(event) elif self._currentAreaActionType == 'boundary': self.OnAddBoundary(event) elif self._currentAreaActionType == 'centroid': self.OnAddCentroid(event) def OnAddAreaMenu(self, event): """Digitize area menu (add area/boundary/centroid)""" menuItems = [] if not self.tools or 'addArea' in self.tools: menuItems.append((self.icons["addArea"], self.OnAddArea)) if not self.fType and not self.tools or 'addBoundary' in self.tools: menuItems.append((self.icons["addBoundary"], self.OnAddBoundary)) if not self.fType and not self.tools or 'addCentroid' in self.tools: menuItems.append((self.icons["addCentroid"], self.OnAddCentroid)) self._onMenu(menuItems) def OnExit(self, event=None): """Quit digitization tool""" # stop editing of the currently selected map layer if self.mapLayer: self.StopEditing() # close dialogs if still open if self.settingsDialog: self.settingsDialog.OnCancel(None) # set default mouse settings self.parent.GetMapToolbar().SelectDefault() self.MapWindow.polycoords = [] # TODO: replace this by binding wx event in parent (or use signals...) if not self.parent.IsStandalone(): # disable the toolbar self.parent.RemoveToolbar("vdigit") else: self.parent.Close() def OnMoveVertex(self, event): """Move line vertex""" Debug.msg(2, "Digittoolbar.OnMoveVertex():") self.action = {'desc': "moveVertex", 'id': self.moveVertex} self.MapWindow.mouse['box'] = 'point' def OnAddVertex(self, event): """Add line vertex""" Debug.msg(2, "Digittoolbar.OnAddVertex():") self.action = {'desc': "addVertex", 'id': self.addVertex} self.MapWindow.mouse['box'] = 'point' def OnRemoveVertex(self, event): """Remove line vertex""" Debug.msg(2, "Digittoolbar.OnRemoveVertex():") self.action = {'desc': "removeVertex", 'id': self.removeVertex} self.MapWindow.mouse['box'] = 'point' def OnEditLine(self, event): """Edit line""" Debug.msg(2, "Digittoolbar.OnEditLine():") self.action = {'desc': "editLine", 'id': self.editLine} self.MapWindow.mouse['box'] = 'line' def OnMoveLine(self, event): """Move line""" Debug.msg(2, "Digittoolbar.OnMoveLine():") self.action = {'desc': "moveLine", 'id': self.moveLine} self.MapWindow.mouse['box'] = 'box' def OnDeleteLine(self, event): """Delete line""" Debug.msg(2, "Digittoolbar.OnDeleteLine():") self.action = {'desc': "deleteLine", 'id': self.deleteLine} self.MapWindow.mouse['box'] = 'box' def OnDeleteArea(self, event): """Delete Area""" Debug.msg(2, "Digittoolbar.OnDeleteArea():") self.action = {'desc': "deleteArea", 'id': self.deleteArea} self.MapWindow.mouse['box'] = 'box' def OnDisplayCats(self, event): """Display/update categories""" Debug.msg(2, "Digittoolbar.OnDisplayCats():") self.action = {'desc': "displayCats", 'id': self.displayCats} self.MapWindow.mouse['box'] = 'point' def OnDisplayAttr(self, event): """Display/update attributes""" Debug.msg(2, "Digittoolbar.OnDisplayAttr():") self.action = {'desc': "displayAttrs", 'id': self.displayAttr} self.MapWindow.mouse['box'] = 'point' def OnUndo(self, event): """Undo previous changes""" self.digit.Undo() event.Skip() def OnRedo(self, event): """Undo previous changes""" self.digit.Undo(level=1) event.Skip() def EnableUndo(self, enable=True): """Enable 'Undo' in toolbar :param enable: False for disable """ self._enableTool(self.undo, enable) def EnableRedo(self, enable=True): """Enable 'Redo' in toolbar :param enable: False for disable """ self._enableTool(self.redo, enable) def _enableTool(self, tool, enable): if not self.FindById(tool): return if enable: if self.GetToolEnabled(tool) is False: self.EnableTool(tool, True) else: if self.GetToolEnabled(tool) is True: self.EnableTool(tool, False) def GetAction(self, type='desc'): """Get current action info""" return self.action.get(type, '') def OnSettings(self, event): """Show settings dialog""" if self.digit is None: try: self.digit = self.MapWindow.digit = self.digitClass( mapwindow=self.MapWindow) except SystemExit: self.digit = self.MapWindow.digit = None if not self.settingsDialog: self.settingsDialog = VDigitSettingsDialog( parent=self.parent, giface=self._giface) self.settingsDialog.Show() def OnHelp(self, event): """Show digitizer help page in web browser""" self._giface.Help('wxGUI.vdigit') def OnAdditionalToolMenu(self, event): """Menu for additional tools""" point = wx.GetMousePosition() toolMenu = wx.Menu() for label, itype, handler, desc in ( (_('Break selected lines/boundaries at intersection'), wx.ITEM_CHECK, self.OnBreak, "breakLine"), (_('Connect selected lines/boundaries'), wx.ITEM_CHECK, self.OnConnect, "connectLine"), (_('Copy categories'), wx.ITEM_CHECK, self.OnCopyCats, "copyCats"), (_('Copy features from (background) vector map'), wx.ITEM_CHECK, self.OnCopy, "copyLine"), (_('Copy attributes'), wx.ITEM_CHECK, self.OnCopyAttrb, "copyAttrs"), (_('Feature type conversion'), wx.ITEM_CHECK, self.OnTypeConversion, "typeConv"), (_('Flip selected lines/boundaries'), wx.ITEM_CHECK, self.OnFlip, "flipLine"), (_('Merge selected lines/boundaries'), wx.ITEM_CHECK, self.OnMerge, "mergeLine"), (_('Snap selected lines/boundaries (only to nodes)'), wx.ITEM_CHECK, self.OnSnap, "snapLine"), (_('Split line/boundary'), wx.ITEM_CHECK, self.OnSplitLine, "splitLine"), (_('Query features'), wx.ITEM_CHECK, self.OnQuery, "queryLine"), (_('Z bulk-labeling of 3D lines'), wx.ITEM_CHECK, self.OnZBulk, "zbulkLine")): # Add items to the menu item = wx.MenuItem(parentMenu=toolMenu, id=wx.ID_ANY, text=label, kind=itype) toolMenu.AppendItem(item) self.MapWindow.Bind(wx.EVT_MENU, handler, item) if self.action['desc'] == desc: item.Check(True) # Popup the menu. If an item is selected then its handler # will be called before PopupMenu returns. self.MapWindow.PopupMenu(toolMenu) toolMenu.Destroy() if self.action['desc'] == 'addPoint': self.ToggleTool(self.additionalTools, False) def OnCopy(self, event): """Copy selected features from (background) vector map""" if not self.digit: GError(_("No vector map open for editing."), self.parent) return # select background map dlg = VectorDialog(self.parent, title=_("Select background vector map"), layerTree=self._giface.GetLayerTree()) if dlg.ShowModal() != wx.ID_OK: dlg.Destroy() return mapName = dlg.GetName(full=True) dlg.Destroy() # close open background map if any bgMap = UserSettings.Get(group='vdigit', key='bgmap', subkey='value', settings_type='internal') if bgMap: self.digit.CloseBackgroundMap() self.editingBgMap.emit(mapName=bgMap, unset=True) # open background map for reading UserSettings.Set(group='vdigit', key='bgmap', subkey='value', value=str(mapName), settings_type='internal') self.digit.OpenBackgroundMap(mapName) self.editingBgMap.emit(mapName=mapName) if self.action['desc'] == 'copyLine': # select previous action self.ToggleTool(self.addPoint, True) self.ToggleTool(self.additionalTools, False) self.OnAddPoint(event) return Debug.msg(2, "Digittoolbar.OnCopy():") self.action = {'desc': "copyLine", 'id': self.additionalTools} self.MapWindow.mouse['box'] = 'box' def OnSplitLine(self, event): """Split line""" if self.action['desc'] == 'splitLine': # select previous action self.ToggleTool(self.addPoint, True) self.ToggleTool(self.additionalTools, False) self.OnAddPoint(event) return Debug.msg(2, "Digittoolbar.OnSplitLine():") self.action = {'desc': "splitLine", 'id': self.additionalTools} self.MapWindow.mouse['box'] = 'point' def OnCopyCats(self, event): """Copy categories""" if self.action['desc'] == 'copyCats': # select previous action self.ToggleTool(self.addPoint, True) self.ToggleTool(self.copyCats, False) self.OnAddPoint(event) return Debug.msg(2, "Digittoolbar.OnCopyCats():") self.action = {'desc': "copyCats", 'id': self.additionalTools} self.MapWindow.mouse['box'] = 'point' def OnCopyAttrb(self, event): """Copy attributes""" if self.action['desc'] == 'copyAttrs': # select previous action self.ToggleTool(self.addPoint, True) self.ToggleTool(self.copyCats, False) self.OnAddPoint(event) return Debug.msg(2, "Digittoolbar.OnCopyAttrb():") self.action = {'desc': "copyAttrs", 'id': self.additionalTools} self.MapWindow.mouse['box'] = 'point' def OnFlip(self, event): """Flip selected lines/boundaries""" if self.action['desc'] == 'flipLine': # select previous action self.ToggleTool(self.addPoint, True) self.ToggleTool(self.additionalTools, False) self.OnAddPoint(event) return Debug.msg(2, "Digittoolbar.OnFlip():") self.action = {'desc': "flipLine", 'id': self.additionalTools} self.MapWindow.mouse['box'] = 'box' def OnMerge(self, event): """Merge selected lines/boundaries""" if self.action['desc'] == 'mergeLine': # select previous action self.ToggleTool(self.addPoint, True) self.ToggleTool(self.additionalTools, False) self.OnAddPoint(event) return Debug.msg(2, "Digittoolbar.OnMerge():") self.action = {'desc': "mergeLine", 'id': self.additionalTools} self.MapWindow.mouse['box'] = 'box' def OnBreak(self, event): """Break selected lines/boundaries""" if self.action['desc'] == 'breakLine': # select previous action self.ToggleTool(self.addPoint, True) self.ToggleTool(self.additionalTools, False) self.OnAddPoint(event) return Debug.msg(2, "Digittoolbar.OnBreak():") self.action = {'desc': "breakLine", 'id': self.additionalTools} self.MapWindow.mouse['box'] = 'box' def OnSnap(self, event): """Snap selected features""" if self.action['desc'] == 'snapLine': # select previous action self.ToggleTool(self.addPoint, True) self.ToggleTool(self.additionalTools, False) self.OnAddPoint(event) return Debug.msg(2, "Digittoolbar.OnSnap():") self.action = {'desc': "snapLine", 'id': self.additionalTools} self.MapWindow.mouse['box'] = 'box' def OnConnect(self, event): """Connect selected lines/boundaries""" if self.action['desc'] == 'connectLine': # select previous action self.ToggleTool(self.addPoint, True) self.ToggleTool(self.additionalTools, False) self.OnAddPoint(event) return Debug.msg(2, "Digittoolbar.OnConnect():") self.action = {'desc': "connectLine", 'id': self.additionalTools} self.MapWindow.mouse['box'] = 'box' def OnQuery(self, event): """Query selected lines/boundaries""" if self.action['desc'] == 'queryLine': # select previous action self.ToggleTool(self.addPoint, True) self.ToggleTool(self.additionalTools, False) self.OnAddPoint(event) return Debug.msg( 2, "Digittoolbar.OnQuery(): %s" % UserSettings.Get( group='vdigit', key='query', subkey='selection')) self.action = {'desc': "queryLine", 'id': self.additionalTools} self.MapWindow.mouse['box'] = 'box' def OnZBulk(self, event): """Z bulk-labeling selected lines/boundaries""" if not self.digit.IsVector3D(): GError(parent=self.parent, message=_("Vector map is not 3D. Operation canceled.")) return if self.action['desc'] == 'zbulkLine': # select previous action self.ToggleTool(self.addPoint, True) self.ToggleTool(self.additionalTools, False) self.OnAddPoint(event) return Debug.msg(2, "Digittoolbar.OnZBulk():") self.action = {'desc': "zbulkLine", 'id': self.additionalTools} self.MapWindow.mouse['box'] = 'line' def OnTypeConversion(self, event): """Feature type conversion Supported conversions: - point <-> centroid - line <-> boundary """ if self.action['desc'] == 'typeConv': # select previous action self.ToggleTool(self.addPoint, True) self.ToggleTool(self.additionalTools, False) self.OnAddPoint(event) return Debug.msg(2, "Digittoolbar.OnTypeConversion():") self.action = {'desc': "typeConv", 'id': self.additionalTools} self.MapWindow.mouse['box'] = 'box' def OnSelectMap(self, event): """Select vector map layer for editing If there is a vector map layer already edited, this action is firstly terminated. The map layer is closed. After this the selected map layer activated for editing. """ if event.GetSelection() == 0: # create new vector map layer if self.mapLayer: openVectorMap = self.mapLayer.GetName( fullyQualified=False)['name'] else: openVectorMap = None dlg = CreateNewVector(self.parent, exceptMap=openVectorMap, giface=self._giface, cmd=(('v.edit', {'tool': 'create'}, 'map')), disableAdd=True) if dlg and dlg.GetName(): # add layer to map layer tree if self._giface.GetLayerTree(): mapName = dlg.GetName() + '@' + grass.gisenv()['MAPSET'] self._giface.GetLayerList().AddLayer( ltype='vector', name=mapName, checked=True, cmd=['d.vect', 'map=%s' % mapName]) vectLayers = self.UpdateListOfLayers(updateTool=True) selection = vectLayers.index(mapName) # create table ? if dlg.IsChecked('table'): # TODO: replace this by signal # also note that starting of tools such as atm, iclass, # plots etc. should be handled in some better way # than starting randomly from mapdisp and lmgr lmgr = self.parent.GetLayerManager() if lmgr: lmgr.OnShowAttributeTable(None, selection='table') dlg.Destroy() else: self.combo.SetValue(_('Select vector map')) if dlg: dlg.Destroy() return else: selection = event.GetSelection() - 1 # first option is 'New vector map' # skip currently selected map if self.layers[selection] == self.mapLayer: return if self.mapLayer: # deactive map layer for editing self.StopEditing() # select the given map layer for editing self.StartEditing(self.layers[selection]) event.Skip() def StartEditing(self, mapLayer): """Start editing selected vector map layer. :param mapLayer: MapLayer to be edited """ # check if topology is available (skip for hidden - temporary # maps, see iclass for details) if not mapLayer.IsHidden() and grass.vector_info( mapLayer.GetName())['level'] != 2: dlg = wx.MessageDialog( parent=self.MapWindow, message=_( "Topology for vector map <%s> is not available. " "Topology is required by digitizer.\nDo you want to " "rebuild topology (takes some time) and open the vector map " "for editing?") % mapLayer.GetName(), caption=_("Digitizer error"), style=wx.YES_NO | wx.YES_DEFAULT | wx.ICON_QUESTION | wx.CENTRE) if dlg.ShowModal() == wx.ID_YES: RunCommand('v.build', map=mapLayer.GetName()) else: return # deactive layer self.Map.ChangeLayerActive(mapLayer, False) # clean map canvas self.MapWindow.EraseMap() # unset background map if needed if mapLayer: if UserSettings.Get( group='vdigit', key='bgmap', subkey='value', settings_type='internal') == mapLayer.GetName(): UserSettings.Set( group='vdigit', key='bgmap', subkey='value', value='', settings_type='internal') self.parent.SetStatusText(_("Please wait, " "opening vector map <%s> for editing...") % mapLayer.GetName(), 0) self.MapWindow.pdcVector = wx.PseudoDC() self.digit = self.MapWindow.digit = self.digitClass( mapwindow=self.MapWindow) self.mapLayer = mapLayer # open vector map (assume that 'hidden' map layer is temporary vector # map) if self.digit.OpenMap( mapLayer.GetName(), tmp=mapLayer.IsHidden()) is None: self.mapLayer = None self.StopEditing() return False # check feature type (only for OGR layers) self.fType = self.digit.GetFeatureType() self.EnableAll() self.EnableUndo(False) self.EnableRedo(False) if self.fType == 'point': for tool in (self.addLine, self.addArea, self.moveVertex, self.addVertex, self.removeVertex, self.editLine): self.EnableTool(tool, False) elif self.fType == 'linestring': for tool in (self.addPoint, self.addArea): self.EnableTool(tool, False) elif self.fType == 'polygon': for tool in (self.addPoint, self.addLine): self.EnableTool(tool, False) elif self.fType: GError( parent=self, message=_( "Unsupported feature type '%(type)s'. Unable to edit " "OGR layer <%(layer)s>.") % {'type': self.fType, 'layer': mapLayer.GetName()}) self.digit.CloseMap() self.mapLayer = None self.StopEditing() return False # update toolbar if self.combo: self.combo.SetValue(mapLayer.GetName()) if 'map' in self.parent.toolbars: self.parent.toolbars['map'].combo.SetValue(_('Vector digitizer')) # here was dead code to enable vdigit button in toolbar # with if to ignore iclass # some signal (DigitizerStarted) can be emitted here Debug.msg( 4, "VDigitToolbar.StartEditing(): layer=%s" % mapLayer.GetName()) # change cursor if self.MapWindow.mouse['use'] == 'pointer': self.MapWindow.SetNamedCursor('cross') if not self.MapWindow.resize: self.MapWindow.UpdateMap(render=True) # respect opacity opacity = mapLayer.GetOpacity() if opacity < 1.0: alpha = int(opacity * 255) self.digit.GetDisplay().UpdateSettings(alpha=alpha) # emit signal layerTree = self._giface.GetLayerTree() if layerTree: item = layerTree.FindItemByData('maplayer', self.mapLayer) else: item = None self.editingStarted.emit( vectMap=mapLayer.GetName(), digit=self.digit, layerItem=item) return True def StopEditing(self): """Stop editing of selected vector map layer. :return: True on success :return: False on failure """ item = None if self.combo: self.combo.SetValue(_('Select vector map')) # save changes if self.mapLayer: Debug.msg( 4, "VDigitToolbar.StopEditing(): layer=%s" % self.mapLayer.GetName()) if UserSettings.Get(group='vdigit', key='saveOnExit', subkey='enabled') is False: if self.digit.GetUndoLevel() > -1: dlg = wx.MessageDialog( parent=self.parent, message=_( "Do you want to save changes " "in vector map <%s>?") % self.mapLayer.GetName(), caption=_("Save changes?"), style=wx.YES_NO | wx.YES_DEFAULT | wx.ICON_QUESTION) if dlg.ShowModal() == wx.ID_NO: # revert changes self.digit.Undo(0) dlg.Destroy() self.parent.SetStatusText(_("Please wait, " "closing and rebuilding topology of " "vector map <%s>...") % self.mapLayer.GetName(), 0) self.digit.CloseMap() # close open background map if any bgMap = UserSettings.Get( group='vdigit', key='bgmap', subkey='value', settings_type='internal') if bgMap: self.digit.CloseBackgroundMap() self.editingBgMap.emit(mapName=bgMap, unset=True) self._giface.GetProgress().SetValue(0) self._giface.WriteCmdLog( _("Editing of vector map <%s> successfully finished") % self.mapLayer.GetName(), notification=Notification.HIGHLIGHT) # re-active layer layerTree = self._giface.GetLayerTree() if layerTree: item = layerTree.FindItemByData('maplayer', self.mapLayer) if item and layerTree.IsItemChecked(item): self.Map.ChangeLayerActive(self.mapLayer, True) # change cursor self.MapWindow.SetNamedCursor('default') self.MapWindow.pdcVector = None # close dialogs for dialog in ('attributes', 'category'): if self.parent.dialogs[dialog]: self.parent.dialogs[dialog].Close() self.parent.dialogs[dialog] = None self.digit = None self.MapWindow.digit = None self.editingStopped.emit(layerItem=item) self.mapLayer = None self.MapWindow.redrawAll = True return True def UpdateListOfLayers(self, updateTool=False): """Update list of available vector map layers. This list consists only editable layers (in the current mapset) :param updateTool: True to update also toolbar :type updateTool: bool """ Debug.msg(4, "VDigitToolbar.UpdateListOfLayers(): updateTool=%d" % updateTool) layerNameSelected = None # name of currently selected layer if self.mapLayer: layerNameSelected = self.mapLayer.GetName() # select vector map layer in the current mapset layerNameList = [] self.layers = self.Map.GetListOfLayers(ltype="vector", mapset=grass.gisenv()['MAPSET']) for layer in self.layers: if not layer.name in layerNameList: # do not duplicate layer layerNameList.append(layer.GetName()) if updateTool: # update toolbar if not self.mapLayer: value = _('Select vector map') else: value = layerNameSelected if not self.comboid: if not self.tools or 'selector' in self.tools: self.combo = wx.ComboBox( self, id=wx.ID_ANY, value=value, choices=[_('New vector map'), ] + layerNameList, size=(80, -1), style=wx.CB_READONLY) self.comboid = self.InsertControl(0, self.combo) self.parent.Bind( wx.EVT_COMBOBOX, self.OnSelectMap, self.comboid) else: self.combo.SetItems([_('New vector map'), ] + layerNameList) self.Realize() return layerNameList def GetLayer(self): """Get selected layer for editing -- MapLayer instance""" return self.mapLayer
class SQLBuilderUpdate(SQLBuilder): """Class for building UPDATE SQL statement""" def __init__(self, parent, vectmap, id = wx.ID_ANY, layer = 1, column = None): self.column = column # set dialog title title = _("GRASS SQL Builder (%(type)s) - <%(map)s>") % \ { 'type' : "UPDATE", 'map' : vectmap } modeChoices = [_("Column to set (SET clause)"), _("Constraint for query (WHERE clause)"), _("Calculate column value to set")] SQLBuilder.__init__(self, parent, title, vectmap, id = wx.ID_ANY, modeChoices = modeChoices, layer = layer) # signals self.sqlApplied = Signal("SQLBuilder.sqlApplied") if parent: # TODO: replace by giface self.sqlApplied.connect(parent.Update) def _doLayout(self, modeChoices): """Do dialog layout""" SQLBuilder._doLayout(self, modeChoices) self.initText = "UPDATE %s SET" % self.tablename if self.column: self.initText += " %s = " % self.column self.text_sql.SetValue(self.initText) self.btn_arithmetic = { 'eq' : ['=', ], 'brac' : ['()',], 'plus' : ['+', ], 'minus' : ['-', ], 'divide' : ['/', ], 'multiply' : ['*', ]} self.btn_arithmeticpanel = wx.Panel(parent = self.panel, id = wx.ID_ANY) for key, value in self.btn_arithmetic.iteritems(): btn = wx.Button(parent = self.btn_arithmeticpanel, id = wx.ID_ANY, label = value[0]) self.btn_arithmetic[key].append(btn.GetId()) btn_arithmeticsizer = wx.GridBagSizer(hgap = 5, vgap = 5) btn_arithmeticsizer.Add(item = self.FindWindowById(self.btn_arithmetic['eq'][1]), pos = (0, 0)) btn_arithmeticsizer.Add(item = self.FindWindowById(self.btn_arithmetic['brac'][1]), pos = (1, 0)) btn_arithmeticsizer.Add(item = self.FindWindowById(self.btn_arithmetic['plus'][1]), pos = (0, 1)) btn_arithmeticsizer.Add(item = self.FindWindowById(self.btn_arithmetic['minus'][1]), pos = (1, 1)) btn_arithmeticsizer.Add(item = self.FindWindowById(self.btn_arithmetic['divide'][1]), pos = (0, 2)) btn_arithmeticsizer.Add(item = self.FindWindowById(self.btn_arithmetic['multiply'][1]), pos = (1, 2)) self.btn_arithmeticpanel.SetSizer(btn_arithmeticsizer) self.pagesizer.Insert(item = self.btn_arithmeticpanel, before = 3, proportion = 0, flag = wx.ALIGN_CENTER_HORIZONTAL) self.funcpanel = wx.Panel(parent = self.panel, id = wx.ID_ANY) self._initSqlFunctions() funcsbox = wx.StaticBox(parent = self.funcpanel, id = wx.ID_ANY, label = " %s " % _("Functions")) funcsizer = wx.StaticBoxSizer(funcsbox, wx.VERTICAL) self.list_func = wx.ListBox(parent = self.funcpanel, id = wx.ID_ANY, choices = self.sqlFuncs['sqlite'].keys(), style = wx.LB_SORT) funcsizer.Add(item = self.list_func, proportion = 1, flag = wx.EXPAND) self.funcpanel.SetSizer(funcsizer) self.hsizer.Insert(item = self.funcpanel, before = 2, proportion = 1, flag = wx.EXPAND) self.list_func.Bind(wx.EVT_LISTBOX, self.OnAddFunc) for key, value in self.btn_arithmetic.iteritems(): self.FindWindowById(value[1]).Bind(wx.EVT_BUTTON, self.OnAddMark) self.mode.SetSelection(0) self.OnMode(None) self.text_sql.SetInsertionPoint(self.text_sql.GetLastPosition()) def OnApply(self, event): """Apply button pressed""" ret, msg = RunCommand('db.execute', getErrorMsg = True, parent = self, stdin = self.text_sql.GetValue(), input = '-', driver = self.driver, database = self.database) if ret != 0 and msg: self.statusbar.SetStatusText(_("SQL statement was not applied"), 0) else: self.statusbar.SetStatusText(_("SQL statement applied"), 0) self.sqlApplied.emit() def OnClear(self, event): """Clear button pressed""" self.text_sql.SetValue(self.initText) def OnMode(self, event): """Adjusts builder for chosen mode""" if self.mode.GetSelection() == 0: self.valuespanel.Hide() self.btn_logicpanel.Hide() self.btn_arithmeticpanel.Hide() self.funcpanel.Hide() elif self.mode.GetSelection() == 1: self.valuespanel.Show() self.btn_logicpanel.Show() self.btn_arithmeticpanel.Hide() self.funcpanel.Hide() elif self.mode.GetSelection() == 2: self.valuespanel.Hide() self.btn_logicpanel.Hide() self.btn_arithmeticpanel.Show() self.funcpanel.Show() self.pagesizer.Layout() def OnAddFunc(self, event): """Add function to the query""" if self.driver == 'dbf': GMessage(parent = self, message = _("Dbf driver does not support usage of SQL functions.")) return idx = self.list_func.GetSelections() for i in idx: func = self.sqlFuncs['sqlite'][self.list_func.GetString(i)][0] self._add(element = 'func', value = func) def _add(self, element, value): """Add element to the query :param element: element to add (column, value) """ sqlstr = self.text_sql.GetValue() curspos = self.text_sql.GetInsertionPoint() newsqlstr = '' if element in ['value', 'mark', 'func'] or \ (element == 'column' and self.mode.GetSelection() == 2): addstr = ' ' + value + ' ' newsqlstr = sqlstr[:curspos] + addstr + sqlstr[curspos:] curspos += len(addstr) elif element == 'column': if self.mode.GetSelection() == 0: # -> column idx1 = sqlstr.lower().find('set') + len('set') idx2 = sqlstr.lower().find('where') if idx2 >= 0: colstr = sqlstr[idx1:idx2].strip() else: colstr = sqlstr[idx1:].strip() cols = [col.split('=')[0].strip() for col in colstr.split(',')] if unicode(value) in cols: self.text_sql.SetInsertionPoint(curspos) wx.CallAfter(self.text_sql.SetFocus) return if colstr: colstr += ',' colstr = ' ' + colstr colstr += ' ' + value + '= ' newsqlstr = sqlstr[:idx1] + colstr if idx2 >= 0: newsqlstr += sqlstr[idx2:] curspos = idx1 + len(colstr) elif self.mode.GetSelection() == 1: # -> where newsqlstr = '' if sqlstr.lower().find('where') < 0: newsqlstr += ' WHERE' newsqlstr += ' ' + value curspos = self.text_sql.GetLastPosition() + len(newsqlstr) newsqlstr = sqlstr + newsqlstr if newsqlstr: self.text_sql.SetValue(newsqlstr) wx.CallAfter(self.text_sql.SetFocus) self.text_sql.SetInsertionPoint(curspos) def _initSqlFunctions(self): self.sqlFuncs = {} # TODO add functions for other drivers self.sqlFuncs['sqlite'] = { 'ABS' : ['ABS()'], 'LENGTH' : ['LENGTH()'], 'LOWER' : ['LOWER()'], 'LTRIM' : ['LTRIM(,)'], 'MAX' : ['MAX()'], 'MIN' : ['MIN()'], 'RTRIM' : ['RTRIM(,)'], 'SUBSTR' : ['SUBSTR (,[,])'], 'TRIM' : ['TRIM (,)'] }
class DataCatalogTree(LocationMapTree): def __init__(self, parent, giface=None): """Data Catalog Tree constructor.""" super(DataCatalogTree, self).__init__(parent) self._giface = giface self._initVariablesCatalog() self.beginDrag = Signal('DataCatalogTree.beginDrag') self.endDrag = Signal('DataCatalogTree.endDrag') self.startEdit = Signal('DataCatalogTree.startEdit') self.endEdit = Signal('DataCatalogTree.endEdit') self.Bind(wx.EVT_TREE_BEGIN_DRAG, lambda evt: self._emitSignal(evt.GetItem(), self.beginDrag, event=evt)) self.Bind(wx.EVT_TREE_END_DRAG, lambda evt: self._emitSignal(evt.GetItem(), self.endDrag, event=evt)) self.beginDrag.connect(self.OnBeginDrag) self.endDrag.connect(self.OnEndDrag) self.Bind(wx.EVT_TREE_BEGIN_LABEL_EDIT, lambda evt: self._emitSignal(evt.GetItem(), self.startEdit, event=evt)) self.Bind(wx.EVT_TREE_END_LABEL_EDIT, lambda evt: self._emitSignal(evt.GetItem(), self.endEdit, event=evt)) ###self.startEdit.connect(self.OnStartEditLabel) ###self.endEdit.connect(self.OnEditLabel) def _initVariablesCatalog(self): """Init variables.""" self.copy_layer = None self.copy_type = None self.copy_mapset = None self.copy_location = None def _runCommand(self, prog, **kwargs): cmdString = ' '.join(gscript.make_command(prog, **kwargs)) ret = RunCommand(prog, parent=self, **kwargs) return ret, cmdString def InitTreeItems(self): """Add locations, mapsets and layers to the tree.""" self._initTreeItems() def OnCopy(self, event): """Copy layer or mapset (just save it temporarily, copying is done by paste)""" self.copy_layer = self.selected_layer self.copy_type = self.selected_type self.copy_mapset = self.selected_mapset self.copy_location = self.selected_location label = _("Map <{layer}> marked for copying. " "You can paste it to the current mapset " "<{mapset}>.".format(layer=self.copy_layer.label, mapset=self.gmapset)) self.showNotification.emit(message=label) def OnRename(self, event): """Rename levent with dialog""" if self.selected_layer: self.old_name = self.selected_layer.label self.new_name = self._getUserEntry(_('New name'), _('Rename map'), self.old_name) self.Rename() def OnStartEditLabel(self, node, event): """Start label editing""" self.DefineItems(node) Debug.msg(1, "Start label edit {name}".format(name=node.label)) label = _("Editing {name}").format(name=node.label) self.showNotification.emit(message=label) if not self.selected_layer: event.Veto() def OnEditLabel(self, node, event): """End label editing""" if self.selected_layer and not event.IsEditCancelled(): self.old_name = node.label Debug.msg(1, "End label edit {name}".format(name=self.old_name)) self.new_name = event.GetLabel() self.Rename() def Rename(self): """Rename layer""" if self.selected_layer and self.new_name: string = self.old_name + ',' + self.new_name gisrc, env = getEnvironment(self.gisdbase, self.selected_location.label, self.selected_mapset.label) renamed = 0 label = _("Renaming map <{name}>...").format(name=string) self.showNotification.emit(message=label) if self.selected_type.label == 'vector': renamed, cmd = self._runCommand('g.rename', vector=string, env=env) elif self.selected_type.label == 'raster': renamed, cmd = self._runCommand('g.rename', raster=string, env=env) else: renamed, cmd = self._runCommand('g.rename', raster3d=string, env=env) if renamed == 0: self.selected_layer.label = self.new_name self.selected_layer.data['name'] = self.new_name self.RefreshNode(self.selected_layer) self.showNotification.emit(message=_("{cmd} -- completed").format(cmd=cmd)) Debug.msg(1, "LAYER RENAMED TO: " + self.new_name) gscript.try_remove(gisrc) def OnPaste(self, event): """Paste layer or mapset""" # copying between mapsets of one location if not self.copy_layer: GMessage(_("No map selected for copying."), parent=self) return if self.selected_location == self.copy_location and \ self.selected_mapset.data['name'] == gscript.gisenv()['MAPSET']: if self.selected_type: if self.copy_type.label != self.selected_type.label: # copy raster to vector or vice versa GError(_("Failed to copy map: invalid map type " "({} vs. {}).".format(self.copy_type.label, self.selected_type.label)), parent=self) return self.new_name = self._getUserEntry(_('New name'), _('Copy map'), self.copy_layer.label + '_copy') if not self.new_name: return if self.copy_layer.label == self.new_name: GMessage(_("Failed to copy map: new map has the same name"), parent=self) return if not self.selected_type: found = self._model.SearchNodes(parent=self.selected_mapset, type='element', name=self.copy_type.label) self.selected_type = found[0] if found else None overwrite = False if self.selected_type: found = self._model.SearchNodes(parent=self.selected_type, type=self.copy_type.label, name=self.new_name) if found and found[0]: dlg = wx.MessageDialog(parent=self, message = _("Map <{map}> already exists " "in the current mapset. " "Do you want to overwrite it?").format(map=self.new_name), caption = _("Overwrite?"), style = wx.YES_NO | wx.YES_DEFAULT | wx.ICON_QUESTION) ret = dlg.ShowModal() dlg.Destroy() if ret == wx.ID_YES: overwrite = True string = self.copy_layer.label + '@' + self.copy_mapset.label + ',' + self.new_name gisrc, env = getEnvironment(self.gisdbase, self.selected_location.label, self.selected_mapset.label) pasted = 0 label = _("Copying <{name}>...").format(name=string) self.showNotification.emit(message=label) if self.copy_type.label == 'vector': pasted, cmd = self._runCommand('g.copy', vector=string, overwrite=overwrite, env=env) node = 'vector' elif self.copy_type.label == 'raster': pasted, cmd = self._runCommand('g.copy', raster=string, overwrite=overwrite, env=env) node = 'raster' else: pasted, cmd = self._runCommand('g.copy', raster_3d=string, overwrite=overwrite, env=env) node = 'raster_3d' if pasted == 0: if not self.selected_type: # add type node if not exists self.selected_type = self._model.AppendNode(parent=self.selected_mapset, label=node, data=dict(type='element', name=node)) if not overwrite: self._model.AppendNode(parent=self.selected_type, label=self.new_name, data=dict(type=node, name=self.new_name)) self._model.SortChildren(self.selected_type) self.RefreshNode(self.selected_type, recursive=True) Debug.msg(1, "COPIED TO: " + self.new_name) self.showNotification.emit(message= _("{cmd} -- completed").format(cmd=cmd)) gscript.try_remove(gisrc) else: if self.selected_location != self.copy_location: GError(_("Failed to copy map: action is allowed only within the same location."), parent=self) else: GError(_("Failed to copy map: action is allowed only within the current mapset."), parent=self) # expand selected mapset self.ExpandNode(self.selected_mapset, recursive=True) def OnDelete(self, event): """Delete layer or mapset""" if self.selected_layer: string = self.selected_layer.label gisrc, env = getEnvironment(self.gisdbase, self.selected_location.label, self.selected_mapset.label) removed = 0 # TODO: rewrite this that it will tell map type in the dialog if self._confirmDialog(question=_('Do you really want to delete map <{m}>?').format(m=string), title=_('Delete map')) == wx.ID_YES: label = _("Deleting {name}...").format(name=string) self.showNotification.emit(message=label) if self.selected_type.label == 'vector': removed, cmd = self._runCommand('g.remove', flags='f', type='vector', name=string, env=env) elif self.selected_type.label == 'raster': removed, cmd = self._runCommand('g.remove', flags='f', type='raster', name=string, env=env) else: removed, cmd = self._runCommand('g.remove', flags='f', type='raster_3d', name=string, env=env) if removed == 0: self._model.RemoveNode(self.selected_layer) self.RefreshNode(self.selected_type, recursive=True) Debug.msg(1, "LAYER " + string + " DELETED") self.showNotification.emit(message= _("{cmd} -- completed").format(cmd=cmd)) gscript.try_remove(gisrc) def OnDisplayLayer(self, event): """Display layer in current graphics view""" layerName = [] if self.selected_location.label == self.glocation and self.selected_mapset: string = self.selected_layer.label + '@' + self.selected_mapset.label layerName.append(string) label = _("Displaying {name}...").format(name=string) self.showNotification.emit(message=label) label = "d." + self.selected_type.label[:4] + " --q map=" + string + \ _(" -- completed. Go to Layers tab for further operations.") if self.selected_type.label == 'vector': self._giface.lmgr.AddMaps(layerName, 'vector', True) elif self.selected_type.label == 'raster': self._giface.lmgr.AddMaps(layerName, 'raster', True) else: self._giface.lmgr.AddMaps(layerName, 'raster_3d', True) label = "d.rast --q map=" + string + _(" -- completed. Go to Layers tab for further operations.") # generate this message (command) automatically? self.showNotification.emit(message=label) Debug.msg(1, "LAYER " + self.selected_layer.label + " DISPLAYED") else: GError(_("Failed to display layer: not in current mapset or invalid layer"), parent=self) def OnBeginDrag(self, node, event): """Just copy necessary data""" if wx.GetMouseState().ControlDown(): #cursor = wx.StockCursor(wx.CURSOR_HAND) #self.SetCursor(cursor) event.Allow() self.DefineItems(node) self.OnCopy(event) Debug.msg(1, "DRAG") else: event.Veto() Debug.msg(1, "DRAGGING without ctrl key does nothing") def OnEndDrag(self, node, event): """Copy layer into target""" #cursor = wx.StockCursor(wx.CURSOR_ARROW) #self.SetCursor(cursor) if node: self.DefineItems(node) if self.selected_location == self.copy_location and self.selected_mapset: event.Allow() self.OnPaste(event) #cursor = wx.StockCursor(wx.CURSOR_DEFAULT) #self.SetCursor(cursor) # TODO: change cursor while dragging and then back, this is not working Debug.msg(1, "DROP DONE") else: event.Veto() def _getUserEntry(self, message, title, value): """Dialog for simple text entry""" dlg = TextEntryDialog(self, message, title) dlg.SetValue(value) if dlg.ShowModal() == wx.ID_OK: name = dlg.GetValue() else: name = None dlg.Destroy() return name def _confirmDialog(self, question, title): """Confirm dialog""" dlg = wx.MessageDialog(self, question, title, wx.YES_NO) res = dlg.ShowModal() dlg.Destroy() return res def _popupMenuLayer(self, current_mapset): """Create popup menu for layers""" menu = wx.Menu() item = wx.MenuItem(menu, wx.NewId(), _("&Copy")) menu.AppendItem(item) self.Bind(wx.EVT_MENU, self.OnCopy, item) item = wx.MenuItem(menu, wx.NewId(), _("&Delete")) menu.AppendItem(item) self.Bind(wx.EVT_MENU, self.OnDelete, item) if not current_mapset: item.Enable(False) item = wx.MenuItem(menu, wx.NewId(), _("&Rename")) menu.AppendItem(item) self.Bind(wx.EVT_MENU, self.OnRename, item) if not current_mapset: item.Enable(False) if not isinstance(self._giface, StandaloneGrassInterface): item = wx.MenuItem(menu, wx.NewId(), _("&Display")) menu.AppendItem(item) self.Bind(wx.EVT_MENU, self.OnDisplayLayer, item) self.PopupMenu(menu) menu.Destroy() def _popupMenuMapset(self): """Create popup menu for mapsets""" menu = wx.Menu() item = wx.MenuItem(menu, wx.NewId(), _("&Paste")) menu.AppendItem(item) self.Bind(wx.EVT_MENU, self.OnPaste, item) self.PopupMenu(menu) menu.Destroy()
class DataCatalogTree(LocationMapTree): def __init__(self, parent, giface=None): """Data Catalog Tree constructor.""" super(DataCatalogTree, self).__init__(parent) self._giface = giface self._restricted = True self._initVariablesCatalog() self.beginDrag = Signal("DataCatalogTree.beginDrag") self.endDrag = Signal("DataCatalogTree.endDrag") self.startEdit = Signal("DataCatalogTree.startEdit") self.endEdit = Signal("DataCatalogTree.endEdit") self.Bind(wx.EVT_TREE_BEGIN_DRAG, lambda evt: self._emitSignal(evt.GetItem(), self.beginDrag, event=evt)) self.Bind(wx.EVT_TREE_END_DRAG, lambda evt: self._emitSignal(evt.GetItem(), self.endDrag, event=evt)) self.beginDrag.connect(self.OnBeginDrag) self.endDrag.connect(self.OnEndDrag) self.Bind(wx.EVT_TREE_BEGIN_LABEL_EDIT, lambda evt: self._emitSignal(evt.GetItem(), self.startEdit, event=evt)) self.Bind(wx.EVT_TREE_END_LABEL_EDIT, lambda evt: self._emitSignal(evt.GetItem(), self.endEdit, event=evt)) self.startEdit.connect(self.OnStartEditLabel) self.endEdit.connect(self.OnEditLabel) def _initVariablesCatalog(self): """Init variables.""" self.copy_layer = None self.copy_type = None self.copy_mapset = None self.copy_location = None def SetRestriction(self, restrict): self._restricted = restrict def _runCommand(self, prog, **kwargs): cmdString = " ".join(gscript.make_command(prog, **kwargs)) ret = RunCommand(prog, parent=self, **kwargs) return ret, cmdString def InitTreeItems(self): """Add locations, mapsets and layers to the tree.""" self._initTreeItems() def OnCopyMap(self, event): """Copy layer or mapset (just save it temporarily, copying is done by paste)""" self.copy_layer = self.selected_layer self.copy_type = self.selected_type self.copy_mapset = self.selected_mapset self.copy_location = self.selected_location label = _( "Map <{layer}> marked for copying. " "You can paste it to the current mapset " "<{mapset}>.".format(layer=self.copy_layer.label, mapset=gisenv()["MAPSET"]) ) self.showNotification.emit(message=label) def OnRenameMap(self, event): """Rename layer with dialog""" old_name = self.selected_layer.label gisrc, env = gscript.create_environment( gisenv()["GISDBASE"], self.selected_location.label, mapset=self.selected_mapset.label ) new_name = self._getNewMapName( _("New name"), _("Rename map"), old_name, env=env, mapset=self.selected_mapset.label, element=self.selected_type.label, ) if new_name: self.Rename(old_name, new_name) def OnStartEditLabel(self, node, event): """Start label editing""" self.DefineItems(node) Debug.msg(1, "Start label edit {name}".format(name=node.label)) label = _("Editing {name}").format(name=node.label) self.showNotification.emit(message=label) if not self.selected_layer: event.Veto() def OnEditLabel(self, node, event): """End label editing""" if self.selected_layer and not event.IsEditCancelled(): old_name = node.label Debug.msg(1, "End label edit {name}".format(name=old_name)) new_name = event.GetLabel() self.Rename(old_name, new_name) def Rename(self, old, new): """Rename layer""" string = old + "," + new gisrc, env = gscript.create_environment( gisenv()["GISDBASE"], self.selected_location.label, self.selected_mapset.label ) label = _("Renaming map <{name}>...").format(name=string) self.showNotification.emit(message=label) if self.selected_type.label == "vector": renamed, cmd = self._runCommand("g.rename", vector=string, env=env) elif self.selected_type.label == "raster": renamed, cmd = self._runCommand("g.rename", raster=string, env=env) else: renamed, cmd = self._runCommand("g.rename", raster3d=string, env=env) if renamed == 0: self.selected_layer.label = new self.selected_layer.data["name"] = new self.RefreshNode(self.selected_layer) self.showNotification.emit(message=_("{cmd} -- completed").format(cmd=cmd)) Debug.msg(1, "LAYER RENAMED TO: " + new) gscript.try_remove(gisrc) def OnPasteMap(self, event): """Paste layer""" # copying between mapsets of one location if not self.copy_layer: GMessage(_("No map selected for copying."), parent=self) return if self.selected_location == self.copy_location: gisrc, env = gscript.create_environment( gisenv()["GISDBASE"], self.selected_location.label, mapset=self.selected_mapset.label ) new_name = self._getNewMapName( _("New name"), _("Copy map"), self.copy_layer.label, env=env, mapset=self.selected_mapset.label, element=self.copy_type.label, ) if not new_name: return if map_exists(new_name, element=self.copy_type.label, env=env, mapset=self.selected_mapset.label): GMessage(_("Failed to copy map: new map has the same name"), parent=self) return if not self.selected_type: found = self._model.SearchNodes(parent=self.selected_mapset, type="element", name=self.copy_type.label) self.selected_type = found[0] if found else None overwrite = False if self.selected_type: found = self._model.SearchNodes(parent=self.selected_type, type=self.copy_type.label, name=new_name) if found and found[0]: dlg = wx.MessageDialog( parent=self, message=_( "Map <{map}> already exists " "in the current mapset. " "Do you want to overwrite it?" ).format(map=new_name), caption=_("Overwrite?"), style=wx.YES_NO | wx.YES_DEFAULT | wx.ICON_QUESTION, ) ret = dlg.ShowModal() dlg.Destroy() if ret == wx.ID_YES: overwrite = True string = self.copy_layer.label + "@" + self.copy_mapset.label + "," + new_name pasted = 0 label = _("Copying <{name}>...").format(name=string) self.showNotification.emit(message=label) if self.copy_type.label == "vector": pasted, cmd = self._runCommand("g.copy", vector=string, overwrite=overwrite, env=env) node = "vector" elif self.copy_type.label == "raster": pasted, cmd = self._runCommand("g.copy", raster=string, overwrite=overwrite, env=env) node = "raster" else: pasted, cmd = self._runCommand("g.copy", raster_3d=string, overwrite=overwrite, env=env) node = "raster_3d" if pasted == 0: self.InsertLayer(name=new_name, mapset_node=self.selected_mapset, element_name=node) Debug.msg(1, "COPIED TO: " + new_name) self.showNotification.emit(message=_("g.copy completed").format(cmd=cmd)) gscript.try_remove(gisrc) else: if self.copy_type.label == "raster_3d": GError(_("Reprojection is not implemented for 3D rasters"), parent=self) return gisdbase = gisenv()["GISDBASE"] dlg = CatalogReprojectionDialog( self, self._giface, gisdbase, self.copy_location.label, self.copy_mapset.label, self.copy_layer.label, gisdbase, self.selected_location.label, self.selected_mapset.label, etype=self.copy_type.label, ) dlg.Show() # expand selected mapset self.ExpandNode(self.selected_mapset, recursive=True) def InsertLayer(self, name, mapset_node, element_name): """Insert layer into model and refresh tree""" found_element = self._model.SearchNodes(parent=mapset_node, type="element", name=element_name) found_element = found_element[0] if found_element else None if not found_element: # add type node if not exists found_element = self._model.AppendNode( parent=mapset_node, label=element_name, data=dict(type="element", name=element_name) ) found = self._model.SearchNodes(parent=found_element, name=name) if len(found) == 0: self._model.AppendNode(parent=found_element, label=name, data=dict(type=element_name, name=name)) self._model.SortChildren(found_element) self.RefreshNode(mapset_node, recursive=True) def OnDeleteMap(self, event): """Delete layer or mapset""" name = self.selected_layer.label gisrc, env = gscript.create_environment( gisenv()["GISDBASE"], self.selected_location.label, self.selected_mapset.label ) if ( self._confirmDialog( question=_( "Do you really want to delete map <{m}> of type <{etype}> from mapset " "<{mapset}> in location <{loc}>?" ).format( m=name, mapset=self.selected_mapset.label, etype=self.selected_type.label, loc=self.selected_location.label, ), title=_("Delete map"), ) == wx.ID_YES ): label = _("Deleting {name}...").format(name=name) self.showNotification.emit(message=label) if self.selected_type.label == "vector": removed, cmd = self._runCommand("g.remove", flags="f", type="vector", name=name, env=env) elif self.selected_type.label == "raster": removed, cmd = self._runCommand("g.remove", flags="f", type="raster", name=name, env=env) else: removed, cmd = self._runCommand("g.remove", flags="f", type="raster_3d", name=name, env=env) if removed == 0: self._model.RemoveNode(self.selected_layer) self.RefreshNode(self.selected_type, recursive=True) Debug.msg(1, "LAYER " + name + " DELETED") self.showNotification.emit(message=_("g.remove completed").format(cmd=cmd)) gscript.try_remove(gisrc) def OnDisplayLayer(self, event): """Display layer in current graphics view""" layerName = [] if self.selected_location.label == gisenv()["LOCATION_NAME"] and self.selected_mapset: string = self.selected_layer.label + "@" + self.selected_mapset.label layerName.append(string) label = _("Displaying {name}...").format(name=string) self.showNotification.emit(message=label) label = ( "d." + self.selected_type.label[:4] + " --q map=" + string + _(" -- completed. Go to Layers tab for further operations.") ) if self.selected_type.label == "vector": self._giface.lmgr.AddMaps(layerName, "vector", True) elif self.selected_type.label == "raster": self._giface.lmgr.AddMaps(layerName, "raster", True) else: self._giface.lmgr.AddMaps(layerName, "raster_3d", True) # generate this message (command) automatically? label = "d.rast --q map=" + string + _(" -- completed. Go to Layers tab for further operations.") self.showNotification.emit(message=label) Debug.msg(1, "LAYER " + self.selected_layer.label + " DISPLAYED") else: GError(_("Failed to display layer: not in current mapset or invalid layer"), parent=self) def OnBeginDrag(self, node, event): """Just copy necessary data""" self.DefineItems(node) if self.selected_layer and not (self._restricted and gisenv()["LOCATION_NAME"] != self.selected_location.label): event.Allow() self.OnCopyMap(event) Debug.msg(1, "DRAG") else: event.Veto() def OnEndDrag(self, node, event): """Copy layer into target""" if not wx.GetMouseState().ControlDown(): GMessage(_("Moving maps not implemented"), parent=self) event.Veto() return if node: self.DefineItems(node) if self._restricted and gisenv()["MAPSET"] != self.selected_mapset.label: GMessage(_("Maps can be copied only to current mapset"), parent=self) event.Veto() return if self.selected_location == self.copy_location and self.selected_mapset: event.Allow() self.OnPasteMap(event) Debug.msg(1, "DROP DONE") else: event.Veto() def OnSwitchLocationMapset(self, event): genv = gisenv() if self.selected_location.label == genv["LOCATION_NAME"]: self.changeMapset.emit(mapset=self.selected_mapset.label) else: self.changeLocation.emit(mapset=self.selected_mapset.label, location=self.selected_location.label) self.ExpandCurrentMapset() def Filter(self, text): """Filter tree based on name and type.""" text = text.strip() if len(text.split(":")) > 1: name = text.split(":")[1].strip() elem = text.split(":")[0].strip() if "r" == elem: element = "raster" elif "r3" == elem: element = "raster_3d" elif "v" == elem: element = "vector" else: element = None else: element = None name = text.strip() self._model = filterModel(self._orig_model, name=name, element=element) self.RefreshItems() self.ExpandCurrentMapset() def _getNewMapName(self, message, title, value, element, mapset, env): """Dialog for simple text entry""" dlg = NameEntryDialog(parent=self, message=message, caption=title, element=element, env=env, mapset=mapset) dlg.SetValue(value) if dlg.ShowModal() == wx.ID_OK: name = dlg.GetValue() else: name = None dlg.Destroy() return name def _confirmDialog(self, question, title): """Confirm dialog""" dlg = wx.MessageDialog(self, question, title, wx.YES_NO) res = dlg.ShowModal() dlg.Destroy() return res def _isCurrent(self, genv): if self._restricted: currentMapset = currentLocation = False if self.selected_location.label == genv["LOCATION_NAME"]: currentLocation = True if self.selected_mapset.label == genv["MAPSET"]: currentMapset = True return currentLocation, currentMapset else: return True, True def _popupMenuLayer(self): """Create popup menu for layers""" menu = wx.Menu() genv = gisenv() currentLocation, currentMapset = self._isCurrent(genv) item = wx.MenuItem(menu, wx.NewId(), _("&Copy")) menu.AppendItem(item) self.Bind(wx.EVT_MENU, self.OnCopyMap, item) item = wx.MenuItem(menu, wx.NewId(), _("&Paste")) menu.AppendItem(item) self.Bind(wx.EVT_MENU, self.OnPasteMap, item) if not (currentLocation and self.copy_layer): item.Enable(False) item = wx.MenuItem(menu, wx.NewId(), _("&Delete")) menu.AppendItem(item) self.Bind(wx.EVT_MENU, self.OnDeleteMap, item) item.Enable(currentMapset) item = wx.MenuItem(menu, wx.NewId(), _("&Rename")) menu.AppendItem(item) self.Bind(wx.EVT_MENU, self.OnRenameMap, item) item.Enable(currentMapset) if ( not isinstance(self._giface, StandaloneGrassInterface) and self.selected_location.label == genv["LOCATION_NAME"] ): menu.AppendSeparator() item = wx.MenuItem(menu, wx.NewId(), _("&Display layer")) menu.AppendItem(item) self.Bind(wx.EVT_MENU, self.OnDisplayLayer, item) self.PopupMenu(menu) menu.Destroy() def _popupMenuMapset(self): """Create popup menu for mapsets""" menu = wx.Menu() genv = gisenv() currentLocation, currentMapset = self._isCurrent(genv) item = wx.MenuItem(menu, wx.NewId(), _("&Paste")) menu.AppendItem(item) self.Bind(wx.EVT_MENU, self.OnPasteMap, item) if not (currentLocation and self.copy_layer): item.Enable(False) item = wx.MenuItem(menu, wx.NewId(), _("&Switch mapset")) menu.AppendItem(item) self.Bind(wx.EVT_MENU, self.OnSwitchLocationMapset, item) if self.selected_location.label == genv["LOCATION_NAME"] and self.selected_mapset.label == genv["MAPSET"]: item.Enable(False) self.PopupMenu(menu) menu.Destroy() def _popupMenuElement(self): """Create popup menu for elements""" menu = wx.Menu() item = wx.MenuItem(menu, wx.NewId(), _("&Paste")) menu.AppendItem(item) self.Bind(wx.EVT_MENU, self.OnPasteMap, item) genv = gisenv() currentLocation, currentMapset = self._isCurrent(genv) if not (currentLocation and self.copy_layer): item.Enable(False) self.PopupMenu(menu) menu.Destroy()
class SQLBuilderUpdate(SQLBuilder): """Class for building UPDATE SQL statement""" def __init__(self, parent, vectmap, id=wx.ID_ANY, layer=1, column=None): self.column = column # set dialog title title = _("GRASS SQL Builder (%(type)s) - <%(map)s>") % { "type": "UPDATE", "map": vectmap, } modeChoices = [ _("Column to set (SET clause)"), _("Constraint for query (WHERE clause)"), _("Calculate column value to set"), ] SQLBuilder.__init__( self, parent, title, vectmap, id=wx.ID_ANY, modeChoices=modeChoices, layer=layer, ) # signals self.sqlApplied = Signal("SQLBuilder.sqlApplied") if parent: # TODO: replace by giface self.sqlApplied.connect(parent.Update) def _doLayout(self, modeChoices): """Do dialog layout""" SQLBuilder._doLayout(self, modeChoices) self.initText = "UPDATE %s SET" % self.tablename if self.column: self.initText += " %s = " % self.column self.text_sql.SetValue(self.initText) self.btn_arithmetic = { "eq": [ "=", ], "brac": [ "()", ], "plus": [ "+", ], "minus": [ "-", ], "divide": [ "/", ], "multiply": [ "*", ], } self.btn_arithmeticpanel = wx.Panel(parent=self.panel, id=wx.ID_ANY) for key, value in six.iteritems(self.btn_arithmetic): btn = Button(parent=self.btn_arithmeticpanel, id=wx.ID_ANY, label=value[0]) self.btn_arithmetic[key].append(btn.GetId()) btn_arithmeticsizer = wx.GridBagSizer(hgap=5, vgap=5) btn_arithmeticsizer.Add(self.FindWindowById( self.btn_arithmetic["eq"][1]), pos=(0, 0)) btn_arithmeticsizer.Add(self.FindWindowById( self.btn_arithmetic["brac"][1]), pos=(1, 0)) btn_arithmeticsizer.Add(self.FindWindowById( self.btn_arithmetic["plus"][1]), pos=(0, 1)) btn_arithmeticsizer.Add(self.FindWindowById( self.btn_arithmetic["minus"][1]), pos=(1, 1)) btn_arithmeticsizer.Add(self.FindWindowById( self.btn_arithmetic["divide"][1]), pos=(0, 2)) btn_arithmeticsizer.Add(self.FindWindowById( self.btn_arithmetic["multiply"][1]), pos=(1, 2)) self.btn_arithmeticpanel.SetSizer(btn_arithmeticsizer) self.pagesizer.Insert(3, self.btn_arithmeticpanel, proportion=0, flag=wx.ALIGN_CENTER_HORIZONTAL) self.funcpanel = wx.Panel(parent=self.panel, id=wx.ID_ANY) self._initSqlFunctions() funcsbox = StaticBox(parent=self.funcpanel, id=wx.ID_ANY, label=" %s " % _("Functions")) funcsizer = wx.StaticBoxSizer(funcsbox, wx.VERTICAL) self.list_func = wx.ListBox( parent=self.funcpanel, id=wx.ID_ANY, choices=list(self.sqlFuncs["sqlite"].keys()), style=wx.LB_SORT, ) funcsizer.Add(self.list_func, proportion=1, flag=wx.EXPAND) self.funcpanel.SetSizer(funcsizer) self.hsizer.Insert(2, self.funcpanel, proportion=1, flag=wx.EXPAND) self.list_func.Bind(wx.EVT_LISTBOX, self.OnAddFunc) for key, value in six.iteritems(self.btn_arithmetic): self.FindWindowById(value[1]).Bind(wx.EVT_BUTTON, self.OnAddMark) self.mode.SetSelection(0) self.OnMode(None) self.text_sql.SetInsertionPoint(self.text_sql.GetLastPosition()) def OnApply(self, event): """Apply button pressed""" ret, msg = RunCommand( "db.execute", getErrorMsg=True, parent=self, stdin=self.text_sql.GetValue(), input="-", driver=self.driver, database=self.database, ) if ret != 0 and msg: self.statusbar.SetStatusText(_("SQL statement was not applied"), 0) else: self.statusbar.SetStatusText(_("SQL statement applied"), 0) self.sqlApplied.emit() def OnClear(self, event): """Clear button pressed""" self.text_sql.SetValue(self.initText) def OnMode(self, event): """Adjusts builder for chosen mode""" if self.mode.GetSelection() == 0: self.valuespanel.Hide() self.btn_logicpanel.Hide() self.btn_arithmeticpanel.Hide() self.funcpanel.Hide() elif self.mode.GetSelection() == 1: self.valuespanel.Show() self.btn_logicpanel.Show() self.btn_arithmeticpanel.Hide() self.funcpanel.Hide() elif self.mode.GetSelection() == 2: self.valuespanel.Hide() self.btn_logicpanel.Hide() self.btn_arithmeticpanel.Show() self.funcpanel.Show() self.pagesizer.Layout() def OnAddFunc(self, event): """Add function to the query""" if self.driver == "dbf": GMessage( parent=self, message=_( "Dbf driver does not support usage of SQL functions."), ) return idx = self.list_func.GetSelections() for i in idx: func = self.sqlFuncs["sqlite"][self.list_func.GetString(i)][0] self._add(element="func", value=func) def _add(self, element, value): """Add element to the query :param element: element to add (column, value) """ sqlstr = self.text_sql.GetValue() curspos = self.text_sql.GetInsertionPoint() newsqlstr = "" if element in [ "value", "mark", "func" ] or (element == "column" and self.mode.GetSelection() == 2): addstr = " " + value + " " newsqlstr = sqlstr[:curspos] + addstr + sqlstr[curspos:] curspos += len(addstr) elif element == "column": if self.mode.GetSelection() == 0: # -> column idx1 = sqlstr.lower().find("set") + len("set") idx2 = sqlstr.lower().find("where") if idx2 >= 0: colstr = sqlstr[idx1:idx2].strip() else: colstr = sqlstr[idx1:].strip() cols = [col.split("=")[0].strip() for col in colstr.split(",")] if value in cols: self.text_sql.SetInsertionPoint(curspos) wx.CallAfter(self.text_sql.SetFocus) return if colstr: colstr += "," colstr = " " + colstr colstr += " " + value + "= " newsqlstr = sqlstr[:idx1] + colstr if idx2 >= 0: newsqlstr += sqlstr[idx2:] curspos = idx1 + len(colstr) elif self.mode.GetSelection() == 1: # -> where newsqlstr = "" if sqlstr.lower().find("where") < 0: newsqlstr += " WHERE" newsqlstr += " " + value curspos = self.text_sql.GetLastPosition() + len(newsqlstr) newsqlstr = sqlstr + newsqlstr if newsqlstr: self.text_sql.SetValue(newsqlstr) wx.CallAfter(self.text_sql.SetFocus) self.text_sql.SetInsertionPoint(curspos) def _initSqlFunctions(self): self.sqlFuncs = {} # TODO add functions for other drivers self.sqlFuncs["sqlite"] = { "ABS": ["ABS()"], "LENGTH": ["LENGTH()"], "LOWER": ["LOWER()"], "LTRIM": ["LTRIM(,)"], "MAX": ["MAX()"], "MIN": ["MIN()"], "RTRIM": ["RTRIM(,)"], "SUBSTR": ["SUBSTR (,[,])"], "TRIM": ["TRIM (,)"], }
class RenderMapMgr(wx.EvtHandler): def __init__(self, Map): """Render map layers as image composition :param Map: Map object to be rendered """ wx.EvtHandler.__init__(self) self.Map = Map self.updateMap = Signal('RenderMapMgr.updateMap') self.updateProgress = Signal('RenderMapMgr.updateProgress') self.renderDone = Signal('RenderMapMgr.renderDone') self.renderDone.connect(self.OnRenderDone) # GRASS environment variable (for rendering) self._render_env = {"GRASS_RENDER_BACKGROUNDCOLOR" : "000000", "GRASS_RENDER_FILE_COMPRESSION" : "0", "GRASS_RENDER_TRUECOLOR" : "TRUE", "GRASS_RENDER_TRANSPARENT" : "TRUE" } self._init() self._rendering = False def _init(self, env=None): """Init render manager :param env: environmental variables or None """ self._startTime = time.time() self.progressInfo = None self._env = env self.layers = [] # re-render from scratch if os.path.exists(self.Map.mapfile): os.remove(self.Map.mapfile) def _renderLayers(self, env, force = False, overlaysOnly = False): """Render all map layers into files :param dict env: environmental variables to be used for rendering process :param bool force: True to force rendering :param bool overlaysOnly: True to render only overlays :return: number of layers to be rendered """ self.layers = self.Map.GetListOfLayers(ltype='overlay', active=True) if not overlaysOnly: self.layers += self.Map.GetListOfLayers(active=True, ltype='raster_3d', except_ltype=True) # reset progress self.ReportProgress() # render map layers if forced nlayers = 0 for layer in self.layers: if force or layer.forceRender: nlayers += 1 layer.Render(env) else: layer.GetRenderMgr().updateProgress.emit(layer=layer) Debug.msg(1, "RenderMapMgr.Render(): %d layers to be rendered " "(force=%d, all active layers -> %d)" % (nlayers, force, len(self.layers))) return nlayers def GetRenderEnv(self, windres=False): env = os.environ.copy() env.update(self._render_env) # use external gisrc if defined if self.Map.gisrc: env['GISRC'] = self.Map.gisrc env['GRASS_REGION'] = self.Map.SetRegion(windres) env['GRASS_RENDER_WIDTH'] = str(self.Map.width) env['GRASS_RENDER_HEIGHT'] = str(self.Map.height) if UserSettings.Get(group = 'display', key = 'driver', subkey = 'type') == 'png': env['GRASS_RENDER_IMMEDIATE'] = 'png' else: env['GRASS_RENDER_IMMEDIATE'] = 'cairo' return env def Render(self, force = False, windres = False): """Render map composition :param bool force: force rendering all map layers in the composition :param windres: True for region resolution instead for map resolution """ if self._rendering: Debug.msg(1, "RenderMapMgr().Render(): cancelled (already rendering)") return wx.BeginBusyCursor() self._rendering = True env = self.GetRenderEnv(windres) self._init(env) if self._renderLayers(env, force, windres) == 0: self.renderDone.emit() def OnRenderDone(self): """Rendering process done Make image composiotion, emits updateMap event. """ stopTime = time.time() maps = list() masks = list() opacities = list() for layer in self.layers: if layer.GetType() == 'overlay': continue if os.path.isfile(layer.mapfile): maps.append(layer.mapfile) masks.append(layer.maskfile) opacities.append(str(layer.opacity)) # run g.pngcomp to get composite image bgcolor = ':'.join(map(str, UserSettings.Get(group = 'display', key = 'bgcolor', subkey = 'color'))) startCompTime = time.time() if maps: ret, msg = RunCommand('g.pnmcomp', getErrorMsg = True, overwrite = True, input = '%s' % ",".join(maps), mask = '%s' % ",".join(masks), opacity = '%s' % ",".join(opacities), bgcolor = bgcolor, width = self.Map.width, height = self.Map.height, output = self.Map.mapfile, env=self._env) if ret != 0: self._rendering = False if wx.IsBusy(): wx.EndBusyCursor() raise GException(_("Rendering failed: %s" % msg)) stop = time.time() Debug.msg (1, "RenderMapMgr.OnRenderDone() time=%f sec (comp: %f)" % \ (stop - self._startTime, stop - startCompTime)) self._rendering = False if wx.IsBusy(): wx.EndBusyCursor() self.updateMap.emit() def Abort(self): """Abort all rendering processes""" Debug.msg(1, "RenderMapMgr.Abort()") for layer in self.layers: layer.GetRenderMgr().Abort() self._init() if wx.IsBusy(): wx.EndBusyCursor() self.updateProgress.emit(range=0, value=0, text=_("Rendering aborted")) def ReportProgress(self, layer=None): """Calculates progress in rendering/downloading and emits signal to inform progress bar about progress. Emits renderDone event when progressVal is equal to range. :param layer: Layer to be processed or None to reset """ if self.progressInfo is None or layer is None: self.progressInfo = {'progresVal' : 0, # current progress value 'downloading' : [], # layers, which are downloading data 'rendered' : [], # already rendered layers 'range' : len(self.layers)} else: if layer not in self.progressInfo['rendered']: self.progressInfo['rendered'].append(layer) if layer.IsDownloading() and \ layer not in self.progressInfo['downloading']: self.progressInfo['downloading'].append(layer) else: self.progressInfo['progresVal'] += 1 if layer in self.progressInfo['downloading']: self.progressInfo['downloading'].remove(layer) # for updating statusbar text stText = '' first = True for layer in self.progressInfo['downloading']: if first: stText += _("Downloading data ") first = False else: stText += ', ' stText += '<%s>' % layer.GetName() if stText: stText += '...' if self.progressInfo['range'] != len(self.progressInfo['rendered']): if stText: stText = _('Rendering & ') + stText else: stText = _('Rendering...') self.updateProgress.emit(range=self.progressInfo['range'], value=self.progressInfo['progresVal'], text=stText) if layer and self.progressInfo['progresVal'] == self.progressInfo['range']: self.renderDone.emit()
class RenderMapMgr(wx.EvtHandler): def __init__(self, Map): """Render map layers as image composition :param Map: Map object to be rendered """ wx.EvtHandler.__init__(self) self.Map = Map self.updateMap = Signal('RenderMapMgr.updateMap') self.updateProgress = Signal('RenderMapMgr.updateProgress') self.renderDone = Signal('RenderMapMgr.renderDone') self.renderDone.connect(self.OnRenderDone) # GRASS environment variable (for rendering) self._render_env = { "GRASS_RENDER_BACKGROUNDCOLOR": "000000", "GRASS_RENDER_FILE_COMPRESSION": "0", "GRASS_RENDER_TRUECOLOR": "TRUE", "GRASS_RENDER_TRANSPARENT": "TRUE" } self._init() self._rendering = False def _init(self, env=None): """Init render manager :param env: environmental variables or None """ self._startTime = time.time() self.progressInfo = None self._env = env self.layers = [] # re-render from scratch if os.path.exists(self.Map.mapfile): os.remove(self.Map.mapfile) def _renderLayers(self, env, force=False, overlaysOnly=False): """Render all map layers into files :param dict env: environmental variables to be used for rendering process :param bool force: True to force rendering :param bool overlaysOnly: True to render only overlays :return: number of layers to be rendered """ self.layers = self.Map.GetListOfLayers(ltype='overlay', active=True) if not overlaysOnly: self.layers += self.Map.GetListOfLayers(active=True, ltype='raster_3d', except_ltype=True) # reset progress self.ReportProgress() # render map layers if forced nlayers = 0 for layer in self.layers: if force or layer.forceRender: nlayers += 1 layer.Render(env) else: layer.GetRenderMgr().updateProgress.emit(layer=layer) Debug.msg( 1, "RenderMapMgr.Render(): %d layers to be rendered " "(force=%d, all active layers -> %d)" % (nlayers, force, len(self.layers))) return nlayers def GetRenderEnv(self, windres=False): env = os.environ.copy() env.update(self._render_env) # use external gisrc if defined if self.Map.gisrc: env['GISRC'] = self.Map.gisrc env['GRASS_REGION'] = self.Map.SetRegion(windres) env['GRASS_RENDER_WIDTH'] = str(self.Map.width) env['GRASS_RENDER_HEIGHT'] = str(self.Map.height) if UserSettings.Get(group='display', key='driver', subkey='type') == 'png': env['GRASS_RENDER_IMMEDIATE'] = 'png' else: env['GRASS_RENDER_IMMEDIATE'] = 'cairo' return env def Render(self, force=False, windres=False): """Render map composition :param bool force: force rendering all map layers in the composition :param windres: True for region resolution instead for map resolution """ if self._rendering: Debug.msg( 1, "RenderMapMgr().Render(): cancelled (already rendering)") return wx.BeginBusyCursor() self._rendering = True env = self.GetRenderEnv(windres) self._init(env) if self._renderLayers(env, force, windres) == 0: self.renderDone.emit() def OnRenderDone(self): """Rendering process done Make image composiotion, emits updateMap event. """ stopTime = time.time() maps = list() masks = list() opacities = list() for layer in self.layers: if layer.GetType() == 'overlay': continue if os.path.isfile(layer.mapfile): maps.append(layer.mapfile) masks.append(layer.maskfile) opacities.append(str(layer.opacity)) # run g.pngcomp to get composite image bgcolor = ':'.join( map( str, UserSettings.Get(group='display', key='bgcolor', subkey='color'))) startCompTime = time.time() if maps: ret, msg = RunCommand('g.pnmcomp', getErrorMsg=True, overwrite=True, input='%s' % ",".join(maps), mask='%s' % ",".join(masks), opacity='%s' % ",".join(opacities), bgcolor=bgcolor, width=self.Map.width, height=self.Map.height, output=self.Map.mapfile, env=self._env) if ret != 0: self._rendering = False if wx.IsBusy(): wx.EndBusyCursor() raise GException(_("Rendering failed: %s" % msg)) stop = time.time() Debug.msg (1, "RenderMapMgr.OnRenderDone() time=%f sec (comp: %f)" % \ (stop - self._startTime, stop - startCompTime)) self._rendering = False if wx.IsBusy(): wx.EndBusyCursor() self.updateMap.emit() def Abort(self): """Abort all rendering processes""" Debug.msg(1, "RenderMapMgr.Abort()") for layer in self.layers: layer.GetRenderMgr().Abort() self._init() if wx.IsBusy(): wx.EndBusyCursor() self.updateProgress.emit(range=0, value=0, text=_("Rendering aborted")) def ReportProgress(self, layer=None): """Calculates progress in rendering/downloading and emits signal to inform progress bar about progress. Emits renderDone event when progressVal is equal to range. :param layer: Layer to be processed or None to reset """ if self.progressInfo is None or layer is None: self.progressInfo = { 'progresVal': 0, # current progress value 'downloading': [], # layers, which are downloading data 'rendered': [], # already rendered layers 'range': len(self.layers) } else: if layer not in self.progressInfo['rendered']: self.progressInfo['rendered'].append(layer) if layer.IsDownloading() and \ layer not in self.progressInfo['downloading']: self.progressInfo['downloading'].append(layer) else: self.progressInfo['progresVal'] += 1 if layer in self.progressInfo['downloading']: self.progressInfo['downloading'].remove(layer) # for updating statusbar text stText = '' first = True for layer in self.progressInfo['downloading']: if first: stText += _("Downloading data ") first = False else: stText += ', ' stText += '<%s>' % layer.GetName() if stText: stText += '...' if self.progressInfo['range'] != len(self.progressInfo['rendered']): if stText: stText = _('Rendering & ') + stText else: stText = _('Rendering...') self.updateProgress.emit(range=self.progressInfo['range'], value=self.progressInfo['progresVal'], text=stText) if layer and self.progressInfo['progresVal'] == self.progressInfo[ 'range']: self.renderDone.emit()
class DataCatalogTree(LocationMapTree): def __init__(self, parent, giface=None): """Data Catalog Tree constructor.""" super(DataCatalogTree, self).__init__(parent) self._giface = giface self._initVariablesCatalog() self.beginDrag = Signal('DataCatalogTree.beginDrag') self.endDrag = Signal('DataCatalogTree.endDrag') self.startEdit = Signal('DataCatalogTree.startEdit') self.endEdit = Signal('DataCatalogTree.endEdit') self.Bind( wx.EVT_TREE_BEGIN_DRAG, lambda evt: self._emitSignal( evt.GetItem(), self.beginDrag, event=evt)) self.Bind( wx.EVT_TREE_END_DRAG, lambda evt: self._emitSignal( evt.GetItem(), self.endDrag, event=evt)) self.beginDrag.connect(self.OnBeginDrag) self.endDrag.connect(self.OnEndDrag) self.Bind( wx.EVT_TREE_BEGIN_LABEL_EDIT, lambda evt: self._emitSignal( evt.GetItem(), self.startEdit, event=evt)) self.Bind( wx.EVT_TREE_END_LABEL_EDIT, lambda evt: self._emitSignal( evt.GetItem(), self.endEdit, event=evt)) ###self.startEdit.connect(self.OnStartEditLabel) ###self.endEdit.connect(self.OnEditLabel) def _initVariablesCatalog(self): """Init variables.""" self.copy_layer = None self.copy_type = None self.copy_mapset = None self.copy_location = None def _runCommand(self, prog, **kwargs): cmdString = ' '.join(gscript.make_command(prog, **kwargs)) ret = RunCommand(prog, parent=self, **kwargs) return ret, cmdString def InitTreeItems(self): """Add locations, mapsets and layers to the tree.""" self._initTreeItems() def OnCopy(self, event): """Copy layer or mapset (just save it temporarily, copying is done by paste)""" self.copy_layer = self.selected_layer self.copy_type = self.selected_type self.copy_mapset = self.selected_mapset self.copy_location = self.selected_location label = _("Map <{layer}> marked for copying. " "You can paste it to the current mapset " "<{mapset}>.".format(layer=self.copy_layer.label, mapset=self.gmapset)) self.showNotification.emit(message=label) def OnRename(self, event): """Rename levent with dialog""" if self.selected_layer: self.old_name = self.selected_layer.label self.new_name = self._getUserEntry(_('New name'), _('Rename map'), self.old_name) self.Rename() def OnStartEditLabel(self, node, event): """Start label editing""" self.DefineItems(node) Debug.msg(1, "Start label edit {name}".format(name=node.label)) label = _("Editing {name}").format(name=node.label) self.showNotification.emit(message=label) if not self.selected_layer: event.Veto() def OnEditLabel(self, node, event): """End label editing""" if self.selected_layer and not event.IsEditCancelled(): self.old_name = node.label Debug.msg(1, "End label edit {name}".format(name=self.old_name)) self.new_name = event.GetLabel() self.Rename() def Rename(self): """Rename layer""" if self.selected_layer and self.new_name: string = self.old_name + ',' + self.new_name gisrc, env = getEnvironment(self.gisdbase, self.selected_location.label, self.selected_mapset.label) renamed = 0 label = _("Renaming map <{name}>...").format(name=string) self.showNotification.emit(message=label) if self.selected_type.label == 'vector': renamed, cmd = self._runCommand('g.rename', vector=string, env=env) elif self.selected_type.label == 'raster': renamed, cmd = self._runCommand('g.rename', raster=string, env=env) else: renamed, cmd = self._runCommand('g.rename', raster3d=string, env=env) if renamed == 0: self.selected_layer.label = self.new_name self.selected_layer.data['name'] = self.new_name self.RefreshNode(self.selected_layer) self.showNotification.emit( message=_("{cmd} -- completed").format(cmd=cmd)) Debug.msg(1, "LAYER RENAMED TO: " + self.new_name) gscript.try_remove(gisrc) def OnPaste(self, event): """Paste layer or mapset""" # copying between mapsets of one location if not self.copy_layer: GMessage(_("No map selected for copying."), parent=self) return if self.selected_location == self.copy_location and \ self.selected_mapset.data['name'] == gscript.gisenv()['MAPSET']: if self.selected_type: if self.copy_type.label != self.selected_type.label: # copy raster to vector or vice versa GError(_("Failed to copy map: invalid map type " "({} vs. {}).".format(self.copy_type.label, self.selected_type.label)), parent=self) return self.new_name = self._getUserEntry(_('New name'), _('Copy map'), self.copy_layer.label + '_copy') if not self.new_name: return if self.copy_layer.label == self.new_name: GMessage(_("Failed to copy map: new map has the same name"), parent=self) return if not self.selected_type: found = self._model.SearchNodes(parent=self.selected_mapset, type='element', name=self.copy_type.label) self.selected_type = found[0] if found else None overwrite = False if self.selected_type: found = self._model.SearchNodes(parent=self.selected_type, type=self.copy_type.label, name=self.new_name) if found and found[0]: dlg = wx.MessageDialog( parent=self, message=_("Map <{map}> already exists " "in the current mapset. " "Do you want to overwrite it?").format( map=self.new_name), caption=_("Overwrite?"), style=wx.YES_NO | wx.YES_DEFAULT | wx.ICON_QUESTION) ret = dlg.ShowModal() dlg.Destroy() if ret == wx.ID_YES: overwrite = True string = self.copy_layer.label + '@' + self.copy_mapset.label + ',' + self.new_name gisrc, env = getEnvironment(self.gisdbase, self.selected_location.label, self.selected_mapset.label) pasted = 0 label = _("Copying <{name}>...").format(name=string) self.showNotification.emit(message=label) if self.copy_type.label == 'vector': pasted, cmd = self._runCommand('g.copy', vector=string, overwrite=overwrite, env=env) node = 'vector' elif self.copy_type.label == 'raster': pasted, cmd = self._runCommand('g.copy', raster=string, overwrite=overwrite, env=env) node = 'raster' else: pasted, cmd = self._runCommand('g.copy', raster_3d=string, overwrite=overwrite, env=env) node = 'raster_3d' if pasted == 0: if not self.selected_type: # add type node if not exists self.selected_type = self._model.AppendNode( parent=self.selected_mapset, label=node, data=dict(type='element', name=node)) if not overwrite: self._model.AppendNode(parent=self.selected_type, label=self.new_name, data=dict(type=node, name=self.new_name)) self._model.SortChildren(self.selected_type) self.RefreshNode(self.selected_type, recursive=True) Debug.msg(1, "COPIED TO: " + self.new_name) self.showNotification.emit( message=_("{cmd} -- completed").format(cmd=cmd)) gscript.try_remove(gisrc) else: if self.selected_location != self.copy_location: GError(_( "Failed to copy map: action is allowed only within the same location." ), parent=self) else: GError(_( "Failed to copy map: action is allowed only within the current mapset." ), parent=self) # expand selected mapset self.ExpandNode(self.selected_mapset, recursive=True) def OnDelete(self, event): """Delete layer or mapset""" if self.selected_layer: string = self.selected_layer.label gisrc, env = getEnvironment(self.gisdbase, self.selected_location.label, self.selected_mapset.label) removed = 0 # TODO: rewrite this that it will tell map type in the dialog if self._confirmDialog(question=_( 'Do you really want to delete map <{m}>?').format( m=string), title=_('Delete map')) == wx.ID_YES: label = _("Deleting {name}...").format(name=string) self.showNotification.emit(message=label) if self.selected_type.label == 'vector': removed, cmd = self._runCommand('g.remove', flags='f', type='vector', name=string, env=env) elif self.selected_type.label == 'raster': removed, cmd = self._runCommand('g.remove', flags='f', type='raster', name=string, env=env) else: removed, cmd = self._runCommand('g.remove', flags='f', type='raster_3d', name=string, env=env) if removed == 0: self._model.RemoveNode(self.selected_layer) self.RefreshNode(self.selected_type, recursive=True) Debug.msg(1, "LAYER " + string + " DELETED") self.showNotification.emit( message=_("{cmd} -- completed").format(cmd=cmd)) gscript.try_remove(gisrc) def OnDisplayLayer(self, event): """Display layer in current graphics view""" layerName = [] if self.selected_location.label == self.glocation and self.selected_mapset: string = self.selected_layer.label + '@' + self.selected_mapset.label layerName.append(string) label = _("Displaying {name}...").format(name=string) self.showNotification.emit(message=label) label = "d." + self.selected_type.label[:4] + " --q map=" + string + \ _(" -- completed. Go to Layers tab for further operations.") if self.selected_type.label == 'vector': self._giface.lmgr.AddMaps(layerName, 'vector', True) elif self.selected_type.label == 'raster': self._giface.lmgr.AddMaps(layerName, 'raster', True) else: self._giface.lmgr.AddMaps(layerName, 'raster_3d', True) label = "d.rast --q map=" + string + _( " -- completed. Go to Layers tab for further operations." ) # generate this message (command) automatically? self.showNotification.emit(message=label) Debug.msg(1, "LAYER " + self.selected_layer.label + " DISPLAYED") else: GError(_( "Failed to display layer: not in current mapset or invalid layer" ), parent=self) def OnBeginDrag(self, node, event): """Just copy necessary data""" if wx.GetMouseState().ControlDown(): #cursor = wx.StockCursor(wx.CURSOR_HAND) #self.SetCursor(cursor) event.Allow() self.DefineItems(node) self.OnCopy(event) Debug.msg(1, "DRAG") else: event.Veto() Debug.msg(1, "DRAGGING without ctrl key does nothing") def OnEndDrag(self, node, event): """Copy layer into target""" #cursor = wx.StockCursor(wx.CURSOR_ARROW) #self.SetCursor(cursor) if node: self.DefineItems(node) if self.selected_location == self.copy_location and self.selected_mapset: event.Allow() self.OnPaste(event) #cursor = wx.StockCursor(wx.CURSOR_DEFAULT) #self.SetCursor(cursor) # TODO: change cursor while dragging and then back, this is not working Debug.msg(1, "DROP DONE") else: event.Veto() def _getUserEntry(self, message, title, value): """Dialog for simple text entry""" dlg = TextEntryDialog(self, message, title) dlg.SetValue(value) if dlg.ShowModal() == wx.ID_OK: name = dlg.GetValue() else: name = None dlg.Destroy() return name def _confirmDialog(self, question, title): """Confirm dialog""" dlg = wx.MessageDialog(self, question, title, wx.YES_NO) res = dlg.ShowModal() dlg.Destroy() return res def _popupMenuLayer(self, current_mapset): """Create popup menu for layers""" menu = wx.Menu() item = wx.MenuItem(menu, wx.NewId(), _("&Copy")) menu.AppendItem(item) self.Bind(wx.EVT_MENU, self.OnCopy, item) item = wx.MenuItem(menu, wx.NewId(), _("&Delete")) menu.AppendItem(item) self.Bind(wx.EVT_MENU, self.OnDelete, item) if not current_mapset: item.Enable(False) item = wx.MenuItem(menu, wx.NewId(), _("&Rename")) menu.AppendItem(item) self.Bind(wx.EVT_MENU, self.OnRename, item) if not current_mapset: item.Enable(False) if not isinstance(self._giface, StandaloneGrassInterface): item = wx.MenuItem(menu, wx.NewId(), _("&Display")) menu.AppendItem(item) self.Bind(wx.EVT_MENU, self.OnDisplayLayer, item) self.PopupMenu(menu) menu.Destroy() def _popupMenuMapset(self): """Create popup menu for mapsets""" menu = wx.Menu() item = wx.MenuItem(menu, wx.NewId(), _("&Paste")) menu.AppendItem(item) self.Bind(wx.EVT_MENU, self.OnPaste, item) self.PopupMenu(menu) menu.Destroy()