Example #1
0
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 (,)']
        }
Example #2
0
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)
Example #3
0
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()
Example #4
0
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()
Example #5
0
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
Example #6
0
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 (,)']                            
                                }
Example #7
0
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()
Example #8
0
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()
Example #9
0
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 (,)"],
        }
Example #10
0
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()
Example #11
0
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()
Example #12
0
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()