Exemplo n.º 1
0
class ProfileController(AnalysisControllerBase):
    """Class controls profiling in map display.
    It should be used inside ProfileFrame
    """
    def __init__(self, giface, mapWindow):
        AnalysisControllerBase.__init__(self, giface=giface, mapWindow=mapWindow)

        self.transectChanged = Signal('ProfileController.transectChanged')
        self._graphicsType = 'line'

    def _doAnalysis(self, coords):
        """Informs profile dialog that profile changed.

        :param coords: EN coordinates
        """
        self.transectChanged.emit(coords=coords)

    def _disconnectAll(self):
        self._mapWindow.mouseLeftDown.disconnect(self._start)
        self._mapWindow.mouseLeftUp.disconnect(self._addPoint)

    def _connectAll(self):
        self._mapWindow.mouseLeftDown.connect(self._start)
        self._mapWindow.mouseLeftUp.connect(self._addPoint)

    def _getPen(self):
        return wx.Pen(colour=wx.Colour(0, 100, 0), width=2, style=wx.SHORT_DASH)

    def Stop(self, restore=True):
        AnalysisControllerBase.Stop(self, restore=restore)

        self.transectChanged.emit(coords=[])
Exemplo n.º 2
0
class ProfileController(AnalysisControllerBase):
    """Class controls profiling in map display.
    It should be used inside ProfileFrame
    """

    def __init__(self, giface, mapWindow):
        AnalysisControllerBase.__init__(self, giface=giface, mapWindow=mapWindow)

        self.transectChanged = Signal("ProfileController.transectChanged")
        self._graphicsType = "line"

    def _doAnalysis(self, coords):
        """Informs profile dialog that profile changed.

        :param coords: EN coordinates
        """
        self.transectChanged.emit(coords=coords)

    def _disconnectAll(self):
        self._mapWindow.mouseLeftDown.disconnect(self._start)
        self._mapWindow.mouseLeftUp.disconnect(self._addPoint)

    def _connectAll(self):
        self._mapWindow.mouseLeftDown.connect(self._start)
        self._mapWindow.mouseLeftUp.connect(self._addPoint)

    def _getPen(self):
        return wx.Pen(colour=wx.Colour(0, 100, 0), width=2, style=wx.SHORT_DASH)

    def Stop(self, restore=True):
        AnalysisControllerBase.Stop(self, restore=restore)

        self.transectChanged.emit(coords=[])
Exemplo n.º 3
0
class ExportPanel(wx.Panel):
    def __init__(self, parent, giface, settings):
        wx.Panel.__init__(self, parent)
        self.giface = giface
        self.settings = settings
        self.settingsChanged = Signal('ScanningPanel.settingsChanged')

        if 'export' not in self.settings:
            self.settings['export'] = {}
            self.settings['export']['PLY'] = False
            self.settings['export']['PLY_file'] = ''

        if self.settings['export']['PLY_file']:
            initDir = os.path.dirname(self.settings['export']['PLY_file'])
        else:
            initDir = ""
        self.ifPLY = wx.CheckBox(self, label="")
        self.ifPLY.SetValue(self.settings['export']['PLY'])
        self.ifPLY.Bind(wx.EVT_CHECKBOX, self.OnChange)
        self.exportPLY = filebrowse.FileBrowseButton(
            self,
            labelText="Export PLY:",
            fileMode=wx.SAVE,
            startDirectory=initDir,
            initialValue=self.settings['export']['PLY_file'],
            changeCallback=self.OnChange)

        mainSizer = wx.BoxSizer(wx.VERTICAL)
        sizer = wx.BoxSizer(wx.HORIZONTAL)
        sizer.Add(self.ifPLY,
                  flag=wx.ALL | wx.ALIGN_CENTER_VERTICAL | wx.LEFT,
                  border=3)
        sizer.Add(self.exportPLY,
                  flag=wx.ALL | wx.ALIGN_CENTER_VERTICAL,
                  proportion=1,
                  border=0)
        mainSizer.Add(sizer, flag=wx.EXPAND | wx.ALL, border=5)
        self.SetSizer(mainSizer)
        mainSizer.Fit(self)

    def OnChange(self, event):
        self.settings['export']['PLY'] = self.ifPLY.IsChecked()
        self.settings['export']['PLY_file'] = self.exportPLY.GetValue()
        self.settingsChanged.emit()
Exemplo n.º 4
0
class StatisticsData:
    """Stores all statistics.
    """

    def __init__(self):
        self.statisticsDict = {}
        self.statisticsList = []

        self.statisticsAdded = Signal("StatisticsData.statisticsAdded")
        self.statisticsDeleted = Signal("StatisticsData.statisticsDeleted")
        self.allStatisticsDeleted = Signal(
            "StatisticsData.allStatisticsDeleted")

        self.statisticsSet = Signal("StatisticsData.statisticsSet")

    def GetStatistics(self, cat):
        return self.statisticsDict[cat]

    def AddStatistics(self, cat, name, color):
        st = Statistics()
        st.SetBaseStatistics(cat=cat, name=name, color=color)
        st.statisticsSet.connect(
            lambda stats: self.statisticsSet.emit(
                cat=cat, stats=stats))

        self.statisticsDict[cat] = st
        self.statisticsList.append(cat)

        self.statisticsAdded.emit(cat=cat, name=name, color=color)

    def DeleteStatistics(self, cat):
        del self.statisticsDict[cat]
        self.statisticsList.remove(cat)

        self.statisticsDeleted.emit(cat=cat)

    def GetCategories(self):
        return self.statisticsList[:]

    def DeleteAllStatistics(self):
        self.statisticsDict.clear()
        del self.statisticsList[:]  # not ...=[] !

        self.allStatisticsDeleted.emit()
Exemplo n.º 5
0
class PlotsRenderingManager:
    """Manages rendering of scatter plot.

    .. todo::
        still space for optimalization
    """

    def __init__(self, scatt_mgr, cats_mgr, core):
        self.scatt_mgr = scatt_mgr
        self.cats_mgr = cats_mgr
        self.core = core
        self.scatts_dt, self.scatt_conds_dt = self.core.GetScattsData()

        self.runningProcesses = 0

        self.data_to_render = {}
        self.render_queue = []

        self.cat_ids = []
        self.cat_cond_ids = []

        self.renderingStarted = Signal("ScattsManager.renderingStarted")
        self.renderingFinished = Signal("ScattsManager.renderingFinished")

    def AddRenderRequest(self, scatts):
        for scatt_id, cat_ids in scatts:
            if not self.data_to_render.has_key[scatt_id]:
                self.data_to_render = cat_ids
            else:
                for c in cat_ids:
                    if c not in self.data_to_render[scatt_id]:
                        self.data_to_render[scatt_id].append(c)

    def NewRunningProcess(self):
        self.runningProcesses += 1

    def RunningProcessDone(self):
        self.runningProcesses -= 1
        if self.runningProcesses <= 1:
            self.RenderScattPlts()

    def RenderRequest(self):
        if self.runningProcesses <= 1:
            self.RenderScattPlts()

    def CategoryChanged(self, cat_ids):
        for c in cat_ids:
            if c not in self.cat_ids:
                self.cat_ids.append(c)

    def CategoryCondsChanged(self, cat_ids):
        for c in cat_ids:
            if c not in self.cat_cond_ids:
                self.cat_cond_ids.append(c)

    def RenderScattPlts(self, scatt_ids=None):
        if len(self.render_queue) > 1:
            return

        self.renderingStarted.emit()
        self.render_queue.append(self.scatt_mgr.thread.GetId())

        cats_attrs = deepcopy(self.cats_mgr.GetCategoriesAttrs())
        cats = self.cats_mgr.GetCategories()[:]
        self.scatt_mgr.thread.Run(
            callable=self._renderscattplts,
            scatt_ids=scatt_ids,
            cats=cats,
            cats_attrs=cats_attrs,
            ondone=self.RenderingDone)

    def _renderscattplts(self, scatt_ids, cats, cats_attrs):
        cats.reverse()
        cats.insert(0, 0)
        for i_scatt_id, scatt in self.scatt_mgr.plots.items():
            if scatt_ids is not None and \
               i_scatt_id not in scatt_ids:
                continue
            if not scatt['scatt']:
                continue

            scatt_dt = self.scatts_dt.GetScatt(i_scatt_id)
            if self._showConfEllipses():
                ellipses_dt = self.scatts_dt.GetEllipses(
                    i_scatt_id, cats_attrs)
            else:
                ellipses_dt = {}

            for c in six.iterkeys(scatt_dt):
                try:
                    self.cat_ids.remove(c)
                    scatt_dt[c]['render'] = True
                except:
                    scatt_dt[c]['render'] = False

            if self.scatt_mgr.pol_sel_mode[0]:
                self._getSelectedAreas(cats, i_scatt_id, scatt_dt, cats_attrs)

            scatt['scatt'].Plot(cats_order=cats,
                                scatts=scatt_dt,
                                ellipses=ellipses_dt,
                                styles=cats_attrs)

    def RenderingDone(self, event):
        self.render_queue.remove(event.pid)
        if not self.render_queue:
            self.renderingFinished.emit()

    def _getSelectedAreas(self, cats_order, scatt_id, scatt_dt, cats_attrs):

        cat_id = self.cats_mgr.GetSelectedCat()
        if not cat_id:
            return

        sel_a_cat_id = -1

        s = self.scatt_conds_dt.GetScatt(scatt_id, [cat_id])
        if not s:
            return

        cats_order.append(sel_a_cat_id)

        col = UserSettings.Get(group='scatt',
                               key='selection',
                               subkey='sel_area')

        col = ":".join(map(str, col))
        opac = UserSettings.Get(group='scatt',
                                key='selection',
                                subkey='sel_area_opacty') / 100.0

        cats_attrs[sel_a_cat_id] = {'color': col,
                                    'opacity': opac,
                                    'show': True}

        scatt_dt[sel_a_cat_id] = s[cat_id]

        scatt_dt[sel_a_cat_id]['render'] = False
        if cat_id in self.cat_cond_ids:
            scatt_dt[sel_a_cat_id]['render'] = True
            self.cat_cond_ids.remove(cat_id)

    def _showConfEllipses(self):
        return UserSettings.Get(group='scatt',
                                key="ellipses",
                                subkey="show_ellips")
Exemplo n.º 6
0
class GPrompt(object):
    """Abstract class for interactive wxGUI prompt
    
    Signal promptRunCmd - emitted to run command from prompt
                        - attribute 'cmd'

    See subclass GPromptPopUp and GPromptSTC.
    """
    def __init__(self, parent, menuModel):
        self.parent = parent                 # GConsole
        self.panel  = self.parent.GetPanel()

        self.promptRunCmd = Signal('GPrompt.promptRunCmd')

        # probably only subclasses need this
        self._menuModel = menuModel

        self.mapList    = self._getListOfMaps()
        self.mapsetList = utils.ListOfMapsets()
        
        # auto complete items
        self.autoCompList   = list()
        self.autoCompFilter = None
        
        # command description (gtask.grassTask)
        self.cmdDesc   = None
        
        self.cmdbuffer = self._readHistory()
        self.cmdindex  = len(self.cmdbuffer)
        
        # list of traced commands
        self.commands = list()
        
    def _readHistory(self):
        """Get list of commands from history file"""
        hist = list()
        env = grass.gisenv()
        try:
            fileHistory = codecs.open(os.path.join(env['GISDBASE'],
                                                   env['LOCATION_NAME'],
                                                   env['MAPSET'],
                                                   '.bash_history'),
                                      encoding = 'utf-8', mode = 'r', errors='replace')
        except IOError:
            return hist
        
        try:
            for line in fileHistory.readlines():
                hist.append(line.replace('\n', ''))
        finally:
            fileHistory.close()
        
        return hist

    def _getListOfMaps(self):
        """Get list of maps"""
        result = dict()
        result['raster'] = grass.list_strings('raster')
        result['vector'] = grass.list_strings('vector')
        
        return result
    
    def _runCmd(self, cmdString):
        """Run command
        
        :param str cmdString: command to run
        """
        if not cmdString:
            return

        self.commands.append(cmdString) # trace commands

        # parse command into list
        try:
            cmd = utils.split(str(cmdString))
        except UnicodeError:
            cmd = utils.split(EncodeString((cmdString)))
        cmd = map(DecodeString, cmd)

        self.promptRunCmd.emit(cmd=cmd)

        # add command to history & clean prompt
        ### self.UpdateCmdHistory(cmd)
        self.OnCmdErase(None)
        self.ShowStatusText('')
        
    def GetCommands(self):
        """Get list of launched commands"""
        return self.commands
    
    def ClearCommands(self):
        """Clear list of commands"""
        del self.commands[:]
Exemplo n.º 7
0
class ManageSettingsWidget(wx.Panel):
    """Widget which allows loading and saving settings into file."""
    def __init__(self, parent, settingsFile):
        """
        Signals:
            settingsChanged - called when users changes setting
                            - attribute 'data' with chosen setting data
            settingsSaving - called when settings are saving
                           - attribute 'name' with chosen settings name
            settingsLoaded - called when settings are loaded
                           - attribute 'settings' is dict with loaded settings
                             {nameofsetting : settingdata, ....}

        :param settingsFile: path to file, where settings will be saved and loaded from
        """
        self.settingsFile = settingsFile

        self.settingsChanged = Signal('ManageSettingsWidget.settingsChanged')
        self.settingsSaving = Signal('ManageSettingsWidget.settingsSaving')
        self.settingsLoaded = Signal('ManageSettingsWidget.settingsLoaded')

        wx.Panel.__init__(self, parent=parent, id=wx.ID_ANY)

        self.settingsBox = wx.StaticBox(parent=self, id=wx.ID_ANY,
                                        label=" %s " % _("Settings"))

        self.settingsChoice = wx.Choice(parent=self, id=wx.ID_ANY)
        self.settingsChoice.Bind(wx.EVT_CHOICE, self.OnSettingsChanged)
        self.btnSettingsSave = wx.Button(parent=self, id=wx.ID_SAVE)
        self.btnSettingsSave.Bind(wx.EVT_BUTTON, self.OnSettingsSave)
        self.btnSettingsSave.SetToolTipString(_("Save current settings"))
        self.btnSettingsDel = wx.Button(parent=self, id=wx.ID_REMOVE)
        self.btnSettingsDel.Bind(wx.EVT_BUTTON, self.OnSettingsDelete)
        self.btnSettingsSave.SetToolTipString(_("Delete currently selected settings"))

        # escaping with '$' character - index in self.esc_chars
        self.e_char_i = 0
        self.esc_chars = ['$', ';']

        self._settings = self._loadSettings() # -> self.settingsChoice.SetItems()
        self.settingsLoaded.emit(settings=self._settings)

        self.data_to_save = []

        self._layout()

        self.SetSizer(self.settingsSizer)
        self.settingsSizer.Fit(self)

    def _layout(self):

        self.settingsSizer = wx.StaticBoxSizer(self.settingsBox, wx.HORIZONTAL)
        self.settingsSizer.Add(item=wx.StaticText(parent=self, id=wx.ID_ANY,
                                                  label=_("Load settings:")),
                               flag=wx.ALIGN_CENTER_VERTICAL | wx.RIGHT,
                               border=5)
        self.settingsSizer.Add(item=self.settingsChoice,
                               proportion=1, flag=wx.EXPAND)
        self.settingsSizer.Add(item=self.btnSettingsSave,
                               flag=wx.LEFT | wx.RIGHT, border=5)
        self.settingsSizer.Add(item=self.btnSettingsDel,
                               flag=wx.RIGHT,border=5)

    def OnSettingsChanged(self, event):
        """Load named settings"""
        name = event.GetString()
        if name not in self._settings:
            GError(parent = self,
                   message = _("Settings <%s> not found") % name)
            return

        data = self._settings[name]
        self.settingsChanged.emit(data=data)

    def GetSettings(self):
        """Load named settings"""
        return self._settings.copy()

    def OnSettingsSave(self, event):
        """Save settings"""
        dlg = wx.TextEntryDialog(parent=self,
                                 message=_("Name:"),
                                 caption=_("Save settings"))
        if dlg.ShowModal() == wx.ID_OK:
            name = dlg.GetValue()
            if not name:
                GMessage(parent=self,
                         message=_("Name not given, settings is not saved."))
            else:
                self.settingsSaving.emit(name=name)

            dlg.Destroy()

    def SaveSettings(self, name):
        # check if settings item already exists
        if name in self._settings:
            dlgOwt = wx.MessageDialog(self, message=_("Settings <%s> already exists. "
                                                      "Do you want to overwrite the settings?") % name,
                                      caption=_("Save settings"),
                                      style=wx.YES_NO | wx.YES_DEFAULT | wx.ICON_QUESTION)
            if dlgOwt.ShowModal() != wx.ID_YES:
                dlgOwt.Destroy()
                return

        if self.data_to_save:
            self._settings[name] = self.data_to_save

        self._saveSettings()
        self.settingsChoice.SetStringSelection(name)

        self.data_to_save = []

    def _saveSettings(self):
        """Save settings and reload if successful"""
        if self._writeSettings() == 0:
            self._settings = self._loadSettings()

    def SetDataToSave(self, data):
        """Set data for setting, which will be saved.

        :param data: - list of strings, which will be saved
        """
        self.data_to_save = data

    def SetSettings(self, settings):
        """Set settings

        :param settings: - dict with all settigs {nameofsetting : settingdata, ....}
        """
        self._settings = settings
        self._saveSettings()

    def AddSettings(self, settings):
        """Add settings

        :param settings: - dict with all settigs {nameofsetting : settingdata, ....}
        """
        self._settings = dict(self._settings.items() + settings.items())
        self._saveSettings()

    def OnSettingsDelete(self, event):
        """Save settings
        """
        name = self.settingsChoice.GetStringSelection()
        if not name:
            GMessage(parent = self,
                     message = _("No settings is defined. Operation canceled."))
            return

        self._settings.pop(name)
        if self._writeSettings() == 0:
            self._settings = self._loadSettings()

    def _writeSettings(self):
        """Save settings into the file

        :return: 0 on success
        :return: -1 on failure
        """
        try:
            fd = open(self.settingsFile, 'w')
            fd.write('format_version=2.0\n')
            for key, values in self._settings.iteritems():
                first = True
                for v in values:
                    # escaping characters
                    for e_ch in self.esc_chars:
                        v = v.replace(e_ch, self.esc_chars[self.e_char_i] + e_ch)
                    if first:
                        # escaping characters
                        for e_ch in self.esc_chars:
                            key = key.replace(e_ch, self.esc_chars[self.e_char_i] + e_ch)
                        fd.write('%s;%s;' % (key, v))
                        first = False
                    else:
                        fd.write('%s;' % (v))
                fd.write('\n')

        except IOError:
            GError(parent = self,
                   message = _("Unable to save settings"))
            return -1
        fd.close()

        return 0

    def _loadSettings(self):
        """Load settings from the file

        The file is defined by self.SettingsFile.

        :return: parsed dict
        :return: empty dict on error
        """

        data = dict()
        if not os.path.exists(self.settingsFile):
            return data

        try:
            fd = open(self.settingsFile, 'r')
        except IOError:
            return data

        fd_lines = fd.readlines()

        if not fd_lines:
            fd.close()
            return data

        if fd_lines[0].strip() == 'format_version=2.0':
            data = self._loadSettings_v2(fd_lines)
        else:
            data = self._loadSettings_v1(fd_lines)

        self.settingsChoice.SetItems(sorted(data.keys()))
        fd.close()

        self.settingsLoaded.emit(settings=data)

        return data

    def _loadSettings_v2(self, fd_lines):
        """Load settings from the file in format version 2.0

        The file is defined by self.SettingsFile.

        :return: parsed dict
        :return: empty dict on error
        """
        data = dict()

        for line in fd_lines[1:]:
            try:
                lineData = []
                line = line.rstrip('\n')
                i_last_found = i_last = 0
                key = ''
                while True:
                    idx = line.find(';', i_last)
                    if idx < 0:
                        break
                    elif idx != 0:

                        # find out whether it is separator
                        # $$$$; - it is separator
                        # $$$$$; - it is not separator
                        i_esc_chars = 0
                        while True:
                            if line[idx - (i_esc_chars + 1)] == self.esc_chars[self.e_char_i]:
                                i_esc_chars += 1
                            else:
                                break
                        if i_esc_chars%2 != 0:
                            i_last = idx + 1
                            continue

                    lineItem = line[i_last_found : idx]
                    # unescape characters
                    for e_ch in self.esc_chars:
                        lineItem = lineItem.replace(self.esc_chars[self.e_char_i] + e_ch, e_ch)
                    if i_last_found == 0:
                        key = lineItem
                    else:
                        lineData.append(lineItem)
                    i_last_found = i_last = idx + 1
                if key and lineData:
                    data[key] = lineData
            except ValueError:
                pass

        return data

    def _loadSettings_v1(self, fd_lines):
        """Load settings from the file in format version 1.0 (backward compatibility)

        The file is defined by self.SettingsFile.

        :return: parsed dict
        :return: empty dict on error
        """
        data = dict()

        for line in fd_lines:
            try:
                lineData = line.rstrip('\n').split(';')
                if len(lineData) > 4:
                    # type, dsn, format, options
                    data[lineData[0]] = (lineData[1], lineData[2], lineData[3], lineData[4])
                else:
                    data[lineData[0]] = (lineData[1], lineData[2], lineData[3], '')
            except ValueError:
                pass

        return data
Exemplo n.º 8
0
class SwipeMapDialog(wx.Dialog):
    """Dialog used to select maps.

    There are two modes - simple (only two raster maps),
    or two layer lists.
    """
    def __init__(self, parent, title=_("Select raster maps"),
                 first=None, second=None,
                 firstLayerList=None, secondLayerList=None):

        wx.Dialog.__init__(self, parent=parent, title=title,
                           style=wx.RESIZE_BORDER | wx.DEFAULT_DIALOG_STYLE)

        if firstLayerList is None:
            self._firstLayerList = LayerList()
        else:
            self._firstLayerList = copy.deepcopy(firstLayerList)
        if secondLayerList is None:
            self._secondLayerList = LayerList()
        else:
            self._secondLayerList = copy.deepcopy(secondLayerList)

        self._firstPanel = self._createSimplePanel()
        self._secondPanel = self._createAdvancedPanel()

        self.btnSwitch = wx.Button(self)
        self.btnCancel = wx.Button(self, id=wx.ID_CANCEL)
        self.btnApply = wx.Button(self, id=wx.ID_APPLY)
        self.btnOK = wx.Button(self, id=wx.ID_OK)
        self.btnOK.SetDefault()

        self.btnSwitch.Bind(wx.EVT_BUTTON, self.OnSwitchMode)
        self.btnApply.Bind(wx.EVT_BUTTON, lambda evt: self._apply())
        self.btnOK.Bind(wx.EVT_BUTTON, lambda evt: self._ok())
        self.btnCancel.Bind(wx.EVT_BUTTON, lambda evt: self.Close())
        self.Bind(wx.EVT_CLOSE, lambda evt: self.Hide())

        self.applyChanges = Signal('SwipeMapDialog.applyChanges')

        if first:
            self._firstRaster.SetValue(first)
        if second:
            self._secondRaster.SetValue(second)

        self._layout()

    def _layout(self):
        """Do layout"""
        mainSizer = wx.BoxSizer(wx.VERTICAL)
        self._switchSizer = wx.BoxSizer()
        self._switchSizer.Add(self._firstPanel, proportion=1,
                              flag=wx.EXPAND | wx.ALL, border=5)
        self._switchSizer.Add(self._secondPanel, proportion=1,
                              flag=wx.EXPAND | wx.ALL, border=5)
        mainSizer.Add(self._switchSizer, proportion=1,
                      flag=wx.EXPAND | wx.ALL)

        self.btnSizer = wx.StdDialogButtonSizer()
        self.btnSizer.AddButton(self.btnCancel)
        self.btnSizer.AddButton(self.btnOK)
        self.btnSizer.AddButton(self.btnApply)
        self.btnSizer.Realize()

        mainSizer.Add(item=self.btnSwitch, proportion=0,
                      flag=wx.ALL | wx.ALIGN_LEFT, border=5)
        mainSizer.Add(item=self.btnSizer, proportion=0,
                      flag=wx.EXPAND | wx.ALL | wx.ALIGN_CENTER, border=5)
        self.mainSizer = mainSizer
        self.SetSizer(mainSizer)
        mainSizer.Fit(self)
        self._switchMode(simple=True)

    def _createSimplePanel(self):
        panel = wx.Panel(self)
        sizer = wx.BoxSizer(wx.VERTICAL)

        self._firstRaster = gselect.Select(parent=panel, type='raster',
                                           size=globalvar.DIALOG_GSELECT_SIZE,
                                           validator=SimpleValidator(callback=self.ValidatorCallback))

        self._secondRaster = gselect.Select(parent=panel, type='raster',
                                            size=globalvar.DIALOG_GSELECT_SIZE,
                                            validator=SimpleValidator(callback=self.ValidatorCallback))
        sizer.Add(wx.StaticText(panel, label=_("Name of top/left raster map:")),
                  proportion=0, flag=wx.EXPAND | wx.ALL, border=5)
        sizer.Add(self._firstRaster, proportion=0,
                  flag=wx.EXPAND | wx.ALL, border=1)
        sizer.Add(wx.StaticText(panel, label=_("Name of bottom/right raster map:")),
                  proportion=0, flag=wx.EXPAND | wx.ALL, border=1)
        sizer.Add(self._secondRaster, proportion=0,
                  flag=wx.EXPAND | wx.ALL, border=1)

        self._firstRaster.SetFocus()

        panel.SetSizer(sizer)
        sizer.Fit(panel)

        return panel

    def _createAdvancedPanel(self):
        panel = wx.Panel(self)
        sizer = wx.BoxSizer(wx.HORIZONTAL)

        self._firstLmgr = SimpleLayerManager(parent=panel, layerList=self._firstLayerList,
                                             lmgrStyle=SIMPLE_LMGR_RASTER | SIMPLE_LMGR_RGB |
                                             SIMPLE_LMGR_VECTOR | SIMPLE_LMGR_TB_LEFT)
        self._secondLmgr = SimpleLayerManager(parent=panel, layerList=self._secondLayerList,
                                              lmgrStyle=SIMPLE_LMGR_RASTER | SIMPLE_LMGR_RGB |
                                              SIMPLE_LMGR_VECTOR | SIMPLE_LMGR_TB_RIGHT)
        sizer.Add(self._firstLmgr, proportion=1, flag=wx.EXPAND | wx.ALL, border=5)
        sizer.Add(self._secondLmgr, proportion=1, flag=wx.EXPAND | wx.ALL, border=5)
        panel.SetSizer(sizer)
        sizer.Fit(panel)

        return panel

    def _switchMode(self, simple):
        if simple:
            self._switchSizer.Show(self._firstPanel, show=True, recursive=True)
            self._switchSizer.Show(self._secondPanel, show=False, recursive=True)
            self.btnSwitch.SetLabel(_("Switch to advanced mode"))
            self.btnCancel.SetLabel(_("Cancel"))
        else:
            self._switchSizer.Show(self._firstPanel, show=False, recursive=True)
            self._switchSizer.Show(self._secondPanel, show=True, recursive=True)
            self.btnSwitch.SetLabel(_("Switch to simple mode"))
            self.btnCancel.SetLabel(_("Close"))

        self.Freeze()  # doesn't do anything (at least on Ubuntu)
        self.btnSizer.Show(self.btnApply, simple)
        self.btnSizer.Show(self.btnOK, simple)
        self.btnSizer.Layout()
        self._switchSizer.Layout()
        self.Fit()
        self.Thaw()

        self.applyChanges.emit()

    def OnSwitchMode(self, event):
        if self._switchSizer.IsShown(self._secondPanel):
            self._switchMode(simple=True)
        else:
            self._switchMode(simple=False)

    def ValidatorCallback(self, win):
        if self._switchSizer.IsShown(self._secondPanel):
            return

        if win == self._firstRaster.GetTextCtrl():
            GMessage(parent=self, message=_("Name of the first map is missing."))
        else:
            GMessage(parent=self, message=_("Name of the second map is missing."))

    def _ok(self):
        self._apply()
        self.Close()

    def _apply(self):
        # TODO check if not empty
        self.applyChanges.emit()

    def GetValues(self):
        """Get raster maps"""
        if self.IsSimpleMode():
            return (self._firstRaster.GetValue(), self._secondRaster.GetValue())
        else:
            return (self._firstLayerList, self._secondLayerList)

    def IsSimpleMode(self):
        if self._switchSizer.IsShown(self._firstPanel):
            return True
        return False

    def GetFirstSimpleLmgr(self):
        return self._firstLmgr

    def GetSecondSimpleLmgr(self):
        return self._secondLmgr
Exemplo n.º 9
0
class VNETPointsData:
    def __init__(self, mapWin, an_data, an_params):

        self.mapWin = mapWin
        self.an_data = an_data
        self.an_params = an_params

        # information, whether mouse event handler is registered in map window
        self.handlerRegistered = False

        self.pointsChanged = Signal("VNETPointsData.pointsChanged")
        self.an_params.parametersChanged.connect(self.ParametersChanged)

        self.snapping = False

        self.data = []
        self.cols = {
            "name": ["use", "type", "topology", "e", "n"],
            "label": [_("use"), _("type"), _("topology"), "e", "n"],
            # TDO
            "type": [None, ["", _("Start point"), _("End Point")], None, float, float],
            "def_vals": [False, 0, "new point", 0, 0],
        }

        # registration graphics for drawing
        self.pointsToDraw = self.mapWin.RegisterGraphicsToDraw(
            graphicsType="point", setStatusFunc=self.SetPointStatus
        )

        self.SetPointDrawSettings()

        self.AddPoint()
        self.AddPoint()

        self.SetPointData(0, {"use": True, "type": 1})
        self.SetPointData(1, {"use": True, "type": 2})

        self.selected = 0

    def __del__(self):
        self.CleanUp()

    def CleanUp(self):
        self.mapWin.UnregisterGraphicsToDraw(self.pointsToDraw)

        if self.handlerRegistered:
            self.mapWin.UnregisterMouseEventHandler(
                wx.EVT_LEFT_DOWN, self.OnMapClickHandler
            )

    def SetSnapping(self, activate):
        self.snapping = activate

    def GetSnapping(self):
        return self.snapping

    def AddPoint(self):

        self.pointsToDraw.AddItem(
            coords=(self.cols["def_vals"][3], self.cols["def_vals"][4])
        )
        self.data.append(self.cols["def_vals"][:])

        self.pointsChanged.emit(method="AddPoint", kwargs={})

    def DeletePoint(self, pt_id):
        item = self.pointsToDraw.GetItem(pt_id)
        if item:
            self.pointsToDraw.DeleteItem(item)
            self.data.pop(pt_id)

        self.pointsChanged.emit(method="DeletePoint", kwargs={"pt_id": pt_id})

    def SetPoints(self, pts_data):

        for item in self.pointsToDraw.GetAllItems():
            self.pointsToDraw.DeleteItem(item)

        self.data = []
        for pt_data in pts_data:
            pt_data_list = self._ptDataToList(pt_data)
            self.data.append(pt_data_list)
            self.pointsToDraw.AddItem(coords=(pt_data_list[3], pt_data_list[4]))

        self.pointsChanged.emit(method="SetPoints", kwargs={"pts_data": pts_data})

    def SetPointData(self, pt_id, data):
        for col, v in six.iteritems(data):
            if col == "use":
                continue

            idx = self.cols["name"].index(col)
            self.data[pt_id][idx] = v

        # if type is changed checked columns must be recalculated by _usePoint
        if "type" in data and "use" not in data:
            data["use"] = self.GetPointData(pt_id)["use"]

        if "use" in data:
            if self._usePoint(pt_id, data["use"]) == -1:
                data["use"] = False
            idx = self.cols["name"].index("use")
            self.data[pt_id][idx] = data["use"]

        self.pointsChanged.emit(
            method="SetPointData", kwargs={"pt_id": pt_id, "data": data}
        )

    def GetPointData(self, pt_id):
        return self._ptListDataToPtData(self.data[pt_id])

    def GetPointsCount(self):
        return len(self.data)

    def SetPointStatus(self, item, itemIndex):
        """Before point is drawn, decides properties of drawing style"""
        analysis, valid = self.an_params.GetParam("analysis")
        cats = self.an_data[analysis]["cmdParams"]["cats"]

        if itemIndex == self.selected:
            wxPen = "selected"
        elif not self.data[itemIndex][0]:
            wxPen = "unused"
            item.hide = False
        elif len(cats) > 1:
            idx = self.data[itemIndex][1]
            if idx == 2:  # End/To/Sink point
                wxPen = "used2cat"
            else:
                wxPen = "used1cat"
        else:
            wxPen = "used1cat"

        item.SetPropertyVal("label", str(itemIndex + 1))
        item.SetPropertyVal("penName", wxPen)

    def SetSelected(self, pt_id):
        self.selected = pt_id
        self.pointsChanged.emit(method="SetSelected", kwargs={"pt_id": pt_id})

    def GetSelected(self):
        return self.selected

    def SetPointDrawSettings(self):
        """Set settings for drawing of points"""
        ptSize = int(
            UserSettings.Get(group="vnet", key="point_symbol", subkey="point_size")
        )
        self.pointsToDraw.SetPropertyVal("size", ptSize)

        colors = UserSettings.Get(group="vnet", key="point_colors")
        ptWidth = int(
            UserSettings.Get(group="vnet", key="point_symbol", subkey="point_width")
        )

        textProp = self.pointsToDraw.GetPropertyVal("text")
        textProp["font"].SetPointSize(ptSize + 2)

        for colKey, col in six.iteritems(colors):
            pen = self.pointsToDraw.GetPen(colKey)
            if pen:
                pen.SetColour(wx.Colour(col[0], col[1], col[2], 255))
                pen.SetWidth(ptWidth)
            else:
                self.pointsToDraw.AddPen(
                    colKey,
                    wx.Pen(
                        colour=wx.Colour(col[0], col[1], col[2], 255), width=ptWidth
                    ),
                )

    def ParametersChanged(self, method, kwargs):
        if "analysis" in list(kwargs["changed_params"].keys()):
            self._updateTypeCol()

            if self.an_params.GetParam("analysis")[0] == "v.net.path":
                self._vnetPathUpdateUsePoints(None)

    def _updateTypeCol(self):
        """Rename category values when module is changed. Expample: Start point -> Sink point"""
        colValues = [""]
        analysis, valid = self.an_params.GetParam("analysis")
        anParamsCats = self.an_data[analysis]["cmdParams"]["cats"]

        for ptCat in anParamsCats:
            colValues.append(ptCat[1])

        type_idx = self.cols["name"].index("type")
        self.cols["type"][type_idx] = colValues

    def _ptDataToList(self, pt_data):

        pt_list_data = [None] * len(self.cols["name"])

        for k, val in six.iteritems(pt_data):
            pt_list_data[self.cols["name"].index(k)] = val

        return pt_list_data

    def _ptListDataToPtData(self, pt_list_data):

        pt_data = {}
        for i, val in enumerate(pt_list_data):
            pt_data[self.cols["name"][i]] = val

        return pt_data

    def _usePoint(self, pt_id, use):
        """Item is checked/unchecked"""
        analysis, valid = self.an_params.GetParam("analysis")
        cats = self.an_data[analysis]["cmdParams"]["cats"]
        # TODO move
        # if self.updateMap:
        #    up_map_evt = gUpdateMap(render = False, renderVector = False)
        #    wx.PostEvent(self.dialog.mapWin, up_map_evt)

        if len(cats) <= 1:
            return 0

        use_idx = self.cols["name"].index("use")
        checkedVal = self.data[pt_id][1]

        # point without given type cannot be selected
        if checkedVal == 0:
            self.data[pt_id][use_idx] = False
            self.pointsChanged.emit(
                method="SetPointData", kwargs={"pt_id": pt_id, "data": {"use": False}}
            )
            return -1

        if analysis == "v.net.path" and use:
            self._vnetPathUpdateUsePoints(pt_id)

    def _vnetPathUpdateUsePoints(self, checked_pt_id):

        alreadyChecked = []

        type_idx = self.cols["name"].index("type")
        use_idx = self.cols["name"].index("use")

        if checked_pt_id is not None:
            checkedKey = checked_pt_id
            alreadyChecked.append(self.data[checked_pt_id][type_idx])
        else:
            checkedKey = -1

        for iKey, dt in enumerate(self.data):
            pt_type = dt[type_idx]

            if (
                (pt_type in alreadyChecked and checkedKey != iKey) or pt_type == 0
            ) and self.data[iKey][use_idx]:
                self.data[iKey][use_idx] = False
                self.pointsChanged.emit(
                    method="SetPointData",
                    kwargs={"pt_id": iKey, "data": {"use": False}},
                )
            elif self.data[iKey][use_idx]:
                alreadyChecked.append(pt_type)

    def EditPointMode(self, activate):
        """Registers/unregisters mouse handler into map window"""

        if activate == self.handlerRegistered:
            return

        if activate:
            self.mapWin.RegisterMouseEventHandler(
                wx.EVT_LEFT_DOWN, self.OnMapClickHandler, "cross"
            )
            self.handlerRegistered = True
        else:
            self.mapWin.UnregisterMouseEventHandler(
                wx.EVT_LEFT_DOWN, self.OnMapClickHandler
            )
            self.handlerRegistered = False

        self.pointsChanged.emit(method="EditMode", kwargs={"activated": activate})

    def IsEditPointModeActive(self):
        return self.handlerRegistered

    def OnMapClickHandler(self, event):
        """Take coordinates from map window"""
        # TODO update snapping after input change
        if event == "unregistered":
            self.handlerRegistered = False
            return

        if not self.data:
            self.AddPoint()

        e, n = self.mapWin.GetLastEN()

        if self.snapping:

            # compute threshold
            snapTreshPix = int(
                UserSettings.Get(group="vnet", key="other", subkey="snap_tresh")
            )
            res = max(self.mapWin.Map.region["nsres"], self.mapWin.Map.region["ewres"])
            snapTreshDist = snapTreshPix * res

            params, err_params, flags = self.an_params.GetParams()
            vectMap = params["input"]

            if "input" in err_params:
                msg = _("new point")

            coords = SnapToNode(e, n, snapTreshDist, vectMap)
            if coords:
                e = coords[0]
                n = coords[1]

                msg = "snapped to node"
            else:
                msg = _("new point")

        else:
            msg = _("new point")

        self.SetPointData(self.selected, {"topology": msg, "e": e, "n": n})

        self.pointsToDraw.GetItem(self.selected).SetCoords([e, n])

        if self.selected == len(self.data) - 1:
            self.SetSelected(0)
        else:
            self.SetSelected(self.GetSelected() + 1)

    def GetColumns(self, only_relevant=True):

        cols_data = deepcopy(self.cols)

        hidden_cols = []
        hidden_cols.append(self.cols["name"].index("e"))
        hidden_cols.append(self.cols["name"].index("n"))

        analysis, valid = self.an_params.GetParam("analysis")
        if only_relevant and len(self.an_data[analysis]["cmdParams"]["cats"]) <= 1:
            hidden_cols.append(self.cols["name"].index("type"))

        i_red = 0
        hidden_cols.sort()
        for idx in hidden_cols:
            for dt in six.itervalues(cols_data):
                dt.pop(idx - i_red)
            i_red += 1

        return cols_data
Exemplo n.º 10
0
class VNETManager:
    def __init__(self, guiparent, giface):

        self.data = {}

        self.guiparent = guiparent
        self.giface = giface
        self.mapWin = giface.GetMapWindow()

        self.goutput = GConsole(guiparent=guiparent)

        self.vnet_data = VNETData(guiparent=guiparent, mapWin=self.mapWin)

        self.results = {
            "analysis": None,
            "vect_map": None
        }  # TODO more results

        # this class instance manages all temporary vector maps created during
        # life of VNETDialog
        self.tmp_maps = VNETTmpVectMaps(parent=guiparent, mapWin=self.mapWin)

        # initialization of History class used for saving and reading data from file
        # it is used for browsing analysis results
        self.history = VNETHistory(self.guiparent, self.vnet_data,
                                   self.tmp_maps)
        self.analyses = VNETAnalyses(self.vnet_data, self.RunAnDone,
                                     self.goutput, self.tmp_maps)

        self.snap_nodes = SnappingNodes(self.giface, self.vnet_data,
                                        self.tmp_maps, self.mapWin)

        self.ttbCreated = Signal('VNETManager.ttbCreated')
        self.analysisDone = Signal('VNETManager.analysisDone')
        self.pointsChanged = self.vnet_data.pointsChanged
        self.parametersChanged = self.vnet_data.parametersChanged

        self.snapping = self.snap_nodes.snapping
        self.pointsChanged.connect(self.PointsChanged)

    def __del__(self):
        self.CleanUp()

    def CleanUp(self):
        """Removes temp layers, unregisters handlers and graphics"""

        update = self.tmp_maps.DeleteAllTmpMaps()

        self.vnet_data.CleanUp()

        if update:
            self.giface.updateMap.emit(render=True, renderVector=True)
        else:
            self.giface.updateMap.emit(render=False, renderVector=False)

    def GetPointsManager(self):
        return self.vnet_data.GetPointsData()

    def GetGlobalTurnsData(self):
        return self.vnet_data.GetGlobalTurnsData()

    def RunAnalysis(self):

        analysis, valid = self.vnet_data.GetParam("analysis")

        params, err_params, flags = self.vnet_data.GetParams()
        relevant_params = self.vnet_data.GetRelevantParams(analysis)

        if not relevant_params:
            return -1

        if not self.vnet_data.InputsErrorMsgs(_("Unable to perform analysis."),
                                              analysis, params, flags,
                                              err_params, relevant_params):
            return -2

        if self.results["vect_map"]:
            self.results["vect_map"].DeleteRenderLayer()

        # history - delete data in buffer for hist step
        self.history.DeleteNewHistStepData()

        # create new map (included to history) for result of analysis
        self.results["vect_map"] = self.history.NewTmpVectMapToHist(
            'vnet_tmp_result')

        if not self.results["vect_map"]:
            return False

        # for case there is some map with same name
        # (when analysis does not produce any map, this map would have been shown as result)
        RunCommand('g.remove',
                   flags='f',
                   type='vector',
                   name=self.results["vect_map"].GetVectMapName())

        # save data from
        self.history._saveAnInputToHist(analysis, params, flags)

        ret = self.analyses.RunAnalysis(
            self.results["vect_map"].GetVectMapName(), params, flags)
        if not ret:
            return -3
        else:
            return 1

    def RunAnDone(self, cmd, returncode, results):

        self.results["analysis"] = cmd[0]

        self.results["vect_map"].SaveVectMapState()

        cmd, cmd_colors = self.vnet_data.GetLayerStyle()
        self.results["vect_map"].AddRenderLayer(cmd, cmd_colors)

        self.history.SaveHistStep()

        self.analysisDone.emit()

    def ShowResult(self, show):
        # TODO can be more results e. g. smallest cut

        if show:
            self._checkResultMapChanged(self.results["vect_map"])
            cmd, cmd_colors = self.vnet_data.GetLayerStyle()
            self.results["vect_map"].AddRenderLayer(cmd, cmd_colors)
        else:
            self.results["vect_map"].DeleteRenderLayer()

        self.giface.updateMap.emit(render=True, renderVector=True)

    def GetAnalysisProperties(self, analysis=None):
        return self.vnet_data.GetAnalysisProperties(analysis=analysis)

    def GetResults(self):
        return self.results["vect_map"]

    def Undo(self):
        self._updateDataForHistStep(self.history.Undo())
        # SetUpdateMap TODO
        return self.history.GetHistStep()

    def Redo(self):
        self._updateDataForHistStep(self.history.Redo())
        # SetUpdateMap
        return self.history.GetHistStep()

    def _updateDataForHistStep(self, data):
        if not data:
            return

        analysis, resultMapName, params, flags = data

        self.results["analysis"] = analysis
        self.vnet_data.SetParams(params, flags)

        self.results["vect_map"].DeleteRenderLayer()
        self.results["vect_map"] = self.tmp_maps.GetTmpVectMap(resultMapName)
        self._checkResultMapChanged(self.results["vect_map"])

        cmd, cmd_colors = self.vnet_data.GetLayerStyle()
        self.results["vect_map"].AddRenderLayer(cmd, cmd_colors)

        self.giface.updateMap.emit(render=True, renderVector=True)

    def GetHistStep(self):
        return self.history.GetHistStep()

    def SetParams(self, params, flags):
        self.vnet_data.SetParams(params, flags)

    def GetParams(self):
        params, inv_params, flags = self.vnet_data.GetParams()
        return params, inv_params, flags

    def GetParam(self, param):
        return self.vnet_data.GetParam(param)

    def _checkResultMapChanged(self, resultVectMap):
        """Check if map was modified outside"""
        if resultVectMap.VectMapState() == 0:
            dlg = wx.MessageDialog(
                parent=self,
                message=_("Temporary map '%s' with result " +
                          "was changed outside vector network analysis tool.\n"
                          + "Showed result may not correspond " +
                          "original analysis result.") %
                resultVectMap.GetVectMapName(),
                caption=_("Result changed outside"),
                style=wx.ICON_INFORMATION | wx.CENTRE)
            dlg.ShowModal()
            dlg.Destroy()

    def IsSnappingActive(self):
        return self.vnet_data.GetSnapping()

    def Snapping(self, activate):
        self.snap_nodes.ComputeNodes(activate)

    def GetAnalyses(self):
        return self.vnet_data.GetAnalyses()

    def SettingsUpdated(self):
        self.vnet_data.GetPointsData().SetPointDrawSettings()
        if not self.results["vect_map"] or not self.tmp_maps.HasTmpVectMap(
                self.results["vect_map"].GetVectMapName()):
            self.giface.updateMap.emit(render=False, renderVector=False)
        elif self.results["vect_map"].GetRenderLayer():
            cmd, cmd_colors = self.vnet_data.GetLayerStyle()
            self.results["vect_map"].AddRenderLayer(cmd, cmd_colors)

            self.giface.updateMap.emit(render=True, renderVector=True)
            # TODO optimization
        else:
            self.giface.updateMap.emit(render=False, renderVector=False)

    def PointsChanged(self, method, kwargs):
        self.giface.updateMap.emit(render=False, renderVector=False)

    def CreateTttb(self, params):

        outputMap = params["output"]
        mapName, mapSet = ParseMapStr(outputMap)
        if mapSet != grass.gisenv()['MAPSET']:
            GMessage(parent=self,
                     message=_("Map can be created only in current mapset"))
            return False
        existsMap = grass.find_file(name=mapName,
                                    element='vector',
                                    mapset=grass.gisenv()['MAPSET'])
        if existsMap["name"]:
            dlg = wx.MessageDialog(parent=self.guiparent,
                                   message=_("Vector map %s already exists. " +
                                             "Do you want to overwrite it?") %
                                   (existsMap["fullname"]),
                                   caption=_("Overwrite vector map"),
                                   style=wx.YES_NO | wx.NO_DEFAULT
                                   | wx.ICON_QUESTION | wx.CENTRE)
            ret = dlg.ShowModal()
            dlg.Destroy()
            if ret == wx.ID_NO:
                return False

            cmdTtb = [
                "v.net.turntable",
                "input=" + params["input"],
                "output=" + params["output"],
                "arc_layer=" + params["arc_layer"],
                "turn_layer=" + params["turn_layer"],
                "turn_cat_layer=" + params["turn_cat_layer"],
                "--overwrite",
            ]

            self.goutput.RunCmd(command=cmdTtb, onDone=self._createTtbDone)

        return True

    def _createTtbDone(self, event):

        if event.returncode != 0:
            GMessage(parent=self.guiparent,
                     message=_("Creation of turntable failed."))
            return
        else:
            params = {}
            for c in event.cmd:
                spl_c = c.split("=")
                if len(spl_c) != 2:
                    continue

                if spl_c[0] and spl_c != "input":
                    params[spl_c[0]] = spl_c[1]
                if spl_c[0] == "output":
                    params["input"] = spl_c[1]

            self.vnet_data.SetParams(params, {})

        self.ttbCreated.emit(returncode=event.returncode)

    def SaveTmpLayer(self, layer_name):
        """Permanently saves temporary map of analysis result"""
        msg = _("Vector map with analysis result does not exist.")

        if not hasattr(self.results["vect_map"], "GetVectMapName"):
            GMessage(parent=self.guiparent, message=msg)
            return

        mapToAdd = self.results["vect_map"].GetVectMapName()
        mapToAddEx = grass.find_file(name=mapToAdd,
                                     element='vector',
                                     mapset=grass.gisenv()['MAPSET'])

        if not mapToAddEx["name"]:
            GMessage(parent=self.guiparent, message=msg)
            return

        addedMap = layer_name
        mapName, mapSet = ParseMapStr(addedMap)
        if mapSet != grass.gisenv()['MAPSET']:
            GMessage(
                parent=self.guiparent,
                message=_("Map can be saved only to currently set mapset"))
            return
        existsMap = grass.find_file(name=mapName,
                                    element='vector',
                                    mapset=grass.gisenv()['MAPSET'])
        if existsMap["name"]:
            dlg = wx.MessageDialog(parent=self.guiparent,
                                   message=_("Vector map %s already exists. " +
                                             "Do you want to overwrite it?") %
                                   (existsMap["fullname"]),
                                   caption=_("Overwrite vector map"),
                                   style=wx.YES_NO | wx.NO_DEFAULT
                                   | wx.ICON_QUESTION | wx.CENTRE)
            ret = dlg.ShowModal()
            if ret == wx.ID_NO:
                dlg.Destroy()
                return
            dlg.Destroy()

        RunCommand("g.copy",
                   overwrite=True,
                   vector=[self.results["vect_map"].GetVectMapName(), mapName])

        if len(self.giface.GetLayerList().GetLayersByName(mapName)) == 0:
            # TODO: get rid of insert
            cmd, cmd_colors = self.vnet_data.GetLayerStyle()
            cmd.insert(0, 'd.vect')
            cmd.append('map=%s' % mapName)

            self.giface.GetLayerList().AddLayer(ltype="vector",
                                                name=mapName,
                                                cmd=cmd,
                                                checked=True)
            if cmd_colors:
                layerStyleVnetColors = cmdlist_to_tuple(cmd_colors)

                RunCommand(layerStyleVnetColors[0], **layerStyleVnetColors[1])
        else:
            self.giface.updateMap.emit(render=True, renderVector=True)
Exemplo n.º 11
0
class ToolSwitcher:
    """Class handling switching tools in toolbar and custom toggle buttons."""
    def __init__(self):
        self._groups = defaultdict(lambda: defaultdict(list))
        self._toolsGroups = defaultdict(list)
        
        # emitted when tool is changed
        self.toggleToolChanged = Signal('ToolSwitcher.toggleToolChanged')

    def AddToolToGroup(self, group, toolbar, tool):
        """Adds tool from toolbar to group of exclusive tools.
        
        :param group: name of group (e.g. 'mouseUse')
        :param toolbar: instance of toolbar
        :param tool: id of a tool from the toolbar
        """
        self._groups[group][toolbar].append(tool)
        self._toolsGroups[tool].append(group)
        
    def AddCustomToolToGroup(self, group, btnId, toggleHandler):
        """Adds custom tool from to group of exclusive tools (some toggle button).
        
        :param group: name of group (e.g. 'mouseUse')
        :param btnId: id of a tool (typically button)
        :param toggleHandler: handler to be called to switch the button
        """
        self._groups[group]['custom'].append((btnId, toggleHandler))
        self._toolsGroups[btnId].append(group)
       
    def RemoveCustomToolFromGroup(self, tool):
        """Removes custom tool from group.

        :param tool: id of the button
        """
        if not tool in self._toolsGroups:
            return
        for group in self._toolsGroups[tool]:
            self._groups[group]['custom'] = \
                [(bid, hdlr) for (bid, hdlr)
                in self._groups[group]['custom'] if bid != tool]
        
    def RemoveToolbarFromGroup(self, group, toolbar):
        """Removes toolbar from group.
        
        Before toolbar is destroyed, it must be removed from group, too.
        Otherwise we can expect some DeadObject errors.
        
        :param group: name of group (e.g. 'mouseUse')
        :param toolbar: instance of toolbar
        """
        for tb in self._groups[group]:
            if tb == toolbar:
                del self._groups[group][tb]
                break

    def IsToolInGroup(self, tool, group):
        """Checks whether a tool is in a specified group.

        :param tool: tool id
        :param group: name of group (e.g. 'mouseUse')
        """
        for group in self._toolsGroups[tool]:
            for tb in self._groups[group]:
                if tb.FindById(tool):
                    return True
        return False

    def ToolChanged(self, tool):
        """When any tool/button is pressed, other tools from group must be unchecked.
        
        :param tool: id of a tool/button
        """
        for group in self._toolsGroups[tool]:
            for tb in self._groups[group]:
                if tb == 'custom':
                    for btnId, handler in self._groups[group][tb]:
                        if btnId != tool:
                            handler(False)
                else:
                    for tl in self._groups[group][tb]:
                        if tb.FindById(tl):  # check if still exists
                            if tl != tool:
                                tb.ToggleTool(tl, False)
                            else:
                                tb.ToggleTool(tool, True)

        self.toggleToolChanged.emit(id=tool)
Exemplo n.º 12
0
class RDigitController(wx.EvtHandler):
    def __init__(self, giface, mapWindow):
        wx.EvtHandler.__init__(self)
        self._giface = giface
        self._mapWindow = mapWindow

        self._thread = gThread()
        self._editedRaster = None
        self._backgroundRaster = None
        self._backupRasterName = None
        self._areas = None
        self._lines = None
        self._points = None
        self._all = []
        self._drawing = False
        self._running = False
        self._drawColor = wx.GREEN
        self._drawTransparency = 100
        self._graphicsType = 'area'
        self._currentCellValue = None
        self._currentWidthValue = None

        self._oldMouseUse = None
        self._oldCursor = None

        self.newRasterCreated = Signal('RDigitController:newRasterCreated')
        self.newFeatureCreated = Signal('RDigitController:newFeatureCreated')
        self.uploadMapCategories = Signal('RDigitController:uploadMapCategories')
        self.quitDigitizer = Signal('RDigitController:quitDigitizer')
        self.showNotification = Signal('RDigitController:showNotification')

    def _connectAll(self):
        self._mapWindow.mouseLeftDown.connect(self._start)
        self._mapWindow.mouseLeftUp.connect(self._addPoint)
        self._mapWindow.mouseRightUp.connect(self._finish)
        self._mapWindow.Unbind(wx.EVT_CONTEXT_MENU)

    def _disconnectAll(self):
        self._mapWindow.mouseLeftDown.disconnect(self._start)
        self._mapWindow.mouseLeftUp.disconnect(self._addPoint)
        self._mapWindow.mouseRightUp.connect(self._finish)
        self._mapWindow.Bind(wx.EVT_CONTEXT_MENU, self._mapWindow.OnContextMenu)

    def _start(self, x, y):
        if self._running:
            return

        if not self._editedRaster:
            GMessage(parent=self._mapWindow, message=_("Please select first the raster map"))
            return
        if not self._drawing:
            if self._graphicsType == 'area':
                item = self._areas.AddItem(coords=[])
                item.SetPropertyVal('penName', 'pen1')
                self._all.append(item)
            elif self._graphicsType == 'line':
                item = self._lines.AddItem(coords=[])
                item.SetPropertyVal('penName', 'pen1')
                self._all.append(item)
            elif self._graphicsType == 'point':
                item = self._points.AddItem(coords=[])
                item.SetPropertyVal('penName', 'pen1')
                self._all.append(item)
            self._drawing = True

    def _addPoint(self, x, y):
        if self._running:
            return

        if not self._drawing:
            return

        if self._graphicsType == 'area':
            area = self._areas.GetItem(-1)
            coords = area.GetCoords() + [[x, y]]
            area.SetCoords(coords)
            self.showNotification.emit(text=_("Right click to finish area"))
        elif self._graphicsType == 'line':
            line = self._lines.GetItem(-1)
            coords = line.GetCoords() + [[x, y]]
            line.SetCoords(coords)
            self.showNotification.emit(text=_("Right click to finish line"))
        elif self._graphicsType == 'point':
            point = self._points.GetItem(-1)
            point.SetCoords([x, y])
            self._finish(x, y)
        # draw
        self._mapWindow.ClearLines()
        self._lines.Draw(pdc=self._mapWindow.pdcTmp)
        self._areas.Draw(pdc=self._mapWindow.pdcTmp)
        self._points.Draw(pdc=self._mapWindow.pdcTmp)
        self._mapWindow.Refresh()

    def _finish(self, x, y):
        if self._running:
            return

        if self._graphicsType == 'point':
            item = self._points.GetItem(-1)
        elif self._graphicsType == 'area':
            item = self._areas.GetItem(-1)
        elif self._graphicsType == 'line':
            item = self._lines.GetItem(-1)

        self._drawing = False
        item.SetPropertyVal('brushName', 'done')
        item.AddProperty('cellValue')
        item.AddProperty('widthValue')
        item.SetPropertyVal('cellValue', self._currentCellValue)
        item.SetPropertyVal('widthValue', self._currentWidthValue)
        self.newFeatureCreated.emit()

        self._mapWindow.ClearLines()
        self._points.Draw(pdc=self._mapWindow.pdcTmp)
        self._areas.Draw(pdc=self._mapWindow.pdcTmp)
        self._lines.Draw(pdc=self._mapWindow.pdcTmp)

        self._mapWindow.Refresh()

    def SelectType(self, drawingType):
        if self._graphicsType and not drawingType:
            self._mapWindow.ClearLines(pdc=self._mapWindow.pdcTmp)
            self._mapWindow.mouse['end'] = self._mapWindow.mouse['begin']
            # disconnect mouse events
            self._disconnectAll()
            self._mapWindow.SetNamedCursor(self._oldCursor)
            self._mapWindow.mouse['use'] = self._oldMouseUse
        elif self._graphicsType is None and drawingType:
            self._connectAll()
            # change mouse['box'] and pen to draw line during dragging
            # TODO: better solution for drawing this line
            self._mapWindow.mouse['use'] = None
            self._mapWindow.mouse['box'] = "line"
            self._mapWindow.pen = wx.Pen(colour='red', width=2, style=wx.SHORT_DASH)
             # change the cursor
            self._mapWindow.SetNamedCursor('pencil')

        self._graphicsType = drawingType

    def SetCellValue(self, value):
        self._currentCellValue = value

    def SetWidthValue(self, value):
        self._currentWidthValue = value

    def ChangeDrawColor(self, color):
        self._drawColor = color[:3] + (self._drawTransparency,)
        for each in (self._areas, self._lines, self._points):
            each.GetPen('pen1').SetColour(self._drawColor)
            each.GetBrush('done').SetColour(self._drawColor)
        self._mapWindow.UpdateMap(render=False)

    def Start(self):
        """register graphics to map window,
        connect required mouse signals.
        """
        self._oldMouseUse = self._mapWindow.mouse['use']
        self._oldCursor = self._mapWindow.GetNamedCursor()

        self._connectAll()

        # change mouse['box'] and pen to draw line during dragging
        # TODO: better solution for drawing this line
        self._mapWindow.mouse['use'] = None
        self._mapWindow.mouse['box'] = "line"
        self._mapWindow.pen = wx.Pen(colour='red', width=2, style=wx.SHORT_DASH)

        color = self._drawColor[:3] + (self._drawTransparency,)
        self._areas = self._mapWindow.RegisterGraphicsToDraw(graphicsType='polygon',
                                                             mapCoords=True)
        self._areas.AddPen('pen1', wx.Pen(colour=color, width=2, style=wx.SOLID))
        self._areas.AddBrush('done', wx.Brush(colour=color, style=wx.SOLID))

        self._lines = self._mapWindow.RegisterGraphicsToDraw(graphicsType='line',
                                                             mapCoords=True)
        self._lines.AddPen('pen1', wx.Pen(colour=color, width=2, style=wx.SOLID))
        self._lines.AddBrush('done', wx.Brush(colour=color, style=wx.SOLID))

        self._points = self._mapWindow.RegisterGraphicsToDraw(graphicsType='point',
                                                              mapCoords=True)
        self._points.AddPen('pen1', wx.Pen(colour=color, width=2, style=wx.SOLID))
        self._points.AddBrush('done', wx.Brush(colour=color, style=wx.SOLID))

        # change the cursor
        self._mapWindow.SetNamedCursor('pencil')

    def Stop(self):
        dlg = wx.MessageDialog(self._mapWindow, _("Do you want to save edits?"),
                               _("Save raster map edits"), wx.YES_NO)
        if dlg.ShowModal() == wx.ID_YES:
            self._running = True
            self._thread.Run(callable=self._exportRaster,
                             ondone=lambda event: self._updateAndQuit())
        else:
            self.quitDigitizer.emit()

    def Save(self):
        self._thread.Run(callable=self._exportRaster,
                         ondone=lambda event: self._update())

    def Undo(self):
        if len(self._all):
            removed = self._all.pop(-1)
            # try to remove from each, it fails quietly when theitem is not there
            self._areas.DeleteItem(removed)
            self._lines.DeleteItem(removed)
            self._points.DeleteItem(removed)
            self._drawing = False
            self._mapWindow.UpdateMap(render=False)

    def CleanUp(self, restore=True):
        """
        :param restore: if restore previous cursor, mouse['use']
        """
        try:
            gcore.run_command('g.remove', type='rast', flags='f', name=self._backupRasterName, quiet=True)
        except CalledModuleError:
            pass

        self._mapWindow.ClearLines(pdc=self._mapWindow.pdcTmp)
        self._mapWindow.mouse['end'] = self._mapWindow.mouse['begin']
        # disconnect mouse events
        if self._graphicsType:
            self._disconnectAll()
        # unregister
        self._mapWindow.UnregisterGraphicsToDraw(self._areas)
        self._mapWindow.UnregisterGraphicsToDraw(self._lines)
        self._mapWindow.UnregisterGraphicsToDraw(self._points)
        #self._registeredGraphics = None
        self._mapWindow.UpdateMap(render=False)

        if restore:
            # restore mouse['use'] and cursor to the state before measuring starts
            self._mapWindow.SetNamedCursor(self._oldCursor)
            self._mapWindow.mouse['use'] = self._oldMouseUse

    def _updateAndQuit(self):
        self._running = False
        self._mapWindow.UpdateMap(render=True)
        self.quitDigitizer.emit()

    def _update(self):
        self._running = False
        self._mapWindow.UpdateMap(render=True)

    def SelectOldMap(self, name):
        try:
            self._backupRaster(name)
        except ScriptError:
            GError(parent=self._mapWindow, message=_("Failed to create backup copy of edited raster map."))
            return False
        self._editedRaster = name
        return True

    def SelectNewMap(self):
        dlg = NewRasterDialog(parent=self._mapWindow)
        if dlg.ShowModal() == wx.ID_OK:
            try:
                self._createNewMap(mapName=dlg.GetMapName(),
                                   backgroundMap=dlg.GetBackgroundMapName(),
                                   mapType=dlg.GetMapType())
            except ScriptError:
                GError(parent=self._mapWindow, message=_("Failed to create new raster map."))
                return False
            finally:
                dlg.Destroy()
            return True
        else:
            dlg.Destroy()
            return False

    def _createNewMap(self, mapName, backgroundMap, mapType):
        name = mapName.split('@')[0]
        background = backgroundMap.split('@')[0]
        types = {'CELL': 'int', 'FCELL': 'float', 'DCELL': 'double'}
        if background:
            back = background
        else:
            back = 'null()'
        try:
            grast.mapcalc(exp="{name} = {mtype}({back})".format(name=name, mtype=types[mapType],
                                                                back=back),
                          overwrite=True, quiet=True)
            if background:
                self._backgroundRaster = backgroundMap
                if mapType == 'CELL':
                    values = gcore.read_command('r.describe', flags='1n',
                                                map=backgroundMap, quiet=True).strip()
                    if values:
                        self.uploadMapCategories.emit(values=values.split('\n'))
        except CalledModuleError:
            raise ScriptError
        self._backupRaster(name)

        name = name + '@' + gcore.gisenv()['MAPSET']
        self._editedRaster = name
        self.newRasterCreated.emit(name=name)

    def _backupRaster(self, name):
        name = name.split('@')[0]
        backup = name + '_backupcopy_' + str(os.getpid())
        try:
            gcore.run_command('g.copy', rast=[name, backup], quiet=True)
        except CalledModuleError:
            raise ScriptError

        self._backupRasterName = backup

    def _exportRaster(self):
        if not self._editedRaster:
            return

        if len(self._all) < 1:
            return
        tempRaster = 'tmp_rdigit_rast_' + str(os.getpid())
        text = []
        rastersToPatch = []
        i = 0
        lastCellValue = lastWidthValue = None
        evt = updateProgress(range=len(self._all), value=0, text=_("Rasterizing..."))
        wx.PostEvent(self, evt)
        lastCellValue = self._all[0].GetPropertyVal('cellValue')
        lastWidthValue = self._all[0].GetPropertyVal('widthValue')
        for item in self._all:
            if item.GetPropertyVal('widthValue') and \
                (lastCellValue != item.GetPropertyVal('cellValue') or
                lastWidthValue != item.GetPropertyVal('widthValue')):
                if text:
                    out = self._rasterize(text, lastWidthValue, tempRaster)
                    rastersToPatch.append(out)
                    text = []
                self._writeItem(item, text)
                out = self._rasterize(text, item.GetPropertyVal('widthValue'),
                                      tempRaster)
                rastersToPatch.append(out)
                text = []
            else:
                self._writeItem(item, text)
            lastCellValue = item.GetPropertyVal('cellValue')
            lastWidthValue = item.GetPropertyVal('widthValue')

            i += 1
            evt = updateProgress(range=len(self._all), value=i, text=_("Rasterizing..."))
            wx.PostEvent(self, evt)
        if text:
            out = self._rasterize(text, item.GetPropertyVal('widthValue'),
                                  tempRaster)
            rastersToPatch.append(out)

        gcore.run_command('r.patch', input=sorted(rastersToPatch, reverse=True) + [self._backupRasterName],
                          output=self._editedRaster, overwrite=True, quiet=True)
        gcore.run_command('g.remove', type='rast', flags='f', name=rastersToPatch + [tempRaster],
                          quiet=True)
        try:
            if not self._backgroundRaster:
                table = UserSettings.Get(group='rasterLayer', key='colorTable', subkey='selection')
                gcore.run_command('r.colors', color=table, map=self._editedRaster, quiet=True)
            else:
                gcore.run_command('r.colors', map=self._editedRaster,
                                  raster=self._backgroundRaster, quiet=True)
        except CalledModuleError:
            GError(parent=self._mapWindow,
                   message=_("Failed to set default color table for edited raster map"))

    def _writeFeature(self, item, vtype, text):
        coords = item.GetCoords()
        if vtype == 'P':
            coords = [coords]
        cellValue = item.GetPropertyVal('cellValue')
        record = '{vtype}\n'.format(vtype=vtype)
        for coord in coords:
            record += ' '.join([str(c) for c in coord])
            record += '\n'
        record += '= {cellValue}\n'.format(cellValue=cellValue)

        text.append(record)

    def _writeItem(self, item, text):
        if item in self._areas.GetAllItems():
            self._writeFeature(item, vtype='A', text=text)
        elif item in self._lines.GetAllItems():
            self._writeFeature(item, vtype='L', text=text)
        elif item in self._points.GetAllItems():
            self._writeFeature(item, vtype='P', text=text)

    def _rasterize(self, text, bufferDist, tempRaster):
        output = 'x' + str(uuid.uuid4())[:8]
        asciiFile = tempfile.NamedTemporaryFile(delete=False)
        asciiFile.write('\n'.join(text))
        asciiFile.close()
        if bufferDist:
            gcore.run_command('r.in.poly', input=asciiFile.name, output=tempRaster,
                              overwrite=True, quiet=True)
            gcore.run_command('r.grow', input=tempRaster, output=output,
                              flags='m', radius=bufferDist, quiet=True)
        else:
            gcore.run_command('r.in.poly', input=asciiFile.name, output=output,
                              quiet=True)
        os.unlink(asciiFile.name)
        return output
Exemplo n.º 13
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
Exemplo n.º 14
0
class LayersList(TreeListCtrl, listmix.ListCtrlAutoWidthMixin):

    def __init__(self, parent, web_service, style, pos=wx.DefaultPosition):
        """List of layers and styles available in capabilities file
        """
        self.parent = parent
        self.ws = web_service

        TreeListCtrl.__init__(self, parent=parent, id=wx.ID_ANY, style=style)

        # setup mixins
        listmix.ListCtrlAutoWidthMixin.__init__(self)
        if self.ws != 'OnEarth':
            self.AddColumn(_('Name'))
            self.AddColumn(_('Type'))
        else:
            self.AddColumn(_('Layer name'))

        self.SetMainColumn(0)  # column with the tree
        self.setResizeColumn(0)

        self.root = None
        self.Bind(wx.EVT_TREE_SEL_CHANGING, self.OnListSelChanging)

        self.layerSelected = Signal('LayersList.layerSelected')

    def LoadData(self, cap=None):
        """Load data into list
        """
        # detete first all items
        self.DeleteAllItems()

        if not cap:
            return

        def AddLayerChildrenToTree(parent_layer, parent_item):
            """Recursive function which adds all capabilities
            layers/styles to the LayersList.
            """
            def gettitle(layer):
                """Helper function"""
                if layer.GetLayerData('title') is not None:
                    layer_title = layer.GetLayerData('title')
                elif layer.GetLayerData('name') is not None:
                    layer_title = layer.GetLayerData('name')
                else:
                    layer_title = str(layer.GetId())

                return layer_title

            def addlayer(layer, item):

                if self.ws != 'OnEarth':
                    self.SetItemText(item, _('layer'), 1)

                styles = layer.GetLayerData('styles')

                def_st = None
                for st in styles:

                    if st['name']:
                        style_name = st['name']
                    else:
                        continue

                    if st['title']:
                        style_name = st['title']

                    if st['isDefault']:
                        def_st = st

                    style_item = self.AppendItem(item, style_name)
                    if self.ws != 'OnEarth':
                        self.SetItemText(style_item, _('style'), 1)

                    self.SetPyData(style_item, {'type': 'style',
                                                'layer': layer,  # it is parent layer of style
                                                'style': st})

                self.SetPyData(item, {'type': 'layer',  # is it layer or style?
                                      'layer': layer,  # Layer instance from web_services.cap_interface
                                      'style': def_st})  # layer can have assigned default style

            if parent_layer is None:
                parent_layer = cap.GetRootLayer()
                layer_title = gettitle(parent_layer)
                parent_item = self.AddRoot(layer_title)
                addlayer(parent_layer, parent_item)

            for layer in parent_layer.GetChildren():
                item = self.AppendItem(parent_item, gettitle(layer))
                addlayer(layer, item)
                AddLayerChildrenToTree(layer, item)

        AddLayerChildrenToTree(None, None)
        # self.ExpandAll(self.GetRootItem())

    def GetSelectedLayers(self):
        """Get selected layers/styles in LayersList

        :return: dict with these items:
                 * 'name'  : layer name used for request
                   if it is style, it is name of parent layer
                 * 'title' : layer title
                 * 'style' : {'name' : 'style name', title : 'style title'}
                 * 'cap_intf_l' : \*Layer instance from web_services.cap_interface
        """
        sel_layers = self.GetSelections()
        sel_layers_dict = []
        for s in sel_layers:
            try:
                layer = self.GetPyData(s)['layer']
            except ValueError:
                continue
            sel_layers_dict.append({
                'name': layer.GetLayerData('name'),
                'title': layer.GetLayerData('title'),
                'style': self.GetPyData(s)['style'],
                'cap_intf_l': layer
            })
        return sel_layers_dict

    def OnListSelChanging(self, event):
        """Do not allow selecting items, which cannot be requested from server.
        """
        def _emitSelected(layer):
            title = layer.GetLayerData('title')
            self.layerSelected.emit(title=title)

        def _selectRequestableChildren(item, list_to_check, items_to_sel):

            self.Expand(item)
            child_item, cookie = self.GetFirstChild(item)
            while child_item.IsOk():
                if  self.GetPyData(child_item)['layer'].IsRequestable() \
                        and not self.IsSelected(child_item):
                    items_to_sel.append(child_item)
                elif not self.GetPyData(child_item)['layer'].IsRequestable():
                    list_to_check.append(child_item)

                child_item, cookie = self.GetNextChild(item, cookie)

        cur_item = event.GetItem()
        if not self.GetPyData(cur_item)['layer'].IsRequestable():
            event.Veto()

            if not self.HasFlag(wx.TR_MULTIPLE):
                return

            _emitSelected(self.GetPyData(cur_item)['layer'])

            items_to_chck = []
            items_to_sel = []
            chck_item = cur_item

            while True:
                _selectRequestableChildren(
                    chck_item, items_to_chck, items_to_sel)
                if items_to_chck:
                    chck_item = items_to_chck.pop()
                else:
                    break

            while items_to_sel:
                self.SelectItem(items_to_sel.pop(), unselect_others=False)
        else:
            _emitSelected(self.GetPyData(cur_item)['layer'])

    def GetItemCount(self):
        """Required for listmix.ListCtrlAutoWidthMixin
        """
        return 0

    def GetCountPerPage(self):
        """Required for listmix.ListCtrlAutoWidthMixin
        """
        return 0

    def SelectLayers(self, l_st_list):
        """Select layers/styles in LayersList

        :param l_st_list: [{style : 'style_name', layer : 'layer_name'}, ...]
        :return: items from l_st_list which were not found
        """
        def checknext(item, l_st_list, items_to_sel):
            def compare(item, l_name, st_name):
                it_l_name = self.GetPyData(item)['layer'].GetLayerData('name')
                it_st = self.GetPyData(item)['style']
                it_type = self.GetPyData(item)['type']

                if it_l_name == l_name and ((not it_st and not st_name) or (
                        it_st and it_st['name'] == st_name and it_type == 'style')):

                    return True

                return False

            for i, l_st in enumerate(l_st_list):
                l_name = l_st['layer']
                st_name = l_st['style']

                if compare(item, l_name, st_name):
                    items_to_sel[i] = [item, l_st]
                    break

            if len(items_to_sel) == len(l_st_list):
                item = self.GetNext(item)
                if not item.IsOk():
                    return
                checknext(item, l_st_list, items_to_sel)

        self.UnselectAll()

        l_st_list = deepcopy(l_st_list)
        root_item = self.GetRootItem()

        items_to_sel = [None] * len(l_st_list)
        checknext(root_item, l_st_list, items_to_sel)

        # items are selected according to position in l_st_list
        # to be added to Layers order list in right order
        for i in items_to_sel:
            if not i:
                continue

            item, l_st = i
            un_o = True
            if self.HasFlag(wx.TR_MULTIPLE):
                un_o = False

            self.SelectItem(item, unselect_others=un_o)
            l_st_list.remove(l_st)

        return l_st_list
Exemplo n.º 15
0
class WSPanel(wx.Panel):

    def __init__(self, parent, web_service, **kwargs):
        """Show data from capabilities file.

        Signal: capParsed - this signal is emitted when capabilities file is downloaded
                            (after ConnectToServer method was called)

        :param parent:  parent widget
        :param web_service:  web service to be panel generated for
        """
        wx.Panel.__init__(self, parent=parent, id=wx.ID_ANY)

        self.parent = parent
        self.ws = web_service

        self.capParsed = Signal('WSPanel.capParsed')

        # stores widgets, which represents parameters/flags of d.wms
        self.params = {}
        self.flags = {}

        self.o_layer_name = ''

        # stores err output from r.in.wms during getting capabilities
        self.cmd_err_str = ''

        # stores selected layer from layer list
        self.sel_layers = []

        # downloaded and parsed data from server successfully?
        self.is_connected = False

        # common part of command for r.in.wms -c and d.wms
        self.ws_cmdl = None

        # provides information about driver parameters
        self.drv_info = WMSDriversInfo()
        self.drv_props = self.drv_info.GetDrvProperties(self.ws)

        self.ws_drvs = {
            'WMS_1.1.1': {
                'cmd': [
                    'wms_version=1.1.1',
                    'driver=WMS_GRASS'],
                'cap_parser': lambda temp_file: WMSCapabilities(
                    temp_file,
                    '1.1.1'),
            },
            'WMS_1.3.0': {
                'cmd': [
                    'wms_version=1.3.0',
                    'driver=WMS_GRASS'],
                'cap_parser': lambda temp_file: WMSCapabilities(
                    temp_file,
                    '1.3.0'),
            },
            'WMTS': {
                'cmd': ['driver=WMTS_GRASS'],
                'cap_parser': WMTSCapabilities,
            },
            'OnEarth': {
                'cmd': ['driver=OnEarth_GRASS'],
                'cap_parser': OnEarthCapabilities,
            }}

        self.cmdStdErr = GStderr(self)
        self.cmd_thread = CmdThread(self)
        self.cap_file = grass.tempfile()

        reqDataBox = wx.StaticBox(
            parent=self, label=_(" Requested data settings "))
        self._nb_sizer = wx.StaticBoxSizer(reqDataBox, wx.VERTICAL)
        self.notebook = GNotebook(parent=self,
                                  style=FN.FNB_FANCY_TABS | FN.FNB_NO_X_BUTTON)

        self._requestPage()
        self._advancedSettsPage()

        self._layout()

        self.layerSelected = self.list.layerSelected

        self.Bind(EVT_CMD_DONE, self.OnCapDownloadDone)
        self.Bind(EVT_CMD_OUTPUT, self.OnCmdOutput)

    def __del__(self):
        self.cmd_thread.abort(abortall=True)
        grass.try_remove(self.cap_file)

    def _layout(self):
        self._nb_sizer.Add(item=self.notebook, proportion=1, flag=wx.EXPAND)
        self.SetSizer(self._nb_sizer)

    def _requestPage(self):
        """Create request page"""
        self.req_page_panel = wx.Panel(parent=self, id=wx.ID_ANY)
        self.notebook.AddPage(page=self.req_page_panel,
                              text=_('Request'),
                              name='request')

        # list of layers
        self.layersBox = wx.StaticBox(parent=self.req_page_panel, id=wx.ID_ANY,
                                      label=_("List of layers "))

        style = wx.TR_DEFAULT_STYLE | wx.TR_HAS_BUTTONS | wx.TR_FULL_ROW_HIGHLIGHT
        if self.drv_props['req_multiple_layers']:
            style = style | wx.TR_MULTIPLE
        if 'WMS' not in self.ws:
            style = style | wx.TR_HIDE_ROOT

        self.list = LayersList(parent=self.req_page_panel,
                               web_service=self.ws,
                               style=style)

        self.params['format'] = None

        self.params['srs'] = None
        if 'srs' not in self.drv_props['ignored_params']:
            projText = wx.StaticText(
                parent=self.req_page_panel, id=wx.ID_ANY,
                label=_("Source projection:"))
            self.params['srs'] = wx.Choice(
                parent=self.req_page_panel, id=wx.ID_ANY)

        self.list.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnListSelChanged)

        # layout
        self.req_page_sizer = wx.BoxSizer(wx.VERTICAL)

        layersSizer = wx.StaticBoxSizer(self.layersBox, wx.HORIZONTAL)

        layersSizer.Add(
            item=self.list,
            proportion=1,
            flag=wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND,
            border=5)

        self.req_page_sizer.Add(
            item=layersSizer,
            proportion=1,
            flag=wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND,
            border=5)

        self.source_sizer = wx.BoxSizer(wx.HORIZONTAL)

        if self.params['format'] is not None:
            self.source_sizer.Add(
                item=self.params['format'],
                flag=wx.LEFT | wx.RIGHT | wx.BOTTOM,
                border=5)

        if self.params['srs'] is not None:
            self.source_sizer.Add(
                item=projText,
                flag=wx.ALIGN_CENTER_VERTICAL | wx.ALL,
                border=5)
            self.source_sizer.Add(
                item=self.params['srs'],
                flag=wx.ALIGN_CENTER_VERTICAL | wx.RIGHT | wx.TOP | wx.BOTTOM,
                border=5)

        self.req_page_sizer.Add(
            item=self.source_sizer,
            flag=wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND,
            border=5)

        self.req_page_panel.SetSizer(self.req_page_sizer)

    def enableButtons(self, enable=True):
        """Enable/disable up, down, buttons
        """
        self.btnUp.Enable(enable)
        self.btnDown.Enable(enable)

    def _advancedSettsPage(self):
        """Create advanced settings page
        """
        # TODO parse maxcol, maxrow, settings from d.wms module?
        # TODO OnEarth driver - add selection of time
        adv_setts_panel = wx.Panel(parent=self, id=wx.ID_ANY)
        self.notebook.AddPage(page=adv_setts_panel,
                              text=_('Advanced request settings'),
                              name='adv_req_setts')

        labels = {}
        self.l_odrder_list = None
        if 'WMS' in self.ws:
            labels['l_order'] = wx.StaticBox(
                parent=adv_setts_panel, id=wx.ID_ANY,
                label=_("Order of layers in raster"))
            self.l_odrder_list = wx.ListBox(
                adv_setts_panel, id=wx.ID_ANY, choices=[],
                style=wx.LB_SINGLE | wx.LB_NEEDED_SB)
            self.btnUp = wx.Button(
                adv_setts_panel, id=wx.ID_ANY, label=_("Up"))
            self.btnDown = wx.Button(
                adv_setts_panel, id=wx.ID_ANY, label=_("Down"))

            self.btnUp.Bind(wx.EVT_BUTTON, self.OnUp)
            self.btnDown.Bind(wx.EVT_BUTTON, self.OnDown)

        labels['method'] = wx.StaticText(parent=adv_setts_panel, id=wx.ID_ANY,
                                         label=_("Reprojection method:"))

        self.reproj_methods = ['nearest', 'linear', 'cubic', 'cubicspline']
        self.params['method'] = wx.Choice(
            parent=adv_setts_panel,
            id=wx.ID_ANY,
            choices=[
                _('Nearest neighbor'),
                _('Linear interpolation'),
                _('Cubic interpolation'),
                _('Cubic spline interpolation')])

        labels['maxcols'] = wx.StaticText(
            parent=adv_setts_panel, id=wx.ID_ANY,
            label=_("Maximum columns to request from server at time:"))
        self.params['maxcols'] = wx.SpinCtrl(
            parent=adv_setts_panel, id=wx.ID_ANY, size=(100, -1))

        labels['maxrows'] = wx.StaticText(
            parent=adv_setts_panel, id=wx.ID_ANY,
            label=_("Maximum rows to request from server at time:"))
        self.params['maxrows'] = wx.SpinCtrl(
            parent=adv_setts_panel, id=wx.ID_ANY, size=(100, -1))

        min = 100
        max = 10000
        self.params['maxcols'].SetRange(min, max)
        self.params['maxrows'].SetRange(min, max)

        val = 500
        self.params['maxcols'].SetValue(val)
        self.params['maxrows'].SetValue(val)

        self.flags['o'] = self.params['bgcolor'] = None
        if not 'o' in self.drv_props['ignored_flags']:
            self.flags['o'] = wx.CheckBox(
                parent=adv_setts_panel, id=wx.ID_ANY,
                label=_("Do not request transparent data"))

            self.flags['o'].Bind(wx.EVT_CHECKBOX, self.OnTransparent)
            labels['bgcolor'] = wx.StaticText(
                parent=adv_setts_panel, id=wx.ID_ANY,
                label=_("Background color:"))
            self.params['bgcolor'] = csel.ColourSelect(
                parent=adv_setts_panel, id=wx.ID_ANY, colour=(
                    255, 255, 255), size=globalvar.DIALOG_COLOR_SIZE)
            self.params['bgcolor'].Enable(False)

        self.params['urlparams'] = None
        if self.params['urlparams'] not in self.drv_props['ignored_params']:
            labels['urlparams'] = wx.StaticText(
                parent=adv_setts_panel, id=wx.ID_ANY,
                label=_("Additional query parameters for server:"))
            self.params['urlparams'] = wx.TextCtrl(
                parent=adv_setts_panel, id=wx.ID_ANY)

        # layout

        border = wx.BoxSizer(wx.VERTICAL)

        if 'WMS' in self.ws:

            boxSizer = wx.StaticBoxSizer(labels['l_order'], wx.VERTICAL)
            gridSizer = wx.GridBagSizer(hgap=3, vgap=3)

            gridSizer.Add(self.l_odrder_list,
                          pos=(0, 0),
                          span=(4, 1),
                          flag=wx.ALIGN_CENTER_VERTICAL | wx.EXPAND,
                          border=0)

            gridSizer.Add(self.btnUp,
                          pos=(0, 1),
                          flag=wx.ALIGN_CENTER_VERTICAL,
                          border=0)

            gridSizer.Add(self.btnDown,
                          pos=(1, 1),
                          flag=wx.ALIGN_CENTER_VERTICAL,
                          border=0)

            gridSizer.AddGrowableCol(0)
            boxSizer.Add(gridSizer,
                         flag=wx.EXPAND | wx.ALL,
                         border=5)

            border.Add(item=boxSizer,
                       flag=wx.LEFT | wx.RIGHT | wx.UP | wx.EXPAND,
                       border=5)

        gridSizer = wx.GridBagSizer(hgap=3, vgap=3)

        row = 0
        for k in ['method', 'maxcols', 'maxrows', 'o', 'bgcolor']:

            if k in self.params:
                param = self.params[k]
            elif k in self.flags:
                param = self.flags[k]

            if param is None:
                continue

            if k in labels or k == 'o':
                if k != 'o':
                    label = labels[k]
                else:
                    label = param

                gridSizer.Add(label,
                              flag=wx.ALIGN_LEFT |
                              wx.ALIGN_CENTER_VERTICAL,
                              pos=(row, 0))

            if k != 'o':
                gridSizer.Add(item=param,
                              flag=wx.ALIGN_RIGHT |
                              wx.ALIGN_CENTER_VERTICAL,
                              pos=(row, 1))
            row += 1

        gridSizer.AddGrowableCol(0)
        border.Add(item=gridSizer,
                   flag=wx.LEFT | wx.RIGHT | wx.TOP | wx.EXPAND,
                   border=5)

        if self.params['urlparams']:
            gridSizer = wx.GridBagSizer(hgap=3, vgap=3)

            row = 0
            gridSizer.Add(labels['urlparams'],
                          flag=wx.ALIGN_LEFT |
                          wx.ALIGN_CENTER_VERTICAL,
                          pos=(row, 0))

            gridSizer.Add(item=self.params['urlparams'],
                          flag=wx.ALIGN_RIGHT |
                          wx.ALIGN_CENTER_VERTICAL | wx.EXPAND,
                          pos=(row, 1))

            gridSizer.AddGrowableCol(1)

            border.Add(item=gridSizer,
                       flag=wx.LEFT | wx.RIGHT | wx.TOP | wx.EXPAND,
                       border=5)

        adv_setts_panel.SetSizer(border)

    def OnUp(self, event):
        """Move selected layer up
        """
        if self.l_odrder_list.GetSelections():
            pos = self.l_odrder_list.GetSelection()
            if pos:
                self.sel_layers.insert(pos - 1, self.sel_layers.pop(pos))
            if pos > 0:
                self._updateLayerOrderList(selected=(pos - 1))
            else:
                self._updateLayerOrderList(selected=0)

    def OnDown(self, event):
        """Move selected to down
        """
        if self.l_odrder_list.GetSelections():
            pos = self.l_odrder_list.GetSelection()
            if pos != len(self.sel_layers) - 1:
                self.sel_layers.insert(pos + 1, self.sel_layers.pop(pos))
            if pos < len(self.sel_layers) - 1:
                self._updateLayerOrderList(selected=(pos + 1))
            else:
                self._updateLayerOrderList(selected=len(self.sel_layers) - 1)

    def _updateLayerOrderList(self, selected=None):
        """Update order in list.
        """
        def getlayercaption(layer):
            if l['title']:
                cap = (l['title'])
            else:
                cap = (l['name'])

            if l['style']:
                if l['style']['title']:
                    cap += ' / ' + l['style']['title']
                else:
                    cap += ' / ' + l['style']['name']
            return cap

        layer_capts = [getlayercaption(l) for l in self.sel_layers]
        self.l_odrder_list.Set(layer_capts)
        if self.l_odrder_list.IsEmpty():
            self.enableButtons(False)
        else:
            self.enableButtons(True)
            if selected is not None:
                self.l_odrder_list.SetSelection(selected)
                self.l_odrder_list.EnsureVisible(selected)

    def OnTransparent(self, event):
        checked = event.IsChecked()
        if checked:
            self.params['bgcolor'].Enable(True)
        else:
            self.params['bgcolor'].Enable(False)

    def ConnectToServer(self, url, username, password):
        """Download and parse data from capabilities file.

        :param url: server url
        :type url: str
        :param username: username for connection
        :type username: str
        :param password: password for connection
        :type password: str
        """
        self._prepareForNewConn(url, username, password)
        cap_cmd = [
            'r.in.wms',
            '-c',
            ('capfile_output=%s' % self.cap_file),
            '--overwrite'] + self.ws_cmdl

        self.currentPid = self.cmd_thread.GetId()
        self.cmd_thread.RunCmd(cap_cmd, stderr=self.cmdStdErr)

    def OnCmdOutput(self, event):
        """Manage cmd output.
        """
        if Debug.GetLevel() != 0:
            Debug.msg(1, event.text)
        elif event.type != 'message' and event.type != 'warning':
            self.cmd_err_str += event.text + os.linesep

    def _prepareForNewConn(self, url, username, password):
        """Prepare panel for new connection
        """
        self.is_connected = False

        self.sel_layers = []
        self.formats_list = []
        self.projs_list = []

        self.conn = {
            'url': url,
            'password': password,
            'username': username
        }

        conn_cmd = []
        for k, v in self.conn.iteritems():
            if v:
                conn_cmd.append("%s=%s" % (k, v))

        self.ws_cmdl = self.ws_drvs[self.ws]['cmd'] + conn_cmd

    def OnCapDownloadDone(self, event):
        """Process donwloaded capabilities file and emits capParsed
        signal (see class constructor).
        """
        if event.pid != self.currentPid:
            return

        if event.returncode != 0:
            if self.cmd_err_str:
                self.cmd_err_str = _(
                    "Unable to download %s capabilities file\nfrom <%s>:\n" %
                    (self.ws.replace('_', ' '),
                     self.conn['url'])) + self.cmd_err_str
            self._postCapParsedEvt(error_msg=self.cmd_err_str)
            self.cmd_err_str = ''
            return

        self._parseCapFile(self.cap_file)

    def _parseCapFile(self, cap_file):
        """Parse capabilities data and emits capParsed signal
        (see class constructor).
        """
        try:
            self.cap = self.ws_drvs[self.ws]['cap_parser'](cap_file)
        except (IOError, ParseError) as error:
            error_msg = _(
                "%s web service was not found in fetched capabilities file from <%s>:\n%s\n" %
                (self.ws, self.conn['url'], str(error)))
            if Debug.GetLevel() != 0:
                Debug.msg(1, error_msg)
                self._postCapParsedEvt(None)
            else:
                self._postCapParsedEvt(error_msg=error_msg)
            return

        self.is_connected = True

        # WMS standard has formats defined for all layers
        if 'WMS' in self.ws:
            self.formats_list = sorted(self._getFormats())
            self._updateFormatRadioBox(self.formats_list)
            self._setDefaultFormatVal()

        self.list.LoadData(self.cap)
        self.OnListSelChanged(event=None)

        self._postCapParsedEvt(None)

    def ParseCapFile(self, url, username, password, cap_file=None,):
        """Parse capabilities data and emits capParsed signal
        (see class constructor).
        """
        self._prepareForNewConn(url, username, password)

        if cap_file is None or not url:
            self._postCapParsedEvt(None)
            return

        shutil.copyfile(cap_file, self.cap_file)

        self._parseCapFile(self.cap_file)

    def UpdateWidgetsByCmd(self, cmd):
        """Update panel widgets accordnig to passed cmd tuple

        :param cmd: cmd in tuple
        """

        dcmd = cmd[1]

        layers = []

        if 'layers' in dcmd:
            layers = dcmd['layers']

        styles = []
        if 'styles' in dcmd:
            styles = dcmd['styles']

        if 'WMS' in self.ws:
            layers = layers.split(',')
            styles = styles.split(',')
        else:
            layers = [layers]
            styles = [styles]

        if len(layers) != len(styles):
            styles = [''] * len(layers)

        l_st_list = []
        for i in range(len(layers)):
            l_st_list.append({'style': styles[i],
                              'layer': layers[i]})

        # WMS standard - first layer in params is most bottom...
        # therefore layers order need to be reversed
        l_st_list = [l for l in reversed(l_st_list)]
        self.list.SelectLayers(l_st_list)

        params = {}
        if 'format' in dcmd:
            params['format'] = dcmd['format']
        if 'srs' in dcmd:
            params['srs'] = 'EPSG:' + dcmd['srs']
        if 'method' in dcmd:
            params['method'] = dcmd['method']

        for p, v in params.iteritems():
            if self.params[p]:
                self.params[p].SetStringSelection(v)

        for p, conv_f in [
                ('urlparams', None),
                ('maxcols', int),
                ('maxrows', int)]:
            if p in dcmd:
                v = dcmd[p]
                if conv_f:
                    v = conv_f(v)
                self.params[p].SetValue(v)

        if 'flags' in dcmd and \
           'o' in dcmd['flags']:
            self.flags['o'].SetValue(1)
            self.params['bgcolor'].Enable(True)

        if 'bgcolor' in dcmd and \
           self.params['bgcolor']:
            bgcolor = dcmd['bgcolor'].strip().lower()
            if len(bgcolor) == 8 and \
               '0x' == bgcolor[:2]:

                colour = '#' + bgcolor[2:]
                self.params['bgcolor'].SetColour(colour)

    def IsConnected(self):
        """Was successful in downloading and parsing capabilities data?
        """
        return self.is_connected

    def _postCapParsedEvt(self, error_msg):
        """Helper function
        """
        self.capParsed.emit(error_msg=error_msg)

    def CreateCmd(self):
        """Create d.wms cmd from values of panels widgets

        :return: cmd list
        :return: None if required widgets do not have selected/filled values.
        """

        # check required widgets
        if not self._checkImportValues():
            return None

        # create d.wms command
        lcmd = self.ws_cmdl
        lcmd = ['d.wms'] + lcmd

        layers = "layers="
        styles = 'styles='
        first = True

        # WMS standard - first layer in params is most bottom...
        # therefore layers order need to be reversed
        for layer in reversed(self.sel_layers):
            if not first:
                layers += ','
                styles += ','
            first = False
            layers += layer['name']
            if layer['style'] is not None:
                styles += layer['style']['name']

        lcmd.append(layers)
        lcmd.append(styles)

        if 'format' not in self.drv_props['ignored_params']:
            i_format = self.params['format'].GetSelection()
            lcmd.append("format=%s" % self.formats_list[i_format])

        if 'srs' not in self.drv_props['ignored_params']:
            i_srs = self.params['srs'].GetSelection()
            epsg_num = int(self.projs_list[i_srs].split(':')[-1])
            lcmd.append("srs=%s" % epsg_num)

        for k in ['maxcols', 'maxrows', 'urlparams']:
            lcmd.append(k + '=' + str(self.params[k].GetValue()))

        i_method = self.params['method'].GetSelection()
        lcmd.append('method=' + self.reproj_methods[i_method])

        if not 'o' in self.drv_props['ignored_flags'] and \
                self.flags['o'].IsChecked():
            lcmd.append('-o')

            c = self.params['bgcolor'].GetColour()
            hex_color = wx.Colour(
                c[0],
                c[1],
                c[2]).GetAsString(
                wx.C2S_HTML_SYNTAX)
            lcmd.append("bgcolor=" + '0x' + hex_color[1:])

        lcmd.append("map=" + self.o_layer_name)

        return lcmd

    def OnListSelChanged(self, event):
        """Update widgets according to selected layer in list.
        """
        curr_sel_ls = self.list.GetSelectedLayers()
        # update self.sel_layers (selected layer list)
        if 'WMS' in self.ws:
            for sel_l in self.sel_layers[:]:
                if sel_l not in curr_sel_ls:
                    self.sel_layers.remove(sel_l)

            for l in curr_sel_ls:
                if l not in self.sel_layers:
                    self.sel_layers.append(l)

            self._updateLayerOrderList()
        else:
            self.sel_layers = curr_sel_ls

        # update projection

        self.projs_list = []
        projs_list = []

        intersect_proj = []
        first = True
        for l in curr_sel_ls:
            layer_projs = l['cap_intf_l'].GetLayerData('srs')
            if first:
                projs_list = layer_projs
                first = False
                continue

            projs_list = set(projs_list).intersection(layer_projs)

        if 'srs' not in self.drv_props['ignored_params']:

            for proj in projs_list:
                proj_code = Srs(proj.strip()).getcode()
                proj_spl = proj_code.split(':')
                if proj_spl[0].strip().lower() in self.drv_info.GetSrs():
                    try:
                        int(proj_spl[1])
                        self.projs_list.append(proj_code)
                    except ValueError as IndexError:
                        continue

            cur_sel = self.params['srs'].GetStringSelection()

            self.projs_list = sorted(self.projs_list)
            self.params['srs'].SetItems(self.projs_list)

            if cur_sel:
                self.params['srs'].SetStringSelection(cur_sel)
            else:
                try:
                    i = self.projs_list.index('EPSG:4326')
                    self.params['srs'].SetSelection(i)
                except ValueError:
                    if len(self.projs_list) > 0:
                        self.params['srs'].SetSelection(0)

        # update format

        if 'WMS' not in self.ws and \
           'format' not in self.drv_props['ignored_params']:
            self.formats_list = []
            cur_sel = None

            if self.params['format'] is not None:
                cur_sel = self.params['format'].GetStringSelection()

            if len(curr_sel_ls) > 0:
                self.formats_list = sorted(
                    self._getFormats(
                        curr_sel_ls[0]['cap_intf_l']))
                self._updateFormatRadioBox(self.formats_list)

                if cur_sel:
                    self.params['format'].SetStringSelection(cur_sel)
                else:
                    self._setDefaultFormatVal()

        self.Layout()

    def _setDefaultFormatVal(self):
        """Set default format value.
        """
        try:
            i = self.formats_list.index('png')
            self.params['format'].SetSelection(i)
        except ValueError:
            pass

    def _updateFormatRadioBox(self, formats_list):
        """Helper function
        """
        if self.params['format'] is not None:
            self.req_page_sizer.Detach(self.params['format'])
            self.params['format'].Destroy()
        if len(self.formats_list) > 0:
            self.params['format'] = wx.RadioBox(
                parent=self.req_page_panel,
                id=wx.ID_ANY,
                label=_("Source image format"),
                pos=wx.DefaultPosition,
                choices=formats_list,
                majorDimension=4,
                style=wx.RA_SPECIFY_COLS)
            self.source_sizer.Insert(item=self.params['format'], before=2,
                                     flag=wx.LEFT | wx.RIGHT | wx.BOTTOM,
                                     border=5)

    def _getFormats(self, layer=None):
        """Get formats

        WMS has formats defined generally for whole cap.
        In WMTS and NASA OnEarh formats are defined for layer.
        """
        formats_label = []
        if layer is None:
            formats_list = self.cap.GetFormats()
        else:
            formats_list = layer.GetLayerData('format')

        for frmt in formats_list:
            frmt = frmt.strip()
            label = self.drv_info.GetFormatLabel(frmt)

            if label:
                formats_label.append(label)

        return formats_label

    def _checkImportValues(self,):
        """Check if required widgets are selected/filled
        """
        warning_str = ""
        show_war = False
        if not self.list or not self.list.GetSelectedLayers():
            warning_str += _("Select layer in layer list.\n")
            show_war = True

        if self.params['format'] is not None and \
           self.params['format'].GetSelection() == -1:
            warning_str += _("Select source image format.\n")
            show_war = True

        if self.params['srs'] is not None and \
           self.params['srs'].GetSelection() == -1:
            warning_str += _("Select source projection.\n")
            show_war = True

        if not self.o_layer_name:
            warning_str += _("Choose output layer name.\n")
            show_war = True

        if show_war:
            GMessage(parent=self.parent, message=warning_str)
            return False

        return True

    def SetOutputLayerName(self, name):
        """Set name of layer to be added to layer tree
        """
        self.o_layer_name = name

    def GetOutputLayerName(self):
        return self.o_layer_name

    def GetCapFile(self):
        """Get path to file where capabilities are saved
        """
        return self.cap_file

    def GetWebService(self):
        """Get web service
        """
        return self.ws
Exemplo n.º 16
0
class Animation:
    """Class represents animation as a sequence of states (views).
    It enables to record, replay the sequence and finally generate
    all image files. Recording and replaying is based on timer events.
    There is no frame interpolation like in the Tcl/Tk based Nviz.
    """

    def __init__(self, mapWindow, timer):
        """Animation constructor

        Signals:
            animationFinished - emitted when animation finished
                              - attribute 'mode'
            animationUpdateIndex - emitted during animation to update gui
                                 - attributes 'index' and 'mode'

        :param mapWindow: glWindow where rendering takes place
        :param timer: timer for recording and replaying
        """
        self.animationFinished = Signal('Animation.animationFinished')
        self.animationUpdateIndex = Signal('Animation.animationUpdateIndex')

        self.animationList = []         # view states
        self.timer = timer
        self.mapWindow = mapWindow
        self.actions = {'record': self.Record,
                        'play': self.Play}
        self.formats = ['tif', 'ppm']   # currently supported formats
        self.mode = 'record'            # current mode (record, play, save)
        self.paused = False             # recording/replaying paused
        self.currentFrame = 0           # index of current frame
        self.fps = 24  # user settings   # Frames per second

        self.stopSaving = False         # stop during saving images
        self.animationSaved = False     # current animation saved or not

    def Start(self):
        """Start recording/playing"""
        self.timer.Start(self.GetInterval())

    def Pause(self):
        """Pause recording/playing"""
        self.timer.Stop()

    def Stop(self):
        """Stop recording/playing"""
        self.timer.Stop()
        self.PostFinishedEvent()

    def Update(self):
        """Record/play next view state (on timer event)"""
        self.actions[self.mode]()

    def Record(self):
        """Record new view state"""
        self.animationList.append(
            {'view': copy.deepcopy(self.mapWindow.view),
             'iview': copy.deepcopy(self.mapWindow.iview)})
        self.currentFrame += 1
        self.PostUpdateIndexEvent(index=self.currentFrame)
        self.animationSaved = False

    def Play(self):
        """Render next frame"""
        if not self.animationList:
            self.Stop()
            return
        try:
            self.IterAnimation()
        except IndexError:
            # no more frames
            self.Stop()

    def IterAnimation(self):
        params = self.animationList[self.currentFrame]
        self.UpdateView(params)
        self.currentFrame += 1

        self.PostUpdateIndexEvent(index=self.currentFrame)

    def UpdateView(self, params):
        """Update view data in map window and render"""
        toolWin = self.mapWindow.GetToolWin()
        toolWin.UpdateState(view=params['view'], iview=params['iview'])

        self.mapWindow.UpdateView()

        self.mapWindow.render['quick'] = True
        self.mapWindow.Refresh(False)

    def IsRunning(self):
        """Test if timer is running"""
        return self.timer.IsRunning()

    def SetMode(self, mode):
        """Start animation mode

        :param mode: animation mode (record, play, save)
        """
        self.mode = mode

    def GetMode(self):
        """Get animation mode (record, play, save)"""
        return self.mode

    def IsPaused(self):
        """Test if animation is paused"""
        return self.paused

    def SetPause(self, pause):
        self.paused = pause

    def Exists(self):
        """Returns if an animation has been recorded"""
        return bool(self.animationList)

    def GetFrameCount(self):
        """Return number of recorded frames"""
        return len(self.animationList)

    def Clear(self):
        """Clear all records"""
        self.animationList = []
        self.currentFrame = 0

    def GoToFrame(self, index):
        """Render frame of given index"""
        if index >= len(self.animationList):
            return

        self.currentFrame = index
        params = self.animationList[self.currentFrame]
        self.UpdateView(params)

    def PostFinishedEvent(self):
        """Animation ends"""
        self.animationFinished.emit(mode=self.mode)

    def PostUpdateIndexEvent(self, index):
        """Frame index changed, update tool window"""
        self.animationUpdateIndex(index=index, mode=self.mode)

    def StopSaving(self):
        """Abort image files generation"""
        self.stopSaving = True

    def IsSaved(self):
        """"Test if animation has been saved (to images)"""
        return self.animationSaved

    def SaveAnimationFile(self, path, prefix, format):
        """Generate image files

        :param path: path to direcory
        :param prefix: file prefix
        :param format: index of image file format
        """
        w, h = self.mapWindow.GetClientSizeTuple()
        toolWin = self.mapWindow.GetToolWin()

        formatter = ':04.0f'
        n = len(self.animationList)
        if n < 10:
            formatter = ':01.0f'
        elif n < 100:
            formatter = ':02.0f'
        elif n < 1000:
            formatter = ':03.0f'

        self.currentFrame = 0
        self.mode = 'save'
        for params in self.animationList:
            if not self.stopSaving:
                self.UpdateView(params)
                number = (
                    '{frame' +
                    formatter +
                    '}').format(
                    frame=self.currentFrame)
                filename = "{prefix}_{number}.{ext}".format(
                    prefix=prefix, number=number, ext=self.formats[format])
                filepath = os.path.join(path, filename)
                self.mapWindow.SaveToFile(
                    FileName=filepath,
                    FileType=self.formats[format],
                    width=w,
                    height=h)
                self.currentFrame += 1

                wx.Yield()
                toolWin.UpdateFrameIndex(
                    index=self.currentFrame, goToFrame=False)
            else:
                self.stopSaving = False
                break
        self.animationSaved = True
        self.PostFinishedEvent()

    def SetFPS(self, fps):
        """Set Frames Per Second value
        :param fps: frames per second
        """
        self.fps = fps

    def GetInterval(self):
        """Return timer interval in ms"""
        return 1000. / self.fps
Exemplo n.º 17
0
class SingleSymbolPanel(wx.Panel):
    """Panel for displaying one symbol.

    Changes background when selected. Assumes that parent will catch
    events emitted on mouse click. Used in gui_core::dialog::SymbolDialog.
    """
    def __init__(self, parent, symbolPath):
        """Panel constructor

        Signal symbolSelectionChanged - symbol selected
                                      - attribute 'name' (symbol name)
                                      - attribute 'doubleClick' (underlying cause)

        :param parent: parent (gui_core::dialog::SymbolDialog)
        :param symbolPath: absolute path to symbol
        """
        self.symbolSelectionChanged = Signal('SingleSymbolPanel.symbolSelectionChanged')

        wx.Panel.__init__(self, parent, id=wx.ID_ANY, style=wx.BORDER_RAISED)
        self.SetName(os.path.splitext(os.path.basename(symbolPath))[0])
        self.sBmp = wx.StaticBitmap(self, wx.ID_ANY, wx.Bitmap(symbolPath))

        self.selected = False
        self.selectColor = wx.SystemSettings.GetColour(wx.SYS_COLOUR_HIGHLIGHT)
        self.deselectColor = wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOW)

        sizer = wx.BoxSizer()
        sizer.Add(item = self.sBmp, proportion=0, flag=wx.ALL | wx.ALIGN_CENTER, border = 5)
        self.SetBackgroundColour(self.deselectColor)
        self.SetMinSize(self.GetBestSize())
        self.SetSizerAndFit(sizer)

        # binding to both (staticBitmap, Panel) necessary
        self.sBmp.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown)
        self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown)
        self.Bind(wx.EVT_LEFT_DCLICK, self.OnDoubleClick)
        self.sBmp.Bind(wx.EVT_LEFT_DCLICK, self.OnDoubleClick)

    def OnLeftDown(self, event):
        """Panel selected, background changes"""
        self.selected = True
        self.SetBackgroundColour(self.selectColor)
        self.Refresh()
        event.Skip()

        self.symbolSelectionChanged.emit(name=self.GetName(), doubleClick=False)

    def OnDoubleClick(self, event):
        self.symbolSelectionChanged.emit(name=self.GetName(), doubleClick=True)

    def Deselect(self):
        """Panel deselected, background changes back to default"""
        self.selected = False
        self.SetBackgroundColour(self.deselectColor)
        self.Refresh()

    def Select(self):
        """Select panel, no event emitted"""
        self.selected = True
        self.SetBackgroundColour(self.selectColor)
        self.Refresh()
Exemplo n.º 18
0
class BitmapRenderer:
    """Class which renders 2D and 3D images to files."""
    def __init__(self, mapFilesPool, tempDir, imageWidth, imageHeight):
        self._mapFilesPool = mapFilesPool
        self._tempDir = tempDir
        self.imageWidth = imageWidth
        self.imageHeight = imageHeight

        self.renderingContinues = Signal("BitmapRenderer.renderingContinues")
        self._stopRendering = False
        self._isRendering = False

    def Render(self, cmdList, regions, regionFor3D, bgcolor, force, nprocs):
        """Renders all maps and stores files.

        :param cmdList: list of rendering commands to run
        :param regions: regions for 2D rendering assigned to commands
        :param regionFor3D: region for setting 3D view
        :param bgcolor: background color as a tuple of 3 values 0 to 255
        :param force: if True reload all data, otherwise only missing data
        :param nprocs: number of procs to be used for rendering
        """
        Debug.msg(3, "BitmapRenderer.Render")
        count = 0

        # Variables for parallel rendering
        proc_count = 0
        proc_list = []
        queue_list = []
        cmd_list = []

        filteredCmdList = []
        for cmd, region in zip(cmdList, regions):
            if cmd[0] == "m.nviz.image":
                region = None
            filename = GetFileFromCmd(self._tempDir, cmd, region)
            if (not force and os.path.exists(filename)
                    and self._mapFilesPool.GetSize(HashCmd(
                        cmd, region)) == (self.imageWidth, self.imageHeight)):
                # for reference counting
                self._mapFilesPool[HashCmd(cmd, region)] = filename
                continue
            filteredCmdList.append((cmd, region))

        mapNum = len(filteredCmdList)
        stopped = False
        self._isRendering = True
        for cmd, region in filteredCmdList:
            count += 1

            # Queue object for interprocess communication
            q = Queue()
            # The separate render process
            if cmd[0] == "m.nviz.image":
                p = Process(
                    target=RenderProcess3D,
                    args=(
                        self.imageWidth,
                        self.imageHeight,
                        self._tempDir,
                        cmd,
                        regionFor3D,
                        bgcolor,
                        q,
                    ),
                )
            else:
                p = Process(
                    target=RenderProcess2D,
                    args=(
                        self.imageWidth,
                        self.imageHeight,
                        self._tempDir,
                        cmd,
                        region,
                        bgcolor,
                        q,
                    ),
                )
            p.start()

            queue_list.append(q)
            proc_list.append(p)
            cmd_list.append((cmd, region))

            proc_count += 1
            # Wait for all running processes and read/store the created images
            if proc_count == nprocs or count == mapNum:
                for i in range(len(cmd_list)):
                    proc_list[i].join()
                    filename = queue_list[i].get()
                    self._mapFilesPool[HashCmd(cmd_list[i][0],
                                               cmd_list[i][1])] = filename
                    self._mapFilesPool.SetSize(
                        HashCmd(cmd_list[i][0], cmd_list[i][1]),
                        (self.imageWidth, self.imageHeight),
                    )

                proc_count = 0
                proc_list = []
                queue_list = []
                cmd_list = []

            self.renderingContinues.emit(current=count,
                                         text=_("Rendering map layers"))
            if self._stopRendering:
                self._stopRendering = False
                stopped = True
                break

        self._isRendering = False
        return not stopped

    def RequestStopRendering(self):
        """Requests to stop rendering."""
        if self._isRendering:
            self._stopRendering = True
Exemplo n.º 19
0
class BitmapComposer:
    """Class which handles the composition of image files with g.pnmcomp."""
    def __init__(self, tempDir, mapFilesPool, bitmapPool, imageWidth,
                 imageHeight):
        self._mapFilesPool = mapFilesPool
        self._bitmapPool = bitmapPool
        self._tempDir = tempDir
        self.imageWidth = imageWidth
        self.imageHeight = imageHeight

        self.compositionContinues = Signal("BitmapComposer.composingContinues")
        self._stopComposing = False
        self._isComposing = False

    def Compose(self, cmdLists, regions, opacityList, bgcolor, force, nprocs):
        """Performs the composition of ppm/pgm files.

        :param cmdLists: lists of rendering commands lists to compose
        :param regions: regions for 2D rendering assigned to commands
        :param opacityList: list of lists of opacity values
        :param bgcolor: background color as a tuple of 3 values 0 to 255
        :param force: if True reload all data, otherwise only missing data
        :param nprocs: number of procs to be used for rendering
        """
        Debug.msg(3, "BitmapComposer.Compose")

        count = 0

        # Variables for parallel rendering
        proc_count = 0
        proc_list = []
        queue_list = []
        cmd_lists = []

        filteredCmdLists = []
        for cmdList, region in zip(cmdLists, regions):
            if (not force and HashCmds(cmdList, region) in self._bitmapPool
                    and self._bitmapPool[HashCmds(cmdList, region)].GetSize()
                    == (self.imageWidth, self.imageHeight)):
                # TODO: find a better way than to assign the same to increase
                # the reference
                self._bitmapPool[HashCmds(cmdList,
                                          region)] = self._bitmapPool[HashCmds(
                                              cmdList, region)]
                continue
            filteredCmdLists.append((cmdList, region))

        num = len(filteredCmdLists)

        self._isComposing = True
        for cmdList, region in filteredCmdLists:
            count += 1
            # Queue object for interprocess communication
            q = Queue()
            # The separate render process
            p = Process(
                target=CompositeProcess,
                args=(
                    self.imageWidth,
                    self.imageHeight,
                    self._tempDir,
                    cmdList,
                    region,
                    opacityList,
                    bgcolor,
                    q,
                ),
            )
            p.start()

            queue_list.append(q)
            proc_list.append(p)
            cmd_lists.append((cmdList, region))

            proc_count += 1

            # Wait for all running processes and read/store the created images
            if proc_count == nprocs or count == num:
                for i in range(len(cmd_lists)):
                    proc_list[i].join()
                    filename = queue_list[i].get()
                    if filename is None:
                        self._bitmapPool[HashCmds(
                            cmd_lists[i][0],
                            cmd_lists[i][1])] = createNoDataBitmap(
                                self.imageWidth,
                                self.imageHeight,
                                text="Failed to render")
                    else:
                        self._bitmapPool[HashCmds(
                            cmd_lists[i][0],
                            cmd_lists[i][1])] = BitmapFromImage(
                                wx.Image(filename))
                        os.remove(filename)
                proc_count = 0
                proc_list = []
                queue_list = []
                cmd_lists = []

            self.compositionContinues.emit(current=count,
                                           text=_("Overlaying map layers"))
            if self._stopComposing:
                self._stopComposing = False
                break

        self._isComposing = False

    def RequestStopComposing(self):
        """Requests to stop the composition."""
        if self._isComposing:
            self._stopComposing = True
Exemplo n.º 20
0
class QueryDialog(wx.Dialog):
    def __init__(self, parent, data = None):
        wx.Dialog.__init__(self, parent, id = wx.ID_ANY,
                           title = _("Query results"),
                           size = (420, 400),
                           style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER)
        # send query output to console
        self.redirectOutput = Signal('QueryDialog.redirectOutput')

        self.data = data

        self.panel = wx.Panel(self, id = wx.ID_ANY)
        self.mainSizer = wx.BoxSizer(wx.VERTICAL)

        helpText = wx.StaticText(self.panel, wx.ID_ANY,
                                 label=_("Right click to copy selected values to clipboard."))
        helpText.SetForegroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_GRAYTEXT))
        self.mainSizer.Add(item=helpText, proportion=0, flag=wx.ALL, border=5)

        self._colNames = [_("Feature"), _("Value")]
        self._model = QueryTreeBuilder(self.data, column=self._colNames[1])
        self.tree = TreeListView(model=self._model, parent=self.panel,
                                 columns=self._colNames,
                                 style=wx.TR_DEFAULT_STYLE | 
                                 wx.TR_FULL_ROW_HIGHLIGHT | wx.TR_MULTIPLE)

        self.tree.SetColumnWidth(0, 220)
        self.tree.SetColumnWidth(1, 1000)
        self.tree.ExpandAll(self._model.root)
        self.tree.contextMenu.connect(self.ShowContextMenu)
        self.mainSizer.Add(item = self.tree, proportion = 1, flag = wx.EXPAND | wx.ALL, border = 5)

        
        close = wx.Button(self.panel, id = wx.ID_CLOSE)
        close.Bind(wx.EVT_BUTTON, lambda event: self.Close())
        copy = wx.Button(self.panel, id = wx.ID_ANY, label = _("Copy all to clipboard"))
        copy.Bind(wx.EVT_BUTTON, self.Copy)
        self.Bind(wx.EVT_CLOSE, self.OnClose)
        self.redirect = wx.CheckBox(self.panel, label=_("Redirect to console"))
        self.redirect.SetValue(False)
        self.redirect.Bind(wx.EVT_CHECKBOX, lambda evt: self._onRedirect(evt.IsChecked()))

        hbox = wx.BoxSizer(wx.HORIZONTAL)
        hbox.Add(item=self.redirect, proportion=0, flag=wx.EXPAND | wx.RIGHT, border=5)
        hbox.AddStretchSpacer(1)
        hbox.Add(item=copy, proportion=0, flag=wx.EXPAND | wx.RIGHT, border=5)
        hbox.Add(item=close, proportion=0, flag=wx.EXPAND | wx.ALL, border=0)

        self.mainSizer.Add(item=hbox, proportion=0, flag=wx.EXPAND | wx.ALL, border=5)
        self.panel.SetSizer(self.mainSizer)
        self.mainSizer.Fit(self.panel)
        # for Windows
        self.SendSizeEvent()

    def SetData(self, data):
        state = self.tree.GetExpansionState()
        self.data = data
        self._model = QueryTreeBuilder(self.data, column=self._colNames[1])
        self.tree.SetModel(self._model)
        self.tree.SetExpansionState(state)

        if self.redirect.IsChecked():
            self.redirectOutput.emit(output=self._textToRedirect())

    def Copy(self, event):
        text = printResults(self._model, self._colNames[1])
        self._copyText(text)

    def ShowContextMenu(self, node):
        """Show context menu.
        
        Menu for copying distinguishes single and multiple selection.
        """
        nodes = self.tree.GetSelected()
        if not nodes:
            return
            
        menu = wx.Menu()
        texts = []
        if len(nodes) > 1:
            values = []
            for node in nodes:
                values.append((node.label, node.data[self._colNames[1]] if node.data else ''))
            col1 = '\n'.join([val[1] for val in values if val[1]])
            col2 = '\n'.join([val[0] for val in values if val[0]])
            table = '\n'.join([val[0] + ': ' + val[1] for val in values])
            texts.append((_("Copy from '%s' column") % self._colNames[1], col1))
            texts.append((_("Copy from '%s' column") % self._colNames[0], col2))
            texts.append((_("Copy selected lines"), table))
        else:
            label1 = nodes[0].label
            texts.append((_("Copy '%s'" % self._cutLabel(label1)), label1))
            if nodes[0].data and nodes[0].data[self._colNames[1]]:
                label2 = nodes[0].data[self._colNames[1]]
                texts.insert(0, (_("Copy '%s'" % self._cutLabel(label2)), label2))
                texts.append((_("Copy line"), label1 + ': ' + label2))

        ids = []
        for text in texts:
            id = wx.NewId()
            ids.append(id)
            self.Bind(wx.EVT_MENU, lambda evt, t=text[1], id=id: self._copyText(t), id=id)
 
            menu.Append(id, text[0])
 
        # show the popup menu
        self.PopupMenu(menu)
        menu.Destroy()
        for id in ids:
            self.Unbind(wx.EVT_MENU, id=id)

    def _onRedirect(self, redirect):
        """Emits instructions to redirect query results.
        
        :param redirect: True to start redirecting, False to stop        
        """
        if redirect:
            self.redirectOutput.emit(output=_("Query results:"), style='cmd')
            self.redirectOutput.emit(output=self._textToRedirect())
        else:
            self.redirectOutput.emit(output=_(" "), style='cmd')

    def _textToRedirect(self):
        text = printResults(self._model, self._colNames[1])
        text += '\n' + "-"* 50 + '\n'
        return text

    def _cutLabel(self, label):
        limit = 15
        if len(label) > limit:
            return label[:limit] + '...'
            
        return label

    def _copyText(self, text):
        """Helper function for copying"""
        if wx.TheClipboard.Open():
            do = wx.TextDataObject()
            do.SetText(text)
            wx.TheClipboard.SetData(do)
            wx.TheClipboard.Close()

    def OnClose(self, event):
        if self.redirect.IsChecked():
            self._onRedirect(False)
        self.Destroy()
        event.Skip()
Exemplo n.º 21
0
class Statistics:
    """Statistis conected to one class (category).

    It is Python counterpart of similar C structure.
    But it adds some attributes or features used in wxIClass.
    It is not interface to C structure (it copies values).
    """

    def __init__(self):
        self.category = -1
        self.name = ""
        self.rasterName = ""
        self.color = "0:0:0"
        self.nbands = 0
        self.ncells = 0
        self.nstd = 1.5
        self.bands = []
        self.ready = False

        self.statisticsSet = Signal("Statistics.statisticsSet")

    def SetReady(self, ready=True):
        self.ready = ready

    def IsReady(self):
        return self.ready

    def SetBaseStatistics(self, cat, name, color):
        """Sets basic (non-statistical) values.

        .. todo::
            Later self.name is changed but self.rasterName is not.
            self.rasterName should not be set by user. It can remains
            the same. But it should be done more explicitly. Currently
            it looks like unintentional feature or bug.
        """
        self.category = cat
        self.name = name
        self.color = color

        rasterPath = grass.tempfile(create=False)
        name = name.replace(' ', '_')
        self.rasterName = name + '_' + os.path.basename(rasterPath)

    def SetFromcStatistics(self, cStatistics):
        """Sets all statistical values.

        Copies all statistic values from \a cStattistics.

        :param cStatistics: pointer to C statistics structure
        """
        cat = c_int()

        set_stats = {}
        I_iclass_statistics_get_cat(cStatistics, byref(cat))
        if self.category != cat.value:
            set_stats["category"] = cat.value

        name = c_char_p()
        I_iclass_statistics_get_name(cStatistics, byref(name))
        if self.name != name.value:
            set_stats["name"] = name.value

        color = c_char_p()
        I_iclass_statistics_get_color(cStatistics, byref(color))
        if self.color != color.value:
            set_stats["color"] = color.value

        nbands = c_int()
        I_iclass_statistics_get_nbands(cStatistics, byref(nbands))
        if self.nbands != nbands.value:
            set_stats["nbands"] = nbands.value

        ncells = c_int()
        I_iclass_statistics_get_ncells(cStatistics, byref(ncells))
        if self.ncells != ncells.value:
            set_stats["ncells"] = ncells.value

        nstd = c_float()
        I_iclass_statistics_get_nstd(cStatistics, byref(nstd))
        if self.nstd != nstd.value:
            set_stats["nstd"] = nstd.value

        self.SetStatistics(set_stats)
        self.SetBandStatistics(cStatistics)

    def SetBandStatistics(self, cStatistics):
        """Sets all band statistics.

        :param cStatistics: pointer to C statistics structure
        """
        self.bands = []
        for i in range(self.nbands):
            band = BandStatistics()
            band.SetFromcStatistics(cStatistics, index=i)
            self.bands.append(band)

    def SetStatistics(self, stats):

        for st, val in stats.iteritems():
            setattr(self, st, val)

        self.statisticsSet.emit(stats=stats)
Exemplo n.º 22
0
class SimpleLayerManager(wx.Panel):
    """Simple layer manager class provides similar functionality to
    Layertree, but it's just list, not tree."""

    def __init__(
        self,
        parent,
        layerList,
        lmgrStyle=SIMPLE_LMGR_RASTER | SIMPLE_LMGR_VECTOR | SIMPLE_LMGR_TB_LEFT,
        toolbarCls=None,
        modal=False,
    ):
        wx.Panel.__init__(self, parent=parent, name="SimpleLayerManager")

        self._style = lmgrStyle
        self._layerList = layerList
        self._checkList = wx.CheckListBox(self, style=wx.LB_EXTENDED)
        if not toolbarCls:
            toolbarCls = SimpleLmgrToolbar
        self._toolbar = toolbarCls(self, lmgrStyle=self._style)

        self._auimgr = wx.aui.AuiManager(self)

        self._modal = modal
        # d.* dialogs are recreated each time, attempt to hide it resulted
        # in completely mysterious memory corruption and crash when opening
        # any dialog with stock labels (wx.ID_OK and so on)

        # needed in order not to change selection when moving layers
        self._blockSelectionChanged = False

        self._checkList.Bind(wx.EVT_LISTBOX, lambda evt: self._selectionChanged())
        self._checkList.Bind(wx.EVT_LISTBOX_DCLICK, self.OnLayerChangeProperties)
        self._checkList.Bind(wx.EVT_CHECKLISTBOX, self.OnLayerChecked)
        self._checkList.Bind(wx.EVT_CONTEXT_MENU, self.OnContextMenu)

        # signal emitted when somethin in layer list changes
        self.opacityChanged = Signal("SimpleLayerManager.opacityChanged")
        self.cmdChanged = Signal("SimpleLayerManager.cmdChanged")
        self.layerAdded = Signal("SimpleLayerManager.layerAdded")
        self.layerRemoved = Signal("SimpleLayerManager.layerRemoved")
        self.layerActivated = Signal("SimpleLayerManager.layerActivated")
        self.layerMovedUp = Signal("SimpleLayerManager.layerMovedUp")
        self.layerMovedDown = Signal("SimpleLayerManager.layerMovedDown")
        # emitted by any change (e.g. for rerendering)
        self.anyChange = Signal("SimpleLayerManager.layerChange")

        self._layout()
        self.SetMinSize((200, -1))
        self._update()

    def _layout(self):
        self._auimgr.AddPane(
            self._checkList,
            wx.aui.AuiPaneInfo()
            .Name("checklist")
            .CenterPane()
            .CloseButton(False)
            .BestSize((self._checkList.GetBestSize())),
        )
        paneInfo = (
            wx.aui.AuiPaneInfo()
            .Name("toolbar")
            .Caption(_("Toolbar"))
            .ToolbarPane()
            .CloseButton(False)
            .Layer(1)
            .Gripper(False)
            .BestSize((self._toolbar.GetBestSize()))
        )
        if self._style & SIMPLE_LMGR_TB_LEFT:
            paneInfo.Left()
        elif self._style & SIMPLE_LMGR_TB_RIGHT:
            paneInfo.Right()
        elif self._style & SIMPLE_LMGR_TB_TOP:
            paneInfo.Top()
        else:
            paneInfo.Bottom()

        self._auimgr.AddPane(self._toolbar, paneInfo)
        self._auimgr.Update()

    def _selectionChanged(self):
        """Selection was changed externally,
        updates selection info in layers."""
        if self._blockSelectionChanged:
            return
        selected = self._checkList.GetSelections()
        for i, layer in enumerate(self._layerList):
            layer.Select(i in selected)

    def OnContextMenu(self, event):
        """Show context menu.

        So far offers only copying layer list to clipboard
        """
        if len(self._layerList) < 1:
            event.Skip()
            return

        menu = wx.Menu()
        llist = [layer.name for layer in self._layerList]
        texts = [",".join(llist), ",".join(reversed(llist))]
        labels = [_("Copy map names to clipboard (top to bottom)"), _("Copy map names to clipboard (bottom to top)")]
        for label, text in zip(labels, texts):
            id = wx.NewId()
            self.Bind(wx.EVT_MENU, lambda evt, t=text, id=id: self._copyText(t), id=id)

            menu.Append(id, label)

        # show the popup menu
        self.PopupMenu(menu)
        menu.Destroy()
        event.Skip()

    def _copyText(self, text):
        """Helper function for copying

        TODO: move to utils?
        """
        if wx.TheClipboard.Open():
            do = wx.TextDataObject()
            do.SetText(text)
            wx.TheClipboard.SetData(do)
            wx.TheClipboard.Close()

    def OnLayerChecked(self, event):
        """Layer was (un)checked, update layer's info."""
        checkedIdxs = self._checkList.GetChecked()
        for i, layer in enumerate(self._layerList):
            if i in checkedIdxs and not layer.IsActive():
                layer.Activate()
                self.layerActivated.emit(index=i, layer=layer)
            elif i not in checkedIdxs and layer.IsActive():
                layer.Activate(False)
                self.layerActivated.emit(index=i, layer=layer)
        self.anyChange.emit()
        event.Skip()

    def OnAddRaster(self, event):
        """Opens d.rast dialog and adds layer.
        Dummy layer is added first."""
        cmd = ["d.rast"]
        layer = self.AddRaster(name="", cmd=cmd, hidden=True, dialog=None)
        GUI(parent=self, giface=None, modal=self._modal).ParseCommand(cmd=cmd, completed=(self.GetOptData, layer, ""))
        event.Skip()

    def OnAddVector(self, event):
        """Opens d.vect dialog and adds layer.
        Dummy layer is added first."""
        cmd = ["d.vect"]

        layer = self.AddVector(name="", cmd=cmd, hidden=True, dialog=None)
        GUI(parent=self, giface=None, modal=self._modal).ParseCommand(cmd=cmd, completed=(self.GetOptData, layer, ""))
        event.Skip()

    def OnAddRast3d(self, event):
        """Opens d.rast3d dialog and adds layer.
        Dummy layer is added first."""
        cmd = ["d.rast3d"]
        layer = self.AddRast3d(name="", cmd=cmd, hidden=True, dialog=None)
        GUI(parent=self, giface=None, modal=self._modal).ParseCommand(cmd=cmd, completed=(self.GetOptData, layer, ""))
        event.Skip()

    def OnAddRGB(self, event):
        """Opens d.rgb dialog and adds layer.
        Dummy layer is added first."""
        cmd = ["d.rgb"]
        layer = self.AddRGB(name="", cmd=cmd, hidden=True, dialog=None)
        GUI(parent=self, giface=None, modal=self._modal).ParseCommand(cmd=cmd, completed=(self.GetOptData, layer, ""))
        event.Skip()

    def OnRemove(self, event):
        """Removes selected layers from list."""
        layers = self._layerList.GetSelectedLayers(activeOnly=False)
        for layer in layers:
            self.layerRemoved.emit(index=self._layerList.GetLayerIndex(layer), layer=layer)
            self._layerList.RemoveLayer(layer)
        self._update()
        self.anyChange.emit()
        event.Skip()

    def OnLayerUp(self, event):
        """Moves selected layers one step up.

        Note: not completely correct for multiple layers."""
        layers = self._layerList.GetSelectedLayers()
        self._blockSelectionChanged = True
        for layer in layers:
            idx = self._layerList.GetLayerIndex(layer)
            if idx > 0:
                self.layerMovedUp.emit(index=idx, layer=layer)
                self._layerList.MoveLayerUp(layer)
        self._update()
        self._blockSelectionChanged = False
        self.anyChange.emit()
        event.Skip()

    def OnLayerDown(self, event):
        """Moves selected layers one step down.

        Note: not completely correct for multiple layers."""
        layers = self._layerList.GetSelectedLayers()
        self._blockSelectionChanged = True
        for layer in layers:
            idx = self._layerList.GetLayerIndex(layer)
            if idx < len(self._layerList) - 1:
                self.layerMovedDown.emit(index=self._layerList.GetLayerIndex(layer), layer=layer)
                self._layerList.MoveLayerDown(layer)
        self._update()
        self._blockSelectionChanged = False
        self.anyChange.emit()
        event.Skip()

    def OnLayerChangeProperties(self, event):
        """Opens module dialog to edit layer command."""
        layers = self._layerList.GetSelectedLayers()
        if not layers or len(layers) > 1:
            return
        self._layerChangeProperties(layers[0])
        event.Skip()

    def _layerChangeProperties(self, layer):
        """Opens new module dialog or recycles it."""
        GUI(parent=self, giface=None, modal=self._modal).ParseCommand(
            cmd=layer.cmd, completed=(self.GetOptData, layer, "")
        )

    def OnLayerChangeOpacity(self, event):
        """Opacity of a layer is changing."""
        layers = self._layerList.GetSelectedLayers()
        if not layers or len(layers) > 1:
            return
        layer = layers[0]
        dlg = SetOpacityDialog(self, opacity=layer.opacity, title=_("Set opacity of <%s>") % layer.name)
        dlg.applyOpacity.connect(lambda value: self._setLayerOpacity(layer, value))
        dlg.CentreOnParent()

        if dlg.ShowModal() == wx.ID_OK:
            self._setLayerOpacity(layer, dlg.GetOpacity())
        dlg.Destroy()
        event.Skip()

    def _setLayerOpacity(self, layer, value):
        """Sets layer's opacity.'"""
        layer.opacity = value
        self._update()
        self.opacityChanged.emit(index=self._layerList.GetLayerIndex(layer), layer=layer)
        self.anyChange.emit()

    def _update(self):
        """Updates checklistbox according to layerList structure."""
        items = []
        active = []
        selected = []

        # remove hidden (temporary) layers first
        for layer in reversed(self._layerList):
            if layer.hidden:
                self._layerList.RemoveLayer(layer)

        for layer in self._layerList:
            if layer.opacity < 1:
                items.append("{name} (opacity {opacity}%)".format(name=layer.name, opacity=int(layer.opacity * 100)))
            else:
                items.append(layer.name)
            active.append(layer.IsActive())
            selected.append(layer.IsSelected())

        self._checkList.SetItems(items)
        for i, check in enumerate(active):
            self._checkList.Check(i, check)

        for i, layer in enumerate(self._layerList):
            if selected[i]:
                self._checkList.Select(i)
            else:
                self._checkList.Deselect(i)

    def GetOptData(self, dcmd, layer, params, propwin):
        """Handler for module dialogs."""
        if dcmd:
            layer.cmd = dcmd
            layer.selected = True
            mapName, found = GetLayerNameFromCmd(dcmd)
            if found:
                try:
                    if layer.hidden:
                        layer.hidden = False
                        signal = self.layerAdded
                    else:
                        signal = self.cmdChanged

                    layer.name = mapName
                    signal.emit(index=self._layerList.GetLayerIndex(layer), layer=layer)
                except ValueError as e:
                    self._layerList.RemoveLayer(layer)
                    GError(parent=self, message=str(e), showTraceback=False)

            self._update()
            self.anyChange.emit()

    def AddRaster(self, name, cmd, hidden, dialog):
        """Ads new raster layer."""
        layer = self._layerList.AddNewLayer(name=name, mapType="raster", active=True, cmd=cmd, hidden=hidden)
        return layer

    def AddRast3d(self, name, cmd, hidden, dialog):
        """Ads new raster3d layer."""
        layer = self._layerList.AddNewLayer(name=name, mapType="raster_3d", active=True, cmd=cmd, hidden=hidden)
        return layer

    def AddVector(self, name, cmd, hidden, dialog):
        """Ads new vector layer."""
        layer = self._layerList.AddNewLayer(name=name, mapType="vector", active=True, cmd=cmd, hidden=hidden)
        return layer

    def AddRGB(self, name, cmd, hidden, dialog):
        """Ads new vector layer."""
        layer = self._layerList.AddNewLayer(name=name, mapType="rgb", active=True, cmd=cmd, hidden=hidden)
        return layer

    def GetLayerInfo(self, layer, key):
        """Just for compatibility, should be removed in the future"""
        value = getattr(layer, key)
        # hack to return empty list, required in OnCancel in forms
        # not sure why it should be empty
        if key == "cmd" and len(value) == 1:
            return []
        return value

    def Delete(self, layer):
        """Just for compatibility, should be removed in the future"""
        self._layerList.RemoveLayer(layer)
Exemplo n.º 23
0
class QueryDialog(wx.Dialog):
    def __init__(self, parent, data=None):
        wx.Dialog.__init__(self,
                           parent,
                           id=wx.ID_ANY,
                           title=_("Query results"),
                           size=(420, 400),
                           style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER)
        # send query output to console
        self.redirectOutput = Signal('QueryDialog.redirectOutput')

        self.data = data

        self.panel = wx.Panel(self, id=wx.ID_ANY)
        self.mainSizer = wx.BoxSizer(wx.VERTICAL)

        helpText = StaticText(
            self.panel,
            wx.ID_ANY,
            label=_("Right click to copy selected values to clipboard."))
        helpText.SetForegroundColour(
            wx.SystemSettings.GetColour(wx.SYS_COLOUR_GRAYTEXT))
        self.mainSizer.Add(helpText, proportion=0, flag=wx.ALL, border=5)

        self._colNames = [_("Feature"), _("Value")]
        self._model = QueryTreeBuilder(self.data, column=self._colNames[1])
        self.tree = TreeListView(model=self._model,
                                 parent=self.panel,
                                 columns=self._colNames,
                                 style=wx.TR_DEFAULT_STYLE | wx.TR_HIDE_ROOT
                                 | wx.TR_FULL_ROW_HIGHLIGHT | wx.TR_MULTIPLE)

        self.tree.SetColumnWidth(0, 220)
        self.tree.SetColumnWidth(1, 1000)
        self.tree.ExpandAll(self._model.root)
        self.tree.RefreshItems()
        self.tree.contextMenu.connect(self.ShowContextMenu)
        self.mainSizer.Add(self.tree,
                           proportion=1,
                           flag=wx.EXPAND | wx.ALL,
                           border=5)

        close = Button(self.panel, id=wx.ID_CLOSE)
        close.Bind(wx.EVT_BUTTON, lambda event: self.Close())
        copy = Button(self.panel,
                      id=wx.ID_ANY,
                      label=_("Copy all to clipboard"))
        copy.Bind(wx.EVT_BUTTON, self.Copy)
        self.Bind(wx.EVT_CLOSE, self.OnClose)
        self.redirect = wx.CheckBox(self.panel, label=_("Redirect to console"))
        self.redirect.SetValue(False)
        self.redirect.Bind(wx.EVT_CHECKBOX,
                           lambda evt: self._onRedirect(evt.IsChecked()))

        hbox = wx.BoxSizer(wx.HORIZONTAL)
        hbox.Add(self.redirect,
                 proportion=0,
                 flag=wx.EXPAND | wx.RIGHT,
                 border=5)
        hbox.AddStretchSpacer(1)
        hbox.Add(copy, proportion=0, flag=wx.EXPAND | wx.RIGHT, border=5)
        hbox.Add(close, proportion=0, flag=wx.EXPAND | wx.ALL, border=0)

        self.mainSizer.Add(hbox,
                           proportion=0,
                           flag=wx.EXPAND | wx.ALL,
                           border=5)
        self.panel.SetSizer(self.mainSizer)
        self.mainSizer.Fit(self.panel)
        # for Windows
        self.SendSizeEvent()

    def SetData(self, data):
        state = self.tree.GetExpansionState()
        self.data = data
        self._model = QueryTreeBuilder(self.data, column=self._colNames[1])
        self.tree.SetModel(self._model)
        self.tree.SetExpansionState(state)

        if self.redirect.IsChecked():
            self.redirectOutput.emit(output=self._textToRedirect())

    def Copy(self, event):
        text = printResults(self._model, self._colNames[1])
        self._copyText(text)

    def ShowContextMenu(self, node):
        """Show context menu.

        Menu for copying distinguishes single and multiple selection.
        """
        nodes = self.tree.GetSelected()
        if not nodes:
            return

        menu = Menu()
        texts = []
        if len(nodes) > 1:
            values = []
            for node in nodes:
                values.append(
                    (node.label,
                     node.data[self._colNames[1]] if node.data else ''))
            col1 = '\n'.join([val[1] for val in values if val[1]])
            col2 = '\n'.join([val[0] for val in values if val[0]])
            table = '\n'.join([val[0] + ': ' + val[1] for val in values])
            texts.append(
                (_("Copy from '%s' column") % self._colNames[1], col1))
            texts.append(
                (_("Copy from '%s' column") % self._colNames[0], col2))
            texts.append((_("Copy selected lines"), table))
        else:
            label1 = nodes[0].label
            texts.append((_("Copy '%s'" % self._cutLabel(label1)), label1))
            if nodes[0].data and nodes[0].data[self._colNames[1]]:
                label2 = nodes[0].data[self._colNames[1]]
                texts.insert(0,
                             (_("Copy '%s'" % self._cutLabel(label2)), label2))
                texts.append((_("Copy line"), label1 + ': ' + label2))

        ids = []
        for text in texts:
            id = NewId()
            ids.append(id)
            self.Bind(wx.EVT_MENU,
                      lambda evt, t=text[1], id=id: self._copyText(t),
                      id=id)

            menu.Append(id, text[0])

        # show the popup menu
        self.PopupMenu(menu)
        menu.Destroy()
        for id in ids:
            self.Unbind(wx.EVT_MENU, id=id)

    def _onRedirect(self, redirect):
        """Emits instructions to redirect query results.

        :param redirect: True to start redirecting, False to stop
        """
        if redirect:
            self.redirectOutput.emit(output=_("Query results:"), style='cmd')
            self.redirectOutput.emit(output=self._textToRedirect())
        else:
            self.redirectOutput.emit(output=_(" "), style='cmd')

    def _textToRedirect(self):
        text = printResults(self._model, self._colNames[1])
        text += '\n' + "-" * 50 + '\n'
        return text

    def _cutLabel(self, label):
        limit = 15
        if len(label) > limit:
            return label[:limit] + '...'

        return label

    def _copyText(self, text):
        """Helper function for copying"""
        if wx.TheClipboard.Open():
            do = wx.TextDataObject()
            do.SetText(text)
            wx.TheClipboard.SetData(do)
            wx.TheClipboard.Close()

    def OnClose(self, event):
        if self.redirect.IsChecked():
            self._onRedirect(False)
        self.Destroy()
        event.Skip()
Exemplo n.º 24
0
class SbProgress(SbItem):
    """General progress bar to show progress.
    
    Underlaying widget is wx.Gauge.
    """

    def __init__(self, mapframe, statusbar, sbManager, position=0):
        self.progressShown = Signal("SbProgress.progressShown")
        self.progressHidden = Signal("SbProgress.progressHidden")
        SbItem.__init__(self, mapframe, statusbar, position)
        self.name = "progress"
        self.sbManager = sbManager
        # on-render gauge
        self.widget = wx.Gauge(parent=self.statusbar, id=wx.ID_ANY, range=0, style=wx.GA_HORIZONTAL)
        self.Hide()

    def GetRange(self):
        """Returns progress range."""
        return self.widget.GetRange()

    def SetRange(self, range):
        """Sets progress range."""
        if range > 0:
            if self.GetRange() != range:
                self.widget.SetRange(range)
            self.Show()
        else:
            self.Hide()

    def Show(self):
        if not self.IsShown():
            self.progressShown.emit()
            self.widget.Show()

    def Hide(self):
        if self.IsShown():
            self.progressHidden.emit()
            self.widget.Hide()

    def IsShown(self):
        """Is progress bar shown
        """
        return self.widget.IsShown()

    def SetValue(self, value):
        """Sets value of progressbar."""
        if value > self.GetRange():
            self.Hide()
            return

        self.widget.SetValue(value)
        if value == self.GetRange():
            self.Hide()

    def GetWidget(self):
        """Returns underlaying winget.
        
        :return: widget or None if doesn't exist
        """
        return self.widget

    def Update(self):
        pass
Exemplo n.º 25
0
class ScatterPlotWidget(wx.Panel, ManageBusyCursorMixin):
    def __init__(self, parent, scatt_id, scatt_mgr, transpose, id=wx.ID_ANY):
        # TODO should not be transpose and scatt_id but x, y
        wx.Panel.__init__(self, parent, id)
        # bacause of aui (if floatable it can not take cursor from parent)
        ManageBusyCursorMixin.__init__(self, window=self)

        self.parent = parent
        self.full_extend = None
        self.mode = None

        self._createWidgets()
        self._doLayout()
        self.scatt_id = scatt_id
        self.scatt_mgr = scatt_mgr

        self.cidpress = None
        self.cidrelease = None

        self.rend_dt = {}

        self.transpose = transpose

        self.inverse = False

        self.SetSize((200, 100))
        self.Layout()

        self.base_scale = 1.2
        self.Bind(wx.EVT_CLOSE, lambda event: self.CleanUp())

        self.plotClosed = Signal("ScatterPlotWidget.plotClosed")
        self.cursorMove = Signal("ScatterPlotWidget.cursorMove")

        self.contex_menu = ScatterPlotContextMenu(plot=self)

        self.ciddscroll = None

        self.canvas.mpl_connect('motion_notify_event', self.Motion)
        self.canvas.mpl_connect('button_press_event', self.OnPress)
        self.canvas.mpl_connect('button_release_event', self.OnRelease)
        self.canvas.mpl_connect('draw_event', self.DrawCallback)
        self.canvas.mpl_connect('figure_leave_event', self.OnCanvasLeave)

    def DrawCallback(self, event):
        self.polygon_drawer.DrawCallback(event)
        self.axes.draw_artist(self.zoom_rect)

    def _createWidgets(self):

        # Create the mpl Figure and FigCanvas objects.
        # 5x4 inches, 100 dots-per-inch
        #
        self.dpi = 100
        self.fig = Figure((1.0, 1.0), dpi=self.dpi)
        self.fig.autolayout = True

        self.canvas = FigCanvas(self, -1, self.fig)

        self.axes = self.fig.add_axes([0.0, 0.0, 1, 1])

        pol = Polygon(list(zip([0], [0])), animated=True)
        self.axes.add_patch(pol)
        self.polygon_drawer = PolygonDrawer(self.axes, pol=pol, empty_pol=True)

        self.zoom_wheel_coords = None
        self.zoom_rect_coords = None
        self.zoom_rect = Polygon(list(zip([0], [0])), facecolor='none')
        self.zoom_rect.set_visible(False)
        self.axes.add_patch(self.zoom_rect)

    def ZoomToExtend(self):
        if self.full_extend:
            self.axes.axis(self.full_extend)
            self.canvas.draw()

    def SetMode(self, mode):
        self._deactivateMode()
        if mode == 'zoom':
            self.ciddscroll = self.canvas.mpl_connect('scroll_event',
                                                      self.ZoomWheel)
            self.mode = 'zoom'
        elif mode == 'zoom_extend':
            self.mode = 'zoom_extend'
        elif mode == 'pan':
            self.mode = 'pan'
        elif mode:
            self.polygon_drawer.SetMode(mode)

    def SetSelectionPolygonMode(self, activate):
        self.polygon_drawer.SetSelectionPolygonMode(activate)

    def _deactivateMode(self):
        self.mode = None
        self.polygon_drawer.SetMode(None)

        if self.ciddscroll:
            self.canvas.mpl_disconnect(self.ciddscroll)

        self.zoom_rect.set_visible(False)
        self._stopCategoryEdit()

    def GetCoords(self):

        coords = self.polygon_drawer.GetCoords()
        if coords is None:
            return

        if self.transpose:
            for c in coords:
                tmp = c[0]
                c[0] = c[1]
                c[1] = tmp

        return coords

    def SetEmpty(self):
        return self.polygon_drawer.SetEmpty()

    def OnRelease(self, event):
        if not self.mode == "zoom":
            return
        self.zoom_rect.set_visible(False)
        self.ZoomRectangle(event)
        self.canvas.draw()

    def OnPress(self, event):
        'on button press we will see if the mouse is over us and store some data'
        if not event.inaxes:
            return
        if self.mode == "zoom_extend":
            self.ZoomToExtend()

        if event.xdata and event.ydata:
            self.zoom_wheel_coords = {'x': event.xdata, 'y': event.ydata}
            self.zoom_rect_coords = {'x': event.xdata, 'y': event.ydata}
        else:
            self.zoom_wheel_coords = None
            self.zoom_rect_coords = None

    def _stopCategoryEdit(self):
        'disconnect all the stored connection ids'

        if self.cidpress:
            self.canvas.mpl_disconnect(self.cidpress)
        if self.cidrelease:
            self.canvas.mpl_disconnect(self.cidrelease)
        # self.canvas.mpl_disconnect(self.cidmotion)

    def _doLayout(self):

        self.main_sizer = wx.BoxSizer(wx.VERTICAL)
        self.main_sizer.Add(self.canvas, 1, wx.LEFT | wx.TOP | wx.GROW)
        self.SetSizer(self.main_sizer)
        self.main_sizer.Fit(self)

    def Plot(self, cats_order, scatts, ellipses, styles):
        """Redraws the figure
        """

        callafter_list = []

        if self.full_extend:
            cx = self.axes.get_xlim()
            cy = self.axes.get_ylim()
            c = cx + cy
        else:
            c = None

        q = Queue()
        _rendDtMemmapsToFiles(self.rend_dt)
        p = Process(target=MergeImg,
                    args=(cats_order, scatts, styles, self.rend_dt, q))
        p.start()
        merged_img, self.full_extend, self.rend_dt = q.get()
        p.join()

        _rendDtFilesToMemmaps(self.rend_dt)
        merged_img = np.memmap(filename=merged_img['dt'],
                               shape=merged_img['sh'])

        #merged_img, self.full_extend = MergeImg(cats_order, scatts, styles, None)
        self.axes.clear()
        self.axes.axis('equal')

        if self.transpose:
            merged_img = np.transpose(merged_img, (1, 0, 2))

        img = imshow(self.axes,
                     merged_img,
                     extent=[int(ceil(x)) for x in self.full_extend],
                     origin='lower',
                     interpolation='nearest',
                     aspect="equal")

        callafter_list.append([self.axes.draw_artist, [img]])
        callafter_list.append([grass.try_remove, [merged_img.filename]])

        for cat_id in cats_order:
            if cat_id == 0:
                continue
            if cat_id not in ellipses:
                continue

            e = ellipses[cat_id]
            if not e:
                continue

            colors = styles[cat_id]['color'].split(":")
            if self.transpose:
                e['theta'] = 360 - e['theta'] + 90
                if e['theta'] >= 360:
                    e['theta'] = abs(360 - e['theta'])

                e['pos'] = [e['pos'][1], e['pos'][0]]

            ellip = Ellipse(xy=e['pos'],
                            width=e['width'],
                            height=e['height'],
                            angle=e['theta'],
                            edgecolor="w",
                            linewidth=1.5,
                            facecolor='None')
            self.axes.add_artist(ellip)
            callafter_list.append([self.axes.draw_artist, [ellip]])

            color = [
                int(v) / 255.0 for v in styles[cat_id]['color'].split(":")[:3]
            ]

            ellip = Ellipse(xy=e['pos'],
                            width=e['width'],
                            height=e['height'],
                            angle=e['theta'],
                            edgecolor=color,
                            linewidth=1,
                            facecolor='None')

            self.axes.add_artist(ellip)
            callafter_list.append([self.axes.draw_artist, [ellip]])

            center = Line2D(
                [e['pos'][0]],
                [e['pos'][1]],
                marker='x',
                markeredgecolor='w',
                # markerfacecolor=color,
                markersize=2)
            self.axes.add_artist(center)
            callafter_list.append([self.axes.draw_artist, [center]])

        callafter_list.append([self.fig.canvas.blit, []])

        if c:
            self.axes.axis(c)
        wx.CallAfter(lambda: self.CallAfter(callafter_list))

    def CallAfter(self, funcs_list):
        while funcs_list:
            fcn, args = funcs_list.pop(0)
            fcn(*args)

        self.canvas.draw()

    def CleanUp(self):
        self.plotClosed.emit(scatt_id=self.scatt_id)
        self.Destroy()

    def ZoomWheel(self, event):
        # get the current x and y limits
        if not event.inaxes:
            return
        # tcaswell
        # http://stackoverflow.com/questions/11551049/matplotlib-plot-zooming-with-scroll-wheel
        cur_xlim = self.axes.get_xlim()
        cur_ylim = self.axes.get_ylim()

        xdata = event.xdata
        ydata = event.ydata
        if event.button == 'up':
            scale_factor = 1 / self.base_scale
        elif event.button == 'down':
            scale_factor = self.base_scale
        else:
            scale_factor = 1

        extend = (xdata - (xdata - cur_xlim[0]) * scale_factor,
                  xdata + (cur_xlim[1] - xdata) * scale_factor,
                  ydata - (ydata - cur_ylim[0]) * scale_factor,
                  ydata + (cur_ylim[1] - ydata) * scale_factor)

        self.axes.axis(extend)

        self.canvas.draw()

    def ZoomRectangle(self, event):
        # get the current x and y limits
        if not self.mode == "zoom":
            return
        if event.inaxes is None:
            return
        if event.button != 1:
            return

        cur_xlim = self.axes.get_xlim()
        cur_ylim = self.axes.get_ylim()

        x1, y1 = event.xdata, event.ydata
        x2 = deepcopy(self.zoom_rect_coords['x'])
        y2 = deepcopy(self.zoom_rect_coords['y'])

        if x1 == x2 or y1 == y2:
            return

        if x1 > x2:
            tmp = x1
            x1 = x2
            x2 = tmp

        if y1 > y2:
            tmp = y1
            y1 = y2
            y2 = tmp

        self.axes.axis((x1, x2, y1, y2))
        # self.axes.set_xlim(x1, x2)#, auto = True)
        # self.axes.set_ylim(y1, y2)#, auto = True)
        self.canvas.draw()

    def Motion(self, event):
        self.PanMotion(event)
        self.ZoomRectMotion(event)

        if event.inaxes is None:
            return

        self.cursorMove.emit(x=event.xdata,
                             y=event.ydata,
                             scatt_id=self.scatt_id)

    def OnCanvasLeave(self, event):
        self.cursorMove.emit(x=None, y=None, scatt_id=self.scatt_id)

    def PanMotion(self, event):
        'on mouse movement'
        if not self.mode == "pan":
            return
        if event.inaxes is None:
            return
        if event.button != 1:
            return

        cur_xlim = self.axes.get_xlim()
        cur_ylim = self.axes.get_ylim()

        x, y = event.xdata, event.ydata

        mx = (x - self.zoom_wheel_coords['x']) * 0.6
        my = (y - self.zoom_wheel_coords['y']) * 0.6

        extend = (cur_xlim[0] - mx, cur_xlim[1] - mx, cur_ylim[0] - my,
                  cur_ylim[1] - my)

        self.zoom_wheel_coords['x'] = x
        self.zoom_wheel_coords['y'] = y

        self.axes.axis(extend)

        # self.canvas.copy_from_bbox(self.axes.bbox)
        # self.canvas.restore_region(self.background)
        self.canvas.draw()

    def ZoomRectMotion(self, event):
        if not self.mode == "zoom":
            return
        if event.inaxes is None:
            return
        if event.button != 1:
            return

        x1, y1 = event.xdata, event.ydata
        self.zoom_rect.set_visible(True)
        x2 = self.zoom_rect_coords['x']
        y2 = self.zoom_rect_coords['y']

        self.zoom_rect.xy = ((x1, y1), (x1, y2), (x2, y2), (x2, y1), (x1, y1))

        # self.axes.draw_artist(self.zoom_rect)
        self.canvas.draw()
Exemplo n.º 26
0
class MapWindowProperties(object):
    def __init__(self):
        self._resolution = None
        self.resolutionChanged = Signal('MapWindowProperties.resolutionChanged')
        self._autoRender = None
        self.autoRenderChanged = Signal('MapWindowProperties.autoRenderChanged')
        self._showRegion = None
        self.showRegionChanged = Signal('MapWindowProperties.showRegionChanged')
        self._alignExtent = None
        self.alignExtentChanged = Signal('MapWindowProperties.alignExtentChanged')

    def setValuesFromUserSettings(self):
        """Convenient function to get values from user settings into this object."""
        self._resolution = UserSettings.Get(group='display',
                                            key='compResolution',
                                            subkey='enabled')
        self._autoRender = UserSettings.Get(group='display',
                                            key='autoRendering',
                                            subkey='enabled')
        self._showRegion = False  # in statusbar.py was not from settings
        self._alignExtent = UserSettings.Get(group='display',
                                             key='alignExtent',
                                             subkey='enabled')

    @property
    def resolution(self):
        return self._resolution

    @resolution.setter
    def resolution(self, value):
        if value != self._resolution:
            self._resolution = value
            self.resolutionChanged.emit(value=value)

    @property
    def autoRender(self):
        return self._autoRender

    @autoRender.setter
    def autoRender(self, value):
        if value != self._autoRender:
            self._autoRender = value
            self.autoRenderChanged.emit(value=value)

    @property
    def showRegion(self):
        return self._showRegion

    @showRegion.setter
    def showRegion(self, value):
        if value != self._showRegion:
            self._showRegion = value
            self.showRegionChanged.emit(value=value)

    @property
    def alignExtent(self):
        return self._alignExtent

    @alignExtent.setter
    def alignExtent(self, value):
        if value != self._alignExtent:
            self._alignExtent = value
            self.alignExtentChanged.emit(value=value)
Exemplo n.º 27
0
class RenderWMSMgr(wx.EvtHandler):
    """Fetch and prepare WMS data for rendering.
    """
    def __init__(self, layer, mapfile, maskfile):
        if not haveGdal:
            sys.stderr.write(_("Unable to load GDAL Python bindings.\n"\
                               "WMS layers can not be displayed without the bindings.\n"))

        self.layer = layer

        wx.EvtHandler.__init__(self)

        # thread for d.wms commands
        self.thread = CmdThread(self)
        self.Bind(EVT_CMD_DONE, self.OnDataFetched)

        self.downloading = False
        self.renderedRegion = None
        self.updateMap = True
        self.fetched_data_cmd = None

        self.cmdStdErr = GStderr(self)

        self.mapfile = mapfile
        self.maskfile = maskfile
        self.tempMap = grass.tempfile()
        self.dstSize = {}
 
        self.Bind(EVT_CMD_OUTPUT, self.OnCmdOutput)
        
        self.dataFetched = Signal('RenderWMSMgr.dataFetched')
        self.updateProgress = Signal('RenderWMSMgr.updateProgress')

    def __del__(self):
        try_remove(self.tempMap)

    def Render(self, cmd, env):
        """If it is needed, download missing WMS data.

        .. todo::
            lmgr deletes mapfile and maskfile when order of layers
            was changed (drag and drop) - if deleted, fetch data again
        """
        if not haveGdal:
            return

        env = copy.copy(env)
        self.dstSize['cols'] = int(env["GRASS_RENDER_WIDTH"])
        self.dstSize['rows'] = int(env["GRASS_RENDER_HEIGHT"])

        region = self._getRegionDict(env)
        self._fitAspect(region, self.dstSize)

        self.updateMap = True
        fetchData = False
        zoomChanged = False

        if self.renderedRegion is None or \
           cmd != self.fetched_data_cmd:
            fetchData = True
        else:
            for c in ['north', 'south', 'east', 'west']:
                if self.renderedRegion and \
                   region[c] != self.renderedRegion[c]:
                    fetchData = True
                    break

            for c in ['e-w resol', 'n-s resol']:
                if self.renderedRegion and \
                    region[c] != self.renderedRegion[c]:
                    zoomChanged = True
                    break

        if fetchData:
            self.fetched_data_cmd = None
            self.renderedRegion = region

            try_remove(self.mapfile)
            try_remove(self.tempMap)

            self.currentPid = self.thread.GetId()
            self.thread.abort()
            self.downloading = True

            self.fetching_cmd = cmd
            cmdList = utils.CmdTupleToList(cmd)

            if Debug.GetLevel() < 3:
                cmdList.append('--quiet')

            env["GRASS_RENDER_FILE"] = self.tempMap
            env["GRASS_REGION"] = self._createRegionStr(region)

            self.thread.RunCmd(cmdList, env=env, stderr=self.cmdStdErr)

    def OnCmdOutput(self, event):
        """Print cmd output according to debug level.
        """
        if Debug.GetLevel() == 0:
            if event.type == 'error':
                sys.stderr.write(event.text)
                sys.stderr.flush()
        else:
            Debug.msg(1, event.text)

    def OnDataFetched(self, event):
        """Fetch data
        """
        if event.pid != self.currentPid:
            return
        self.downloading = False
        if not self.updateMap:
            self.updateProgress.emit(layer=self.layer)
            self.renderedRegion = None
            self.fetched_data_cmd = None
            return

        self.mapMerger = GDALRasterMerger(targetFile = self.mapfile, region = self.renderedRegion,
                                          bandsNum = 3, gdalDriver = 'PNM', fillValue = 0)
        self.mapMerger.AddRasterBands(self.tempMap, {1 : 1, 2 : 2, 3 : 3})
        del self.mapMerger

        self.maskMerger = GDALRasterMerger(targetFile = self.maskfile, region = self.renderedRegion,
                                           bandsNum = 1, gdalDriver = 'PNM', fillValue = 0)
        #{4 : 1} alpha channel (4) to first and only channel (1) in mask
        self.maskMerger.AddRasterBands(self.tempMap, {4 : 1}) 
        del self.maskMerger

        self.fetched_data_cmd = self.fetching_cmd

        self.dataFetched.emit()

    def _getRegionDict(self, env):
        """Parse string from GRASS_REGION env variable into dict.
        """
        region = {}
        parsedRegion = env["GRASS_REGION"].split(';')
        for r in parsedRegion:
            r = r.split(':')
            r[0] = r[0].strip()
            if len(r) < 2:
                continue
            try:
                if r[0] in ['cols', 'rows']:
                    region[r[0]] = int(r[1])
                else:
                    region[r[0]] = float(r[1])
            except ValueError:
                region[r[0]] = r[1]

        return region

    def _createRegionStr(self, region):
        """Create string for GRASS_REGION env variable from  dict created by _getRegionDict.
        """
        regionStr = ''
        for k, v in region.iteritems():
            item = k + ': ' + str(v)
            if regionStr:
                regionStr += '; '
            regionStr += item

        return regionStr

    def IsDownloading(self):
        """Is it downloading any data from WMS server? 
        """
        return self.downloading

    def _fitAspect(self, region, size):
        """Compute region parameters to have direction independent resolution.
        """
        if region['n-s resol'] > region['e-w resol']:
            delta = region['n-s resol'] * size['cols'] / 2

            center = (region['east'] - region['west'])/2

            region['east'] = center + delta + region['west']
            region['west'] = center - delta + region['west']
            region['e-w resol'] = region['n-s resol']

        else:
            delta = region['e-w resol'] * size['rows'] / 2

            center = (region['north'] - region['south'])/2 

            region['north'] = center + delta + region['south']
            region['south'] = center - delta + region['south']
            region['n-s resol'] = region['e-w resol']

    def Abort(self):
        """Abort process"""
        self.updateMap = False
        self.thread.abort(abortall = True)        
Exemplo n.º 28
0
class MapWindowBase(object):
    """Abstract map display window class

    Superclass for BufferedWindow class (2D display mode), and GLWindow
    (3D display mode).

    Subclasses have to define
     - _bindMouseEvents method which binds MouseEvent handlers
     - Pixel2Cell
     - Cell2Pixel (if it is possible)
    """
    def __init__(self, parent, giface, Map):
        self.parent = parent
        self.Map = Map
        self._giface = giface

        # Emitted when someone registers as mouse event handler
        self.mouseHandlerRegistered = Signal('MapWindow.mouseHandlerRegistered')
        # Emitted when mouse event handler is unregistered
        self.mouseHandlerUnregistered = Signal('MapWindow.mouseHandlerUnregistered')
        # emitted after double click in pointer mode on legend, text, scalebar
        self.overlayActivated = Signal('MapWindow.overlayActivated')
        # emitted when overlay should be hidden
        self.overlayHidden = Signal('MapWindow.overlayHidden')

        # mouse attributes -- position on the screen, begin and end of
        # dragging, and type of drawing
        self.mouse = {
            'begin': [0, 0], # screen coordinates
            'end'  : [0, 0],
            'use'  : "pointer",
            'box'  : "point"
            }
        # last east, north coordinates, changes on mouse motion
        self.lastEN = None

        # stores overridden cursor
        self._overriddenCursor = None

        # dictionary where event types are stored as keys and lists of
        # handlers for these types as values
        self.handlersContainer = {
            wx.EVT_LEFT_DOWN : [],
            wx.EVT_LEFT_UP : [],
            wx.EVT_LEFT_DCLICK : [],
            wx.EVT_MIDDLE_DOWN : [],
            wx.EVT_MIDDLE_UP : [],
            wx.EVT_MIDDLE_DCLICK : [],
            wx.EVT_RIGHT_DOWN : [],
            wx.EVT_RIGHT_UP : [],
            wx.EVT_RIGHT_DCLICK : [],
            wx.EVT_MOTION : [],
            wx.EVT_ENTER_WINDOW : [],
            wx.EVT_LEAVE_WINDOW : [],
            wx.EVT_MOUSEWHEEL : [],
            wx.EVT_MOUSE_EVENTS : []
            }

        # available cursors
        self._cursors = {
            "default": wx.StockCursor(wx.CURSOR_ARROW),
            "cross": wx.StockCursor(wx.CURSOR_CROSS),
            "hand": wx.StockCursor(wx.CURSOR_HAND),
            "pencil": wx.StockCursor(wx.CURSOR_PENCIL),
            "sizenwse": wx.StockCursor(wx.CURSOR_SIZENWSE)
            }

        # default cursor for window is arrow (at least we rely on it here)
        # but we need to define attribute here
        # cannot call SetNamedCursor since it expects the instance
        # to be a wx window, so setting only the attribute
        self._cursor = 'default'

        wx.CallAfter(self.InitBinding)

    def __del__(self):
        self.UnregisterAllHandlers()

    def InitBinding(self):
        """Binds helper functions, which calls all handlers
           registered to events with the events
        """
        for ev, handlers in self.handlersContainer.iteritems():
            self.Bind(ev, self.EventTypeHandler(handlers))

    def EventTypeHandler(self, evHandlers):
        return lambda event : self.HandlersCaller(event, evHandlers)

    def HandlersCaller(self, event, handlers):
        """Hepler function which calls all handlers registered for
        event
        """
        for handler in handlers:
            try:
                handler(event)
            except:
                handlers.remove(handler)
                GError(parent=self,
                       message=_("Error occurred during calling of handler: %s \n"
                                 "Handler was unregistered.") % handler.__name__)

        event.Skip() 

    def RegisterMouseEventHandler(self, event, handler, cursor=None):
        """Binds event handler

        @depreciated This method is depreciated. Use Signals or drawing API
        instead. Signals do not cover all events but new Signals can be added
        when needed consider also adding generic signal. However, more
        interesing and useful is higher level API to create objects, graphics etc.

        Call event.Skip() in handler to allow default processing in MapWindow.

        If any error occures inside of handler, the handler is removed.

        Before handler is unregistered it is called with
        string value "unregistered" of event parameter.

        ::

            # your class methods
            def OnButton(self, event):
                # current map display's map window
                # expects LayerManager to be the parent
                self.mapwin = self.parent.GetLayerTree().GetMapDisplay().GetWindow()
                if self.mapwin.RegisterEventHandler(wx.EVT_LEFT_DOWN, self.OnMouseAction,
                                                    'cross'):
                    self.parent.GetLayerTree().GetMapDisplay().Raise()
                else:
                    # handle that you cannot get coordinates

            def OnMouseAction(self, event):
                # get real world coordinates of mouse click
                coor = self.mapwin.Pixel2Cell(event.GetPositionTuple()[:])
                self.text.SetLabel('Coor: ' + str(coor))
                self.mapwin.UnregisterMouseEventHandler(wx.EVT_LEFT_DOWN, self.OnMouseAction)
                event.Skip()


        Emits mouseHandlerRegistered signal before handler is registered.

        :param event: one of mouse events
        :param handler: function to handle event
        :param cursor: cursor which temporary overrides current cursor

        :return: True if successful
        :return: False if event cannot be bind
        """
        self.mouseHandlerRegistered.emit()
        # inserts handler into list
        for containerEv, handlers in self.handlersContainer.iteritems():
            if event == containerEv: 
                handlers.append(handler)
        
        self.mouse['useBeforeGenericEvent'] = self.mouse['use']
        self.mouse['use'] = 'genericEvent'
        
        if cursor:
            self._overriddenCursor = self.GetNamedCursor()
            self.SetNamedCursor(cursor)
        
        return True

    def UnregisterAllHandlers(self):
        """Unregisters all registered handlers 

        @depreciated This method is depreciated. Use Signals or drawing API instead.

        Before each handler is unregistered it is called with string
        value "unregistered" of event parameter.
        """
        for containerEv, handlers in self.handlersContainer.iteritems():
            for handler in handlers:
                try:
                    handler("unregistered")
                    handlers.remove(handler)
                except:
                    GError(parent = self,
                           message = _("Error occurred during unregistration of handler: %s \n \
                                       Handler was unregistered.") % handler.__name__)
                    handlers.remove(handler)
        
    def UnregisterMouseEventHandler(self, event, handler):
        """Unbinds event handler for event

        @depreciated This method is depreciated. Use Signals or drawing API instead.

        Before handler is unregistered it is called with string value
        "unregistered" of event parameter.

        Emits mouseHandlerUnregistered signal after handler is unregistered.

        :param handler: handler to unbind
        :param event: event from which handler will be unbinded
        
        :return: True if successful
        :return: False if event cannot be unbind
        """
        # removes handler from list 
        for containerEv, handlers in self.handlersContainer.iteritems():
            if event != containerEv:
                continue
            try:
                handler("unregistered")
                if handler in handlers:
                    handlers.remove(handler)
                else:
                    grass.warning(_("Handler: %s was not registered") \
                                      % handler.__name__)
            except:
                GError(parent = self,
                       message = _("Error occurred during unregistration of handler: %s \n \
                                       Handler was unregistered") % handler.__name__)
                handlers.remove(handler) 
        
        # restore mouse use (previous state)
        self.mouse['use'] = self.mouse['useBeforeGenericEvent']
        
        # restore overridden cursor
        if self._overriddenCursor:
            self.SetNamedCursor(self._overriddenCursor)

        self.mouseHandlerUnregistered.emit()
        return True
    
    def Pixel2Cell(self, xyCoords):
        raise NotImplementedError()
    
    def Cell2Pixel(self, enCoords):
        raise NotImplementedError()

    def OnMotion(self, event):
        """Tracks mouse motion and update statusbar

        .. todo::
            remove this method when lastEN is not used

        :func:`GetLastEN`
        """
        try:
            self.lastEN = self.Pixel2Cell(event.GetPositionTuple())
        except (ValueError):
            self.lastEN = None

        event.Skip()

    def GetLastEN(self):
        """Returns last coordinates of mouse cursor.

        @depreciated This method is depreciated. Use Signal with coordinates as parameters.

        :func:`OnMotion`
        """
        return self.lastEN

    def SetNamedCursor(self, cursorName):
        """Sets cursor defined by name."""
        cursor = self._cursors[cursorName]
        self.SetCursor(cursor)
        self._cursor = cursorName

    def GetNamedCursor(self):
        """Returns current cursor name."""
        return self._cursor

    cursor = property(fget=GetNamedCursor, fset=SetNamedCursor)

    def SetModePointer(self):
        """Sets mouse mode to pointer."""
        self.mouse['use'] = 'pointer'
        self.mouse['box'] = 'point'
        self.SetNamedCursor('default')

    def SetModePan(self):
        """Sets mouse mode to pan."""
        self.mouse['use'] = "pan"
        self.mouse['box'] = "box"
        self.zoomtype = 0
        self.SetNamedCursor('hand')

    def SetModeZoomIn(self):
        self._setModeZoom(zoomType=1)

    def SetModeZoomOut(self):
        self._setModeZoom(zoomType=-1)

    def _setModeZoom(self, zoomType):
        self.zoomtype = zoomType
        self.mouse['use'] = "zoom"
        self.mouse['box'] = "box"
        self.pen = wx.Pen(colour='Red', width=2, style=wx.SHORT_DASH)
        self.SetNamedCursor('cross')

    def SetModeDrawRegion(self):
        self.mouse['use'] = 'drawRegion'
        self.mouse['box'] = "box"
        self.pen = wx.Pen(colour='Red', width=2, style=wx.SHORT_DASH)
        self.SetNamedCursor('cross')

    def SetModeQuery(self):
        """Query mode on"""
        self.mouse['use'] = "query"
        self.mouse['box'] = "point"
        self.zoomtype = 0
        self.SetNamedCursor('cross')

    def DisactivateWin(self):
        """Use when the class instance is hidden in MapFrame."""
        raise NotImplementedError()

    def ActivateWin(self):
        """Used when the class instance is activated in MapFrame."""
        raise NotImplementedError()
Exemplo n.º 29
0
class GPromptSTC(GPrompt, wx.stc.StyledTextCtrl):
    """Styled wxGUI prompt with autocomplete and calltips"""  
    def __init__(self, parent, menuModel, margin = False):
        GPrompt.__init__(self, parent = parent, menuModel = menuModel)
        wx.stc.StyledTextCtrl.__init__(self, self.panel, id = wx.ID_ANY)
        
        #
        # styles
        #                
        self.SetWrapMode(True)
        self.SetUndoCollection(True)        
        
        #
        # create command and map lists for autocompletion
        #
        self.AutoCompSetIgnoreCase(False) 
                
        #
        # line margins
        #
        # TODO print number only from cmdlog
        self.SetMarginWidth(1, 0)
        self.SetMarginWidth(2, 0)
        if margin:
            self.SetMarginType(0, wx.stc.STC_MARGIN_NUMBER)
            self.SetMarginWidth(0, 30)
        else:
            self.SetMarginWidth(0, 0)
        
        #
        # miscellaneous
        #
        self.SetViewWhiteSpace(False)
        self.SetUseTabs(False)
        self.UsePopUp(True)
        self.SetSelBackground(True, "#FFFF00")
        self.SetUseHorizontalScrollBar(True)
        
        #
        # bindings
        #
        self.Bind(wx.EVT_WINDOW_DESTROY, self.OnDestroy)
        self.Bind(wx.EVT_CHAR, self.OnChar)
        self.Bind(wx.EVT_KEY_DOWN, self.OnKeyPressed)
        self.Bind(wx.stc.EVT_STC_AUTOCOMP_SELECTION, self.OnItemSelected)
        self.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnItemChanged)
        if sys.platform != 'darwin':  # unstable on Mac with wxPython 3
            self.Bind(wx.EVT_KILL_FOCUS, self.OnKillFocus)

        # signal which requests showing of a notification
        self.showNotification = Signal('GPromptSTC.showNotification')

        # signal to notify selected command
        self.commandSelected = Signal('GPromptSTC.commandSelected')
        
    def OnTextSelectionChanged(self, event):
        """Copy selected text to clipboard and skip event.
        The same function is in GStc class (goutput.py).
        """
        wx.CallAfter(self.Copy)
        event.Skip()
        
    def OnItemChanged(self, event):
        """Change text in statusbar 
        if the item selection in the auto-completion list is changed"""
        # list of commands
        if self.toComplete['entity'] == 'command':
            item = self.toComplete['cmd'].rpartition('.')[0] + '.' + self.autoCompList[event.GetIndex()] 
            try:
                nodes = self._menuModel.SearchNodes(key='command', value=item)
                desc = ''
                if nodes:
                    self.commandSelected.emit(command=item)
                    desc = nodes[0].data['description']
            except KeyError:
                desc = '' 
            self.ShowStatusText(desc)
        # list of flags    
        elif self.toComplete['entity'] == 'flags':
            desc = self.cmdDesc.get_flag(self.autoCompList[event.GetIndex()])['description']
            self.ShowStatusText(desc)
        # list of parameters
        elif self.toComplete['entity'] == 'params':
            item = self.cmdDesc.get_param(self.autoCompList[event.GetIndex()])
            desc = item['name'] + '=' + item['type']
            if not item['required']:
                desc = '[' + desc + ']'
            desc += ': ' + item['description']
            self.ShowStatusText(desc)
        # list of flags and commands       
        elif self.toComplete['entity'] == 'params+flags':
            if self.autoCompList[event.GetIndex()][0] == '-':
                desc = self.cmdDesc.get_flag(self.autoCompList[event.GetIndex()].strip('-'))['description']
            else:
                item = self.cmdDesc.get_param(self.autoCompList[event.GetIndex()])
                desc = item['name'] + '=' + item['type']
                if not item['required']:
                    desc = '[' + desc + ']'
                desc += ': ' + item['description']
            self.ShowStatusText(desc)
        else:
            self.ShowStatusText('')
            
    def OnItemSelected(self, event):
        """Item selected from the list"""
        lastWord = self.GetWordLeft()
        # to insert selection correctly if selected word partly matches written text
        match = difflib.SequenceMatcher(None, event.GetText(), lastWord)
        matchTuple = match.find_longest_match(0, len(event.GetText()), 0, len(lastWord))
    
        compl = event.GetText()[matchTuple[2]:]
        text = self.GetTextLeft() + compl
        # add space or '=' at the end
        end = '='
        for char in ('.','-','='):
            if text.split(' ')[-1].find(char) >= 0:
                end = ' '
        
        compl += end
        text += end

        self.AddText(compl)
        pos = len(text)
        self.SetCurrentPos(pos)
        
        cmd = text.strip().split(' ')[0]
        
        if not self.cmdDesc or cmd != self.cmdDesc.get_name():
            try:
                self.cmdDesc = gtask.parse_interface(cmd)
            except IOError:
                self.cmdDesc = None

    def OnKillFocus(self, event):
        """Hides autocomplete"""
        # hide autocomplete
        if self.AutoCompActive():
            self.AutoCompCancel()
        event.Skip()

    def SetTextAndFocus(self, text):
        pos = len(text)
        self.commandSelected.emit(command=text)
        self.SetText(text)
        self.SetSelectionStart(pos)
        self.SetCurrentPos(pos)
        self.SetFocus()

    def UpdateCmdHistory(self, cmd):
        """Update command history
        
        :param cmd: command given as a string
        """
        # add command to history    
        self.cmdbuffer.append(cmd)
        
        # keep command history to a managable size
        if len(self.cmdbuffer) > 200:
            del self.cmdbuffer[0]
        self.cmdindex = len(self.cmdbuffer)
        
    def EntityToComplete(self):
        """Determines which part of command (flags, parameters) should
        be completed at current cursor position"""
        entry = self.GetTextLeft()
        toComplete = dict(cmd=None, entity=None)
        try:
            cmd = entry.split()[0].strip()
        except IndexError:
            return toComplete
        
        try:
            splitted = utils.split(str(entry))
        except ValueError: # No closing quotation error
            return toComplete
        if len(splitted) > 0 and cmd in globalvar.grassCmd:
            toComplete['cmd'] = cmd
            if entry[-1] == ' ':
                words = entry.split(' ')
                if any(word.startswith('-') for word in words):
                    toComplete['entity'] = 'params'
                else:
                    toComplete['entity'] = 'params+flags'
            else:
                # get word left from current position
                word = self.GetWordLeft(withDelimiter = True)
                
                if word[0] == '=' and word[-1] == '@':
                    toComplete['entity'] = 'mapsets'
                elif word[0] == '=':
                    # get name of parameter
                    paramName = self.GetWordLeft(withDelimiter = False, ignoredDelimiter = '=').strip('=')
                    if paramName:
                        try:
                            param = self.cmdDesc.get_param(paramName)
                        except (ValueError, AttributeError):
                            return toComplete
                    else:
                        return toComplete
                    
                    if param['values']:
                        toComplete['entity'] = 'param values'
                    elif param['prompt'] == 'raster' and param['element'] == 'cell':
                        toComplete['entity'] = 'raster map'
                    elif param['prompt'] == 'vector' and param['element'] == 'vector':
                        toComplete['entity'] = 'vector map'
                elif word[0] == '-':
                    toComplete['entity'] = 'flags'
                elif word[0] == ' ':
                    toComplete['entity'] = 'params'
        else:
            toComplete['entity'] = 'command'
            toComplete['cmd'] = cmd
        
        return toComplete
    
    def GetWordLeft(self, withDelimiter = False, ignoredDelimiter = None):
        """Get word left from current cursor position. The beginning
        of the word is given by space or chars: .,-= 
        
        :param withDelimiter: returns the word with the initial delimeter
        :param ignoredDelimiter: finds the word ignoring certain delimeter
        """
        textLeft = self.GetTextLeft()
        
        parts = list()
        if ignoredDelimiter is None:
            ignoredDelimiter = ''
        
        for char in set(' .,-=') - set(ignoredDelimiter):
            if not withDelimiter:
                delimiter = ''
            else:
                delimiter = char
            parts.append(delimiter + textLeft.rpartition(char)[2])
        return min(parts, key=lambda x: len(x))
         
    def ShowList(self):
        """Show sorted auto-completion list if it is not empty"""
        if len(self.autoCompList) > 0:
            self.autoCompList.sort()
            self.AutoCompShow(lenEntered = 0, itemList = ' '.join(self.autoCompList))    

    def OnKeyPressed(self, event):
        """Key pressed capture special treatment for tabulator to show help"""
        pos = self.GetCurrentPos()
        if event.GetKeyCode() == wx.WXK_TAB:
            # show GRASS command calltips (to hide press 'ESC')
            entry = self.GetTextLeft()
            try:
                cmd = entry.split()[0].strip()
            except IndexError:
                cmd = ''

            if cmd not in globalvar.grassCmd:
                return

            info = gtask.command_info(cmd)

            self.CallTipSetBackground("#f4f4d1")
            self.CallTipSetForeground("BLACK")
            self.CallTipShow(pos, info['usage'] + '\n\n' + info['description'])
        elif event.GetKeyCode() in (wx.WXK_RETURN, wx.WXK_NUMPAD_ENTER) and \
                not self.AutoCompActive():
            # run command on line when <return> is pressed
            self._runCmd(self.GetCurLine()[0].strip())
        elif event.GetKeyCode() in [wx.WXK_UP, wx.WXK_DOWN] and \
                not self.AutoCompActive():
            # Command history using up and down
            if len(self.cmdbuffer) < 1:
                return

            self.DocumentEnd()

            # move through command history list index values
            if event.GetKeyCode() == wx.WXK_UP:
                self.cmdindex = self.cmdindex - 1
            if event.GetKeyCode() == wx.WXK_DOWN:
                self.cmdindex = self.cmdindex + 1
            if self.cmdindex < 0:
                self.cmdindex = 0
            if self.cmdindex > len(self.cmdbuffer) - 1:
                self.cmdindex = len(self.cmdbuffer) - 1

            try:
                # without strip causes problem on windows
                txt = self.cmdbuffer[self.cmdindex].strip()
            except KeyError:
                txt = ''

            # clear current line and insert command history
            self.DelLineLeft()
            self.DelLineRight()
            pos = self.GetCurrentPos()
            self.InsertText(pos, txt)
            self.LineEnd()

            self.ShowStatusText('')
        else:
            event.Skip()

    def OnChar(self, event):
        """Key char capture for autocompletion, calltips, and command history

        .. todo::
            event.ControlDown() for manual autocomplete
        """
        # keycodes used: "." = 46, "=" = 61, "-" = 45 
        pos = self.GetCurrentPos()
        # complete command after pressing '.'
        if event.GetKeyCode() == 46:
            self.autoCompList = list()
            entry = self.GetTextLeft()
            self.InsertText(pos, '.')
            self.CharRight()
            self.toComplete = self.EntityToComplete()
            try:
                if self.toComplete['entity'] == 'command': 
                    for command in globalvar.grassCmd:
                        try:
                            if command.find(self.toComplete['cmd']) == 0:
                                dotNumber = list(self.toComplete['cmd']).count('.') 
                                self.autoCompList.append(command.split('.',dotNumber)[-1])
                        except UnicodeDecodeError as e: # TODO: fix it
                            sys.stderr.write(DecodeString(command) + ": " + unicode(e))
                            
            except (KeyError, TypeError):
                return
            self.ShowList()

        # complete flags after pressing '-'
        elif (event.GetKeyCode() == 45) \
                or event.GetKeyCode() == wx.WXK_NUMPAD_SUBTRACT \
                or event.GetKeyCode() == wx.WXK_SUBTRACT:
            self.autoCompList = list()
            entry = self.GetTextLeft()
            self.InsertText(pos, '-')
            self.CharRight()
            self.toComplete = self.EntityToComplete()
            if self.toComplete['entity'] == 'flags' and self.cmdDesc:
                if self.GetTextLeft()[-2:] == ' -': # complete e.g. --quite
                    for flag in self.cmdDesc.get_options()['flags']:
                        if len(flag['name']) == 1:
                            self.autoCompList.append(flag['name'])
                else:
                    for flag in self.cmdDesc.get_options()['flags']:
                        if len(flag['name']) > 1:
                            self.autoCompList.append(flag['name'])            
            self.ShowList()
            
        # complete map or values after parameter
        elif event.GetKeyCode() == 61:
            self.autoCompList = list()
            self.InsertText(pos, '=')
            self.CharRight()
            self.toComplete = self.EntityToComplete()
            if self.toComplete['entity'] == 'raster map':
                self.autoCompList = self.mapList['raster']
            elif self.toComplete['entity'] == 'vector map':
                self.autoCompList = self.mapList['vector']
            elif self.toComplete['entity'] == 'param values':
                param = self.GetWordLeft(withDelimiter = False, ignoredDelimiter='=').strip(' =')
                self.autoCompList = self.cmdDesc.get_param(param)['values']
            self.ShowList()
        
        # complete mapset ('@')
        elif event.GetKeyCode() == 64:
            self.autoCompList = list()
            self.InsertText(pos, '@')
            self.CharRight()
            self.toComplete = self.EntityToComplete()
            
            if self.toComplete['entity'] == 'mapsets':
                self.autoCompList = self.mapsetList
            self.ShowList()
            
        # complete after pressing CTRL + Space          
        elif event.GetKeyCode() == wx.WXK_SPACE and event.ControlDown():
            self.autoCompList = list()
            self.toComplete = self.EntityToComplete()

            #complete command
            if self.toComplete['entity'] == 'command':
                for command in globalvar.grassCmd:
                    if command.find(self.toComplete['cmd']) == 0:
                        dotNumber = list(self.toComplete['cmd']).count('.') 
                        self.autoCompList.append(command.split('.',dotNumber)[-1])
                
            
            # complete flags in such situations (| is cursor):
            # r.colors -| ...w, q, l
            # r.colors -w| ...w, q, l  
            elif self.toComplete['entity'] == 'flags' and self.cmdDesc:
                for flag in self.cmdDesc.get_options()['flags']:
                    if len(flag['name']) == 1:
                        self.autoCompList.append(flag['name'])
                    
            # complete parameters in such situations (| is cursor):
            # r.colors -w | ...color, map, rast, rules
            # r.colors col| ...color
            elif self.toComplete['entity'] == 'params' and self.cmdDesc:
                for param in self.cmdDesc.get_options()['params']:
                    if param['name'].find(self.GetWordLeft(withDelimiter=False)) == 0:
                        self.autoCompList.append(param['name'])           
            
            # complete flags or parameters in such situations (| is cursor):
            # r.colors | ...-w, -q, -l, color, map, rast, rules
            # r.colors color=grey | ...-w, -q, -l, color, map, rast, rules
            elif self.toComplete['entity'] == 'params+flags' and self.cmdDesc:
                self.autoCompList = list()
                
                for param in self.cmdDesc.get_options()['params']:
                    self.autoCompList.append(param['name'])
                for flag in self.cmdDesc.get_options()['flags']:
                    if len(flag['name']) == 1:
                        self.autoCompList.append('-' + flag['name'])
                    else:
                        self.autoCompList.append('--' + flag['name'])
                    
                self.ShowList() 
                   
            # complete map or values after parameter  
            # r.buffer input=| ...list of raster maps
            # r.buffer units=| ... feet, kilometers, ...   
            elif self.toComplete['entity'] == 'raster map':
                self.autoCompList = list()
                self.autoCompList = self.mapList['raster']
            elif self.toComplete['entity'] == 'vector map':
                self.autoCompList = list()
                self.autoCompList = self.mapList['vector']
            elif self.toComplete['entity'] == 'param values':
                self.autoCompList = list()
                param = self.GetWordLeft(withDelimiter = False, ignoredDelimiter='=').strip(' =')
                self.autoCompList = self.cmdDesc.get_param(param)['values']
                
            self.ShowList()

        elif event.GetKeyCode() == wx.WXK_SPACE:
            items = self.GetTextLeft().split()
            if len(items) == 1:
                cmd = items[0].strip()
                if cmd in globalvar.grassCmd and \
                        (not self.cmdDesc or cmd != self.cmdDesc.get_name()):
                    try:
                        self.cmdDesc = gtask.parse_interface(cmd)
                    except IOError:
                        self.cmdDesc = None
            event.Skip()
        
        else:
            event.Skip()

    def ShowStatusText(self, text):
        """Requests showing of notification, e.g. showing in a statusbar."""
        self.showNotification.emit(message=text)
        
    def GetTextLeft(self):
        """Returns all text left of the caret"""
        pos = self.GetCurrentPos()
        self.HomeExtend()
        entry = self.GetSelectedText()
        self.SetCurrentPos(pos)
        
        return entry
    
    def OnDestroy(self, event):
        """The clipboard contents can be preserved after
        the app has exited"""
        wx.TheClipboard.Flush()
        event.Skip()

    def OnCmdErase(self, event):
        """Erase command prompt"""
        self.Home()
        self.DelLineRight()
Exemplo n.º 30
0
class GConsole(wx.EvtHandler):
    """
    """
    def __init__(self, guiparent=None, giface=None, ignoredCmdPattern=None):
        """
        :param guiparent: parent window for created GUI objects
        :param lmgr: layer manager window (TODO: replace by giface)
        :param ignoredCmdPattern: regular expression specifying commads
                                  to be ignored (e.g. @c '^d\..*' for
                                  display commands)
        """
        wx.EvtHandler.__init__(self)

        # Signal when some map is created or updated by a module.
        # attributes: name: map name, ltype: map type,
        self.mapCreated = Signal('GConsole.mapCreated')
        # emitted when map display should be re-render
        self.updateMap = Signal('GConsole.updateMap')
        # emitted when log message should be written
        self.writeLog = Signal('GConsole.writeLog')
        # emitted when command log message should be written
        self.writeCmdLog = Signal('GConsole.writeCmdLog')
        # emitted when warning message should be written
        self.writeWarning = Signal('GConsole.writeWarning')
        # emitted when error message should be written
        self.writeError = Signal('GConsole.writeError')

        self._guiparent = guiparent
        self._giface = giface
        self._ignoredCmdPattern = ignoredCmdPattern

        # create queues
        self.requestQ = Queue.Queue()
        self.resultQ = Queue.Queue()

        self.cmdOutputTimer = wx.Timer(self)
        self.Bind(wx.EVT_TIMER, self.OnProcessPendingOutputWindowEvents)
        self.Bind(EVT_CMD_RUN, self.OnCmdRun)
        self.Bind(EVT_CMD_DONE, self.OnCmdDone)
        self.Bind(EVT_CMD_ABORT, self.OnCmdAbort)

        # stream redirection
        self.cmdStdOut = GStdout(receiver=self)
        self.cmdStdErr = GStderr(receiver=self)

        # thread
        self.cmdThread = CmdThread(self, self.requestQ, self.resultQ)

    def Redirect(self):
        """Redirect stdout/stderr
        """
        if Debug.GetLevel() == 0 and int(grass.gisenv().get('DEBUG', 0)) == 0:
            # don't redirect when debugging is enabled
            sys.stdout = self.cmdStdOut
            sys.stderr = self.cmdStdErr
        else:
            enc = locale.getdefaultlocale()[1]
            if enc:
                sys.stdout = codecs.getwriter(enc)(sys.__stdout__)
                sys.stderr = codecs.getwriter(enc)(sys.__stderr__)
            else:
                sys.stdout = sys.__stdout__
                sys.stderr = sys.__stderr__

    def WriteLog(self, text, style=None, wrap=None,
                 notification=Notification.HIGHLIGHT):
        """Generic method for writing log message in
        given style

        :param text: text line
        :param notification: form of notification
        """
        self.writeLog.emit(text=text, wrap=wrap,
                          notification=notification)

    def WriteCmdLog(self, text, pid=None, notification=Notification.MAKE_VISIBLE):
        """Write message in selected style

        :param text: message to be printed
        :param pid: process pid or None
        :param notification: form of notification
        """
        self.writeCmdLog.emit(text=text, pid=pid,
                              notification=notification)

    def WriteWarning(self, text):
        """Write message in warning style"""
        self.writeWarning.emit(text=text)

    def WriteError(self, text):
        """Write message in error style"""
        self.writeError.emit(text=text)

    def RunCmd(self, command, compReg=True, skipInterface=False,
               onDone=None, onPrepare=None, userData=None, notification=Notification.MAKE_VISIBLE):
        """Run command typed into console command prompt (GPrompt).

        .. todo::
            Document the other event.
        .. todo::
            Solve problem with the other event (now uses gOutputText
            event but there is no text, use onPrepare handler instead?)

        Posts event EVT_IGNORED_CMD_RUN when command which should be ignored
        (according to ignoredCmdPattern) is run.
        For example, see layer manager which handles d.* on its own.

        :param command: command given as a list (produced e.g. by utils.split())
        :param compReg: True use computation region
        :param notification: form of notification
        :param bool skipInterface: True to do not launch GRASS interface
                                   parser when command has no arguments
                                   given
        :param onDone: function to be called when command is finished
        :param onPrepare: function to be called before command is launched
        :param userData: data defined for the command
        """
        if len(command) == 0:
            Debug.msg(2, "GPrompt:RunCmd(): empty command")
            return

        # update history file
        self.UpdateHistoryFile(' '.join(command))
        
        if command[0] in globalvar.grassCmd:
            # send GRASS command without arguments to GUI command interface
            # except ignored commands (event is emitted)

            if self._ignoredCmdPattern and \
              re.compile(self._ignoredCmdPattern).search(' '.join(command)) and \
              '--help' not in command and '--ui' not in command:
                event = gIgnoredCmdRun(cmd=command)
                wx.PostEvent(self, event)
                return
            
            else:
                # other GRASS commands (r|v|g|...)
                try:
                    task = GUI(show=None).ParseCommand(command)
                except GException as e:
                    GError(parent=self._guiparent,
                           message=unicode(e),
                           showTraceback=False)
                    return

                hasParams = False
                if task:
                    options = task.get_options()
                    hasParams = options['params'] and options['flags']
                    # check for <input>=-
                    for p in options['params']:
                        if p.get('prompt', '') == 'input' and \
                                p.get('element', '') == 'file' and \
                                p.get('age', 'new') == 'old' and \
                                p.get('value', '') == '-':
                            GError(parent=self._guiparent,
                                   message=_("Unable to run command:\n%(cmd)s\n\n"
                                             "Option <%(opt)s>: read from standard input is not "
                                             "supported by wxGUI") % {'cmd': ' '.join(command),
                                                                      'opt': p.get('name', '')})
                            return

                if len(command) == 1 and hasParams and \
                        command[0] != 'v.krige':
                    # no arguments given
                    try:
                        GUI(parent=self._guiparent, giface=self._giface).ParseCommand(command)
                    except GException as e:
                        print >> sys.stderr, e
                    return

                # activate computational region (set with g.region)
                # for all non-display commands.
                if compReg:
                    tmpreg = os.getenv("GRASS_REGION")
                    if "GRASS_REGION" in os.environ:
                        del os.environ["GRASS_REGION"]

                # process GRASS command with argument
                self.cmdThread.RunCmd(command,
                                      stdout=self.cmdStdOut,
                                      stderr=self.cmdStdErr,
                                      onDone=onDone, onPrepare=onPrepare,
                                      userData=userData,
                                      env=os.environ.copy(),
                                      notification=notification)
                self.cmdOutputTimer.Start(50)

                # deactivate computational region and return to display settings
                if compReg and tmpreg:
                    os.environ["GRASS_REGION"] = tmpreg
        else:
            # Send any other command to the shell. Send output to
            # console output window
            #
            # Check if the script has an interface (avoid double-launching
            # of the script)

            # check if we ignore the command (similar to grass commands part)
            if self._ignoredCmdPattern and \
               re.compile(self._ignoredCmdPattern).search(' '.join(command)):
                event = gIgnoredCmdRun(cmd=command)
                wx.PostEvent(self, event)
                return

            skipInterface = True
            if os.path.splitext(command[0])[1] in ('.py', '.sh'):
                try:
                    sfile = open(command[0], "r")
                    for line in sfile.readlines():
                        if len(line) < 2:
                            continue
                        if line[0] is '#' and line[1] is '%':
                            skipInterface = False
                            break
                    sfile.close()
                except IOError:
                    pass
            
            if len(command) == 1 and not skipInterface:
                try:
                    task = gtask.parse_interface(command[0])
                except:
                    task = None
            else:
                task = None

            if task:
                # process GRASS command without argument
                GUI(parent=self._guiparent, giface=self._giface).ParseCommand(command)
            else:
                self.cmdThread.RunCmd(command,
                                      stdout=self.cmdStdOut,
                                      stderr=self.cmdStdErr,
                                      onDone=onDone, onPrepare=onPrepare,
                                      userData=userData,
                                      notification=notification)
            self.cmdOutputTimer.Start(50)

    def GetLog(self, err=False):
        """Get widget used for logging

        .. todo::
           what's this?

        :param bool err: True to get stderr widget
        """
        if err:
            return self.cmdStdErr

        return self.cmdStdOut

    def GetCmd(self):
        """Get running command or None"""
        return self.requestQ.get()

    def OnCmdAbort(self, event):
        """Abort running command"""
        self.cmdThread.abort()
        event.Skip()

    def OnCmdRun(self, event):
        """Run command"""
        self.WriteCmdLog('(%s)\n%s' % (str(time.ctime()), ' '.join(event.cmd)),
                         notification=event.notification)
        event.Skip()

    def OnCmdDone(self, event):
        """Command done (or aborted)

        Sends signal mapCreated if map is recognized in output
        parameters or for specific modules (as r.colors).
        """
        # Process results here
        try:
            ctime = time.time() - event.time
            if ctime < 60:
                stime = _("%d sec") % int(ctime)
            else:
                mtime = int(ctime / 60)
                stime = _("%(min)d min %(sec)d sec") % {'min': mtime,
                                                        'sec': int(ctime - (mtime * 60))}
        except KeyError:
            # stopped deamon
            stime = _("unknown")

        if event.aborted:
            # Thread aborted (using our convention of None return)
            self.WriteWarning(_('Please note that the data are left in'
                                ' inconsistent state and may be corrupted'))
            msg = _('Command aborted')
        else:
            msg = _('Command finished')

        self.WriteCmdLog('(%s) %s (%s)' % (str(time.ctime()), msg, stime),
                         notification=event.notification)

        if event.onDone:
            event.onDone(cmd=event.cmd, returncode=event.returncode)

        self.cmdOutputTimer.Stop()

        if event.cmd[0] == 'g.gisenv':
            Debug.SetLevel()
            self.Redirect()

        # do nothing when no map added
        if event.returncode != 0 or event.aborted:
            event.Skip()
            return

        if event.cmd[0] not in globalvar.grassCmd:
            return

        # find which maps were created
        try:
            task = GUI(show=None).ParseCommand(event.cmd)
        except GException as e:
            print >> sys.stderr, e
            task = None
            return

        name = task.get_name()
        for p in task.get_options()['params']:
            prompt = p.get('prompt', '')
            if prompt in ('raster', 'vector', '3d-raster') and p.get('value', None):
                if p.get('age', 'old') == 'new' or \
                        name in ('r.colors', 'r3.colors', 'v.colors', 'v.proj', 'r.proj'):
                    # if multiple maps (e.g. r.series.interp), we need add each
                    if p.get('multiple', False):
                        lnames = p.get('value').split(',')
                        # in case multiple input (old) maps in r.colors
                        # we don't want to rerender it multiple times! just once
                        if p.get('age', 'old') == 'old':
                            lnames = lnames[0:1]
                    else:
                        lnames = [p.get('value')]
                    for lname in lnames:
                        if '@' not in lname:
                            lname += '@' + grass.gisenv()['MAPSET']
                        self.mapCreated.emit(name=lname, ltype=prompt)
        if name == 'r.mask':
            self.updateMap.emit()
        
        event.Skip()

    def OnProcessPendingOutputWindowEvents(self, event):
        wx.GetApp().ProcessPendingEvents()

    def UpdateHistoryFile(self, command):
        """Update history file
        
        :param command: the command given as a string
        """
        env = grass.gisenv()
        try:
            filePath = os.path.join(env['GISDBASE'],
                                    env['LOCATION_NAME'],
                                    env['MAPSET'],
                                    '.bash_history')
            fileHistory = codecs.open(filePath, encoding='utf-8', mode='a')
        except IOError as e:
            GError(_("Unable to write file '%(filePath)s'.\n\nDetails: %(error)s") % 
                    {'filePath': filePath, 'error': e},
                   parent=self._guiparent)
            return
        
        try:
            fileHistory.write(command + os.linesep)
        finally:
            fileHistory.close()
        
        # update wxGUI prompt
        if self._giface:
            self._giface.UpdateCmdHistory(command)
Exemplo n.º 31
0
class ScattsManager:
    """Main controller
    """

    def __init__(self, guiparent, giface, iclass_mapwin=None):
        self.giface = giface
        self.mapDisp = giface.GetMapDisplay()

        if iclass_mapwin:
            self.mapWin = iclass_mapwin
        else:
            self.mapWin = giface.GetMapWindow()

        self.guiparent = guiparent

        self.show_add_scatt_plot = False

        self.core = Core()

        self.cats_mgr = CategoriesManager(self, self.core)
        self.render_mgr = PlotsRenderingManager(scatt_mgr=self,
                                                cats_mgr=self.cats_mgr,
                                                core=self.core)

        self.thread = gThread()

        self.plots = {}

        self.plot_mode = None
        self.pol_sel_mode = [False, None]

        self.data_set = False

        self.cursorPlotMove = Signal("ScattsManager.cursorPlotMove")

        self.renderingStarted = self.render_mgr.renderingStarted
        self.renderingFinished = self.render_mgr.renderingFinished

        self.computingStarted = Signal("ScattsManager.computingStarted")

        if iclass_mapwin:
            self.digit_conn = IClassDigitConnection(self,
                                                    self.mapWin,
                                                    self.core.CatRastUpdater())
            self.iclass_conn = IClassConnection(self,
                                                iclass_mapwin.parent,
                                                self.cats_mgr)
        else:
            self.digit_conn = IMapWinDigitConnection()
            self.iclass_conn = IMapDispConnection(scatt_mgr=self,
                                                  cats_mgr=self.cats_mgr,
                                                  giface=self.giface)

        self._initSettings()

        self.modeSet = Signal("ScattsManager.mondeSet")

    def CleanUp(self):
        self.thread.Terminate()
        # there should be better way hot to clean up the thread
        # than calling the clean up function outside the thread,
        # which still may running
        self.core.CleanUp()

    def CleanUpDone(self):
        for scatt_id, scatt in self.plots.items():
            if scatt['scatt']:
                scatt['scatt'].CleanUp()

        self.plots.clear()

    def _initSettings(self):
        """Initialization of settings (if not already defined)
        """
        # initializes default settings
        initSettings = [
            ['selection', 'sel_pol', (255, 255, 0)],
            ['selection', 'sel_pol_vertex', (255, 0, 0)],
            ['selection', 'sel_area', (0, 255, 19)],
            ['selection', "snap_tresh", 10],
            ['selection', 'sel_area_opacty', 50],
            ['ellipses', 'show_ellips', True],
        ]

        for init in initSettings:
            UserSettings.ReadSettingsFile()
            UserSettings.Append(dict=UserSettings.userSettings,
                                group='scatt',
                                key=init[0],
                                subkey=init[1],
                                value=init[2],
                                overwrite=False)

    def SetData(self):
        self.iclass_conn.SetData()
        self.digit_conn.SetData()

    def SetBands(self, bands):
        self.busy = wx.BusyInfo(_("Loading data..."))
        self.data_set = False
        self.thread.Run(callable=self.core.CleanUp,
                        ondone=lambda event: self.CleanUpDone())

        if self.show_add_scatt_plot:
            show_add = True
        else:
            show_add = False

        self.all_bands_to_bands = dict(zip(bands, [-1] * len(bands)))
        self.all_bands = bands

        self.region = GetRegion()
        ncells = self.region["rows"] * self.region["cols"]

        if ncells > MAX_NCELLS:
            del self.busy
            self.data_set = True
            return

        self.bands = bands[:]
        self.bands_info = {}
        valid_bands = []

        for b in self.bands[:]:
            i = GetRasterInfo(b)

            self.bands_info[b] = i
            if i is not None:
                valid_bands.append(b)

        for i, b in enumerate(valid_bands):
            # name : index in core bands -
            # if not in core bands (not CELL type) -> index = -1
            self.all_bands_to_bands[b] = i

        self.thread.Run(callable=self.core.SetData,
                        bands=valid_bands,
                        ondone=self.SetDataDone,
                        userdata={"show_add": show_add})

    def SetDataDone(self, event):
        del self.busy
        self.data_set = True

        todo = event.ret
        self.bad_bands = event.ret
        bands = self.core.GetBands()

        self.bad_rasts = event.ret
        self.cats_mgr.SetData()
        if event.userdata['show_add']:
            self.AddScattPlot()

    def GetBands(self):
        return self.core.GetBands()

    def AddScattPlot(self):
        if not self.data_set and self.iclass_conn:
            self.show_add_scatt_plot = True
            self.iclass_conn.SetData()
            self.show_add_scatt_plot = False
            return
        if not self.data_set:
            GError(_('No data set.'))
            return

        self.computingStarted.emit()

        bands = self.core.GetBands()

        #added_bands_ids = []
        # for scatt_id in self.plots):
        #    added_bands_ids.append[idBandsToidScatt(scatt_id)]

        self.digit_conn.Update()

        ncells = self.region["rows"] * self.region["cols"]
        if ncells > MAX_NCELLS:
            GError(
                _(
                    parent=self.guiparent, mmessage=_(
                        "Interactive Scatter Plot Tool can not be used.\n"
                        "Number of cells (rows*cols) <%d> in current region"
                        "is higher than maximum limit <%d>.\n\n"
                        "You can reduce number of cells in current region using <g.region> command." %
                        (ncells, MAX_NCELLS))))
            return
        elif ncells > WARN_NCELLS:
            dlg = wx.MessageDialog(
                parent=self.guiparent,
                message=_("Number of cells (rows*cols) <%d> in current region is "
                          "higher than recommended threshold <%d>.\n"
                          "It is strongly advised to reduce number of cells "
                          "in current region below recommend threshold.\n "
                          "It can be done by <g.region> command.\n\n"
                          "Do you want to continue using "
                          "Interactive Scatter Plot Tool with this region?"
                          % (ncells, WARN_NCELLS)),
                style=wx.YES_NO | wx.NO_DEFAULT | wx.ICON_WARNING)
            ret = dlg.ShowModal()
            if ret != wx.ID_YES:
                return

        dlg = AddScattPlotDialog(parent=self.guiparent,
                                 bands=self.all_bands,
                                 check_bands_callback=self.CheckBands)

        if dlg.ShowModal() == wx.ID_OK:

            scatt_ids = []
            sel_bands = dlg.GetBands()

            for b_1, b_2 in sel_bands:
                transpose = False
                if b_1 > b_2:
                    transpose = True
                    tmp_band = b_2
                    b_2 = b_1
                    b_1 = tmp_band

                b_1_id = self.all_bands_to_bands[self.all_bands[b_1]]
                b_2_id = self.all_bands_to_bands[self.all_bands[b_2]]

                scatt_id = idBandsToidScatt(b_1_id, b_2_id, len(bands))
                if scatt_id in self.plots:
                    continue

                self.plots[scatt_id] = {'transpose': transpose,
                                        'scatt': None}
                scatt_ids.append(scatt_id)

            self._addScattPlot(scatt_ids)

        dlg.Destroy()

    def CheckBands(self, b_1, b_2):
        bands = self.core.GetBands()
        added_scatts_ids = self.plots.keys()

        b_1_id = self.all_bands_to_bands[self.all_bands[b_1]]
        b_2_id = self.all_bands_to_bands[self.all_bands[b_1]]

        scatt_id = idBandsToidScatt(b_1_id, b_2_id, len(bands))

        if scatt_id in added_scatts_ids:
            GWarning(
                parent=self.guiparent, message=_(
                    "Scatter plot with same band combination (regardless x y order) "
                    "is already displayed."))
            return False

        b_1_name = self.all_bands[b_1]
        b_2_name = self.all_bands[b_2]

        b_1_i = self.bands_info[b_1_name]
        b_2_i = self.bands_info[b_2_name]

        err = ""
        for b in [b_1_name, b_2_name]:
            if self.bands_info[b] is None:
                err += _("Band <%s> is not CELL (integer) type.\n" % b)
        if err:
            GMessage(parent=self.guiparent,
                     message=_("Scatter plot cannot be added.\n" + err))
            return False

        mrange = b_1_i['range'] * b_2_i['range']
        if mrange > MAX_SCATT_SIZE:
            GWarning(parent=self.guiparent,
                     message=_("Scatter plot cannot be added.\n"
                               "Multiple of bands ranges <%s:%d * %s:%d = %d> "
                               "is higher than maximum limit <%d>.\n"
                               % (b_1_name, b_1_i['range'], b_1_name, b_2_i['range'],
                                  mrange, MAX_SCATT_SIZE)))
            return False
        elif mrange > WARN_SCATT_SIZE:
            dlg = wx.MessageDialog(
                parent=self.guiparent,
                message=_(
                    "Multiple of bands ranges <%s:%d * %s:%d = %d> "
                    "is higher than recommended limit <%d>.\n"
                    "It is strongly advised to reduce range extend of bands"
                    "(e. g. using r.rescale) below recommended threshold.\n\n"
                    "Do you really want to add this scatter plot?" %
                    (b_1_name, b_1_i['range'],
                     b_1_name, b_2_i['range'],
                     mrange, WARN_SCATT_SIZE)),
                style=wx.YES_NO | wx.NO_DEFAULT | wx.ICON_WARNING)
            ret = dlg.ShowModal()
            if ret != wx.ID_YES:
                return False

        return True

    def _addScattPlot(self, scatt_ids):
        self.render_mgr.NewRunningProcess()
        self.thread.Run(callable=self.core.AddScattPlots,
                        scatt_ids=scatt_ids, ondone=self.AddScattPlotDone)

    def AddScattPlotDone(self, event):
        if not self.data_set:
            return

        scatt_ids = event.kwds['scatt_ids']
        for s_id in scatt_ids:
            trans = self.plots[s_id]['transpose']

            self.plots[s_id]['scatt'] = self.guiparent.NewScatterPlot(
                scatt_id=s_id, transpose=trans)

            self.plots[s_id]['scatt'].plotClosed.connect(self.PlotClosed)
            self.plots[s_id]['scatt'].cursorMove.connect(
                lambda x, y, scatt_id:
                self.cursorPlotMove.emit(x=x, y=y,
                                         scatt_id=scatt_id))

            if self.plot_mode:
                self.plots[s_id]['scatt'].SetMode(self.plot_mode)
                self.plots[s_id]['scatt'].ZoomToExtend()

        self.render_mgr.RunningProcessDone()

    def PlotClosed(self, scatt_id):
        del self.plots[scatt_id]

    def SetPlotsMode(self, mode):

        self.plot_mode = mode
        for scatt in six.itervalues(self.plots):
            if scatt['scatt']:
                scatt['scatt'].SetMode(mode)

        self.modeSet.emit(mode=mode)

    def ActivateSelectionPolygonMode(self, activate):
        self.pol_sel_mode[0] = activate
        for scatt in six.itervalues(self.plots):
            if not scatt['scatt']:
                continue
            scatt['scatt'].SetSelectionPolygonMode(activate)
            if not activate and self.plot_mode not in [
                    'zoom', 'pan', 'zoom_extend']:
                self.SetPlotsMode(None)

        self.render_mgr.RunningProcessDone()
        return activate

    def ProcessSelectionPolygons(self, process_mode):
        scatts_polygons = {}
        for scatt_id, scatt in six.iteritems(self.plots):
            if not scatt['scatt']:
                continue
            coords = scatt['scatt'].GetCoords()
            if coords is not None:
                scatts_polygons[scatt_id] = coords

        if not scatts_polygons:
            return

        value = 1
        if process_mode == 'remove':
            value = 0

        sel_cat_id = self.cats_mgr.GetSelectedCat()
        if not sel_cat_id:
            dlg = wx.MessageDialog(
                parent=self.guiparent,
                message=_(
                    "In order to select arrea in scatter plot, "
                    "you have to select class first.\n\n"
                    "There is no class yet, "
                    "do you want to create one?"),
                caption=_("No class selected"),
                style=wx.YES_NO)
            if dlg.ShowModal() == wx.ID_YES:
                self.iclass_conn.EmptyCategories()

        sel_cat_id = self.cats_mgr.GetSelectedCat()
        if not sel_cat_id:
            return

        for scatt in six.itervalues(self.plots):
            if scatt['scatt']:
                scatt['scatt'].SetEmpty()

        self.computingStarted.emit()

        self.render_mgr.NewRunningProcess()
        self.render_mgr.CategoryChanged(cat_ids=[sel_cat_id])
        self.render_mgr.CategoryCondsChanged(cat_ids=[sel_cat_id])

        self.thread.Run(callable=self.core.UpdateCategoryWithPolygons,
                        cat_id=sel_cat_id,
                        scatts_pols=scatts_polygons,
                        value=value, ondone=self.SetEditCatDataDone)

    def SetEditCatDataDone(self, event):
        if not self.data_set:
            return

        self.render_mgr.RunningProcessDone()
        if event.exception:
            GError(
                _("Error occurred during computation of scatter plot category:\n%s"),
                parent=self.guiparent,
                showTraceback=False)

        cat_id = event.ret
        self.iclass_conn.RenderCatRast(cat_id)

    def SettingsUpdated(self, chanaged_setts):
        self.render_mgr.RenderRequest()

        #['ellipses', 'show_ellips']
    def GetCategoriesManager(self):
        return self.cats_mgr
Exemplo n.º 32
0
class Map(object):
    def __init__(self, gisrc = None):
        """Map composition (stack of map layers and overlays)

        :param gisrc: alternative gisrc (used eg. by georectifier)
        """
        # region/extent settigns
        self.wind      = dict() # WIND settings (wind file)
        self.region    = dict() # region settings (g.region)
        self.width     = 640    # map width
        self.height    = 480    # map height

        # list of layers
        self.layers    = list()  # stack of available GRASS layer

        self.overlays  = list()  # stack of available overlays
        self.ovlookup  = dict()  # lookup dictionary for overlay items and overlays

        # path to external gisrc
        self.gisrc = gisrc

        # generated file for g.pnmcomp output for rendering the map
        self.mapfile = grass.tempfile(create = False) + '.ppm'

        # setting some initial env. variables
        if not self.GetWindow():
            sys.stderr.write(_("Trying to recover from default region..."))
            RunCommand('g.region', flags='d')

        # info to report progress
        self.progressInfo = None

        # GRASS environment variable (for rendering)
        self.default_env = {"GRASS_RENDER_BACKGROUNDCOLOR" : "000000",
                            "GRASS_RENDER_FILE_COMPRESSION" : "0",
                            "GRASS_RENDER_TRUECOLOR"       : "TRUE",
                            "GRASS_RENDER_TRANSPARENT"     : "TRUE"
                            }

        # projection info
        self.projinfo = self._projInfo()

        # is some layer being downloaded?
        self.downloading = False

        self.layerChanged = Signal('Map.layerChanged')
        self.updateProgress = Signal('Map.updateProgress')

    def GetProjInfo(self):
        """Get projection info"""
        return self.projinfo

    def _projInfo(self):
        """Return region projection and map units information
        """
        projinfo = dict()
        if not grass.find_program('g.proj', '--help'):
            sys.exit(_("GRASS module '%s' not found. Unable to start map "
                       "display window.") % 'g.proj')
        env = os.environ.copy()
        if self.gisrc:
            env['GISRC'] = self.gisrc
        ret = RunCommand(prog='g.proj', read=True, flags='p', env=env)

        if not ret:
            return projinfo

        for line in ret.splitlines():
            if ':' in line:
                key, val = map(lambda x: x.strip(), line.split(':'))
                if key in ['units']:
                    val = val.lower()
                projinfo[key] = val
            elif "XY location (unprojected)" in line:
                projinfo['proj'] = 'xy'
                projinfo['units'] = ''
                break

        return projinfo

    def GetWindow(self):
        """Read WIND file and set up self.wind dictionary"""
        # FIXME: duplicated region WIND == g.region (at least some values)
        env = grass.gisenv()
        filename = os.path.join (env['GISDBASE'],
                                 env['LOCATION_NAME'],
                                 env['MAPSET'],
                                 "WIND")
        try:
            windfile = open (filename, "r")
        except IOError as e:
            sys.exit(_("Error: Unable to open '%(file)s'. Reason: %(ret)s. wxGUI exited.\n") % \
                         { 'file' : filename, 'ret' : e})

        for line in windfile.readlines():
            line = line.strip()
            try:
                key, value = line.split(":", 1)
            except ValueError as e:
                sys.stderr.write(_("\nERROR: Unable to read WIND file: %s\n") % e)
                return None

            self.wind[key.strip()] = value.strip()

        windfile.close()

        return self.wind

    def AdjustRegion(self):
        """Adjusts display resolution to match monitor size in
        pixels. Maintains constant display resolution, not related to
        computational region. Do NOT use the display resolution to set
        computational resolution. Set computational resolution through
        g.region.
        """
        mapwidth    = abs(self.region["e"] - self.region["w"])
        mapheight   = abs(self.region['n'] - self.region['s'])

        self.region["nsres"] =  mapheight / self.height
        self.region["ewres"] =  mapwidth  / self.width
        self.region['rows']  = round(mapheight / self.region["nsres"])
        self.region['cols']  = round(mapwidth / self.region["ewres"])
        self.region['cells'] = self.region['rows'] * self.region['cols']

        Debug.msg (3, "Map.AdjustRegion(): %s" % self.region)

        return self.region

    def AlignResolution(self):
        """Sets display extents to even multiple of current
        resolution defined in WIND file from SW corner. This must be
        done manually as using the -a flag can produce incorrect
        extents.
        """
        # new values to use for saving to region file
        new = {}
        n = s = e = w = 0.0
        nsres = ewres = 0.0

        # Get current values for region and display
        reg = self.GetRegion()
        nsres = reg['nsres']
        ewres = reg['ewres']

        n = float(self.region['n'])
        s = float(self.region['s'])
        e = float(self.region['e'])
        w = float(self.region['w'])

        # Calculate rows, columns, and extents
        new['rows'] = math.fabs(round((n-s)/nsres))
        new['cols'] = math.fabs(round((e-w)/ewres))

        # Calculate new extents
        new['s'] = nsres * round(s / nsres)
        new['w'] = ewres * round(w / ewres)
        new['n'] = new['s'] + (new['rows'] * nsres)
        new['e'] = new['w'] + (new['cols'] * ewres)

        return new

    def AlignExtentFromDisplay(self):
        """Align region extent based on display size from center
        point"""
        # calculate new bounding box based on center of display
        if self.region["ewres"] > self.region["nsres"]:
            res = self.region["ewres"]
        else:
            res = self.region["nsres"]

        Debug.msg(3, "Map.AlignExtentFromDisplay(): width=%d, height=%d, res=%f, center=%f,%f" % \
                      (self.width, self.height, res, self.region['center_easting'],
                       self.region['center_northing']))

        ew = (self.width / 2) * res
        ns = (self.height / 2) * res

        self.region['n'] = self.region['center_northing'] + ns
        self.region['s'] = self.region['center_northing'] - ns
        self.region['e'] = self.region['center_easting'] + ew
        self.region['w'] = self.region['center_easting'] - ew

        # LL locations
        if self.projinfo['proj'] == 'll':
            self.region['n'] = min(self.region['n'], 90.0)
            self.region['s'] = max(self.region['s'], -90.0)

    def ChangeMapSize(self, size):
        """Change size of rendered map.

        :param size: map size given as tuple
        """
        try:
            self.width  = int(size[0])
            self.height = int(size[1])
            if self.width < 1 or self.height < 1:
                sys.stderr.write(_("Invalid map size %d,%d\n") % (self.width, self.height))
                raise ValueError
        except ValueError:
            self.width  = 640
            self.height = 480

        Debug.msg(2, "Map.ChangeMapSize(): width=%d, height=%d" % \
                      (self.width, self.height))

    def GetRegion(self, rast=None, zoom=False, vect=None, rast3d=None, regionName=None,
                  n=None, s=None, e=None, w=None, default=False,
                  update=False, add3d=False):
        """Get region settings (g.region -upgc)

        Optionally extent, raster or vector map layer can be given.

        :param rast: list of raster maps
        :param zoom: zoom to raster map (ignore NULLs)
        :param vect: list of vector maps
        :param rast3d: 3d raster map (not list, no support of multiple 3d rasters in g.region)
        :param regionName:  named region or None
        :param n,s,e,w: force extent
        :param default: force default region settings
        :param update: if True update current display region settings
        :param add3d: add 3d region settings

        :return: region settings as dictionary, e.g. {
                 'n':'4928010', 's':'4913700', 'w':'589980',...}

        :func:`GetCurrentRegion()`
        """
        region = {}

        env = os.environ.copy()
        if self.gisrc:
            env['GISRC'] = self.gisrc

        # do not update & shell style output
        cmd = {}
        cmd['flags'] = 'ugpc'

        if default:
            cmd['flags'] += 'd'

        if add3d:
            cmd['flags'] += '3'

        if regionName:
            cmd['region'] = regionName

        if n:
            cmd['n'] = n
        if s:
            cmd['s'] = s
        if e:
            cmd['e'] = e
        if w:
            cmd['w'] = w

        if rast:
            if zoom:
                cmd['zoom'] = rast[0]
            else:
                cmd['raster'] = ','.join(rast)

        if vect:
            cmd['vector'] = ','.join(vect)

        if rast3d:
            cmd['raster_3d'] = rast3d

        ret, reg, msg = RunCommand('g.region',
                                   read = True,
                                   getErrorMsg = True,
                                   env=env,
                                   **cmd)

        if ret != 0:
            if rast:
                message = _("Unable to zoom to raster map <%s>.") % rast[0] + \
                    "\n\n" + _("Details:") + " %s" % msg
            elif vect:
                message = _("Unable to zoom to vector map <%s>.") % vect[0] + \
                    "\n\n" + _("Details:") + " %s" % msg
            elif rast3d:
                message = _("Unable to zoom to 3d raster map <%s>.") % rast3d + \
                    "\n\n" + _("Details:") + " %s" % msg
            else:
                message = _("Unable to get current geographic extent. "
                            "Force quiting wxGUI. Please manually run g.region to "
                            "fix the problem.")
            GError(message)
            return self.region

        for r in reg.splitlines():
            key, val = r.split("=", 1)
            try:
                region[key] = float(val)
            except ValueError:
                region[key] = val

        Debug.msg (3, "Map.GetRegion(): %s" % region)

        if update:
            self.region = region

        return region

    def GetCurrentRegion(self):
        """Get current display region settings

        :func:`GetRegion()`
        """
        return self.region

    def SetRegion(self, windres = False, windres3 = False):
        """Render string for GRASS_REGION env. variable, so that the
        images will be rendered from desired zoom level.

        :param windres: uses resolution from WIND file rather than
                        display (for modules that require set resolution
                        like d.rast.num)

        :return: String usable for GRASS_REGION variable or None
        """
        grass_region = ""

        if windres:
            compRegion = self.GetRegion(add3d = windres3)
            region = copy.copy(self.region)
            for key in ('nsres', 'ewres', 'cells'):
                region[key] = compRegion[key]
            if windres3:
                for key in ('nsres3', 'ewres3', 'tbres', 'cells3',
                            'cols3', 'rows3', 'depths'):
                    if key in compRegion:
                        region[key] = compRegion[key]

        else:
            # adjust region settings to match monitor
            region = self.AdjustRegion()

        # read values from wind file
        try:
            for key in self.wind.keys():

                if key == 'north':
                    grass_region += "north: %s; " % \
                        (region['n'])
                    continue
                elif key == "south":
                    grass_region += "south: %s; " % \
                        (region['s'])
                    continue
                elif key == "east":
                    grass_region += "east: %s; " % \
                        (region['e'])
                    continue
                elif key == "west":
                    grass_region += "west: %s; " % \
                        (region['w'])
                    continue
                elif key == "e-w resol":
                    grass_region += "e-w resol: %f; " % \
                        (region['ewres'])
                    continue
                elif key == "n-s resol":
                    grass_region += "n-s resol: %f; " % \
                        (region['nsres'])
                    continue
                elif key == "cols":
                    if windres:
                        continue
                    grass_region += 'cols: %d; ' % \
                        region['cols']
                    continue
                elif key == "rows":
                    if windres:
                        continue
                    grass_region += 'rows: %d; ' % \
                        region['rows']
                    continue
                elif key == "n-s resol3" and windres3:
                    grass_region += "n-s resol3: %f; " % \
                        (region['nsres3'])
                elif key == "e-w resol3" and windres3:
                    grass_region += "e-w resol3: %f; " % \
                        (region['ewres3'])
                elif key == "t-b resol" and windres3:
                    grass_region += "t-b resol: %f; " % \
                        (region['tbres'])
                elif key == "cols3" and windres3:
                    grass_region += "cols3: %d; " % \
                        (region['cols3'])
                elif key == "rows3" and windres3:
                    grass_region += "rows3: %d; " % \
                        (region['rows3'])
                elif key == "depths" and windres3:
                    grass_region += "depths: %d; " % \
                        (region['depths'])
                else:
                    grass_region += key + ": "  + self.wind[key] + "; "

            Debug.msg (3, "Map.SetRegion(): %s" % grass_region)

            return grass_region

        except:
            return None

    def GetListOfLayers(self, ltype = None, mapset = None, name = None,
                        active = None, hidden = None):
        """Returns list of layers of selected properties or list of
        all layers.

        :param ltype: layer type, e.g. raster/vector/wms/overlay (value or tuple of values)
        :param mapset: all layers from given mapset (only for maplayers)
        :param name: all layers with given name
        :param active: only layers with 'active' attribute set to True or False
        :param hidden: only layers with 'hidden' attribute set to True or False

        :return: list of selected layers
        """
        selected = []

        if type(ltype) == types.StringType:
            one_type = True
        else:
            one_type = False

        if one_type and ltype == 'overlay':
            llist = self.overlays
        else:
            llist = self.layers

        # ["raster", "vector", "wms", ... ]
        for layer in llist:
            # specified type only
            if ltype != None:
                if one_type and layer.type != ltype:
                    continue
                elif not one_type and layer.type not in ltype:
                    continue

            # mapset
            if (mapset != None and ltype != 'overlay') and \
                    layer.GetMapset() != mapset:
                continue

            # name
            if name != None and layer.name != name:
                continue

            # hidden and active layers
            if active != None and \
                   hidden != None:
                if layer.active == active and \
                       layer.hidden == hidden:
                    selected.append(layer)

            # active layers
            elif active != None:
                if layer.active == active:
                    selected.append(layer)

            # hidden layers
            elif hidden != None:
                if layer.hidden == hidden:
                    selected.append(layer)

            # all layers
            else:
                selected.append(layer)

        Debug.msg (3, "Map.GetListOfLayers(): numberof=%d" % len(selected))

        return selected

    def _renderLayers(self, env, force = False, overlaysOnly = False):
        """Render all map layers into files

        :param bool force: True to force rendering
        :param bool overlaysOnly: True to render only overlays

        :return: list of maps, masks and opacities
        """
        maps = list()
        masks = list()
        opacities = list()
        # render map layers
        if overlaysOnly:
            layers = self.overlays
        else:
            layers = self.layers + self.overlays

        self.downloading = False

        self.ReportProgress(layer=None)


        for layer in layers:
            # skip non-active map layers
            if not layer or not layer.active:
                continue

            # render
            if force or layer.forceRender:
                layer.SetEnvironment(env)
                if not layer.Render():
                    continue

            if layer.IsDownloading():
                self.downloading = True

            self.ReportProgress(layer=layer)

            # skip map layers when rendering fails
            if not os.path.exists(layer.mapfile):
                continue

            # add image to compositing list
            if layer.type != "overlay":
                maps.append(layer.mapfile)
                masks.append(layer.maskfile)
                opacities.append(str(layer.opacity))

            Debug.msg(3, "Map.Render() type=%s, layer=%s " % (layer.type, layer.name))

        return maps, masks, opacities

    def GetMapsMasksAndOpacities(self, force, windres, env):
        """
        Used by Render function.

        :return: maps, masks, opacities
        """
        return self._renderLayers(force=force, env=env)

    def Render(self, force = False, windres = False):
        """Creates final image composite

        This function can conditionaly use high-level tools, which
        should be avaliable in wxPython library

        :param force: force rendering
        :param windres: use region resolution (True) otherwise display
                        resolution

        :return: name of file with rendered image or None
        """
        wx.BeginBusyCursor()
        env = os.environ.copy()
        env.update(self.default_env)
        # use external gisrc if defined
        if self.gisrc:
            env['GISRC'] = self.gisrc
        env['GRASS_REGION'] = self.SetRegion(windres)
        env['GRASS_RENDER_WIDTH'] = str(self.width)
        env['GRASS_RENDER_HEIGHT'] = str(self.height)
        driver = UserSettings.Get(group = 'display', key = 'driver', subkey = 'type')
        if driver == 'png':
            env['GRASS_RENDER_IMMEDIATE'] = 'png'
        else:
            env['GRASS_RENDER_IMMEDIATE'] = 'cairo'

        maps, masks, opacities = self.GetMapsMasksAndOpacities(force, windres, env)

        # ugly hack for MSYS
        if sys.platform != 'win32':
            mapstr = ",".join(maps)
            maskstr = ",".join(masks)
        else:
            mapstr = ""
            for item in maps:
                mapstr += item.replace('\\', '/')
            mapstr = mapstr.rstrip(',')
            maskstr = ""
            for item in masks:
                maskstr += item.replace('\\', '/')
            maskstr = maskstr.rstrip(',')

        # run g.pngcomp to get composite image
        bgcolor = ':'.join(map(str, UserSettings.Get(group = 'display', key = 'bgcolor',
                                                     subkey = 'color')))

        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.width,
                                  height = self.height,
                                  output = self.mapfile,
                                  env=env)

            if ret != 0:
                print >> sys.stderr, _("ERROR: Rendering failed. Details: %s") % msg
                wx.EndBusyCursor()
                return None

        Debug.msg (3, "Map.Render() force=%s file=%s" % (force, self.mapfile))

        wx.EndBusyCursor()
        if not maps:
            return None

        return self.mapfile

    def AddLayer(self, ltype, command, name = None,
                 active = True, hidden = False, opacity = 1.0, render = False,
                 pos = -1):
        """Adds generic map layer to list of layers

        :param ltype: layer type ('raster', 'vector', etc.)
        :param command:  GRASS command given as list
        :param name: layer name
        :param active: layer render only if True
        :param hidden: layer not displayed in layer tree if True
        :param opacity: opacity level range from 0(transparent) - 1(not transparent)
        :param render: render an image if True
        :param pos: position in layer list (-1 for append)

        :return: new layer on success
        :return: None on failure
        """
        wx.BeginBusyCursor()
        # opacity must be <0;1>
        if opacity < 0:
            opacity = 0
        elif opacity > 1:
            opacity = 1
        layer = MapLayer(ltype = ltype, name = name, cmd = command, Map = self,
                         active = active, hidden = hidden, opacity = opacity)

        # add maplayer to the list of layers
        if pos > -1:
            self.layers.insert(pos, layer)
        else:
            self.layers.append(layer)

        Debug.msg (3, "Map.AddLayer(): layer=%s" % layer.name)
        if render:
            if not layer.Render():
                raise GException(_("Unable to render map layer <%s>.") % name)

        renderMgr = layer.GetRenderMgr()
        if renderMgr:
            renderMgr.dataFetched.connect(self.layerChanged)
            renderMgr.updateProgress.connect(self.ReportProgress)

        wx.EndBusyCursor()

        return layer

    def DeleteAllLayers(self, overlay = False):
        """Delete all layers

        :param overlay: True to delete also overlayes
        """
        self.layers = []
        if overlay:
            self.overlays = []

    def DeleteLayer(self, layer, overlay = False):
        """Removes layer from list of layers

        :param layer: layer instance in layer tree
        :param overlay: delete overlay (use self.DeleteOverlay() instead)

        :return: removed layer on success or None
        """
        Debug.msg (3, "Map.DeleteLayer(): name=%s" % layer.name)

        if overlay:
            list = self.overlays
        else:
            list = self.layers

        if layer in list:
            if layer.mapfile:
                base = os.path.split(layer.mapfile)[0]
                mapfile = os.path.split(layer.mapfile)[1]
                tempbase = mapfile.split('.')[0]
                if base == '' or tempbase == '':
                    return None
                basefile = os.path.join(base, tempbase) + r'.*'
                for f in glob.glob(basefile):
                    os.remove(f)
            list.remove(layer)

            return layer

        return None

    def SetLayers(self, layers):
        self.layers = layers

        # only for debug
        # might be removed including message, it seems more than clear
        layerNameList = ""
        for layer in self.layers:
            if layer.GetName():
                layerNameList += layer.GetName() + ','
        Debug.msg(5, "Map.SetLayers(): layers=%s" % (layerNameList))

    def ChangeLayer(self, layer, render = False, **kargs):
        """Change map layer properties

        :param layer: map layer instance
        :param ltype: layer type ('raster', 'vector', etc.)
        :param command:  GRASS command given as list
        :param name: layer name
        :param active: layer render only if True
        :param hidden: layer not displayed in layer tree if True
        :param opacity: opacity level range from 0(transparent) - 1(not transparent)
        :param render: render an image if True
        """
        Debug.msg (3, "Map.ChangeLayer(): layer=%s" % layer.name)

        if 'ltype' in kargs:
            layer.SetType(kargs['ltype']) # check type

        if 'command' in kargs:
            layer.SetCmd(kargs['command'])

        if 'name' in kargs:
            layer.SetName(kargs['name'])

        if 'active' in kargs:
            layer.SetActive(kargs['active'])

        if 'hidden' in kargs:
            layer.SetHidden(kargs['hidden'])

        if 'opacity' in kargs:
            layer.SetOpacity(kargs['opacity'])

        if render and not layer.Render():
            raise GException(_("Unable to render map layer <%s>.") %
                             layer.GetName())

        return layer

    def ChangeOpacity(self, layer, opacity):
        """Changes opacity value of map layer

        :param layer: layer instance in layer tree
        :param opacity: opacity level <0;1>
        """
        # opacity must be <0;1>
        if opacity < 0: opacity = 0
        elif opacity > 1: opacity = 1

        layer.opacity = opacity
        Debug.msg (3, "Map.ChangeOpacity(): layer=%s, opacity=%f" % \
                   (layer.name, layer.opacity))

    def ChangeLayerActive(self, layer, active):
        """Enable or disable map layer

        :param layer: layer instance in layer tree
        :param active: to be rendered (True)
        """
        layer.active = active

        Debug.msg (3, "Map.ChangeLayerActive(): name='%s' -> active=%d" % \
                   (layer.name, layer.active))

    def ChangeLayerName (self, layer, name):
        """Change name of the layer

        :param layer: layer instance in layer tree
        :param name:  layer name to set up
        """
        Debug.msg (3, "Map.ChangeLayerName(): from=%s to=%s" % \
                   (layer.name, name))
        layer.name =  name

    def RemoveLayer(self, name = None, id = None):
        """Removes layer from layer list

        Layer is defined by name@mapset or id.

        :param name: layer name (must be unique)
        :param id: layer index in layer list def __init__(self,
                   targetFile, region, bandsNum, gdalDriver,
                   fillValue = None):

        :return: removed layer on success
        :return: None on failure
        """
        # delete by name
        if name:
            retlayer = None
            for layer in self.layers:
                if layer.name == name:
                    retlayer = layer
                    os.remove(layer.mapfile)
                    os.remove(layer.maskfile)
                    self.layers.remove(layer)
                    return retlayer
        # del by id
        elif id != None:
            return self.layers.pop(id)

        return None

    def GetLayerIndex(self, layer, overlay = False):
        """Get index of layer in layer list.

        :param layer: layer instace in layer tree
        :param overlay: use list of overlays instead

        :return: layer index
        :return: -1 if layer not found
        """
        if overlay:
            list = self.overlays
        else:
            list = self.layers

        if layer in list:
            return list.index(layer)

        return -1

    def AddOverlay(self, id, ltype, command,
                   active = True, hidden = True, opacity = 1.0, render = False):
        """Adds overlay (grid, barscale, legend, etc.) to list of
        overlays

        :param id: overlay id (PseudoDC)
        :param ltype: overlay type (barscale, legend)
        :param command: GRASS command to render overlay
        :param active: overlay activated (True) or disabled (False)
        :param hidden: overlay is not shown in layer tree (if True)
        :param render: render an image (if True)

        :return: new layer on success
        :return: None on failure
        """
        Debug.msg (2, "Map.AddOverlay(): cmd=%s, render=%d" % (command, render))
        overlay = Overlay(id = id, ltype = ltype, cmd = command, Map = self,
                          active = active, hidden = hidden, opacity = opacity)

        # add maplayer to the list of layers
        self.overlays.append(overlay)

        if render and command != '' and not overlay.Render():
            raise GException(_("Unable to render overlay <%s>.") %
                             ltype)

        return self.overlays[-1]

    def ChangeOverlay(self, id, render = False, **kargs):
        """Change overlay properities

        Add new overlay if overlay with 'id' doesn't exist.

        :param id: overlay id (PseudoDC)
        :param ltype: overlay ltype (barscale, legend)
        :param command: GRASS command to render overlay
        :param active: overlay activated (True) or disabled (False)
        :param hidden: overlay is not shown in layer tree (if True)
        :param render: render an image (if True)

        :return: new layer on success
        """
        overlay = self.GetOverlay(id, list = False)
        if  overlay is None:
            overlay = Overlay(id, ltype = None, cmd = None)

        if 'ltype' in kargs:
            overlay.SetName(kargs['ltype']) # ltype -> overlay

        if 'command' in kargs:
            overlay.SetCmd(kargs['command'])

        if 'active' in kargs:
            overlay.SetActive(kargs['active'])

        if 'hidden' in kargs:
            overlay.SetHidden(kargs['hidden'])

        if 'opacity' in kargs:
            overlay.SetOpacity(kargs['opacity'])

        if render and overlay.GetCmd() != [] and not overlay.Render():
            raise GException(_("Unable to render overlay <%s>.") %
                             overlay.GetType())

        return overlay

    def GetOverlay(self, id, list=False):
        """Return overlay(s) with 'id'

        :param id: overlay id
        :param list: return list of overlays of True
                     otherwise suppose 'id' to be unique

        :return: list of overlays (list=True)
        :return: overlay (list=False)
        :return: None (list=False) if no overlay or more overlays found
        """
        ovl = []
        for overlay in self.overlays:
            if overlay.id == id:
                ovl.append(overlay)

        if not list:
            if len(ovl) != 1:
                return None
            else:
                return ovl[0]

        return ovl

    def DeleteOverlay(self, overlay):
        """Delete overlay

        :param overlay: overlay layer

        :return: removed overlay on success or None
        """
        return self.DeleteLayer(overlay, overlay = True)

    def _clean(self, llist):
        for layer in llist:
            if layer.maskfile:
                try_remove(layer.maskfile)
            if layer.mapfile:
                try_remove(layer.mapfile)
            llist.remove(layer)

    def Clean(self):
        """Clean layer stack - go trough all layers and remove them
        from layer list.

        Removes also mapfile and maskfile.
        """
        self._clean(self.layers)
        self._clean(self.overlays)

    def ReverseListOfLayers(self):
        """Reverse list of layers"""
        return self.layers.reverse()

    def RenderOverlays(self, force):
        """Render overlays only (for nviz)"""
        for layer in self.overlays:
            if force or layer.forceRender:
                layer.Render()

    def AbortAllThreads(self):
        """Abort all layers threads e. g. donwloading data"""
        for l in self.layers + self.overlays:
            l.AbortThread()

    def ReportProgress(self, layer):
        """Calculates progress in rendering/downloading
        and emits signal to inform progress bar about progress.
        """
        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.GetListOfLayers(active = True)) +
                                           len(self.GetListOfLayers(active = True, ltype = 'overlay')) -
                                           len(self.GetListOfLayers(active = True, ltype = '3d-raster'))}
        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)
Exemplo n.º 33
0
class CategoriesManager:
    """Manages categories list of scatter plot.
    """

    def __init__(self, scatt_mgr, core):

        self.core = core
        self.scatt_mgr = scatt_mgr

        self.cats = {}
        self.cats_ids = []

        self.sel_cat_id = None

        self.exportRaster = None

        self.initialized = Signal('CategoriesManager.initialized')
        self.setCategoryAttrs = Signal('CategoriesManager.setCategoryAttrs')
        self.deletedCategory = Signal('CategoriesManager.deletedCategory')
        self.addedCategory = Signal('CategoriesManager.addedCategory')

    def ChangePosition(self, cat_id, new_pos):
        if new_pos >= len(self.cats_ids):
            return False

        try:
            pos = self.cats_ids.index(cat_id)
        except:
            return False

        if pos > new_pos:
            pos -= 1

        self.cats_ids.remove(cat_id)

        self.cats_ids.insert(new_pos, cat_id)

        self.scatt_mgr.render_mgr.RenderRequest()
        return True

    def _addCategory(self, cat_id):
        self.scatt_mgr.thread.Run(callable=self.core.AddCategory,
                                  cat_id=cat_id)

    def SetData(self):

        if not self.scatt_mgr.data_set:
            return

        for cat_id in self.cats_ids:
            self.scatt_mgr.thread.Run(callable=self.core.AddCategory,
                                      cat_id=cat_id)

    def AddCategory(self, cat_id=None, name=None, color=None, nstd=None):

        if cat_id is None:
            if self.cats_ids:
                cat_id = max(self.cats_ids) + 1
            else:
                cat_id = 1

        if self.scatt_mgr.data_set:
            self.scatt_mgr.thread.Run(callable=self.core.AddCategory,
                                      cat_id=cat_id)
            # TODO check number of cats
            # if ret < 0: #TODO
            #    return -1;

        self.cats[cat_id] = {
            'name': 'class_%d' % cat_id,
            'color': "0:0:0",
            'opacity': 1.0,
            'show': True,
            'nstd': 1.0,
        }

        self.cats_ids.insert(0, cat_id)

        if name is not None:
            self.cats[cat_id]["name"] = name

        if color is not None:
            self.cats[cat_id]["color"] = color

        if nstd is not None:
            self.cats[cat_id]["nstd"] = nstd

        self.addedCategory.emit(cat_id=cat_id,
                                name=self.cats[cat_id]["name"],
                                color=self.cats[cat_id]["color"])
        return cat_id

    def SetCategoryAttrs(self, cat_id, attrs_dict):
        render = False
        update_cat_rast = []

        for k, v in six.iteritems(attrs_dict):
            if not render and k in ['color', 'opacity', 'show', 'nstd']:
                render = True
            if k in ['color', 'name']:
                update_cat_rast.append(k)

            self.cats[cat_id][k] = v

        if render:
            self.scatt_mgr.render_mgr.CategoryChanged(cat_ids=[cat_id])
            self.scatt_mgr.render_mgr.RenderRequest()

        if update_cat_rast:
            self.scatt_mgr.iclass_conn.UpdateCategoryRaster(
                cat_id, update_cat_rast)

        self.setCategoryAttrs.emit(cat_id=cat_id, attrs_dict=attrs_dict)

    def DeleteCategory(self, cat_id):

        if self.scatt_mgr.data_set:
            self.scatt_mgr.thread.Run(callable=self.core.DeleteCategory,
                                      cat_id=cat_id)
        del self.cats[cat_id]
        self.cats_ids.remove(cat_id)

        self.deletedCategory.emit(cat_id=cat_id)

    # TODO emit event?
    def SetSelectedCat(self, cat_id):
        self.sel_cat_id = cat_id
        if self.scatt_mgr.pol_sel_mode[0]:
            self.scatt_mgr.render_mgr.RenderRequest()

    def GetSelectedCat(self):
        return self.sel_cat_id

    def GetCategoryAttrs(self, cat_id):
        #TODO is mutable
        return self.cats[cat_id]

    def GetCategoriesAttrs(self):
        #TODO is mutable
        return self.cats

    def GetCategories(self):
        return self.cats_ids[:]

    def ExportCatRast(self, cat_id):

        cat_attrs = self.GetCategoryAttrs(cat_id)

        dlg = ExportCategoryRaster(
            parent=self.scatt_mgr.guiparent,
            rasterName=self.exportRaster,
            title=_("Export scatter plot raster of class <%s>") %
            cat_attrs['name'])

        if dlg.ShowModal() == wx.ID_OK:
            self.exportCatRast = dlg.GetRasterName()
            dlg.Destroy()

            self.scatt_mgr.thread.Run(callable=self.core.ExportCatRast,
                                      userdata={'name': cat_attrs['name']},
                                      cat_id=cat_id,
                                      rast_name=self.exportCatRast,
                                      ondone=self.OnExportCatRastDone)

    def OnExportCatRastDone(self, event):
        ret, err = event.ret
        if ret == 0:
            cat_attrs = self.GetCategoryAttrs(event.kwds['cat_id'])
            GMessage(
                _("Scatter plot raster of class <%s> exported to raster map <%s>.") %
                (event.userdata['name'], event.kwds['rast_name']))
        else:
            GMessage(
                _("Export of scatter plot raster of class <%s> to map <%s> failed.\n%s") %
                (event.userdata['name'], event.kwds['rast_name'], err))
Exemplo n.º 34
0
class RDigitController(wx.EvtHandler):
    """Controller object for raster digitizer.
    Inherits from EvtHandler to be able to send wx events from thraed.
    """
    def __init__(self, giface, mapWindow):
        """Constructs controller

        :param giface: grass interface object
        :param mapWindow: instance of BufferedMapWindow
        """
        wx.EvtHandler.__init__(self)
        self._giface = giface
        self._mapWindow = mapWindow

        # thread for running rasterization process
        self._thread = gThread()
        # name of raster map which is edited (also new one)
        self._editedRaster = None
        # name of optional background raster
        self._backgroundRaster = None
        # name of temporary raster used to backup original state
        self._backupRasterName = None
        # if we edit an old raster or a new one (important for setting color
        # table)
        self._editOldRaster = False
        # type of output raster map (CELL, FCELL, DCELL)
        self._mapType = None
        # GraphicsSet for drawing areas, lines, points
        self._areas = None
        self._lines = None
        self._points = None
        # list of all GraphicsItems in the order of drawing
        self._all = []
        # if in state of drawing lin or area
        self._drawing = False
        # if running digitizing process in thread (to block drawing)
        self._running = False
        # color used to draw (should be moved to settings)
        self._drawColor = wx.GREEN
        # transparency used to draw (should be moved to settings)
        self._drawTransparency = 100
        # current selected drawing method
        self._graphicsType = 'area'
        # last edited cell value
        self._currentCellValue = None
        # last edited buffer value
        self._currentWidthValue = None

        self._oldMouseUse = None
        self._oldCursor = None

        # signal to add new raster to toolbar items
        self.newRasterCreated = Signal('RDigitController:newRasterCreated')
        # signal to add just used cell value in toolbar combo
        self.newFeatureCreated = Signal('RDigitController:newFeatureCreated')
        # signal to upload unique categories of background map into toolbar
        # combo
        self.uploadMapCategories = Signal(
            'RDigitController:uploadMapCategories')
        self.quitDigitizer = Signal('RDigitController:quitDigitizer')
        self.showNotification = Signal('RDigitController:showNotification')

    def _connectAll(self):
        self._mapWindow.mouseLeftDown.connect(self._start)
        self._mapWindow.mouseLeftUp.connect(self._addPoint)
        self._mapWindow.mouseRightUp.connect(self._finish)
        self._mapWindow.Unbind(wx.EVT_CONTEXT_MENU)

    def _disconnectAll(self):
        self._mapWindow.mouseLeftDown.disconnect(self._start)
        self._mapWindow.mouseLeftUp.disconnect(self._addPoint)
        self._mapWindow.mouseRightUp.disconnect(self._finish)
        self._mapWindow.Bind(wx.EVT_CONTEXT_MENU,
                             self._mapWindow.OnContextMenu)

    def _start(self, x, y):
        """Start digitizing a new object.
        :param x: x coordinate in map units
        :param y: y coordinate in map units
        """
        if self._running:
            return

        if not self._editedRaster:
            GMessage(parent=self._mapWindow,
                     message=_("Please select first the raster map"))
            return
        if not self._drawing:
            if self._graphicsType == 'area':
                item = self._areas.AddItem(coords=[])
                item.SetPropertyVal('penName', 'pen1')
                self._all.append(item)
            elif self._graphicsType == 'line':
                item = self._lines.AddItem(coords=[])
                item.SetPropertyVal('penName', 'pen1')
                self._all.append(item)
            elif self._graphicsType == 'point':
                item = self._points.AddItem(coords=[])
                item.SetPropertyVal('penName', 'pen1')
                self._all.append(item)
            self._drawing = True

    def _addPoint(self, x, y):
        """Add point to an object.
        :param x: x coordinate in map units
        :param y: y coordinate in map units
        """
        if self._running:
            return

        if not self._drawing:
            return

        if self._graphicsType == 'area':
            area = self._areas.GetItem(-1)
            coords = area.GetCoords() + [[x, y]]
            area.SetCoords(coords)
            self.showNotification.emit(text=_("Right click to finish area"))
        elif self._graphicsType == 'line':
            line = self._lines.GetItem(-1)
            coords = line.GetCoords() + [[x, y]]
            line.SetCoords(coords)
            self.showNotification.emit(text=_("Right click to finish line"))
        elif self._graphicsType == 'point':
            point = self._points.GetItem(-1)
            point.SetCoords([x, y])
            self._finish()
        # draw
        self._mapWindow.ClearLines()
        self._lines.Draw()
        self._areas.Draw()
        self._points.Draw()
        self._mapWindow.Refresh()

    def _finish(self):
        """Finish digitizing a new object and redraws.
        Saves current cell value and buffer width for that object.

        :param x: x coordinate in map units
        :param y: y coordinate in map units
        """
        if self._running:
            return

        if self._graphicsType == 'point':
            item = self._points.GetItem(-1)
        elif self._graphicsType == 'area':
            item = self._areas.GetItem(-1)
        elif self._graphicsType == 'line':
            item = self._lines.GetItem(-1)
        else:
            return

        self._drawing = False
        item.SetPropertyVal('brushName', 'done')
        item.AddProperty('cellValue')
        item.AddProperty('widthValue')
        item.SetPropertyVal('cellValue', self._currentCellValue)
        item.SetPropertyVal('widthValue', self._currentWidthValue)
        self.newFeatureCreated.emit()

        self._mapWindow.ClearLines()
        self._points.Draw()
        self._areas.Draw()
        self._lines.Draw()

        self._mapWindow.Refresh()

    def SelectType(self, drawingType):
        """Selects method (area/line/point) for drawing.
        Connects and disconnects signal to allow other tools
        in map toolbar to work.
        """
        if self._graphicsType and drawingType and self._graphicsType != drawingType \
                and self._drawing:
            # if we select different drawing tool, finish the feature
            self._finish()

        if self._graphicsType and not drawingType:
            self._mapWindow.ClearLines(pdc=self._mapWindow.pdcTmp)
            self._mapWindow.mouse['end'] = self._mapWindow.mouse['begin']
            # disconnect mouse events
            self._disconnectAll()
            self._mapWindow.SetNamedCursor(self._oldCursor)
            self._mapWindow.mouse['use'] = self._oldMouseUse
        elif self._graphicsType is None and drawingType:
            self._connectAll()
            # change mouse['box'] and pen to draw line during dragging
            # TODO: better solution for drawing this line
            self._mapWindow.mouse['use'] = None
            self._mapWindow.mouse['box'] = "line"
            self._mapWindow.pen = wx.Pen(colour='red',
                                         width=2,
                                         style=wx.SHORT_DASH)
            # change the cursor
            self._mapWindow.SetNamedCursor('pencil')

        self._graphicsType = drawingType

    def SetCellValue(self, value):
        self._currentCellValue = value

    def SetWidthValue(self, value):
        self._currentWidthValue = value

    def ChangeDrawColor(self, color):
        self._drawColor = color[:3] + (self._drawTransparency, )
        for each in (self._areas, self._lines, self._points):
            each.GetPen('pen1').SetColour(self._drawColor)
            each.GetBrush('done').SetColour(self._drawColor)
        self._mapWindow.UpdateMap(render=False)

    def Start(self):
        """Registers graphics to map window,
        connect required mouse signals.
        """
        self._oldMouseUse = self._mapWindow.mouse['use']
        self._oldCursor = self._mapWindow.GetNamedCursor()

        self._connectAll()

        # change mouse['box'] and pen to draw line during dragging
        # TODO: better solution for drawing this line
        self._mapWindow.mouse['use'] = None
        self._mapWindow.mouse['box'] = "line"
        self._mapWindow.pen = wx.Pen(colour='red',
                                     width=2,
                                     style=wx.SHORT_DASH)

        color = self._drawColor[:3] + (self._drawTransparency, )
        self._areas = self._mapWindow.RegisterGraphicsToDraw(
            graphicsType='polygon',
            pdc=self._mapWindow.pdcTransparent,
            mapCoords=True)
        self._areas.AddPen('pen1', wx.Pen(colour=color,
                                          width=2,
                                          style=wx.SOLID))
        self._areas.AddBrush('done', wx.Brush(colour=color, style=wx.SOLID))

        self._lines = self._mapWindow.RegisterGraphicsToDraw(
            graphicsType='line',
            pdc=self._mapWindow.pdcTransparent,
            mapCoords=True)
        self._lines.AddPen('pen1', wx.Pen(colour=color,
                                          width=2,
                                          style=wx.SOLID))
        self._lines.AddBrush('done', wx.Brush(colour=color, style=wx.SOLID))

        self._points = self._mapWindow.RegisterGraphicsToDraw(
            graphicsType='point',
            pdc=self._mapWindow.pdcTransparent,
            mapCoords=True)
        self._points.AddPen('pen1',
                            wx.Pen(colour=color, width=2, style=wx.SOLID))
        self._points.AddBrush('done', wx.Brush(colour=color, style=wx.SOLID))

        # change the cursor
        self._mapWindow.SetNamedCursor('pencil')

    def Stop(self):
        """Before stopping digitizer, asks to save edits"""
        dlg = wx.MessageDialog(self._mapWindow,
                               _("Do you want to save changes?"),
                               _("Save raster map changes"), wx.YES_NO)
        if dlg.ShowModal() == wx.ID_YES:
            if self._drawing:
                self._finish()
            self._thread.Run(callable=self._exportRaster,
                             ondone=lambda event: self._updateAndQuit())
        else:
            self.quitDigitizer.emit()

    def Save(self):
        """Saves current edits to a raster map"""
        if self._drawing:
            self._finish()

        self._thread.Run(callable=self._exportRaster,
                         ondone=lambda event: self._update())

    def Undo(self):
        """Undo a change, goes object back (finished or not finished)"""
        if len(self._all):
            removed = self._all.pop(-1)
            # try to remove from each, it fails quietly when theitem is not
            # there
            self._areas.DeleteItem(removed)
            self._lines.DeleteItem(removed)
            self._points.DeleteItem(removed)
            self._drawing = False
            self._mapWindow.UpdateMap(render=False)

    def CleanUp(self, restore=True):
        """Cleans up drawing, temporary maps.
        :param restore: if restore previous cursor, mouse['use']
        """
        try:
            gcore.run_command('g.remove',
                              type='raster',
                              flags='f',
                              name=self._backupRasterName,
                              quiet=True)
        except CalledModuleError:
            pass

        self._mapWindow.ClearLines(pdc=self._mapWindow.pdcTmp)
        self._mapWindow.mouse['end'] = self._mapWindow.mouse['begin']
        # disconnect mouse events
        if self._graphicsType:
            self._disconnectAll()
        # unregister
        self._mapWindow.UnregisterGraphicsToDraw(self._areas)
        self._mapWindow.UnregisterGraphicsToDraw(self._lines)
        self._mapWindow.UnregisterGraphicsToDraw(self._points)
        #self._registeredGraphics = None
        self._mapWindow.UpdateMap(render=False)

        if restore:
            # restore mouse['use'] and cursor to the state before measuring
            # starts
            self._mapWindow.SetNamedCursor(self._oldCursor)
            self._mapWindow.mouse['use'] = self._oldMouseUse

    def _updateAndQuit(self):
        """Called when thread is done. Updates map and calls to quits digitizer."""
        self._running = False
        self._mapWindow.UpdateMap(render=True)
        self.quitDigitizer.emit()

    def _update(self):
        """Called when thread is done. Updates map."""
        self._running = False
        self._mapWindow.UpdateMap(render=True)

    def SelectOldMap(self, name):
        """After selecting old raster, creates a backup copy for editing."""
        try:
            self._backupRaster(name)
        except ScriptError:
            GError(parent=self._mapWindow,
                   message=_(
                       "Failed to create backup copy of edited raster map."))
            return False
        self._editedRaster = name
        self._mapType = grast.raster_info(map=name)['datatype']
        self._editOldRaster = True
        return True

    def SelectNewMap(self):
        """After selecting new raster, shows dialog to choose name,
        background map and type of the new map."""
        dlg = NewRasterDialog(parent=self._mapWindow)
        dlg.CenterOnParent()
        if dlg.ShowModal() == wx.ID_OK:
            try:
                self._createNewMap(mapName=dlg.GetMapName(),
                                   backgroundMap=dlg.GetBackgroundMapName(),
                                   mapType=dlg.GetMapType())
            except ScriptError:
                GError(parent=self._mapWindow,
                       message=_("Failed to create new raster map."))
                return False
            finally:
                dlg.Destroy()
            return True
        else:
            dlg.Destroy()
            return False

    def _createNewMap(self, mapName, backgroundMap, mapType):
        """Creates a new raster map based on specified background and type."""
        name = mapName.split('@')[0]
        background = backgroundMap.split('@')[0]
        types = {'CELL': 'int', 'FCELL': 'float', 'DCELL': 'double'}
        if background:
            back = background
        else:
            back = 'null()'
        try:
            grast.mapcalc(exp="{name} = {mtype}({back})".format(
                name=name, mtype=types[mapType], back=back),
                          overwrite=True,
                          quiet=True)
            if background:
                self._backgroundRaster = backgroundMap
                gcore.run_command('r.colors',
                                  map=name,
                                  raster=self._backgroundRaster,
                                  quiet=True)
                if mapType == 'CELL':
                    values = gcore.read_command('r.describe',
                                                flags='1n',
                                                map=name,
                                                quiet=True).strip()
                    if values:
                        self.uploadMapCategories.emit(
                            values=values.split('\n'))
        except CalledModuleError:
            raise ScriptError
        self._backupRaster(name)

        name = name + '@' + gcore.gisenv()['MAPSET']
        self._editedRaster = name
        self._mapType = mapType
        self.newRasterCreated.emit(name=name)

    def _backupRaster(self, name):
        """Creates a temporary backup raster necessary for undo behavior.

        :param str name: name of raster map for which we create backup
        """
        name = name.split('@')[0]
        backup = name + '_backupcopy_' + str(os.getpid())
        try:
            gcore.run_command('g.copy', raster=[name, backup], quiet=True)
        except CalledModuleError:
            raise ScriptError

        self._backupRasterName = backup

    def _exportRaster(self):
        """Rasterizes digitized features.

        Uses r.in.poly and r.grow for buffering features. Creates separate raster
        maps depending on common cell values and buffering width necessary to
        keep the order of editing. These rasters are then patched together.
        Sets default color table for the newly digitized raster.
        """
        if not self._editedRaster or self._running:
            return
        self._running = True

        if len(self._all) < 1:
            new = self._editedRaster
            if '@' in self._editedRaster:
                new = self._editedRaster.split('@')[0]
            gcore.run_command('g.copy',
                              raster=[self._backupRasterName, new],
                              overwrite=True,
                              quiet=True)
        else:
            tempRaster = 'tmp_rdigit_rast_' + str(os.getpid())
            text = []
            rastersToPatch = []
            i = 0
            lastCellValue = lastWidthValue = None
            evt = updateProgress(range=len(self._all),
                                 value=0,
                                 text=_("Rasterizing..."))
            wx.PostEvent(self, evt)
            lastCellValue = self._all[0].GetPropertyVal('cellValue')
            lastWidthValue = self._all[0].GetPropertyVal('widthValue')
            for item in self._all:
                if item.GetPropertyVal('widthValue') and \
                    (lastCellValue != item.GetPropertyVal('cellValue') or
                     lastWidthValue != item.GetPropertyVal('widthValue')):
                    if text:
                        out = self._rasterize(text, lastWidthValue,
                                              self._mapType, tempRaster)
                        rastersToPatch.append(out)
                        text = []
                    self._writeItem(item, text)
                    out = self._rasterize(text,
                                          item.GetPropertyVal('widthValue'),
                                          self._mapType, tempRaster)
                    rastersToPatch.append(out)
                    text = []
                else:
                    self._writeItem(item, text)

                lastCellValue = item.GetPropertyVal('cellValue')
                lastWidthValue = item.GetPropertyVal('widthValue')

                i += 1
                evt = updateProgress(range=len(self._all),
                                     value=i,
                                     text=_("Rasterizing..."))
                wx.PostEvent(self, evt)
            if text:
                out = self._rasterize(text, item.GetPropertyVal('widthValue'),
                                      self._mapType, tempRaster)
                rastersToPatch.append(out)

            gcore.run_command('r.patch',
                              input=rastersToPatch[::-1] +
                              [self._backupRasterName],
                              output=self._editedRaster,
                              overwrite=True,
                              quiet=True)
            gcore.run_command('g.remove',
                              type='raster',
                              flags='f',
                              name=rastersToPatch + [tempRaster],
                              quiet=True)
        try:
            # setting the right color table
            if self._editOldRaster:
                return
            if not self._backgroundRaster:
                table = UserSettings.Get(group='rasterLayer',
                                         key='colorTable',
                                         subkey='selection')
                if not table:
                    table = 'rainbow'
                gcore.run_command('r.colors',
                                  color=table,
                                  map=self._editedRaster,
                                  quiet=True)
            else:
                gcore.run_command('r.colors',
                                  map=self._editedRaster,
                                  raster=self._backgroundRaster,
                                  quiet=True)
        except CalledModuleError:
            self._running = False
            GError(
                parent=self._mapWindow,
                message=_(
                    "Failed to set default color table for edited raster map"))

    def _writeFeature(self, item, vtype, text):
        """Writes digitized features in r.in.poly format."""
        coords = item.GetCoords()
        if vtype == 'P':
            coords = [coords]
        cellValue = item.GetPropertyVal('cellValue')
        record = '{vtype}\n'.format(vtype=vtype)
        for coord in coords:
            record += ' '.join([str(c) for c in coord])
            record += '\n'
        record += '= {cellValue}\n'.format(cellValue=cellValue)

        text.append(record)

    def _writeItem(self, item, text):
        if item in self._areas.GetAllItems():
            self._writeFeature(item, vtype='A', text=text)
        elif item in self._lines.GetAllItems():
            self._writeFeature(item, vtype='L', text=text)
        elif item in self._points.GetAllItems():
            self._writeFeature(item, vtype='P', text=text)

    def _rasterize(self, text, bufferDist, mapType, tempRaster):
        """Performs the actual rasterization using r.in.poly
        and buffering with r.grow if required.

        :param str text: string in r.in.poly format
        :param float bufferDist: buffer distance in map units
        :param str mapType: CELL, FCELL, DCELL
        :param str tempRaster: name of temporary raster used in computation

        :return: output raster map name as a result of digitization
        """
        output = 'x' + str(uuid.uuid4())[:8]
        asciiFile = tempfile.NamedTemporaryFile(mode='w', delete=False)
        asciiFile.write('\n'.join(text))
        asciiFile.close()

        if bufferDist:
            bufferDist /= 2.
            gcore.run_command('r.in.poly',
                              input=asciiFile.name,
                              output=tempRaster,
                              type_=mapType,
                              overwrite=True,
                              quiet=True)
            gcore.run_command('r.grow',
                              input=tempRaster,
                              output=output,
                              flags='m',
                              radius=bufferDist,
                              quiet=True)
        else:
            gcore.run_command('r.in.poly',
                              input=asciiFile.name,
                              output=output,
                              type_=mapType,
                              quiet=True)
        os.unlink(asciiFile.name)
        return output
Exemplo n.º 35
0
class BitmapProvider:
    """Class for management of image files and bitmaps.

    There is one instance of this class in the application.
    It handles both 2D and 3D animations.
    """
    def __init__(self,
                 bitmapPool,
                 mapFilesPool,
                 tempDir,
                 imageWidth=640,
                 imageHeight=480):

        self._bitmapPool = bitmapPool
        self._mapFilesPool = mapFilesPool
        self.imageWidth = (
            imageWidth  # width of the image to render with d.rast or d.vect
        )
        # height of the image to render with d.rast or d.vect
        self.imageHeight = imageHeight
        self._tempDir = tempDir

        self._uniqueCmds = []
        self._cmdsForComposition = []
        self._opacities = []

        self._cmds3D = []
        self._regionFor3D = None
        self._regions = []
        self._regionsForUniqueCmds = []

        self._renderer = BitmapRenderer(self._mapFilesPool, self._tempDir,
                                        self.imageWidth, self.imageHeight)
        self._composer = BitmapComposer(
            self._tempDir,
            self._mapFilesPool,
            self._bitmapPool,
            self.imageWidth,
            self.imageHeight,
        )
        self.renderingStarted = Signal("BitmapProvider.renderingStarted")
        self.compositionStarted = Signal("BitmapProvider.compositionStarted")
        self.renderingContinues = Signal("BitmapProvider.renderingContinues")
        self.compositionContinues = Signal(
            "BitmapProvider.compositionContinues")
        self.renderingFinished = Signal("BitmapProvider.renderingFinished")
        self.compositionFinished = Signal("BitmapProvider.compositionFinished")
        self.mapsLoaded = Signal("BitmapProvider.mapsLoaded")

        self._renderer.renderingContinues.connect(self.renderingContinues)
        self._composer.compositionContinues.connect(self.compositionContinues)

    def SetCmds(self, cmdsForComposition, opacities, regions=None):
        """Sets commands to be rendered with opacity levels.
        Applies to 2D mode.

        :param cmdsForComposition: list of lists of command lists
                                   [[['d.rast', 'map=elev_2001'], ['d.vect', 'map=points']], # g.pnmcomp
                                   [['d.rast', 'map=elev_2002'], ['d.vect', 'map=points']],
                                   ...]
        :param opacities: list of opacity values
        :param regions: list of regions
        """
        Debug.msg(
            2, "BitmapProvider.SetCmds: {n} lists".format(
                n=len(cmdsForComposition)))
        self._cmdsForComposition.extend(cmdsForComposition)
        self._opacities.extend(opacities)
        self._regions.extend(regions)

        self._getUniqueCmds()

    def SetCmds3D(self, cmds, region):
        """Sets commands for 3D rendering.

        :param cmds: list of commands m.nviz.image (cmd as a list)
        :param region: for 3D rendering
        """
        Debug.msg(2,
                  "BitmapProvider.SetCmds3D: {c} commands".format(c=len(cmds)))
        self._cmds3D = cmds
        self._regionFor3D = region

    def _getUniqueCmds(self):
        """Returns list of unique commands.
        Takes into account the region assigned."""
        unique = list()
        for cmdList, region in zip(self._cmdsForComposition, self._regions):
            for cmd in cmdList:
                if region:
                    unique.append((tuple(cmd), tuple(sorted(region.items()))))
                else:
                    unique.append((tuple(cmd), None))
        unique = list(set(unique))
        self._uniqueCmds = [cmdAndRegion[0] for cmdAndRegion in unique]
        self._regionsForUniqueCmds.extend([
            dict(cmdAndRegion[1]) if cmdAndRegion[1] else None
            for cmdAndRegion in unique
        ])

    def Unload(self):
        """Unloads currently loaded data.
        Needs to be called before setting new data.
        """
        Debug.msg(2, "BitmapProvider.Unload")
        if self._cmdsForComposition:
            for cmd, region in zip(self._uniqueCmds,
                                   self._regionsForUniqueCmds):
                del self._mapFilesPool[HashCmd(cmd, region)]

            for cmdList, region in zip(self._cmdsForComposition,
                                       self._regions):
                del self._bitmapPool[HashCmds(cmdList, region)]
            self._uniqueCmds = []
            self._cmdsForComposition = []
            self._opacities = []
            self._regions = []
            self._regionsForUniqueCmds = []
        if self._cmds3D:
            self._cmds3D = []
            self._regionFor3D = None

    def _dryRender(self, uniqueCmds, regions, force):
        """Determines how many files will be rendered.

        :param uniqueCmds: list of commands which are to be rendered
        :param force: if forced rerendering
        :param regions: list of regions assigned to the commands
        """
        count = 0
        for cmd, region in zip(uniqueCmds, regions):
            filename = GetFileFromCmd(self._tempDir, cmd, region)
            if (not force and os.path.exists(filename)
                    and self._mapFilesPool.GetSize(HashCmd(
                        cmd, region)) == (self.imageWidth, self.imageHeight)):
                continue
            count += 1

        Debug.msg(
            3, "BitmapProvider._dryRender: {c} files to be rendered".format(
                c=count))

        return count

    def _dryCompose(self, cmdLists, regions, force):
        """Determines how many lists of (commands) files
        will be composed (with g.pnmcomp).

        :param cmdLists: list of commands lists which are to be composed
        :param regions: list of regions assigned to the commands
        :param force: if forced rerendering
        """
        count = 0
        for cmdList, region in zip(cmdLists, regions):
            if (not force and HashCmds(cmdList, region) in self._bitmapPool
                    and self._bitmapPool[HashCmds(cmdList, region)].GetSize()
                    == (self.imageWidth, self.imageHeight)):
                continue
            count += 1

        Debug.msg(
            2, "BitmapProvider._dryCompose: {c} files to be composed".format(
                c=count))

        return count

    def Load(self, force=False, bgcolor=(255, 255, 255), nprocs=4):
        """Loads data, both 2D and 3D. In case of 2D, it creates composites,
        even when there is only 1 layer to compose (to be changed for speedup)

        :param force: if True reload all data, otherwise only missing data
        :param bgcolor: background color as a tuple of 3 values 0 to 255
        :param nprocs: number of procs to be used for rendering
        """
        Debug.msg(
            2,
            "BitmapProvider.Load: "
            "force={f}, bgcolor={b}, nprocs={n}".format(f=force,
                                                        b=bgcolor,
                                                        n=nprocs),
        )
        cmds = []
        regions = []
        if self._uniqueCmds:
            cmds.extend(self._uniqueCmds)
            regions.extend(self._regionsForUniqueCmds)
        if self._cmds3D:
            cmds.extend(self._cmds3D)
            regions.extend([None] * len(self._cmds3D))

        count = self._dryRender(cmds, regions, force=force)
        self.renderingStarted.emit(count=count)

        # create no data bitmap
        if None not in self._bitmapPool or force:
            self._bitmapPool[None] = createNoDataBitmap(
                self.imageWidth, self.imageHeight)

        ok = self._renderer.Render(
            cmds,
            regions,
            regionFor3D=self._regionFor3D,
            bgcolor=bgcolor,
            force=force,
            nprocs=nprocs,
        )
        self.renderingFinished.emit()
        if not ok:
            self.mapsLoaded.emit()  # what to do here?
            return
        if self._cmdsForComposition:
            count = self._dryCompose(self._cmdsForComposition,
                                     self._regions,
                                     force=force)
            self.compositionStarted.emit(count=count)
            self._composer.Compose(
                self._cmdsForComposition,
                self._regions,
                self._opacities,
                bgcolor=bgcolor,
                force=force,
                nprocs=nprocs,
            )
            self.compositionFinished.emit()
        if self._cmds3D:
            for cmd in self._cmds3D:
                self._bitmapPool[HashCmds([cmd], None)] = wx.Bitmap(
                    GetFileFromCmd(self._tempDir, cmd, None))

        self.mapsLoaded.emit()

    def RequestStopRendering(self):
        """Requests to stop rendering/composition"""
        Debug.msg(2, "BitmapProvider.RequestStopRendering")
        self._renderer.RequestStopRendering()
        self._composer.RequestStopComposing()

    def GetBitmap(self, dataId):
        """Returns bitmap with given key
        or 'no data' bitmap if no such key exists.

        :param dataId: name of bitmap
        """
        try:
            bitmap = self._bitmapPool[dataId]
        except KeyError:
            bitmap = self._bitmapPool[None]
        return bitmap

    def WindowSizeChanged(self, width, height):
        """Sets size when size of related window changes."""
        Debug.msg(
            5,
            "BitmapProvider.WindowSizeChanged: w={w}, h={h}".format(w=width,
                                                                    h=height),
        )

        self.imageWidth, self.imageHeight = width, height

        self._composer.imageWidth = self._renderer.imageWidth = width
        self._composer.imageHeight = self._renderer.imageHeight = height

    def LoadOverlay(self, cmd):
        """Creates raster legend with d.legend

        :param cmd: d.legend command as a list

        :return: bitmap with legend
        """
        Debug.msg(5, "BitmapProvider.LoadOverlay: cmd={c}".format(c=cmd))

        fileHandler, filename = tempfile.mkstemp(suffix=".png")
        os.close(fileHandler)
        # Set the environment variables for this process
        _setEnvironment(
            self.imageWidth,
            self.imageHeight,
            filename,
            transparent=True,
            bgcolor=(0, 0, 0),
        )

        Debug.msg(1, "Render raster legend " + str(filename))
        cmdTuple = cmdlist_to_tuple(cmd)
        returncode, stdout, messages = read2_command(cmdTuple[0],
                                                     **cmdTuple[1])

        if returncode == 0:
            return BitmapFromImage(autoCropImageFromFile(filename))
        else:
            os.remove(filename)
            raise GException(messages)
Exemplo n.º 36
0
class SwipeMapDialog(wx.Dialog):
    """Dialog used to select maps.

    There are two modes - simple (only two raster maps),
    or two layer lists.
    """
    def __init__(self,
                 parent,
                 title=_("Select raster maps"),
                 first=None,
                 second=None,
                 firstLayerList=None,
                 secondLayerList=None):

        wx.Dialog.__init__(self,
                           parent=parent,
                           title=title,
                           style=wx.RESIZE_BORDER | wx.DEFAULT_DIALOG_STYLE)

        if firstLayerList is None:
            self._firstLayerList = LayerList()
        else:
            self._firstLayerList = copy.deepcopy(firstLayerList)
        if secondLayerList is None:
            self._secondLayerList = LayerList()
        else:
            self._secondLayerList = copy.deepcopy(secondLayerList)

        self._firstPanel = self._createSimplePanel()
        self._secondPanel = self._createAdvancedPanel()

        self.btnSwitch = wx.Button(self)
        self.btnCancel = wx.Button(self, id=wx.ID_CANCEL)
        self.btnApply = wx.Button(self, id=wx.ID_APPLY)
        self.btnOK = wx.Button(self, id=wx.ID_OK)
        self.btnOK.SetDefault()

        self.btnSwitch.Bind(wx.EVT_BUTTON, self.OnSwitchMode)
        self.btnApply.Bind(wx.EVT_BUTTON, lambda evt: self._apply())
        self.btnOK.Bind(wx.EVT_BUTTON, lambda evt: self._ok())
        self.btnCancel.Bind(wx.EVT_BUTTON, lambda evt: self.Close())
        self.Bind(wx.EVT_CLOSE, lambda evt: self.Hide())

        self.applyChanges = Signal('SwipeMapDialog.applyChanges')

        if first:
            self._firstRaster.SetValue(first)
        if second:
            self._secondRaster.SetValue(second)

        self._layout()

    def _layout(self):
        """Do layout"""
        mainSizer = wx.BoxSizer(wx.VERTICAL)
        self._switchSizer = wx.BoxSizer()
        self._switchSizer.Add(self._firstPanel,
                              proportion=1,
                              flag=wx.EXPAND | wx.ALL,
                              border=5)
        self._switchSizer.Add(self._secondPanel,
                              proportion=1,
                              flag=wx.EXPAND | wx.ALL,
                              border=5)
        mainSizer.Add(self._switchSizer, proportion=1, flag=wx.EXPAND | wx.ALL)

        self.btnSizer = wx.StdDialogButtonSizer()
        self.btnSizer.AddButton(self.btnCancel)
        self.btnSizer.AddButton(self.btnOK)
        self.btnSizer.AddButton(self.btnApply)
        self.btnSizer.Realize()

        mainSizer.Add(item=self.btnSwitch,
                      proportion=0,
                      flag=wx.ALL | wx.ALIGN_LEFT,
                      border=5)
        mainSizer.Add(item=self.btnSizer,
                      proportion=0,
                      flag=wx.EXPAND | wx.ALL | wx.ALIGN_CENTER,
                      border=5)
        self.mainSizer = mainSizer
        self.SetSizer(mainSizer)
        mainSizer.Fit(self)
        self._switchMode(simple=True)

    def _createSimplePanel(self):
        panel = wx.Panel(self)
        sizer = wx.BoxSizer(wx.VERTICAL)

        self._firstRaster = gselect.Select(
            parent=panel,
            type='raster',
            size=globalvar.DIALOG_GSELECT_SIZE,
            validator=SimpleValidator(callback=self.ValidatorCallback))

        self._secondRaster = gselect.Select(
            parent=panel,
            type='raster',
            size=globalvar.DIALOG_GSELECT_SIZE,
            validator=SimpleValidator(callback=self.ValidatorCallback))
        sizer.Add(wx.StaticText(panel,
                                label=_("Name of top/left raster map:")),
                  proportion=0,
                  flag=wx.EXPAND | wx.ALL,
                  border=5)
        sizer.Add(self._firstRaster,
                  proportion=0,
                  flag=wx.EXPAND | wx.ALL,
                  border=1)
        sizer.Add(wx.StaticText(panel,
                                label=_("Name of bottom/right raster map:")),
                  proportion=0,
                  flag=wx.EXPAND | wx.ALL,
                  border=1)
        sizer.Add(self._secondRaster,
                  proportion=0,
                  flag=wx.EXPAND | wx.ALL,
                  border=1)

        self._firstRaster.SetFocus()

        panel.SetSizer(sizer)
        sizer.Fit(panel)

        return panel

    def _createAdvancedPanel(self):
        panel = wx.Panel(self)
        sizer = wx.BoxSizer(wx.HORIZONTAL)

        self._firstLmgr = SimpleLayerManager(
            parent=panel,
            layerList=self._firstLayerList,
            lmgrStyle=SIMPLE_LMGR_RASTER | SIMPLE_LMGR_RGB | SIMPLE_LMGR_VECTOR
            | SIMPLE_LMGR_TB_LEFT)
        self._secondLmgr = SimpleLayerManager(
            parent=panel,
            layerList=self._secondLayerList,
            lmgrStyle=SIMPLE_LMGR_RASTER | SIMPLE_LMGR_RGB | SIMPLE_LMGR_VECTOR
            | SIMPLE_LMGR_TB_RIGHT)
        sizer.Add(self._firstLmgr,
                  proportion=1,
                  flag=wx.EXPAND | wx.ALL,
                  border=5)
        sizer.Add(self._secondLmgr,
                  proportion=1,
                  flag=wx.EXPAND | wx.ALL,
                  border=5)
        panel.SetSizer(sizer)
        sizer.Fit(panel)

        return panel

    def _switchMode(self, simple):
        if simple:
            self._switchSizer.Show(self._firstPanel, show=True, recursive=True)
            self._switchSizer.Show(self._secondPanel,
                                   show=False,
                                   recursive=True)
            self.btnSwitch.SetLabel(_("Switch to advanced mode"))
            self.btnCancel.SetLabel(_("Cancel"))
        else:
            self._switchSizer.Show(self._firstPanel,
                                   show=False,
                                   recursive=True)
            self._switchSizer.Show(self._secondPanel,
                                   show=True,
                                   recursive=True)
            self.btnSwitch.SetLabel(_("Switch to simple mode"))
            self.btnCancel.SetLabel(_("Close"))

        self.Freeze()  # doesn't do anything (at least on Ubuntu)
        self.btnSizer.Show(self.btnApply, simple)
        self.btnSizer.Show(self.btnOK, simple)
        self.btnSizer.Layout()
        self._switchSizer.Layout()
        self.Fit()
        self.Thaw()

        self.applyChanges.emit()

    def OnSwitchMode(self, event):
        if self._switchSizer.IsShown(self._secondPanel):
            self._switchMode(simple=True)
        else:
            self._switchMode(simple=False)

    def ValidatorCallback(self, win):
        if self._switchSizer.IsShown(self._secondPanel):
            return

        if win == self._firstRaster.GetTextCtrl():
            GMessage(parent=self,
                     message=_("Name of the first map is missing."))
        else:
            GMessage(parent=self,
                     message=_("Name of the second map is missing."))

    def _ok(self):
        self._apply()
        self.Close()

    def _apply(self):
        # TODO check if not empty
        self.applyChanges.emit()

    def GetValues(self):
        """Get raster maps"""
        if self.IsSimpleMode():
            return (self._firstRaster.GetValue(),
                    self._secondRaster.GetValue())
        else:
            return (self._firstLayerList, self._secondLayerList)

    def IsSimpleMode(self):
        if self._switchSizer.IsShown(self._firstPanel):
            return True
        return False

    def GetFirstSimpleLmgr(self):
        return self._firstLmgr

    def GetSecondSimpleLmgr(self):
        return self._secondLmgr
Exemplo n.º 37
0
class GConsoleWindow(wx.SplitterWindow):
    """Create and manage output console for commands run by GUI."""

    def __init__(
        self,
        parent,
        giface,
        gconsole,
        menuModel=None,
        margin=False,
        style=wx.TAB_TRAVERSAL | wx.FULL_REPAINT_ON_RESIZE,
        gcstyle=GC_EMPTY,
        **kwargs,
    ):
        """
        :param parent: gui parent
        :param gconsole: console logic
        :param menuModel: tree model of modules (from menu)
        :param margin: use margin in output pane (GStc)
        :param style: wx.SplitterWindow style
        :param gcstyle: GConsole style
                        (GC_EMPTY, GC_PROMPT to show command prompt)
        """
        wx.SplitterWindow.__init__(self, parent, id=wx.ID_ANY, style=style, **kwargs)
        self.SetName("GConsole")

        self.panelOutput = wx.Panel(parent=self, id=wx.ID_ANY)
        self.panelProgress = wx.Panel(
            parent=self.panelOutput, id=wx.ID_ANY, name="progressPanel"
        )
        self.panelPrompt = wx.Panel(parent=self, id=wx.ID_ANY)
        # initialize variables
        self.parent = parent  # GMFrame | CmdPanel | ?
        self._gconsole = gconsole
        self._menuModel = menuModel

        self._gcstyle = gcstyle
        self.lineWidth = 80

        # signal which requests showing of a notification
        self.showNotification = Signal("GConsoleWindow.showNotification")
        # signal emitted when text appears in the console
        # parameter 'notification' suggests form of notification (according to
        # core.giface.Notification)
        self.contentChanged = Signal("GConsoleWindow.contentChanged")

        # progress bar
        self.progressbar = wx.Gauge(
            parent=self.panelProgress,
            id=wx.ID_ANY,
            range=100,
            pos=(110, 50),
            size=(-1, 25),
            style=wx.GA_HORIZONTAL,
        )
        self._gconsole.Bind(EVT_CMD_PROGRESS, self.OnCmdProgress)
        self._gconsole.Bind(EVT_CMD_OUTPUT, self.OnCmdOutput)
        self._gconsole.Bind(EVT_CMD_RUN, self.OnCmdRun)
        self._gconsole.Bind(EVT_CMD_DONE, self.OnCmdDone)

        self._gconsole.writeLog.connect(self.WriteLog)
        self._gconsole.writeCmdLog.connect(self.WriteCmdLog)
        self._gconsole.writeWarning.connect(self.WriteWarning)
        self._gconsole.writeError.connect(self.WriteError)

        # text control for command output
        self.cmdOutput = GStc(
            parent=self.panelOutput, id=wx.ID_ANY, margin=margin, wrap=None
        )

        # command prompt
        # move to the if below
        # search depends on cmd prompt
        self.cmdPrompt = GPromptSTC(
            parent=self, giface=giface, menuModel=self._menuModel
        )
        self.cmdPrompt.promptRunCmd.connect(
            lambda cmd: self._gconsole.RunCmd(command=cmd)
        )
        self.cmdPrompt.showNotification.connect(self.showNotification)

        if not self._gcstyle & GC_PROMPT:
            self.cmdPrompt.Hide()

        if self._gcstyle & GC_PROMPT:
            cmdLabel = _("Command prompt")
            self.outputBox = StaticBox(
                parent=self.panelOutput, id=wx.ID_ANY, label=" %s " % _("Output window")
            )

            self.cmdBox = StaticBox(
                parent=self.panelOutput, id=wx.ID_ANY, label=" %s " % cmdLabel
            )

        # buttons
        self.btnOutputClear = ClearButton(parent=self.panelOutput)
        self.btnOutputClear.SetToolTip(_("Clear output window content"))
        self.btnCmdClear = ClearButton(parent=self.panelOutput)
        self.btnCmdClear.SetToolTip(_("Clear command prompt content"))
        self.btnOutputSave = Button(parent=self.panelOutput, id=wx.ID_SAVE)
        self.btnOutputSave.SetToolTip(_("Save output window content to the file"))
        self.btnCmdAbort = Button(parent=self.panelProgress, id=wx.ID_STOP)
        self.btnCmdAbort.SetToolTip(_("Abort running command"))
        self.btnCmdProtocol = ToggleButton(
            parent=self.panelOutput,
            id=wx.ID_ANY,
            label=_("&Log file"),
            size=self.btnCmdClear.GetSize(),
        )
        self.btnCmdProtocol.SetToolTip(
            _(
                "Toggle to save list of executed commands into "
                "a file; content saved when switching off."
            )
        )
        self.cmdFileProtocol = None

        if not self._gcstyle & GC_PROMPT:
            self.btnCmdClear.Hide()
            self.btnCmdProtocol.Hide()

        self.btnCmdClear.Bind(wx.EVT_BUTTON, self.cmdPrompt.OnCmdErase)
        self.btnOutputClear.Bind(wx.EVT_BUTTON, self.OnOutputClear)
        self.btnOutputSave.Bind(wx.EVT_BUTTON, self.OnOutputSave)
        self.btnCmdAbort.Bind(wx.EVT_BUTTON, self._gconsole.OnCmdAbort)
        self.btnCmdProtocol.Bind(wx.EVT_TOGGLEBUTTON, self.OnCmdProtocol)

        self._layout()

    def _layout(self):
        """Do layout"""
        self.outputSizer = wx.BoxSizer(wx.VERTICAL)
        progressSizer = wx.BoxSizer(wx.HORIZONTAL)
        btnSizer = wx.BoxSizer(wx.HORIZONTAL)
        if self._gcstyle & GC_PROMPT:
            outBtnSizer = wx.StaticBoxSizer(self.outputBox, wx.HORIZONTAL)
            cmdBtnSizer = wx.StaticBoxSizer(self.cmdBox, wx.HORIZONTAL)
        else:
            outBtnSizer = wx.BoxSizer(wx.HORIZONTAL)
            cmdBtnSizer = wx.BoxSizer(wx.HORIZONTAL)

        if self._gcstyle & GC_PROMPT:
            promptSizer = wx.BoxSizer(wx.VERTICAL)
            promptSizer.Add(
                self.cmdPrompt,
                proportion=1,
                flag=wx.EXPAND | wx.LEFT | wx.RIGHT | wx.TOP,
                border=3,
            )
            helpText = StaticText(
                self.panelPrompt,
                id=wx.ID_ANY,
                label="Press Tab to display command help, Ctrl+Space to autocomplete",
            )
            helpText.SetForegroundColour(
                wx.SystemSettings.GetColour(wx.SYS_COLOUR_GRAYTEXT)
            )
            promptSizer.Add(helpText, proportion=0, flag=wx.EXPAND | wx.LEFT, border=5)

        self.outputSizer.Add(
            self.cmdOutput, proportion=1, flag=wx.EXPAND | wx.ALL, border=3
        )
        if self._gcstyle & GC_PROMPT:
            proportion = 1
        else:
            proportion = 0
            outBtnSizer.AddStretchSpacer()

        outBtnSizer.Add(
            self.btnOutputClear,
            proportion=proportion,
            flag=wx.ALIGN_LEFT | wx.LEFT | wx.RIGHT | wx.BOTTOM,
            border=5,
        )

        outBtnSizer.Add(
            self.btnOutputSave,
            proportion=proportion,
            flag=wx.RIGHT | wx.BOTTOM,
            border=5,
        )

        cmdBtnSizer.Add(
            self.btnCmdProtocol,
            proportion=1,
            flag=wx.ALIGN_CENTER
            | wx.ALIGN_CENTER_VERTICAL
            | wx.LEFT
            | wx.RIGHT
            | wx.BOTTOM,
            border=5,
        )
        cmdBtnSizer.Add(
            self.btnCmdClear,
            proportion=1,
            flag=wx.ALIGN_CENTER | wx.RIGHT | wx.BOTTOM,
            border=5,
        )
        progressSizer.Add(
            self.btnCmdAbort, proportion=0, flag=wx.ALL | wx.ALIGN_CENTER, border=5
        )
        progressSizer.Add(
            self.progressbar,
            proportion=1,
            flag=wx.ALIGN_CENTER | wx.RIGHT | wx.TOP | wx.BOTTOM,
            border=5,
        )

        self.panelProgress.SetSizer(progressSizer)
        progressSizer.Fit(self.panelProgress)

        btnSizer.Add(outBtnSizer, proportion=1, flag=wx.ALL | wx.ALIGN_CENTER, border=5)
        btnSizer.Add(
            cmdBtnSizer,
            proportion=1,
            flag=wx.ALIGN_CENTER | wx.TOP | wx.BOTTOM | wx.RIGHT,
            border=5,
        )
        self.outputSizer.Add(self.panelProgress, proportion=0, flag=wx.EXPAND)
        self.outputSizer.Add(btnSizer, proportion=0, flag=wx.EXPAND)

        self.outputSizer.Fit(self)
        self.outputSizer.SetSizeHints(self)
        self.panelOutput.SetSizer(self.outputSizer)
        self.outputSizer.FitInside(self.panelOutput)
        if self._gcstyle & GC_PROMPT:
            promptSizer.Fit(self)
            promptSizer.SetSizeHints(self)
            self.panelPrompt.SetSizer(promptSizer)

        # split window
        if self._gcstyle & GC_PROMPT:
            self.SplitHorizontally(self.panelOutput, self.panelPrompt, -50)
        else:
            self.SplitHorizontally(self.panelOutput, self.panelPrompt, -45)
            self.Unsplit()
        self.SetMinimumPaneSize(self.btnCmdClear.GetSize()[1] + 25)

        self.SetSashGravity(1.0)

        self.outputSizer.Hide(self.panelProgress)
        # layout
        self.SetAutoLayout(True)
        self.Layout()

    def GetPanel(self, prompt=True):
        """Get panel

        :param prompt: get prompt / output panel

        :return: wx.Panel reference
        """
        if prompt:
            return self.panelPrompt

        return self.panelOutput

    def WriteLog(
        self, text, style=None, wrap=None, notification=Notification.HIGHLIGHT
    ):
        """Generic method for writing log message in
        given style.

        Emits contentChanged signal.

        :param line: text line
        :param style: text style (see GStc)
        :param stdout: write to stdout or stderr
        :param notification: form of notification
        """

        self.cmdOutput.SetStyle()

        # documenting old behavior/implementation:
        # switch notebook if required
        # now, let user to bind to the old event

        if not style:
            style = self.cmdOutput.StyleDefault

        # p1 = self.cmdOutput.GetCurrentPos()
        p1 = self.cmdOutput.GetEndStyled()
        # self.cmdOutput.GotoPos(p1)
        self.cmdOutput.DocumentEnd()

        for line in text.splitlines():
            # fill space
            if len(line) < self.lineWidth:
                diff = self.lineWidth - len(line)
                line += diff * " "

            self.cmdOutput.AddTextWrapped(line, wrap=wrap)  # adds '\n'

            p2 = self.cmdOutput.GetCurrentPos()

            # between wxWidgets 3.0 and 3.1 they dropped mask param
            try:
                self.cmdOutput.StartStyling(p1)
            except TypeError:
                self.cmdOutput.StartStyling(p1, 0xFF)
            self.cmdOutput.SetStyling(p2 - p1, style)

        self.cmdOutput.EnsureCaretVisible()

        self.contentChanged.emit(notification=notification)

    def WriteCmdLog(self, text, pid=None, notification=Notification.MAKE_VISIBLE):
        """Write message in selected style

        :param text: message to be printed
        :param pid: process pid or None
        :param switchPage: True to switch page
        """
        if pid:
            text = "(" + str(pid) + ") " + text
        self.WriteLog(
            text, style=self.cmdOutput.StyleCommand, notification=notification
        )

    def WriteWarning(self, text):
        """Write message in warning style"""
        self.WriteLog(
            text,
            style=self.cmdOutput.StyleWarning,
            notification=Notification.MAKE_VISIBLE,
        )

    def WriteError(self, text):
        """Write message in error style"""
        self.WriteLog(
            text,
            style=self.cmdOutput.StyleError,
            notification=Notification.MAKE_VISIBLE,
        )

    def OnOutputClear(self, event):
        """Clear content of output window"""
        self.cmdOutput.SetReadOnly(False)
        self.cmdOutput.ClearAll()
        self.cmdOutput.SetReadOnly(True)
        self.progressbar.SetValue(0)

    def GetProgressBar(self):
        """Return progress bar widget"""
        return self.progressbar

    def OnOutputSave(self, event):
        """Save (selected) text from output window to the file"""
        text = self.cmdOutput.GetSelectedText()
        if not text:
            text = self.cmdOutput.GetText()

        # add newline if needed
        if len(text) > 0 and text[-1] != "\n":
            text += "\n"

        dlg = wx.FileDialog(
            self,
            message=_("Save file as..."),
            defaultFile="grass_cmd_output.txt",
            wildcard=_("%(txt)s (*.txt)|*.txt|%(files)s (*)|*")
            % {"txt": _("Text files"), "files": _("Files")},
            style=wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT,
        )

        # Show the dialog and retrieve the user response. If it is the OK response,
        # process the data.
        if dlg.ShowModal() == wx.ID_OK:
            path = dlg.GetPath()

            try:
                output = open(path, "w")
                output.write(text)
            except IOError as e:
                GError(
                    _("Unable to write file '%(path)s'.\n\nDetails: %(error)s")
                    % {"path": path, "error": e}
                )
            finally:
                output.close()
            message = _("Command output saved into '%s'") % path
            self.showNotification.emit(message=message)

        dlg.Destroy()

    def SetCopyingOfSelectedText(self, copy):
        """Enable or disable copying of selected text in to clipboard.
        Effects prompt and output.

        :param bool copy: True for enable, False for disable
        """
        if copy:
            self.cmdPrompt.Bind(
                stc.EVT_STC_PAINTED, self.cmdPrompt.OnTextSelectionChanged
            )
            self.cmdOutput.Bind(
                stc.EVT_STC_PAINTED, self.cmdOutput.OnTextSelectionChanged
            )
        else:
            self.cmdPrompt.Unbind(stc.EVT_STC_PAINTED)
            self.cmdOutput.Unbind(stc.EVT_STC_PAINTED)

    def OnCmdOutput(self, event):
        """Prints command output.

        Emits contentChanged signal.
        """
        message = event.text
        type = event.type

        self.cmdOutput.AddStyledMessage(message, type)

        if event.type in ("warning", "error"):
            self.contentChanged.emit(notification=Notification.MAKE_VISIBLE)
        else:
            self.contentChanged.emit(notification=Notification.HIGHLIGHT)

    def OnCmdProgress(self, event):
        """Update progress message info"""
        self.progressbar.SetValue(event.value)
        event.Skip()

    def CmdProtocolSave(self):
        """Save list of manually entered commands into a text log file"""
        if self.cmdFileProtocol is None:
            return  # it should not happen

        try:
            with open(self.cmdFileProtocol, "a") as output:
                cmds = self.cmdPrompt.GetCommands()
                output.write("\n".join(cmds))
                if len(cmds) > 0:
                    output.write("\n")
        except IOError as e:
            GError(
                _("Unable to write file '{filePath}'.\n\nDetails: {error}").format(
                    filePath=self.cmdFileProtocol, error=e
                )
            )

        self.showNotification.emit(
            message=_("Command log saved to '{}'".format(self.cmdFileProtocol))
        )
        self.cmdFileProtocol = None

    def OnCmdProtocol(self, event=None):
        """Save commands into file"""
        if not event.IsChecked():
            # stop capturing commands, save list of commands to the
            # protocol file
            self.CmdProtocolSave()
        else:
            # start capturing commands
            self.cmdPrompt.ClearCommands()
            # ask for the file
            dlg = wx.FileDialog(
                self,
                message=_("Save file as..."),
                defaultFile="grass_cmd_log.txt",
                wildcard=_("%(txt)s (*.txt)|*.txt|%(files)s (*)|*")
                % {"txt": _("Text files"), "files": _("Files")},
                style=wx.FD_SAVE,
            )
            if dlg.ShowModal() == wx.ID_OK:
                self.cmdFileProtocol = dlg.GetPath()
            else:
                wx.CallAfter(self.btnCmdProtocol.SetValue, False)

            dlg.Destroy()

        event.Skip()

    def OnCmdRun(self, event):
        """Run command"""
        self.outputSizer.Show(self.panelProgress)
        self.outputSizer.Layout()
        event.Skip()

    def OnCmdDone(self, event):
        """Command done (or aborted)"""
        self.progressbar.SetValue(0)  # reset progress bar on '0%'
        wx.CallLater(100, self._hideProgress)
        event.Skip()

    def _hideProgress(self):
        self.outputSizer.Hide(self.panelProgress)
        self.outputSizer.Layout()

    def ResetFocus(self):
        """Reset focus"""
        self.cmdPrompt.SetFocus()

    def GetPrompt(self):
        """Get prompt"""
        return self.cmdPrompt
Exemplo n.º 38
0
class MapWindowBase(object):
    """Abstract map display window class

    Superclass for BufferedWindow class (2D display mode), and GLWindow
    (3D display mode).

    Subclasses have to define
     - _bindMouseEvents method which binds MouseEvent handlers
     - Pixel2Cell
     - Cell2Pixel (if it is possible)
    """
    def __init__(self, parent, giface, Map):
        self.parent = parent
        self.Map = Map
        self._giface = giface

        # Emitted when someone registers as mouse event handler
        self.mouseHandlerRegistered = Signal(
            'MapWindow.mouseHandlerRegistered')
        # Emitted when mouse event handler is unregistered
        self.mouseHandlerUnregistered = Signal(
            'MapWindow.mouseHandlerUnregistered')
        # emitted after double click in pointer mode on legend, text, scalebar
        self.overlayActivated = Signal('MapWindow.overlayActivated')
        # emitted when overlay should be hidden
        self.overlayRemoved = Signal('MapWindow.overlayRemoved')

        # mouse attributes -- position on the screen, begin and end of
        # dragging, and type of drawing
        self.mouse = {
            'begin': [0, 0],  # screen coordinates
            'end': [0, 0],
            'use': "pointer",
            'box': "point"
        }
        # last east, north coordinates, changes on mouse motion
        self.lastEN = None

        # stores overridden cursor
        self._overriddenCursor = None

        # dictionary where event types are stored as keys and lists of
        # handlers for these types as values
        self.handlersContainer = {
            wx.EVT_LEFT_DOWN: [],
            wx.EVT_LEFT_UP: [],
            wx.EVT_LEFT_DCLICK: [],
            wx.EVT_MIDDLE_DOWN: [],
            wx.EVT_MIDDLE_UP: [],
            wx.EVT_MIDDLE_DCLICK: [],
            wx.EVT_RIGHT_DOWN: [],
            wx.EVT_RIGHT_UP: [],
            wx.EVT_RIGHT_DCLICK: [],
            wx.EVT_MOTION: [],
            wx.EVT_ENTER_WINDOW: [],
            wx.EVT_LEAVE_WINDOW: [],
            wx.EVT_MOUSEWHEEL: [],
            wx.EVT_MOUSE_EVENTS: []
        }

        # available cursors
        self._cursors = {
            "default": wx.StockCursor(wx.CURSOR_ARROW),
            "cross": wx.StockCursor(wx.CURSOR_CROSS),
            "hand": wx.StockCursor(wx.CURSOR_HAND),
            "pencil": wx.StockCursor(wx.CURSOR_PENCIL),
            "sizenwse": wx.StockCursor(wx.CURSOR_SIZENWSE)
        }

        # default cursor for window is arrow (at least we rely on it here)
        # but we need to define attribute here
        # cannot call SetNamedCursor since it expects the instance
        # to be a wx window, so setting only the attribute
        self._cursor = 'default'

        wx.CallAfter(self.InitBinding)

    def __del__(self):
        self.UnregisterAllHandlers()

    def InitBinding(self):
        """Binds helper functions, which calls all handlers
           registered to events with the events
        """
        for ev, handlers in self.handlersContainer.iteritems():
            self.Bind(ev, self.EventTypeHandler(handlers))

    def EventTypeHandler(self, evHandlers):
        return lambda event: self.HandlersCaller(event, evHandlers)

    def HandlersCaller(self, event, handlers):
        """Hepler function which calls all handlers registered for
        event
        """
        for handler in handlers:
            try:
                handler(event)
            except:
                handlers.remove(handler)
                GError(
                    parent=self,
                    message=_("Error occurred during calling of handler: %s \n"
                              "Handler was unregistered.") % handler.__name__)

        event.Skip()

    def RegisterMouseEventHandler(self, event, handler, cursor=None):
        """Binds event handler

        @depreciated This method is depreciated. Use Signals or drawing API
        instead. Signals do not cover all events but new Signals can be added
        when needed consider also adding generic signal. However, more
        interesing and useful is higher level API to create objects, graphics etc.

        Call event.Skip() in handler to allow default processing in MapWindow.

        If any error occurs inside of handler, the handler is removed.

        Before handler is unregistered it is called with
        string value "unregistered" of event parameter.

        ::

            # your class methods
            def OnButton(self, event):
                # current map display's map window
                # expects LayerManager to be the parent
                self.mapwin = self.parent.GetLayerTree().GetMapDisplay().GetWindow()
                if self.mapwin.RegisterEventHandler(wx.EVT_LEFT_DOWN, self.OnMouseAction,
                                                    'cross'):
                    self.parent.GetLayerTree().GetMapDisplay().Raise()
                else:
                    # handle that you cannot get coordinates

            def OnMouseAction(self, event):
                # get real world coordinates of mouse click
                coor = self.mapwin.Pixel2Cell(event.GetPositionTuple()[:])
                self.text.SetLabel('Coor: ' + str(coor))
                self.mapwin.UnregisterMouseEventHandler(wx.EVT_LEFT_DOWN, self.OnMouseAction)
                event.Skip()


        Emits mouseHandlerRegistered signal before handler is registered.

        :param event: one of mouse events
        :param handler: function to handle event
        :param cursor: cursor which temporary overrides current cursor

        :return: True if successful
        :return: False if event cannot be bind
        """
        self.mouseHandlerRegistered.emit()
        # inserts handler into list
        for containerEv, handlers in self.handlersContainer.iteritems():
            if event == containerEv:
                handlers.append(handler)

        self.mouse['useBeforeGenericEvent'] = self.mouse['use']
        self.mouse['use'] = 'genericEvent'

        if cursor:
            self._overriddenCursor = self.GetNamedCursor()
            self.SetNamedCursor(cursor)

        return True

    def UnregisterAllHandlers(self):
        """Unregisters all registered handlers

        @depreciated This method is depreciated. Use Signals or drawing API instead.

        Before each handler is unregistered it is called with string
        value "unregistered" of event parameter.
        """
        for containerEv, handlers in self.handlersContainer.iteritems():
            for handler in handlers:
                try:
                    handler("unregistered")
                    handlers.remove(handler)
                except:
                    GError(
                        parent=self,
                        message=
                        _("Error occurred during unregistration of handler: %s \n \
                                       Handler was unregistered.") %
                        handler.__name__)
                    handlers.remove(handler)

    def UnregisterMouseEventHandler(self, event, handler):
        """Unbinds event handler for event

        @depreciated This method is depreciated. Use Signals or drawing API instead.

        Before handler is unregistered it is called with string value
        "unregistered" of event parameter.

        Emits mouseHandlerUnregistered signal after handler is unregistered.

        :param handler: handler to unbind
        :param event: event from which handler will be unbinded

        :return: True if successful
        :return: False if event cannot be unbind
        """
        # removes handler from list
        for containerEv, handlers in self.handlersContainer.iteritems():
            if event != containerEv:
                continue
            try:
                handler("unregistered")
                if handler in handlers:
                    handlers.remove(handler)
                else:
                    grass.warning(
                        _("Handler: %s was not registered") % handler.__name__)
            except:
                GError(
                    parent=self,
                    message=
                    _("Error occurred during unregistration of handler: %s \n \
                                       Handler was unregistered") %
                    handler.__name__)
                handlers.remove(handler)

        # restore mouse use (previous state)
        self.mouse['use'] = self.mouse['useBeforeGenericEvent']

        # restore overridden cursor
        if self._overriddenCursor:
            self.SetNamedCursor(self._overriddenCursor)

        self.mouseHandlerUnregistered.emit()
        return True

    def Pixel2Cell(self, xyCoords):
        raise NotImplementedError()

    def Cell2Pixel(self, enCoords):
        raise NotImplementedError()

    def OnMotion(self, event):
        """Tracks mouse motion and update statusbar

        .. todo::
            remove this method when lastEN is not used

        :func:`GetLastEN`
        """
        try:
            self.lastEN = self.Pixel2Cell(event.GetPositionTuple())
        except (ValueError):
            self.lastEN = None

        event.Skip()

    def GetLastEN(self):
        """Returns last coordinates of mouse cursor.

        @depreciated This method is depreciated. Use Signal with coordinates as parameters.

        :func:`OnMotion`
        """
        return self.lastEN

    def SetNamedCursor(self, cursorName):
        """Sets cursor defined by name."""
        cursor = self._cursors[cursorName]
        self.SetCursor(cursor)
        self._cursor = cursorName

    def GetNamedCursor(self):
        """Returns current cursor name."""
        return self._cursor

    cursor = property(fget=GetNamedCursor, fset=SetNamedCursor)

    def SetModePointer(self):
        """Sets mouse mode to pointer."""
        self.mouse['use'] = 'pointer'
        self.mouse['box'] = 'point'
        self.SetNamedCursor('default')

    def SetModePan(self):
        """Sets mouse mode to pan."""
        self.mouse['use'] = "pan"
        self.mouse['box'] = "box"
        self.zoomtype = 0
        self.SetNamedCursor('hand')

    def SetModeZoomIn(self):
        self._setModeZoom(zoomType=1)

    def SetModeZoomOut(self):
        self._setModeZoom(zoomType=-1)

    def _setModeZoom(self, zoomType):
        self.zoomtype = zoomType
        self.mouse['use'] = "zoom"
        self.mouse['box'] = "box"
        self.pen = wx.Pen(colour='Red', width=2, style=wx.SHORT_DASH)
        self.SetNamedCursor('cross')

    def SetModeDrawRegion(self):
        self.mouse['use'] = 'drawRegion'
        self.mouse['box'] = "box"
        self.pen = wx.Pen(colour='Red', width=2, style=wx.SHORT_DASH)
        self.SetNamedCursor('cross')

    def SetModeQuery(self):
        """Query mode on"""
        self.mouse['use'] = "query"
        self.mouse['box'] = "point"
        self.zoomtype = 0
        self.SetNamedCursor('cross')

    def DisactivateWin(self):
        """Use when the class instance is hidden in MapFrame."""
        raise NotImplementedError()

    def ActivateWin(self):
        """Used when the class instance is activated in MapFrame."""
        raise NotImplementedError()
Exemplo n.º 39
0
class MapWindowProperties(object):
    def __init__(self):
        self._resolution = None
        self.resolutionChanged = Signal(
            'MapWindowProperties.resolutionChanged')
        self._autoRender = None
        self.autoRenderChanged = Signal(
            'MapWindowProperties.autoRenderChanged')
        self._showRegion = None
        self.showRegionChanged = Signal(
            'MapWindowProperties.showRegionChanged')
        self._alignExtent = None
        self.alignExtentChanged = Signal(
            'MapWindowProperties.alignExtentChanged')

    def setValuesFromUserSettings(self):
        """Convenient function to get values from user settings into this object."""
        self._resolution = UserSettings.Get(group='display',
                                            key='compResolution',
                                            subkey='enabled')
        self._autoRender = UserSettings.Get(group='display',
                                            key='autoRendering',
                                            subkey='enabled')
        self._showRegion = False  # in statusbar.py was not from settings
        self._alignExtent = UserSettings.Get(group='display',
                                             key='alignExtent',
                                             subkey='enabled')

    @property
    def resolution(self):
        return self._resolution

    @resolution.setter
    def resolution(self, value):
        if value != self._resolution:
            self._resolution = value
            self.resolutionChanged.emit(value=value)

    @property
    def autoRender(self):
        return self._autoRender

    @autoRender.setter
    def autoRender(self, value):
        if value != self._autoRender:
            self._autoRender = value
            self.autoRenderChanged.emit(value=value)

    @property
    def showRegion(self):
        return self._showRegion

    @showRegion.setter
    def showRegion(self, value):
        if value != self._showRegion:
            self._showRegion = value
            self.showRegionChanged.emit(value=value)

    @property
    def alignExtent(self):
        return self._alignExtent

    @alignExtent.setter
    def alignExtent(self, value):
        if value != self._alignExtent:
            self._alignExtent = value
            self.alignExtentChanged.emit(value=value)
Exemplo n.º 40
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)
Exemplo n.º 41
0
class SnappingNodes(wx.EvtHandler):
    def __init__(self, giface, data, tmp_maps, mapWin):

        self.giface = giface
        self.data = data
        self.tmp_maps = tmp_maps
        self.mapWin = mapWin

        wx.EvtHandler.__init__(self)
        self.snapping = Signal('VNETManager.snapping')

        # Stores all data related to snapping
        self.snapData = {}

    def ComputeNodes(self, activate):
        """Start/stop snapping mode"""

        if not haveCtypes:
            GMessage(parent=self,
                     message=_("Unable to use ctypes. \n") +
                     _("Snapping mode can not be activated."))
            return -1

        if not activate:

            if self.tmp_maps.HasTmpVectMap("vnet_snap_points"):
                self.snapPts.DeleteRenderLayer()

                self.giface.updateMap.emit(render=False, renderVector=False)

            if 'cmdThread' in self.snapData:
                self.snapData['cmdThread'].abort()

            self.data.SetSnapping(False)

            self.snapping.emit(evt="deactivated")

            return -1

        self.data.SetSnapping(activate)

        params, inv_params, flags = self.data.GetParams()
        if not self.data.InputsErrorMsgs(
                msg=_("Snapping mode can not be activated."),
                analysis=None,
                params=params,
                inv_params=inv_params,
                flags=flags,
                relevant_params=["input", "node_layer"]):
            return -1

        if not self.tmp_maps.HasTmpVectMap("vnet_snap_points"):
            endStr = _(
                "Do you really want to activate snapping and overwrite it?")
            self.snapPts = self.tmp_maps.AddTmpVectMap("vnet_snap_points",
                                                       endStr)

            if not self.snapPts:
                return -1

        elif self.snapPts.VectMapState() == 0:
            dlg = wx.MessageDialog(
                message=_("Temporary map '%s' was changed outside " +
                          "vector analysis tool.\n"
                          "Do you really want to activate " +
                          "snapping and overwrite it? ") %
                self.snapPts.GetVectMapName(),
                caption=_("Overwrite map"),
                style=wx.YES_NO | wx.NO_DEFAULT | wx.ICON_QUESTION | wx.CENTRE)

            ret = dlg.ShowModal()
            dlg.Destroy()

            if ret == wx.ID_NO:
                self.tmp_maps.DeleteTmpMap(self.snapPts)
                return -1

        self.data.SetSnapping(True)

        inpFullName = params["input"]
        inpName, mapSet = inpFullName.split("@")
        computeNodes = True

        if "inputMap" not in self.snapData:
            pass
        elif inpFullName != self.snapData["inputMap"].GetVectMapName():
            self.snapData["inputMap"] = VectMap(None, inpFullName)
        elif self.snapData["inputMap"].VectMapState() == 1:
            computeNodes = False

        # new map needed
        if computeNodes:
            if 'cmdThread' not in self.snapData:
                self.snapData['cmdThread'] = CmdThread(self)
            else:
                self.snapData['cmdThread'].abort()

            cmd = [
                "v.to.points", "input=" + params['input'],
                "output=" + self.snapPts.GetVectMapName(), "use=node",
                "--overwrite"
            ]
            # process GRASS command with argument
            self.snapData["inputMap"] = VectMap(None, inpFullName)
            self.snapData["inputMap"].SaveVectMapState()

            self.Bind(EVT_CMD_DONE, self._onNodesDone)
            self.snapData['cmdThread'].RunCmd(cmd)

            self.snapping.emit(evt="computing_points")

            return 0
        # map is already created and up to date for input data
        else:
            self.snapPts.AddRenderLayer()

            self.giface.updateMap.emit(render=True, renderVector=True)

            self.snapping.emit(evt="computing_points_done")

            return 1

    def _onNodesDone(self, event):
        """Update map window, when map with nodes to snap is created"""
        if not event.aborted:
            self.snapPts.SaveVectMapState()
            self.snapPts.AddRenderLayer()

            self.giface.updateMap.emit(render=True, renderVector=True)

            self.snapping.emit(evt="computing_points_done")
Exemplo n.º 42
0
class SearchModuleWindow(wx.Panel):
    """Menu tree and search widget for searching modules.

    Signal:
        showNotification - attribute 'message'
    """

    def __init__(self, parent, handlerObj, giface, model, id=wx.ID_ANY, **kwargs):
        self.parent = parent
        self._handlerObj = handlerObj
        self._giface = giface
        self._model = model

        self.showNotification = Signal("SearchModuleWindow.showNotification")
        wx.Panel.__init__(self, parent=parent, id=id, **kwargs)

        # search widget
        self._search = SearchCtrl(self)
        self._search.SetDescriptiveText(_("Search"))
        self._search.ShowCancelButton(True)
        self._btnAdvancedSearch = Button(
            self, id=wx.ID_ANY, label=_("Adva&nced search...")
        )
        self._btnAdvancedSearch.SetToolTip(
            _("Do advanced search using %s tool") % "g.search.module"
        )
        # tree
        self._tree = CTreeView(model=model, parent=self)
        self._tree.SetToolTip(_("Double-click to run selected tool"))

        # buttons
        self._btnRun = Button(self, id=wx.ID_OK, label=_("&Run..."))
        self._btnRun.SetToolTip(_("Run selected tool from the tree"))
        self._btnHelp = Button(self, id=wx.ID_ANY, label=_("H&elp"))
        self._btnHelp.SetToolTip(_("Show manual for selected tool from the tree"))

        # bindings
        self._search.Bind(wx.EVT_TEXT, lambda evt: self.Filter(evt.GetString()))
        self._search.Bind(wx.EVT_SEARCHCTRL_CANCEL_BTN, lambda evt: self.Filter(""))
        self._btnRun.Bind(wx.EVT_BUTTON, lambda evt: self.Run())
        self._btnHelp.Bind(wx.EVT_BUTTON, lambda evt: self.Help())
        self._btnAdvancedSearch.Bind(wx.EVT_BUTTON, lambda evt: self.AdvancedSearch())

        self._tree.selectionChanged.connect(self.OnItemSelected)
        self._tree.itemActivated.connect(lambda node: self.Run(node))

        self._layout()

        self._search.SetFocus()

    def _layout(self):
        """Do dialog layout"""
        sizer = wx.BoxSizer(wx.VERTICAL)

        # search
        searchSizer = wx.BoxSizer(wx.HORIZONTAL)
        searchSizer.Add(self._search, proportion=1, flag=wx.EXPAND | wx.RIGHT, border=5)
        searchSizer.Add(self._btnAdvancedSearch, proportion=0, flag=wx.EXPAND)
        sizer.Add(searchSizer, proportion=0, flag=wx.EXPAND | wx.ALL, border=5)
        # body
        sizer.Add(
            self._tree, proportion=1, flag=wx.EXPAND | wx.LEFT | wx.RIGHT, border=5
        )

        # buttons
        btnSizer = wx.BoxSizer(wx.HORIZONTAL)
        btnSizer.AddStretchSpacer()
        btnSizer.Add(self._btnHelp, proportion=0, flag=wx.EXPAND | wx.RIGHT, border=5)
        btnSizer.Add(self._btnRun, proportion=0)

        sizer.Add(btnSizer, proportion=0, flag=wx.EXPAND | wx.ALL, border=5)

        self.SetSizerAndFit(sizer)
        self.SetAutoLayout(True)
        self.Layout()

    def Filter(self, text):
        if text:
            model = self._model.Filtered(
                key=["command", "keywords", "description"], value=text
            )
            self._tree.SetModel(model)
            self._tree.ExpandAll()
        else:
            self._tree.SetModel(self._model)

    def _GetSelectedNode(self):
        selection = self._tree.GetSelected()
        if not selection:
            return None
        return selection[0]

    def Run(self, node=None):
        """Run selected command.

        :param node: a tree node associated with the module or other item
        """
        if not node:
            node = self._GetSelectedNode()
        # nothing selected
        if not node:
            return
        data = node.data
        # non-leaf nodes
        if not data:
            # expand/collapse location/mapset...
            if self._tree.IsNodeExpanded(node):
                self._tree.CollapseNode(node, recursive=False)
            else:
                self._tree.ExpandNode(node, recursive=False)
            return

        # extract name of the handler and create a new call
        handler = "self._handlerObj." + data["handler"].lstrip("self.")

        if data["command"]:
            eval(handler)(event=None, cmd=data["command"].split())
        else:
            eval(handler)(event=None)

    def Help(self, node=None):
        """Show documentation for a module"""
        if not node:
            node = self._GetSelectedNode()
        # nothing selected
        if not node:
            return
        data = node.data
        # non-leaf nodes
        if not data:
            return

        if not data["command"]:
            # showing nothing for non-modules
            return
        # strip parameters from command if present
        name = data["command"].split()[0]
        self._giface.Help(name)
        self.showNotification.emit(
            message=_("Documentation for %s is now open in the web browser") % name
        )

    def AdvancedSearch(self):
        """Show advanced search window"""
        self._handlerObj.RunMenuCmd(cmd=["g.search.modules"])

    def OnItemSelected(self, node):
        """Item selected"""
        data = node.data
        if not data or "command" not in data:
            return

        if data["command"]:
            label = data["command"]
            if data["description"]:
                label += " -- " + data["description"]
        else:
            label = data["description"]

        self.showNotification.emit(message=label)
Exemplo n.º 43
0
class ScatterPlotWidget(wx.Panel, ManageBusyCursorMixin):

    def __init__(self, parent, scatt_id, scatt_mgr, transpose,
                 id=wx.ID_ANY):
        # TODO should not be transpose and scatt_id but x, y
        wx.Panel.__init__(self, parent, id)
        # bacause of aui (if floatable it can not take cursor from parent)
        ManageBusyCursorMixin.__init__(self, window=self)

        self.parent = parent
        self.full_extend = None
        self.mode = None

        self._createWidgets()
        self._doLayout()
        self.scatt_id = scatt_id
        self.scatt_mgr = scatt_mgr

        self.cidpress = None
        self.cidrelease = None

        self.rend_dt = {}

        self.transpose = transpose

        self.inverse = False

        self.SetSize((200, 100))
        self.Layout()

        self.base_scale = 1.2
        self.Bind(wx.EVT_CLOSE, lambda event: self.CleanUp())

        self.plotClosed = Signal("ScatterPlotWidget.plotClosed")
        self.cursorMove = Signal("ScatterPlotWidget.cursorMove")

        self.contex_menu = ScatterPlotContextMenu(plot=self)

        self.ciddscroll = None

        self.canvas.mpl_connect('motion_notify_event', self.Motion)
        self.canvas.mpl_connect('button_press_event', self.OnPress)
        self.canvas.mpl_connect('button_release_event', self.OnRelease)
        self.canvas.mpl_connect('draw_event', self.DrawCallback)
        self.canvas.mpl_connect('figure_leave_event', self.OnCanvasLeave)

    def DrawCallback(self, event):
        self.polygon_drawer.DrawCallback(event)
        self.axes.draw_artist(self.zoom_rect)

    def _createWidgets(self):

        # Create the mpl Figure and FigCanvas objects.
        # 5x4 inches, 100 dots-per-inch
        #
        self.dpi = 100
        self.fig = Figure((1.0, 1.0), dpi=self.dpi)
        self.fig.autolayout = True

        self.canvas = FigCanvas(self, -1, self.fig)

        self.axes = self.fig.add_axes([0.0, 0.0, 1, 1])

        pol = Polygon(list(zip([0], [0])), animated=True)
        self.axes.add_patch(pol)
        self.polygon_drawer = PolygonDrawer(self.axes, pol=pol, empty_pol=True)

        self.zoom_wheel_coords = None
        self.zoom_rect_coords = None
        self.zoom_rect = Polygon(list(zip([0], [0])), facecolor='none')
        self.zoom_rect.set_visible(False)
        self.axes.add_patch(self.zoom_rect)

    def ZoomToExtend(self):
        if self.full_extend:
            self.axes.axis(self.full_extend)
            self.canvas.draw()

    def SetMode(self, mode):
        self._deactivateMode()
        if mode == 'zoom':
            self.ciddscroll = self.canvas.mpl_connect(
                'scroll_event', self.ZoomWheel)
            self.mode = 'zoom'
        elif mode == 'zoom_extend':
            self.mode = 'zoom_extend'
        elif mode == 'pan':
            self.mode = 'pan'
        elif mode:
            self.polygon_drawer.SetMode(mode)

    def SetSelectionPolygonMode(self, activate):
        self.polygon_drawer.SetSelectionPolygonMode(activate)

    def _deactivateMode(self):
        self.mode = None
        self.polygon_drawer.SetMode(None)

        if self.ciddscroll:
            self.canvas.mpl_disconnect(self.ciddscroll)

        self.zoom_rect.set_visible(False)
        self._stopCategoryEdit()

    def GetCoords(self):

        coords = self.polygon_drawer.GetCoords()
        if coords is None:
            return

        if self.transpose:
            for c in coords:
                tmp = c[0]
                c[0] = c[1]
                c[1] = tmp

        return coords

    def SetEmpty(self):
        return self.polygon_drawer.SetEmpty()

    def OnRelease(self, event):
        if not self.mode == "zoom":
            return
        self.zoom_rect.set_visible(False)
        self.ZoomRectangle(event)
        self.canvas.draw()

    def OnPress(self, event):
        'on button press we will see if the mouse is over us and store some data'
        if not event.inaxes:
            return
        if self.mode == "zoom_extend":
            self.ZoomToExtend()

        if event.xdata and event.ydata:
            self.zoom_wheel_coords = {'x': event.xdata, 'y': event.ydata}
            self.zoom_rect_coords = {'x': event.xdata, 'y': event.ydata}
        else:
            self.zoom_wheel_coords = None
            self.zoom_rect_coords = None

    def _stopCategoryEdit(self):
        'disconnect all the stored connection ids'

        if self.cidpress:
            self.canvas.mpl_disconnect(self.cidpress)
        if self.cidrelease:
            self.canvas.mpl_disconnect(self.cidrelease)
        # self.canvas.mpl_disconnect(self.cidmotion)

    def _doLayout(self):

        self.main_sizer = wx.BoxSizer(wx.VERTICAL)
        self.main_sizer.Add(self.canvas, 1, wx.LEFT | wx.TOP | wx.GROW)
        self.SetSizer(self.main_sizer)
        self.main_sizer.Fit(self)

    def Plot(self, cats_order, scatts, ellipses, styles):
        """Redraws the figure
        """

        callafter_list = []

        if self.full_extend:
            cx = self.axes.get_xlim()
            cy = self.axes.get_ylim()
            c = cx + cy
        else:
            c = None

        q = Queue()
        _rendDtMemmapsToFiles(self.rend_dt)
        p = Process(target=MergeImg, args=(cats_order, scatts, styles,
                                           self.rend_dt, q))
        p.start()
        merged_img, self.full_extend, self.rend_dt = q.get()
        p.join()

        _rendDtFilesToMemmaps(self.rend_dt)
        merged_img = np.memmap(
            filename=merged_img['dt'],
            shape=merged_img['sh'])

        #merged_img, self.full_extend = MergeImg(cats_order, scatts, styles, None)
        self.axes.clear()
        self.axes.axis('equal')

        if self.transpose:
            merged_img = np.transpose(merged_img, (1, 0, 2))

        img = imshow(self.axes, merged_img,
                     extent=[int(ceil(x)) for x in self.full_extend],
                     origin='lower',
                     interpolation='nearest',
                     aspect="equal")

        callafter_list.append([self.axes.draw_artist, [img]])
        callafter_list.append([grass.try_remove, [merged_img.filename]])

        for cat_id in cats_order:
            if cat_id == 0:
                continue
            if cat_id not in ellipses:
                continue

            e = ellipses[cat_id]
            if not e:
                continue

            colors = styles[cat_id]['color'].split(":")
            if self.transpose:
                e['theta'] = 360 - e['theta'] + 90
                if e['theta'] >= 360:
                    e['theta'] = abs(360 - e['theta'])

                e['pos'] = [e['pos'][1], e['pos'][0]]

            ellip = Ellipse(xy=e['pos'],
                            width=e['width'],
                            height=e['height'],
                            angle=e['theta'],
                            edgecolor="w",
                            linewidth=1.5,
                            facecolor='None')
            self.axes.add_artist(ellip)
            callafter_list.append([self.axes.draw_artist, [ellip]])

            color = map(
                lambda v: int(v) / 255.0,
                styles[cat_id]['color'].split(":"))

            ellip = Ellipse(xy=e['pos'],
                            width=e['width'],
                            height=e['height'],
                            angle=e['theta'],
                            edgecolor=color,
                            linewidth=1,
                            facecolor='None')

            self.axes.add_artist(ellip)
            callafter_list.append([self.axes.draw_artist, [ellip]])

            center = Line2D([e['pos'][0]], [e['pos'][1]],
                            marker='x',
                            markeredgecolor='w',
                            # markerfacecolor=color,
                            markersize=2)
            self.axes.add_artist(center)
            callafter_list.append([self.axes.draw_artist, [center]])

        callafter_list.append([self.fig.canvas.blit, []])

        if c:
            self.axes.axis(c)
        wx.CallAfter(lambda: self.CallAfter(callafter_list))

    def CallAfter(self, funcs_list):
        while funcs_list:
            fcn, args = funcs_list.pop(0)
            fcn(*args)

        self.canvas.draw()

    def CleanUp(self):
        self.plotClosed.emit(scatt_id=self.scatt_id)
        self.Destroy()

    def ZoomWheel(self, event):
        # get the current x and y limits
        if not event.inaxes:
            return
        # tcaswell
        # http://stackoverflow.com/questions/11551049/matplotlib-plot-zooming-with-scroll-wheel
        cur_xlim = self.axes.get_xlim()
        cur_ylim = self.axes.get_ylim()

        xdata = event.xdata
        ydata = event.ydata
        if event.button == 'up':
            scale_factor = 1 / self.base_scale
        elif event.button == 'down':
            scale_factor = self.base_scale
        else:
            scale_factor = 1

        extend = (xdata - (xdata - cur_xlim[0]) * scale_factor,
                  xdata + (cur_xlim[1] - xdata) * scale_factor,
                  ydata - (ydata - cur_ylim[0]) * scale_factor,
                  ydata + (cur_ylim[1] - ydata) * scale_factor)

        self.axes.axis(extend)

        self.canvas.draw()

    def ZoomRectangle(self, event):
        # get the current x and y limits
        if not self.mode == "zoom":
            return
        if event.inaxes is None:
            return
        if event.button != 1:
            return

        cur_xlim = self.axes.get_xlim()
        cur_ylim = self.axes.get_ylim()

        x1, y1 = event.xdata, event.ydata
        x2 = deepcopy(self.zoom_rect_coords['x'])
        y2 = deepcopy(self.zoom_rect_coords['y'])

        if x1 == x2 or y1 == y2:
            return

        if x1 > x2:
            tmp = x1
            x1 = x2
            x2 = tmp

        if y1 > y2:
            tmp = y1
            y1 = y2
            y2 = tmp

        self.axes.axis((x1, x2, y1, y2))
        # self.axes.set_xlim(x1, x2)#, auto = True)
        # self.axes.set_ylim(y1, y2)#, auto = True)
        self.canvas.draw()

    def Motion(self, event):
        self.PanMotion(event)
        self.ZoomRectMotion(event)

        if event.inaxes is None:
            return

        self.cursorMove.emit(
            x=event.xdata,
            y=event.ydata,
            scatt_id=self.scatt_id)

    def OnCanvasLeave(self, event):
        self.cursorMove.emit(x=None, y=None, scatt_id=self.scatt_id)

    def PanMotion(self, event):
        'on mouse movement'
        if not self.mode == "pan":
            return
        if event.inaxes is None:
            return
        if event.button != 1:
            return

        cur_xlim = self.axes.get_xlim()
        cur_ylim = self.axes.get_ylim()

        x, y = event.xdata, event.ydata

        mx = (x - self.zoom_wheel_coords['x']) * 0.6
        my = (y - self.zoom_wheel_coords['y']) * 0.6

        extend = (
            cur_xlim[0] - mx,
            cur_xlim[1] - mx,
            cur_ylim[0] - my,
            cur_ylim[1] - my)

        self.zoom_wheel_coords['x'] = x
        self.zoom_wheel_coords['y'] = y

        self.axes.axis(extend)

        # self.canvas.copy_from_bbox(self.axes.bbox)
        # self.canvas.restore_region(self.background)
        self.canvas.draw()

    def ZoomRectMotion(self, event):
        if not self.mode == "zoom":
            return
        if event.inaxes is None:
            return
        if event.button != 1:
            return

        x1, y1 = event.xdata, event.ydata
        self.zoom_rect.set_visible(True)
        x2 = self.zoom_rect_coords['x']
        y2 = self.zoom_rect_coords['y']

        self.zoom_rect.xy = ((x1, y1), (x1, y2), (x2, y2), (x2, y1), (x1, y1))

        # self.axes.draw_artist(self.zoom_rect)
        self.canvas.draw()
Exemplo n.º 44
0
class RecentFilesMenu:
    """Add recent files history menu

    Signal FileRequested is emitted if you request file from recent
    files menu
    :param str path: file path you requested
    :param bool file_exists: file path exists or not
    :param obj file_history: file history obj instance


    :param str app_name: required for group name of recent files path
    written into the .recent_files file
    :param obj parent_menu: menu widget instance where be inserted
    recent files menu on the specified position
    :param int pos: position (index) where insert recent files menu in
    the parent menu
    :param int history_len: the maximum number of file paths written
    into the .recent_files file to app name group
    """

    recent_files = ".recent_files"

    def __init__(self, app_name, parent_menu, pos, history_len=10):
        self._history_len = history_len
        self._parent_menu = parent_menu
        self._pos = pos

        self.file_requested = Signal("RecentFilesMenu.FileRequested")

        self._filehistory = wx.FileHistory(maxFiles=history_len)
        # Recent files path stored in GRASS GIS config dir in the
        # .recent_files file in the group by application name
        self._config = wx.FileConfig(
            style=wx.CONFIG_USE_LOCAL_FILE,
            localFilename=os.path.join(
                utils.GetSettingsPath(),
                self.recent_files,
            ),
        )
        self._config.SetPath(strPath=app_name)
        self._filehistory.Load(self._config)
        self.RemoveNonExistentFiles()

        self.recent = wx.Menu()
        self._filehistory.UseMenu(self.recent)
        self._filehistory.AddFilesToMenu()

        # Show recent files menu if count of items in menu > 0
        if self._filehistory.GetCount() > 0:
            self._insertMenu()

    def _insertMenu(self):
        """Insert recent files menu into the parent menu on the
        specified position if count of menu items > 0"""

        self._parent_menu.Insert(
            pos=self._pos,
            id=wx.ID_ANY,
            text=_("&Recent Files"),
            submenu=self.recent,
        )
        self.recent.Bind(
            wx.EVT_MENU_RANGE,
            self._onFileHistory,
            id=wx.ID_FILE1,
            id2=wx.ID_FILE + self._history_len,
        )

    def _onFileHistory(self, event):
        """Choose recent file from menu event"""

        file_exists = True
        file_index = event.GetId() - wx.ID_FILE1
        path = self._filehistory.GetHistoryFile(file_index)

        if not os.path.exists(path):
            self.RemoveFileFromHistory(file_index)
            file_exists = False
        self.file_requested.emit(
            path=path,
            file_exists=file_exists,
            file_history=self._filehistory,
        )

    def AddFileToHistory(self, filename):
        """Add file to history, and save history into '.recent_files'
        file

        :param str filename: file path

        :return None
        """

        if self._filehistory.GetCount() == 0:
            self._insertMenu()
        if filename:
            self._filehistory.AddFileToHistory(filename)
            self._filehistory.Save(self._config)
            self._config.Flush()

    def RemoveFileFromHistory(self, file_index):
        """Remove file from the history.

        :param int file_index: filed index

        :return: None
        """
        self._filehistory.RemoveFileFromHistory(i=file_index)
        self._filehistory.Save(self._config)
        self._config.Flush()

    def RemoveNonExistentFiles(self):
        """Remove non existent files from the history"""
        for i in reversed(range(0, self._filehistory.GetCount())):
            file = self._filehistory.GetHistoryFile(index=i)
            if not os.path.exists(file):
                self._filehistory.RemoveFileFromHistory(i=i)

        self._filehistory.Save(self._config)
        self._config.Flush()
Exemplo n.º 45
0
class RenderWMSMgr(wx.EvtHandler):
    """Fetch and prepare WMS data for rendering.
    """

    def __init__(self, layer, env):
        if not haveGdal:
            sys.stderr.write(_("Unable to load GDAL Python bindings.\n"
                               "WMS layers can not be displayed without the bindings.\n"))

        self.layer = layer

        wx.EvtHandler.__init__(self)

        # thread for d.wms commands
        self.thread = gThread()

        self._startTime = None
        self.downloading = False
        self.renderedRegion = None
        self.updateMap = True
        self.fetched_data_cmd = None

        self.tempMap = grass.tempfile()
        self.dstSize = {}

        self.dataFetched = Signal('RenderWMSMgr.dataFetched')
        self.updateProgress = Signal('RenderWMSMgr.updateProgress')
        self.renderingFailed = Signal('RenderWMSMgr.renderingFailed')

    def __del__(self):
        try_remove(self.tempMap)

    def Render(self, cmd, env):
        """If it is needed, download missing WMS data.

        .. todo::
            lmgr deletes mapfile and maskfile when order of layers
            was changed (drag and drop) - if deleted, fetch data again
        """
        if not haveGdal:
            return

        Debug.msg(1, "RenderWMSMgr.Render(%s): force=%d img=%s" %
                  (self.layer, self.layer.forceRender, self.layer.mapfile))

        env = copy.copy(env)
        self.dstSize['cols'] = int(env["GRASS_RENDER_WIDTH"])
        self.dstSize['rows'] = int(env["GRASS_RENDER_HEIGHT"])

        region = self._getRegionDict(env)
        self._fitAspect(region, self.dstSize)

        self.updateMap = True
        fetchData = True  # changed to True when calling Render()
        zoomChanged = False

        if self.renderedRegion is None or \
           cmd != self.fetched_data_cmd:
            fetchData = True
        else:
            for c in ['north', 'south', 'east', 'west']:
                if self.renderedRegion and \
                   region[c] != self.renderedRegion[c]:
                    fetchData = True
                    break

            for c in ['e-w resol', 'n-s resol']:
                if self.renderedRegion and \
                        region[c] != self.renderedRegion[c]:
                    zoomChanged = True
                    break

        if fetchData:
            self.fetched_data_cmd = None
            self.renderedRegion = region

            try_remove(self.layer.mapfile)
            try_remove(self.tempMap)

            self.currentPid = self.thread.GetId()
            # self.thread.Terminate()
            self.downloading = True

            self.fetching_cmd = cmd

            env["GRASS_RENDER_FILE"] = self.tempMap
            env["GRASS_REGION"] = self._createRegionStr(region)

            cmd_render = copy.deepcopy(cmd)
            cmd_render[1]['quiet'] = True  # be quiet

            self._startTime = time.time()
            self.thread.Run(callable=self._render, cmd=cmd_render, env=env,
                            ondone=self.OnRenderDone)
            self.layer.forceRender = False

        self.updateProgress.emit(layer=self.layer)

    def _render(self, cmd, env):
        try:
            return grass.run_command(cmd[0], env=env, **cmd[1])
        except CalledModuleError as e:
            grass.error(e)
            return 1

    def OnRenderDone(self, event):
        """Fetch data
        """
        if event.pid != self.currentPid:
            return
        self.downloading = False
        if not self.updateMap:
            self.updateProgress.emit(layer=self.layer)
            self.renderedRegion = None
            self.fetched_data_cmd = None
            return

        self.mapMerger = GDALRasterMerger(
            targetFile=self.layer.mapfile,
            region=self.renderedRegion,
            bandsNum=3,
            gdalDriver='PNM',
            fillValue=0)
        self.mapMerger.AddRasterBands(self.tempMap, {1: 1, 2: 2, 3: 3})
        del self.mapMerger

        self.maskMerger = GDALRasterMerger(
            targetFile=self.layer.maskfile,
            region=self.renderedRegion,
            bandsNum=1,
            gdalDriver='PNM',
            fillValue=0)
        #{4 : 1} alpha channel (4) to first and only channel (1) in mask
        self.maskMerger.AddRasterBands(self.tempMap, {4: 1})
        del self.maskMerger

        self.fetched_data_cmd = self.fetching_cmd

        Debug.msg(1, "RenderWMSMgr.OnRenderDone(%s): ret=%d time=%f" %
                  (self.layer, event.ret, time.time() - self._startTime))

        self.dataFetched.emit(layer=self.layer)

    def _getRegionDict(self, env):
        """Parse string from GRASS_REGION env variable into dict.
        """
        region = {}
        parsedRegion = env["GRASS_REGION"].split(';')
        for r in parsedRegion:
            r = r.split(':')
            r[0] = r[0].strip()
            if len(r) < 2:
                continue
            try:
                if r[0] in ['cols', 'rows']:
                    region[r[0]] = int(r[1])
                else:
                    region[r[0]] = float(r[1])
            except ValueError:
                region[r[0]] = r[1]

        return region

    def _createRegionStr(self, region):
        """Create string for GRASS_REGION env variable from  dict created by _getRegionDict.
        """
        regionStr = ''
        for k, v in region.iteritems():
            item = k + ': ' + str(v)
            if regionStr:
                regionStr += '; '
            regionStr += item

        return regionStr

    def IsDownloading(self):
        """Is it downloading any data from WMS server?
        """
        return self.downloading

    def _fitAspect(self, region, size):
        """Compute region parameters to have direction independent resolution.
        """
        if region['n-s resol'] > region['e-w resol']:
            delta = region['n-s resol'] * size['cols'] / 2

            center = (region['east'] - region['west']) / 2

            region['east'] = center + delta + region['west']
            region['west'] = center - delta + region['west']
            region['e-w resol'] = region['n-s resol']

        else:
            delta = region['e-w resol'] * size['rows'] / 2

            center = (region['north'] - region['south']) / 2

            region['north'] = center + delta + region['south']
            region['south'] = center - delta + region['south']
            region['n-s resol'] = region['e-w resol']

    def Abort(self):
        """Abort rendering process"""
        Debug.msg(1, "RenderWMSMgr({}).Abort()".format(self.layer))
        self.thread.Terminate()

        # force rendering layer next time
        self.layer.forceRender = True
        self.updateMap = False
        self.thread.Terminate(False)
Exemplo n.º 46
0
class ScattsManager:
    """Main controller
    """

    def __init__(self, guiparent, giface, iclass_mapwin=None):
        self.giface = giface
        self.mapDisp = giface.GetMapDisplay()

        if iclass_mapwin:
            self.mapWin = iclass_mapwin
        else:
            self.mapWin = giface.GetMapWindow()

        self.guiparent = guiparent

        self.show_add_scatt_plot = False

        self.core = Core()

        self.cats_mgr = CategoriesManager(self, self.core)
        self.render_mgr = PlotsRenderingManager(scatt_mgr=self,
                                                cats_mgr=self.cats_mgr,
                                                core=self.core)

        self.thread = gThread()

        self.plots = {}

        self.plot_mode = None
        self.pol_sel_mode = [False, None]

        self.data_set = False

        self.cursorPlotMove = Signal("ScattsManager.cursorPlotMove")

        self.renderingStarted = self.render_mgr.renderingStarted
        self.renderingFinished = self.render_mgr.renderingFinished

        self.computingStarted = Signal("ScattsManager.computingStarted")

        if iclass_mapwin:
            self.digit_conn = IClassDigitConnection(self,
                                                    self.mapWin,
                                                    self.core.CatRastUpdater())
            self.iclass_conn = IClassConnection(self,
                                                iclass_mapwin.parent,
                                                self.cats_mgr)
        else:
            self.digit_conn = IMapWinDigitConnection()
            self.iclass_conn = IMapDispConnection(scatt_mgr=self,
                                                  cats_mgr=self.cats_mgr,
                                                  giface=self.giface)

        self._initSettings()

        self.modeSet = Signal("ScattsManager.mondeSet")

    def CleanUp(self):
        self.thread.Terminate()
        # there should be better way hot to clean up the thread
        # than calling the clean up function outside the thread,
        # which still may running
        self.core.CleanUp()

    def CleanUpDone(self):
        for scatt_id, scatt in self.plots.items():
            if scatt['scatt']:
                scatt['scatt'].CleanUp()

        self.plots.clear()

    def _initSettings(self):
        """Initialization of settings (if not already defined)
        """
        # initializes default settings
        initSettings = [
            ['selection', 'sel_pol', (255, 255, 0)],
            ['selection', 'sel_pol_vertex', (255, 0, 0)],
            ['selection', 'sel_area', (0, 255, 19)],
            ['selection', "snap_tresh", 10],
            ['selection', 'sel_area_opacty', 50],
            ['ellipses', 'show_ellips', True],
        ]

        for init in initSettings:
            UserSettings.ReadSettingsFile()
            UserSettings.Append(dict=UserSettings.userSettings,
                                group='scatt',
                                key=init[0],
                                subkey=init[1],
                                value=init[2],
                                overwrite=False)

    def SetData(self):
        self.iclass_conn.SetData()
        self.digit_conn.SetData()

    def SetBands(self, bands):
        self.busy = wx.BusyInfo(_("Loading data..."))
        self.data_set = False
        self.thread.Run(callable=self.core.CleanUp,
                        ondone=lambda event: self.CleanUpDone())

        if self.show_add_scatt_plot:
            show_add = True
        else:
            show_add = False

        self.all_bands_to_bands = dict(zip(bands, [-1] * len(bands)))
        self.all_bands = bands

        self.region = GetRegion()
        ncells = self.region["rows"] * self.region["cols"]

        if ncells > MAX_NCELLS:
            del self.busy
            self.data_set = True
            return

        self.bands = bands[:]
        self.bands_info = {}
        valid_bands = []

        for b in self.bands[:]:
            i = GetRasterInfo(b)

            self.bands_info[b] = i
            if i is not None:
                valid_bands.append(b)

        for i, b in enumerate(valid_bands):
            # name : index in core bands -
            # if not in core bands (not CELL type) -> index = -1
            self.all_bands_to_bands[b] = i

        self.thread.Run(callable=self.core.SetData,
                        bands=valid_bands,
                        ondone=self.SetDataDone,
                        userdata={"show_add": show_add})

    def SetDataDone(self, event):
        del self.busy
        self.data_set = True

        todo = event.ret
        self.bad_bands = event.ret
        bands = self.core.GetBands()

        self.bad_rasts = event.ret
        self.cats_mgr.SetData()
        if event.userdata['show_add']:
            self.AddScattPlot()

    def GetBands(self):
        return self.core.GetBands()

    def AddScattPlot(self):
        if not self.data_set and self.iclass_conn:
            self.show_add_scatt_plot = True
            self.iclass_conn.SetData()
            self.show_add_scatt_plot = False
            return
        if not self.data_set:
            GError(_('No data set.'))
            return

        self.computingStarted.emit()

        bands = self.core.GetBands()

        #added_bands_ids = []
        # for scatt_id in self.plots):
        #    added_bands_ids.append[idBandsToidScatt(scatt_id)]

        self.digit_conn.Update()

        ncells = self.region["rows"] * self.region["cols"]
        if ncells > MAX_NCELLS:
            GError(
                _(
                    parent=self.guiparent, mmessage=_(
                        "Interactive Scatter Plot Tool can not be used.\n"
                        "Number of cells (rows*cols) <%d> in current region"
                        "is higher than maximum limit <%d>.\n\n"
                        "You can reduce number of cells in current region using <g.region> command." %
                        (ncells, MAX_NCELLS))))
            return
        elif ncells > WARN_NCELLS:
            dlg = wx.MessageDialog(
                parent=self.guiparent,
                message=_("Number of cells (rows*cols) <%d> in current region is "
                          "higher than recommended threshold <%d>.\n"
                          "It is strongly advised to reduce number of cells "
                          "in current region below recommend threshold.\n "
                          "It can be done by <g.region> command.\n\n"
                          "Do you want to continue using "
                          "Interactive Scatter Plot Tool with this region?"
                          % (ncells, WARN_NCELLS)),
                style=wx.YES_NO | wx.NO_DEFAULT | wx.ICON_WARNING)
            ret = dlg.ShowModal()
            if ret != wx.ID_YES:
                return

        dlg = AddScattPlotDialog(parent=self.guiparent,
                                 bands=self.all_bands,
                                 check_bands_callback=self.CheckBands)

        if dlg.ShowModal() == wx.ID_OK:

            scatt_ids = []
            sel_bands = dlg.GetBands()

            for b_1, b_2 in sel_bands:
                transpose = False
                if b_1 > b_2:
                    transpose = True
                    tmp_band = b_2
                    b_2 = b_1
                    b_1 = tmp_band

                b_1_id = self.all_bands_to_bands[self.all_bands[b_1]]
                b_2_id = self.all_bands_to_bands[self.all_bands[b_2]]

                scatt_id = idBandsToidScatt(b_1_id, b_2_id, len(bands))
                if scatt_id in self.plots:
                    continue

                self.plots[scatt_id] = {'transpose': transpose,
                                        'scatt': None}
                scatt_ids.append(scatt_id)

            self._addScattPlot(scatt_ids)

        dlg.Destroy()

    def CheckBands(self, b_1, b_2):
        bands = self.core.GetBands()
        added_scatts_ids = self.plots.keys()

        b_1_id = self.all_bands_to_bands[self.all_bands[b_1]]
        b_2_id = self.all_bands_to_bands[self.all_bands[b_1]]

        scatt_id = idBandsToidScatt(b_1_id, b_2_id, len(bands))

        if scatt_id in added_scatts_ids:
            GWarning(
                parent=self.guiparent, message=_(
                    "Scatter plot with same band combination (regardless x y order) "
                    "is already displayed."))
            return False

        b_1_name = self.all_bands[b_1]
        b_2_name = self.all_bands[b_2]

        b_1_i = self.bands_info[b_1_name]
        b_2_i = self.bands_info[b_2_name]

        err = ""
        for b in [b_1_name, b_2_name]:
            if self.bands_info[b] is None:
                err += _("Band <%s> is not CELL (integer) type.\n" % b)
        if err:
            GMessage(parent=self.guiparent,
                     message=_("Scatter plot cannot be added.\n" + err))
            return False

        mrange = b_1_i['range'] * b_2_i['range']
        if mrange > MAX_SCATT_SIZE:
            GWarning(parent=self.guiparent,
                     message=_("Scatter plot cannot be added.\n"
                               "Multiple of bands ranges <%s:%d * %s:%d = %d> "
                               "is higher than maximum limit <%d>.\n"
                               % (b_1_name, b_1_i['range'], b_1_name, b_2_i['range'],
                                  mrange, MAX_SCATT_SIZE)))
            return False
        elif mrange > WARN_SCATT_SIZE:
            dlg = wx.MessageDialog(
                parent=self.guiparent,
                message=_(
                    "Multiple of bands ranges <%s:%d * %s:%d = %d> "
                    "is higher than recommended limit <%d>.\n"
                    "It is strongly advised to reduce range extend of bands"
                    "(e. g. using r.rescale) below recommended threshold.\n\n"
                    "Do you really want to add this scatter plot?" %
                    (b_1_name, b_1_i['range'],
                     b_1_name, b_2_i['range'],
                     mrange, WARN_SCATT_SIZE)),
                style=wx.YES_NO | wx.NO_DEFAULT | wx.ICON_WARNING)
            ret = dlg.ShowModal()
            if ret != wx.ID_YES:
                return False

        return True

    def _addScattPlot(self, scatt_ids):
        self.render_mgr.NewRunningProcess()
        self.thread.Run(callable=self.core.AddScattPlots,
                        scatt_ids=scatt_ids, ondone=self.AddScattPlotDone)

    def AddScattPlotDone(self, event):
        if not self.data_set:
            return

        scatt_ids = event.kwds['scatt_ids']
        for s_id in scatt_ids:
            trans = self.plots[s_id]['transpose']

            self.plots[s_id]['scatt'] = self.guiparent.NewScatterPlot(
                scatt_id=s_id, transpose=trans)

            self.plots[s_id]['scatt'].plotClosed.connect(self.PlotClosed)
            self.plots[s_id]['scatt'].cursorMove.connect(
                lambda x, y, scatt_id:
                self.cursorPlotMove.emit(x=x, y=y,
                                         scatt_id=scatt_id))

            if self.plot_mode:
                self.plots[s_id]['scatt'].SetMode(self.plot_mode)
                self.plots[s_id]['scatt'].ZoomToExtend()

        self.render_mgr.RunningProcessDone()

    def PlotClosed(self, scatt_id):
        del self.plots[scatt_id]

    def SetPlotsMode(self, mode):

        self.plot_mode = mode
        for scatt in self.plots.itervalues():
            if scatt['scatt']:
                scatt['scatt'].SetMode(mode)

        self.modeSet.emit(mode=mode)

    def ActivateSelectionPolygonMode(self, activate):
        self.pol_sel_mode[0] = activate
        for scatt in self.plots.itervalues():
            if not scatt['scatt']:
                continue
            scatt['scatt'].SetSelectionPolygonMode(activate)
            if not activate and self.plot_mode not in [
                    'zoom', 'pan', 'zoom_extend']:
                self.SetPlotsMode(None)

        self.render_mgr.RunningProcessDone()
        return activate

    def ProcessSelectionPolygons(self, process_mode):
        scatts_polygons = {}
        for scatt_id, scatt in self.plots.iteritems():
            if not scatt['scatt']:
                continue
            coords = scatt['scatt'].GetCoords()
            if coords is not None:
                scatts_polygons[scatt_id] = coords

        if not scatts_polygons:
            return

        value = 1
        if process_mode == 'remove':
            value = 0

        sel_cat_id = self.cats_mgr.GetSelectedCat()
        if not sel_cat_id:
            dlg = wx.MessageDialog(
                parent=self.guiparent,
                message=_(
                    "In order to select arrea in scatter plot, "
                    "you have to select class first.\n\n"
                    "There is no class yet, "
                    "do you want to create one?"),
                caption=_("No class selected"),
                style=wx.YES_NO)
            if dlg.ShowModal() == wx.ID_YES:
                self.iclass_conn.EmptyCategories()

        sel_cat_id = self.cats_mgr.GetSelectedCat()
        if not sel_cat_id:
            return

        for scatt in self.plots.itervalues():
            if scatt['scatt']:
                scatt['scatt'].SetEmpty()

        self.computingStarted.emit()

        self.render_mgr.NewRunningProcess()
        self.render_mgr.CategoryChanged(cat_ids=[sel_cat_id])
        self.render_mgr.CategoryCondsChanged(cat_ids=[sel_cat_id])

        self.thread.Run(callable=self.core.UpdateCategoryWithPolygons,
                        cat_id=sel_cat_id,
                        scatts_pols=scatts_polygons,
                        value=value, ondone=self.SetEditCatDataDone)

    def SetEditCatDataDone(self, event):
        if not self.data_set:
            return

        self.render_mgr.RunningProcessDone()
        if event.exception:
            GError(
                _("Error occurred during computation of scatter plot category:\n%s"),
                parent=self.guiparent,
                showTraceback=False)

        cat_id = event.ret
        self.iclass_conn.RenderCatRast(cat_id)

    def SettingsUpdated(self, chanaged_setts):
        self.render_mgr.RenderRequest()

        #['ellipses', 'show_ellips']
    def GetCategoriesManager(self):
        return self.cats_mgr
Exemplo n.º 47
0
class VNETAnalysisParameters:
    def __init__(self, an_props):

        self.an_props = an_props

        self.params = {
            "analysis": self.an_props.used_an[0],
            "input": "",
            "arc_layer": "",
            "node_layer": "",
            "arc_column": "",
            "arc_backward_column": "",
            "node_column": "",
            "turn_layer": "",
            "turn_cat_layer": "",
            "iso_lines": "",  # TODO check validity
            "max_dist": 0,
        }  # TODO check validity

        self.flags = {"t": False}

        self.parametersChanged = Signal("VNETAnalysisParameters.parametersChanged")

    def SetParams(self, params, flags):

        changed_params = {}
        for p, v in six.iteritems(params):
            if p == "analysis" and v not in self.an_props.used_an:
                continue

            if p == "input":
                mapName, mapSet = ParseMapStr(v)
                v = mapName + "@" + mapSet

            if p in self.params:
                if isinstance(v, str):
                    v = v.strip()

                self.params[p] = v
                changed_params[p] = v

        changed_flags = {}
        for p, v in six.iteritems(flags):
            if p in self.flags:
                self.flags[p] = v
                changed_flags[p] = v

        self.parametersChanged.emit(
            method="SetParams",
            kwargs={"changed_params": changed_params, "changed_flags": changed_flags},
        )

        return changed_params, changed_flags

    def GetParam(self, param):

        invParams = []
        if param in [
            "input",
            "arc_layer",
            "node_layer",
            "arc_column",
            "arc_backward_column",
            "node_column",
            "turn_layer",
            "turn_cat_layer",
        ]:
            invParams = self._getInvalidParams(self.params)

        if invParams:
            return self.params[param], False

        return self.params[param], True

    def GetParams(self):

        invParams = self._getInvalidParams(self.params)
        return self.params, invParams, self.flags

    def _getInvalidParams(self, params):
        """Check of analysis input data for invalid values (Parameters tab)"""
        # dict of invalid values {key from self.itemData (comboboxes from
        # Parameters tab) : invalid value}
        invParams = []

        # check vector map
        if params["input"]:
            mapName, mapSet = params["input"].split("@")
            if mapSet in grass.list_grouped("vector"):
                vectMaps = grass.list_grouped("vector")[mapSet]

        if not params["input"] or mapName not in vectMaps:
            invParams = list(params.keys())[:]
            return invParams

        # check arc/node layer
        layers = utils.GetVectorNumberOfLayers(params["input"])

        for layer in ["arc_layer", "node_layer", "turn_layer", "turn_cat_layer"]:
            if not layers or params[layer] not in layers:
                invParams.append(layer)

        dbInfo = VectorDBInfo(params["input"])

        try:
            table = dbInfo.GetTable(int(params["arc_layer"]))
            columnchoices = dbInfo.GetTableDesc(table)
        except (KeyError, ValueError):
            table = None

        # check costs columns
        for col in ["arc_column", "arc_backward_column", "node_column"]:

            if col == "node_column":
                try:
                    table = dbInfo.GetTable(int(params["node_layer"]))
                    columnchoices = dbInfo.GetTableDesc(table)
                except (KeyError, ValueError):
                    table = None

            if not table or not params[col] in list(columnchoices.keys()):
                invParams.append(col)
                continue

            if columnchoices[params[col]]["type"] not in [
                "integer",
                "double precision",
            ]:
                invParams.append(col)
                continue

        return invParams
Exemplo n.º 48
0
class CategoriesManager:
    """Manages categories list of scatter plot.
    """

    def __init__(self, scatt_mgr, core):

        self.core = core
        self.scatt_mgr = scatt_mgr

        self.cats = {}
        self.cats_ids = []

        self.sel_cat_id = None

        self.exportRaster = None

        self.initialized = Signal('CategoriesManager.initialized')
        self.setCategoryAttrs = Signal('CategoriesManager.setCategoryAttrs')
        self.deletedCategory = Signal('CategoriesManager.deletedCategory')
        self.addedCategory = Signal('CategoriesManager.addedCategory')

    def ChangePosition(self, cat_id, new_pos):
        if new_pos >= len(self.cats_ids):
            return False

        try:
            pos = self.cats_ids.index(cat_id)
        except:
            return False

        if pos > new_pos:
            pos -= 1

        self.cats_ids.remove(cat_id)

        self.cats_ids.insert(new_pos, cat_id)

        self.scatt_mgr.render_mgr.RenderRequest()
        return True

    def _addCategory(self, cat_id):
        self.scatt_mgr.thread.Run(callable=self.core.AddCategory,
                                  cat_id=cat_id)

    def SetData(self):

        if not self.scatt_mgr.data_set:
            return

        for cat_id in self.cats_ids:
            self.scatt_mgr.thread.Run(callable=self.core.AddCategory,
                                      cat_id=cat_id)

    def AddCategory(self, cat_id=None, name=None, color=None, nstd=None):

        if cat_id is None:
            if self.cats_ids:
                cat_id = max(self.cats_ids) + 1
            else:
                cat_id = 1

        if self.scatt_mgr.data_set:
            self.scatt_mgr.thread.Run(callable=self.core.AddCategory,
                                      cat_id=cat_id)
            # TODO check number of cats
            # if ret < 0: #TODO
            #    return -1;

        self.cats[cat_id] = {
            'name': 'class_%d' % cat_id,
            'color': "0:0:0",
            'opacity': 1.0,
            'show': True,
            'nstd': 1.0,
        }

        self.cats_ids.insert(0, cat_id)

        if name is not None:
            self.cats[cat_id]["name"] = name

        if color is not None:
            self.cats[cat_id]["color"] = color

        if nstd is not None:
            self.cats[cat_id]["nstd"] = nstd

        self.addedCategory.emit(cat_id=cat_id,
                                name=self.cats[cat_id]["name"],
                                color=self.cats[cat_id]["color"])
        return cat_id

    def SetCategoryAttrs(self, cat_id, attrs_dict):
        render = False
        update_cat_rast = []

        for k, v in attrs_dict.iteritems():
            if not render and k in ['color', 'opacity', 'show', 'nstd']:
                render = True
            if k in ['color', 'name']:
                update_cat_rast.append(k)

            self.cats[cat_id][k] = v

        if render:
            self.scatt_mgr.render_mgr.CategoryChanged(cat_ids=[cat_id])
            self.scatt_mgr.render_mgr.RenderRequest()

        if update_cat_rast:
            self.scatt_mgr.iclass_conn.UpdateCategoryRaster(
                cat_id, update_cat_rast)

        self.setCategoryAttrs.emit(cat_id=cat_id, attrs_dict=attrs_dict)

    def DeleteCategory(self, cat_id):

        if self.scatt_mgr.data_set:
            self.scatt_mgr.thread.Run(callable=self.core.DeleteCategory,
                                      cat_id=cat_id)
        del self.cats[cat_id]
        self.cats_ids.remove(cat_id)

        self.deletedCategory.emit(cat_id=cat_id)

    # TODO emit event?
    def SetSelectedCat(self, cat_id):
        self.sel_cat_id = cat_id
        if self.scatt_mgr.pol_sel_mode[0]:
            self.scatt_mgr.render_mgr.RenderRequest()

    def GetSelectedCat(self):
        return self.sel_cat_id

    def GetCategoryAttrs(self, cat_id):
        #TODO is mutable
        return self.cats[cat_id]

    def GetCategoriesAttrs(self):
        #TODO is mutable
        return self.cats

    def GetCategories(self):
        return self.cats_ids[:]

    def SetCategoryPosition(self):
        if newindex > oldindex:
            newindex -= 1

        self.cats_ids.insert(newindex, self.cats_ids.pop(oldindex))

    def ExportCatRast(self, cat_id):

        cat_attrs = self.GetCategoryAttrs(cat_id)

        dlg = ExportCategoryRaster(
            parent=self.scatt_mgr.guiparent,
            rasterName=self.exportRaster,
            title=_("Export scatter plot raster of class <%s>") %
            cat_attrs['name'])

        if dlg.ShowModal() == wx.ID_OK:
            self.exportCatRast = dlg.GetRasterName()
            dlg.Destroy()

            self.scatt_mgr.thread.Run(callable=self.core.ExportCatRast,
                                      userdata={'name': cat_attrs['name']},
                                      cat_id=cat_id,
                                      rast_name=self.exportCatRast,
                                      ondone=self.OnExportCatRastDone)

    def OnExportCatRastDone(self, event):
        ret, err = event.ret
        if ret == 0:
            cat_attrs = self.GetCategoryAttrs(event.kwds['cat_id'])
            GMessage(
                _("Scatter plot raster of class <%s> exported to raster map <%s>.") %
                (event.userdata['name'], event.kwds['rast_name']))
        else:
            GMessage(
                _("Export of scatter plot raster of class <%s> to map <%s> failed.\n%s") %
                (event.userdata['name'], event.kwds['rast_name'], err))
Exemplo n.º 49
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 (,)']
        }
Exemplo n.º 50
0
class SearchModuleWidget(wx.Panel):
    """Search module widget (used e.g. in SearchModuleWindow)

    Signals:
        moduleSelected - attribute 'name' is module name
        showSearchResult - attribute 'result' is a node (representing module)
        showNotification - attribute 'message'
    """
    def __init__(self, parent, model,
                 showChoice=True, showTip=False, **kwargs):
        self._showTip = showTip
        self._showChoice = showChoice
        self._model = model
        self._results = [] # list of found nodes
        self._resultIndex = -1
        self._searchKeys = ['description', 'keywords', 'command']
        self._oldValue = ''

        self.moduleSelected = Signal('SearchModuleWidget.moduleSelected')
        self.showSearchResult = Signal('SearchModuleWidget.showSearchResult')
        self.showNotification = Signal('SearchModuleWidget.showNotification')

        wx.Panel.__init__(self, parent=parent, id=wx.ID_ANY, **kwargs)

#        self._box = wx.StaticBox(parent = self, id = wx.ID_ANY,
#                                label = " %s " % _("Find module - (press Enter for next match)"))

        if sys.platform == 'win32':
            self._search = wx.TextCtrl(parent = self, id = wx.ID_ANY,
                                       size = (-1, 25), style = wx.TE_PROCESS_ENTER)
        else:
            self._search = wx.SearchCtrl(parent = self, id = wx.ID_ANY,
                                         size = (-1, 25), style = wx.TE_PROCESS_ENTER)
            self._search.SetDescriptiveText(_('Fulltext search'))
            self._search.SetToolTipString(_("Type to search in all modules. Press Enter for next match."))

        self._search.Bind(wx.EVT_TEXT, self.OnSearchModule)
        self._search.Bind(wx.EVT_KEY_UP,  self.OnKeyUp)

        if self._showTip:
            self._searchTip = StaticWrapText(parent = self, id = wx.ID_ANY,
                                             size = (-1, 35))

        if self._showChoice:
            self._searchChoice = wx.Choice(parent=self, id=wx.ID_ANY)
            self._searchChoice.SetItems(self._searchModule(keys=['command'], value=''))
            self._searchChoice.Bind(wx.EVT_CHOICE, self.OnSelectModule)

        self._layout()

    def _layout(self):
        """Do layout"""
        sizer = wx.BoxSizer(wx.HORIZONTAL)
        boxSizer = wx.BoxSizer(wx.VERTICAL)

        boxSizer.Add(item=self._search,
                     flag=wx.ALIGN_CENTER_VERTICAL | wx.EXPAND | wx.BOTTOM,
                     border=5)
        if self._showChoice:
            hSizer = wx.BoxSizer(wx.HORIZONTAL)
            hSizer.Add(item=self._searchChoice,
                       flag=wx.ALIGN_CENTER_VERTICAL | wx.EXPAND | wx.BOTTOM,
                       border=5)
            hSizer.AddStretchSpacer()
            boxSizer.Add(item=hSizer, flag=wx.EXPAND)
        if self._showTip:
            boxSizer.Add(item=self._searchTip,
                          flag=wx.ALIGN_CENTER_VERTICAL | wx.EXPAND)

        sizer.Add(item=boxSizer, proportion=1)

        self.SetSizer(sizer)
        sizer.Fit(self)

    def OnKeyUp(self, event):
        """Key or key combination pressed"""
        if event.GetKeyCode() in (wx.WXK_RETURN, wx.WXK_NUMPAD_ENTER) and not event.ControlDown():
            if self._results:
                self._resultIndex += 1
                if self._resultIndex == len(self._results):
                    self._resultIndex = 0
                self.showSearchResult.emit(result=self._results[self._resultIndex])
        event.Skip()

    def OnSearchModule(self, event):
        """Search module by keywords or description"""
        value = self._search.GetValue()
        if value == self._oldValue:
            event.Skip()
            return
        self._oldValue = value

        if len(value) <= 2:
            if len(value) == 0: # reset
                commands = self._searchModule(keys=['command'], value='')
            else:
                self.showNotification.emit(message=_("Searching, please type more characters."))
                return
        else:
            commands = self._searchModule(keys=self._searchKeys, value=value)
        if self._showChoice:
            self._searchChoice.SetItems(commands)
            if commands:
                self._searchChoice.SetSelection(0)

        label = _("%d modules match") % len(commands)
        if self._showTip:
            self._searchTip.SetLabel(label)

        self.showNotification.emit(message=label)

        event.Skip()

    def _searchModule(self, keys, value):
        """Search modules by keys

        :param keys: list of keys
        :param value: patter to match
        """
        nodes = set()
        for key in keys:
            nodes.update(self._model.SearchNodes(key=key, value=value))

        nodes = list(nodes)
        nodes.sort(key=lambda node: self._model.GetIndexOfNode(node))
        self._results = nodes
        self._resultIndex = -1
        commands = [node.data['command'] for node in nodes if node.data['command']]
        commands.sort() # return sorted list of commands (TODO: sort in better way)

        return commands

    def OnSelectModule(self, event):
        """Module selected from choice, update command prompt"""
        cmd  = self._searchChoice.GetStringSelection()
        self.moduleSelected.emit(name=cmd)

        if self._showTip:
            for module in self._results:
                if cmd == module.data['command']:
                    self._searchTip.SetLabel(module.data['description'])
                    break

    def Reset(self):
        """Reset widget"""
        self._search.SetValue('')
        if self._showTip:
            self._searchTip.SetLabel('')
Exemplo n.º 51
0
class PlotsRenderingManager:
    """Manages rendering of scatter plot.

    .. todo::
        still space for optimalization
    """

    def __init__(self, scatt_mgr, cats_mgr, core):
        self.scatt_mgr = scatt_mgr
        self.cats_mgr = cats_mgr
        self.core = core
        self.scatts_dt, self.scatt_conds_dt = self.core.GetScattsData()

        self.runningProcesses = 0

        self.data_to_render = {}
        self.render_queue = []

        self.cat_ids = []
        self.cat_cond_ids = []

        self.renderingStarted = Signal("ScattsManager.renderingStarted")
        self.renderingFinished = Signal("ScattsManager.renderingFinished")

    def AddRenderRequest(self, scatts):
        for scatt_id, cat_ids in scatts:
            if not self.data_to_render.has_key[scatt_id]:
                self.data_to_render = cat_ids
            else:
                for c in cat_ids:
                    if c not in self.data_to_render[scatt_id]:
                        self.data_to_render[scatt_id].append(c)

    def NewRunningProcess(self):
        self.runningProcesses += 1

    def RunningProcessDone(self):
        self.runningProcesses -= 1
        if self.runningProcesses <= 1:
            self.RenderScattPlts()

    def RenderRequest(self):
        if self.runningProcesses <= 1:
            self.RenderScattPlts()

    def CategoryChanged(self, cat_ids):
        for c in cat_ids:
            if c not in self.cat_ids:
                self.cat_ids.append(c)

    def CategoryCondsChanged(self, cat_ids):
        for c in cat_ids:
            if c not in self.cat_cond_ids:
                self.cat_cond_ids.append(c)

    def RenderScattPlts(self, scatt_ids=None):
        if len(self.render_queue) > 1:
            return

        self.renderingStarted.emit()
        self.render_queue.append(self.scatt_mgr.thread.GetId())

        cats_attrs = deepcopy(self.cats_mgr.GetCategoriesAttrs())
        cats = self.cats_mgr.GetCategories()[:]
        self.scatt_mgr.thread.Run(
            callable=self._renderscattplts,
            scatt_ids=scatt_ids,
            cats=cats,
            cats_attrs=cats_attrs,
            ondone=self.RenderingDone)

    def _renderscattplts(self, scatt_ids, cats, cats_attrs):
        cats.reverse()
        cats.insert(0, 0)
        for i_scatt_id, scatt in self.scatt_mgr.plots.items():
            if scatt_ids is not None and \
               i_scatt_id not in scatt_ids:
                continue
            if not scatt['scatt']:
                continue

            scatt_dt = self.scatts_dt.GetScatt(i_scatt_id)
            if self._showConfEllipses():
                ellipses_dt = self.scatts_dt.GetEllipses(
                    i_scatt_id, cats_attrs)
            else:
                ellipses_dt = {}

            for c in scatt_dt.iterkeys():
                try:
                    self.cat_ids.remove(c)
                    scatt_dt[c]['render'] = True
                except:
                    scatt_dt[c]['render'] = False

            if self.scatt_mgr.pol_sel_mode[0]:
                self._getSelectedAreas(cats, i_scatt_id, scatt_dt, cats_attrs)

            scatt['scatt'].Plot(cats_order=cats,
                                scatts=scatt_dt,
                                ellipses=ellipses_dt,
                                styles=cats_attrs)

    def RenderingDone(self, event):
        self.render_queue.remove(event.pid)
        if not self.render_queue:
            self.renderingFinished.emit()

    def _getSelectedAreas(self, cats_order, scatt_id, scatt_dt, cats_attrs):

        cat_id = self.cats_mgr.GetSelectedCat()
        if not cat_id:
            return

        sel_a_cat_id = -1

        s = self.scatt_conds_dt.GetScatt(scatt_id, [cat_id])
        if not s:
            return

        cats_order.append(sel_a_cat_id)

        col = UserSettings.Get(group='scatt',
                               key='selection',
                               subkey='sel_area')

        col = ":".join(map(str, col))
        opac = UserSettings.Get(group='scatt',
                                key='selection',
                                subkey='sel_area_opacty') / 100.0

        cats_attrs[sel_a_cat_id] = {'color': col,
                                    'opacity': opac,
                                    'show': True}

        scatt_dt[sel_a_cat_id] = s[cat_id]

        scatt_dt[sel_a_cat_id]['render'] = False
        if cat_id in self.cat_cond_ids:
            scatt_dt[sel_a_cat_id]['render'] = True
            self.cat_cond_ids.remove(cat_id)

    def _showConfEllipses(self):
        return UserSettings.Get(group='scatt',
                                key="ellipses",
                                subkey="show_ellips")
Exemplo n.º 52
0
class RLiSetupMapPanel(wx.Panel):
    """Panel with mapwindow used in r.li.setup"""

    def __init__(self, parent, samplingType, icon=None, map_=None):
        wx.Panel.__init__(self, parent=parent)

        self.mapWindowProperties = MapWindowProperties()
        self.mapWindowProperties.setValuesFromUserSettings()
        giface = StandaloneGrassInterface()
        self.samplingtype = samplingType
        self.parent = parent

        if map_:
            self.map_ = map_
        else:
            self.map_ = Map()
        self.map_.region = self.map_.GetRegion()

        self._mgr = wx.aui.AuiManager(self)
        self.mapWindow = BufferedMapWindow(parent=self, giface=giface,
                                           Map=self.map_,
                                           properties=self.mapWindowProperties)
        self._mgr.AddPane(self.mapWindow, wx.aui.AuiPaneInfo().CentrePane().
                          Dockable(True).BestSize((-1, -1)).Name('mapwindow').
                          CloseButton(False).DestroyOnClose(True).
                          Layer(0))
        self._toolSwitcher = ToolSwitcher()
        self._toolSwitcher.toggleToolChanged.connect(self._onToolChanged)
        self.toolbar = RLiSetupToolbar(self, self._toolSwitcher)

        self.catId = 1

        self._mgr.AddPane(self.toolbar,
                          wx.aui.AuiPaneInfo().
                          Name("maptoolbar").Caption(_("Map Toolbar")).
                          ToolbarPane().Left().Name('mapToolbar').
                          CloseButton(False).Layer(1).Gripper(False).
                          BestSize((self.toolbar.GetBestSize())))
        self._mgr.Update()

        if self.samplingtype == SamplingType.REGIONS:
            self.afterRegionDrawn = Signal('RLiSetupMapPanel.afterRegionDrawn')
            self._registeredGraphics = self.mapWindow.RegisterGraphicsToDraw(
                graphicsType='line')
        elif self.samplingtype in [SamplingType.MUNITSR, SamplingType.MMVWINR]:
            self.sampleFrameChanged = Signal(
                'RLiSetupMapPanel.sampleFrameChanged')
            self._registeredGraphics = self.mapWindow.RegisterGraphicsToDraw(
                graphicsType='rectangle')
        elif self.samplingtype in [SamplingType.MUNITSC, SamplingType.MMVWINC]:
            self.afterCircleDrawn = Signal('RLiSetupMapPanel.afterCircleDrawn')
            self._registeredGraphics = self.mapWindow.RegisterGraphicsToDraw(
                graphicsType='line')
        else:
            self.sampleFrameChanged = Signal(
                'RLiSetupMapPanel.sampleFrameChanged')
            self._registeredGraphics = self.mapWindow.RegisterGraphicsToDraw(
                graphicsType='rectangle')

        self._registeredGraphics.AddPen('rlisetup', wx.Pen(wx.GREEN, width=2,
                                                           style=wx.SOLID))
        self._registeredGraphics.AddItem(coords=[[0, 0], [0, 0]],
                                         penName='rlisetup', hide=True)

        if self.samplingtype != SamplingType.VECT:
            self.toolbar.SelectDefault()

    def GetMap(self):
        return self.map_

    def OnPan(self, event):
        """Panning, set mouse to drag."""
        self.mapWindow.SetModePan()

    def OnZoomIn(self, event):
        """Zoom in the map."""
        self.mapWindow.SetModeZoomIn()

    def OnZoomOut(self, event):
        """Zoom out the map."""
        self.mapWindow.SetModeZoomOut()

    def OnZoomToMap(self, event):
        layers = self.map_.GetListOfLayers()
        self.mapWindow.ZoomToMap(layers=layers, ignoreNulls=False, render=True)

    def OnDrawRadius(self, event):
        """Start draw mode"""
        self.mapWindow.mouse['use'] = "None"
        self.mapWindow.mouse['box'] = "line"
        self.mapWindow.pen = wx.Pen(colour=wx.RED, width=1,
                                    style=wx.SHORT_DASH)
        self.mapWindow.SetNamedCursor('cross')
        self.mapWindow.mouseLeftUp.connect(self._radiusDrawn)

    def OnDigitizeRegion(self, event):
        """Start draw mode"""
        self.mapWindow.mouse['use'] = "None"
        self.mapWindow.mouse['box'] = "line"
        self.mapWindow.pen = wx.Pen(colour=wx.RED, width=1,
                                    style=wx.SHORT_DASH)
        self.mapWindow.SetNamedCursor('cross')
        self.mapWindow.mouseLeftUp.connect(self._lineSegmentDrawn)
        self.mapWindow.mouseDClick.connect(self._mouseDbClick)

        self._registeredGraphics.GetItem(0).SetCoords([])

    def OnDraw(self, event):
        """Start draw mode"""
        self.mapWindow.mouse['use'] = "None"
        self.mapWindow.mouse['box'] = "box"
        self.mapWindow.pen = wx.Pen(colour=wx.RED, width=2,
                                    style=wx.SHORT_DASH)
        self.mapWindow.SetNamedCursor('cross')
        self.mapWindow.mouseLeftUp.connect(self._rectangleDrawn)

    def _lineSegmentDrawn(self, x, y):
        item = self._registeredGraphics.GetItem(0)
        coords = item.GetCoords()
        if len(coords) == 0:
            coords.extend([self.mapWindow.Pixel2Cell(
                self.mapWindow.mouse['begin'])])
        coords.extend([[x, y]])

        item.SetCoords(coords)
        item.SetPropertyVal('hide', False)
        self.mapWindow.ClearLines()
        self._registeredGraphics.Draw()

    def _mouseDbClick(self, x, y):
        item = self._registeredGraphics.GetItem(0)
        coords = item.GetCoords()
        coords.extend([[x, y]])
        item.SetCoords(coords)
        item.SetPropertyVal('hide', False)
        self.mapWindow.ClearLines()
        self._registeredGraphics.Draw()
        self.createRegion()

    def createRegion(self):
        dlg = wx.TextEntryDialog(None, 'Name of sample region',
                                 'Create region', 'region' + str(self.catId))
        ret = dlg.ShowModal()
        while True:
            if ret == wx.ID_OK:
                raster = dlg.GetValue()
                if checkMapExists(raster):
                    GMessage(
                        parent=self, message=_(
                            "The raster file %s already"
                            " exists, please change name") %
                        raster)
                    ret = dlg.ShowModal()
                else:
                    dlg.Destroy()
                    marea = self.writeArea(
                        self._registeredGraphics.GetItem(0).GetCoords(), raster)
                    self.nextRegion(next=True, area=marea)
                    break
            else:
                self.nextRegion(next=False)
                break

    def nextRegion(self, next=True, area=None):
        self.mapWindow.ClearLines()
        item = self._registeredGraphics.GetItem(0)
        item.SetCoords([])
        item.SetPropertyVal('hide', True)

        layers = self.map_.GetListOfLayers()
        self.mapWindow.ZoomToMap(layers=layers, ignoreNulls=False, render=True)
        if next is True:
            self.afterRegionDrawn.emit(marea=area)
        else:
            gcmd.GMessage(parent=self.parent, message=_(
                "Raster map not created. Please redraw region."))

    def writeArea(self, coords, rasterName):
        polyfile = tempfile.NamedTemporaryFile(delete=False)
        polyfile.write("AREA\n")
        for coor in coords:
            east, north = coor
            point = " %s %s\n" % (east, north)
            polyfile.write(point)

        catbuf = "=%d a\n" % self.catId
        polyfile.write(catbuf)
        self.catId = self.catId + 1

        polyfile.close()
        region_settings = grass.parse_command('g.region', flags='p',
                                              delimiter=':')
        pname = polyfile.name.split('/')[-1]
        tmpraster = "rast_" + pname
        tmpvector = "vect_" + pname
        wx.BeginBusyCursor()
        wx.GetApp().Yield()
        RunCommand('r.in.poly', input=polyfile.name, output=tmpraster,
                   rows=region_settings['rows'], overwrite=True)

        RunCommand('r.to.vect', input=tmpraster, output=tmpvector,
                   type='area', overwrite=True)

        RunCommand('v.to.rast', input=tmpvector, output=rasterName,
                   value=1, use='val')
        wx.EndBusyCursor()
        grass.use_temp_region()
        grass.run_command('g.region', vector=tmpvector)
        region = grass.region()

        marea = MaskedArea(region, rasterName)

        RunCommand('g.remove', flags='f', type='raster', name=tmpraster)
        RunCommand('g.remove', flags='f', type='vector', name=tmpvector)

        os.unlink(polyfile.name)
        return marea

    def _onToolChanged(self):
        """Helper function to disconnect drawing"""
        try:
            self.mapWindow.mouseLeftUp.disconnect(self._rectangleDrawn)
            self.mapWindow.mouseLeftUp.disconnect(self._radiusDrawn)
            self.mapWindow.mouseMoving.disconnect(self._mouseMoving)
            self.mapWindow.mouseLeftDown.disconnect(self._mouseLeftDown)
            self.mapWindow.mouseDClick.disconnect(self._mouseDbClick)
        except DispatcherKeyError:
            pass

    def _radiusDrawn(self, x, y):
        """When drawing finished, get region values"""
        mouse = self.mapWindow.mouse
        item = self._registeredGraphics.GetItem(0)
        p1 = mouse['begin']
        p2 = mouse['end']
        dist, (north, east) = self.mapWindow.Distance(p1, p2, False)
        circle = Circle(p1, dist)
        self.mapWindow.ClearLines()
        self.mapWindow.pdcTmp.SetBrush(wx.Brush(wx.CYAN, wx.TRANSPARENT))
        pen = wx.Pen(colour=wx.RED, width=2)
        self.mapWindow.pdcTmp.SetPen(pen)
        self.mapWindow.pdcTmp.DrawCircle(circle.point[0], circle.point[1],
                                         circle.radius)
        self._registeredGraphics.Draw()
        self.createCricle(circle)

    def createCricle(self, c):
        dlg = wx.TextEntryDialog(None,
                                 'Name of sample circle region',
                                 'Create circle region',
                                 'circle' + str(self.catId))
        ret = dlg.ShowModal()
        while True:
            if ret == wx.ID_OK:
                raster = dlg.GetValue()
                if checkMapExists(raster):
                    GMessage(
                        parent=self, message=_(
                            "The raster file %s already"
                            " exists, please change name") %
                        raster)
                    ret = dlg.ShowModal()
                else:
                    dlg.Destroy()
                    circle = self.writeCircle(c, raster)
                    self.nextCircle(next=True, circle=circle)
                    break
            else:
                self.nextCircle(next=False)
                break

    def nextCircle(self, next=True, circle=None):
        self.mapWindow.ClearLines()
        item = self._registeredGraphics.GetItem(0)
        item.SetPropertyVal('hide', True)
        layers = self.map_.GetListOfLayers()
        self.mapWindow.ZoomToMap(layers=layers, ignoreNulls=False, render=True)
        if next is True:
            self.afterCircleDrawn.emit(region=circle)
        else:
            gcmd.GMessage(parent=self.parent, message=_(
                "Raster map not created. redraw region again."))

    def writeCircle(self, circle, rasterName):
        coords = self.mapWindow.Pixel2Cell(circle.point)
        RunCommand('r.circle', output=rasterName, max=circle.radius,
                   coordinate=coords, flags="b")
        grass.use_temp_region()
        grass.run_command('g.region', zoom=rasterName)
        region = grass.region()
        marea = MaskedArea(region, rasterName, circle.radius)
        return marea

    def _rectangleDrawn(self):
        """When drawing finished, get region values"""
        mouse = self.mapWindow.mouse
        item = self._registeredGraphics.GetItem(0)
        p1 = self.mapWindow.Pixel2Cell(mouse['begin'])
        p2 = self.mapWindow.Pixel2Cell(mouse['end'])
        item.SetCoords([p1, p2])
        region = {'n': max(p1[1], p2[1]),
                  's': min(p1[1], p2[1]),
                  'w': min(p1[0], p2[0]),
                  'e': max(p1[0], p2[0])}
        item.SetPropertyVal('hide', False)
        self.mapWindow.ClearLines()
        self._registeredGraphics.Draw()
        if self.samplingtype in [SamplingType.MUNITSR, SamplingType.MMVWINR]:
            dlg = wx.MessageDialog(self, "Is this area ok?",
                                   "select sampling unit",
                                   wx.YES_NO | wx.ICON_QUESTION)
            ret = dlg.ShowModal()
            if ret == wx.ID_YES:
                grass.use_temp_region()
                grass.run_command('g.region', n=region['n'], s=region['s'],
                                  e=region['e'], w=region['w'])
                tregion = grass.region()
                self.sampleFrameChanged.emit(region=tregion)
                self.mapWindow.ClearLines()
                item = self._registeredGraphics.GetItem(0)
                item.SetPropertyVal('hide', True)
                layers = self.map_.GetListOfLayers()
                self.mapWindow.ZoomToMap(layers=layers, ignoreNulls=False,
                                         render=True)
            else:
                self.nextRegion(next=False)
            dlg.Destroy()

        elif self.samplingtype != SamplingType.WHOLE:
            """When drawing finished, get region values"""
            self.sampleFrameChanged.emit(region=region)
Exemplo n.º 53
0
class Animation:
    """Class represents animation as a sequence of states (views).
    It enables to record, replay the sequence and finally generate
    all image files. Recording and replaying is based on timer events.
    There is no frame interpolation like in the Tcl/Tk based Nviz.
    """
    def __init__(self, mapWindow, timer):
        """Animation constructor

        Signals:
            animationFinished - emitted when animation finished
                              - attribute 'mode'
            animationUpdateIndex - emitted during animation to update gui
                                 - attributes 'index' and 'mode'

        :param mapWindow: glWindow where rendering takes place
        :param timer: timer for recording and replaying
        """
        self.animationFinished = Signal("Animation.animationFinished")
        self.animationUpdateIndex = Signal("Animation.animationUpdateIndex")

        self.animationList = []  # view states
        self.timer = timer
        self.mapWindow = mapWindow
        self.actions = {"record": self.Record, "play": self.Play}
        self.formats = ["tif", "ppm"]  # currently supported formats
        self.mode = "record"  # current mode (record, play, save)
        self.paused = False  # recording/replaying paused
        self.currentFrame = 0  # index of current frame
        self.fps = 24  # user settings   # Frames per second

        self.stopSaving = False  # stop during saving images
        self.animationSaved = False  # current animation saved or not

    def Start(self):
        """Start recording/playing"""
        self.timer.Start(self.GetInterval())

    def Pause(self):
        """Pause recording/playing"""
        self.timer.Stop()

    def Stop(self):
        """Stop recording/playing"""
        self.timer.Stop()
        self.PostFinishedEvent()

    def Update(self):
        """Record/play next view state (on timer event)"""
        self.actions[self.mode]()

    def Record(self):
        """Record new view state"""
        self.animationList.append({
            "view": copy.deepcopy(self.mapWindow.view),
            "iview": copy.deepcopy(self.mapWindow.iview),
        })
        self.currentFrame += 1
        self.PostUpdateIndexEvent(index=self.currentFrame)
        self.animationSaved = False

    def Play(self):
        """Render next frame"""
        if not self.animationList:
            self.Stop()
            return
        try:
            self.IterAnimation()
        except IndexError:
            # no more frames
            self.Stop()

    def IterAnimation(self):
        params = self.animationList[self.currentFrame]
        self.UpdateView(params)
        self.currentFrame += 1

        self.PostUpdateIndexEvent(index=self.currentFrame)

    def UpdateView(self, params):
        """Update view data in map window and render"""
        toolWin = self.mapWindow.GetToolWin()
        toolWin.UpdateState(view=params["view"], iview=params["iview"])

        self.mapWindow.UpdateView()

        self.mapWindow.render["quick"] = True
        self.mapWindow.Refresh(False)

    def IsRunning(self):
        """Test if timer is running"""
        return self.timer.IsRunning()

    def SetMode(self, mode):
        """Start animation mode

        :param mode: animation mode (record, play, save)
        """
        self.mode = mode

    def GetMode(self):
        """Get animation mode (record, play, save)"""
        return self.mode

    def IsPaused(self):
        """Test if animation is paused"""
        return self.paused

    def SetPause(self, pause):
        self.paused = pause

    def Exists(self):
        """Returns if an animation has been recorded"""
        return bool(self.animationList)

    def GetFrameCount(self):
        """Return number of recorded frames"""
        return len(self.animationList)

    def Clear(self):
        """Clear all records"""
        self.animationList = []
        self.currentFrame = 0

    def GoToFrame(self, index):
        """Render frame of given index"""
        if index >= len(self.animationList):
            return

        self.currentFrame = index
        params = self.animationList[self.currentFrame]
        self.UpdateView(params)

    def PostFinishedEvent(self):
        """Animation ends"""
        self.animationFinished.emit(mode=self.mode)

    def PostUpdateIndexEvent(self, index):
        """Frame index changed, update tool window"""
        self.animationUpdateIndex(index=index, mode=self.mode)

    def StopSaving(self):
        """Abort image files generation"""
        self.stopSaving = True

    def IsSaved(self):
        """Test if animation has been saved (to images)"""
        return self.animationSaved

    def SaveAnimationFile(self, path, prefix, format):
        """Generate image files

        :param path: path to direcory
        :param prefix: file prefix
        :param format: index of image file format
        """
        size = self.mapWindow.GetClientSize()
        toolWin = self.mapWindow.GetToolWin()

        formatter = ":04.0f"
        n = len(self.animationList)
        if n < 10:
            formatter = ":01.0f"
        elif n < 100:
            formatter = ":02.0f"
        elif n < 1000:
            formatter = ":03.0f"

        self.currentFrame = 0
        self.mode = "save"
        for params in self.animationList:
            if not self.stopSaving:
                self.UpdateView(params)
                number = ("{frame" + formatter +
                          "}").format(frame=self.currentFrame)
                filename = "{prefix}_{number}.{ext}".format(
                    prefix=prefix, number=number, ext=self.formats[format])
                filepath = os.path.join(path, filename)
                self.mapWindow.SaveToFile(
                    FileName=filepath,
                    FileType=self.formats[format],
                    width=size[0],
                    height=size[1],
                )
                self.currentFrame += 1

                wx.GetApp().Yield()
                toolWin.UpdateFrameIndex(index=self.currentFrame,
                                         goToFrame=False)
            else:
                self.stopSaving = False
                break
        self.animationSaved = True
        self.PostFinishedEvent()

    def SetFPS(self, fps):
        """Set Frames Per Second value
        :param fps: frames per second
        """
        self.fps = fps

    def GetInterval(self):
        """Return timer interval in ms"""
        return 1000.0 / self.fps
Exemplo n.º 54
0
class ToolSwitcher:
    """Class handling switching tools in toolbar and custom toggle buttons."""
    def __init__(self):
        self._groups = defaultdict(lambda: defaultdict(list))
        self._toolsGroups = defaultdict(list)

        # emitted when tool is changed
        self.toggleToolChanged = Signal('ToolSwitcher.toggleToolChanged')

    def AddToolToGroup(self, group, toolbar, tool):
        """Adds tool from toolbar to group of exclusive tools.

        :param group: name of group (e.g. 'mouseUse')
        :param toolbar: instance of toolbar
        :param tool: id of a tool from the toolbar
        """
        self._groups[group][toolbar].append(tool)
        self._toolsGroups[tool].append(group)

    def AddCustomToolToGroup(self, group, btnId, toggleHandler):
        """Adds custom tool from to group of exclusive tools (some toggle button).

        :param group: name of group (e.g. 'mouseUse')
        :param btnId: id of a tool (typically button)
        :param toggleHandler: handler to be called to switch the button
        """
        self._groups[group]['custom'].append((btnId, toggleHandler))
        self._toolsGroups[btnId].append(group)

    def RemoveCustomToolFromGroup(self, tool):
        """Removes custom tool from group.

        :param tool: id of the button
        """
        if not tool in self._toolsGroups:
            return
        for group in self._toolsGroups[tool]:
            self._groups[group]['custom'] = \
                [(bid, hdlr) for (bid, hdlr)
                 in self._groups[group]['custom'] if bid != tool]

    def RemoveToolbarFromGroup(self, group, toolbar):
        """Removes toolbar from group.

        Before toolbar is destroyed, it must be removed from group, too.
        Otherwise we can expect some DeadObject errors.

        :param group: name of group (e.g. 'mouseUse')
        :param toolbar: instance of toolbar
        """
        for tb in self._groups[group]:
            if tb == toolbar:
                del self._groups[group][tb]
                break

    def IsToolInGroup(self, tool, group):
        """Checks whether a tool is in a specified group.

        :param tool: tool id
        :param group: name of group (e.g. 'mouseUse')
        """
        for group in self._toolsGroups[tool]:
            for tb in self._groups[group]:
                if tb == 'custom':
                    for bid, handler in self._groups[group][tb]:
                        if tool == bid:
                            return True
                elif tb.FindById(tool):
                    return True
        return False

    def ToolChanged(self, tool):
        """When any tool/button is pressed, other tools from group must be unchecked.

        :param tool: id of a tool/button
        """
        for group in self._toolsGroups[tool]:
            for tb in self._groups[group]:
                if tb == 'custom':
                    for btnId, handler in self._groups[group][tb]:
                        if btnId != tool:
                            handler(False)
                else:
                    for tl in self._groups[group][tb]:
                        if tb.FindById(tl):  # check if still exists
                            if tl != tool:
                                tb.ToggleTool(tl, False)
                            else:
                                tb.ToggleTool(tool, True)

        self.toggleToolChanged.emit(id=tool)
class ScanningPanel(wx.Panel):
    def __init__(self, parent, giface, settings):
        wx.Panel.__init__(self, parent)
        self.giface = giface
        self.settings = settings
        self.scan = self.settings['scan']

        self.settingsChanged = Signal('ScanningPanel.settingsChanged')

        if 'export' in self.settings and self.settings['export']['file']:
            path = self.settings['export']['file']
            initDir = os.path.dirname(path)
        else:
            path = initDir = ""

        mainSizer = wx.BoxSizer(wx.VERTICAL)
        hSizer = wx.BoxSizer(wx.HORIZONTAL)

        # create widgets
        self.scan_name_ctrl = wx.TextCtrl(self, value='scan')
        # widgets for model
        self.elevInput = Select(self, size=(-1, -1), type='raster')
        self.regionInput = Select(self, size=(-1, -1), type='region')
        self.zexag = wx.TextCtrl(self)
        self.numscans = wx.SpinCtrl(self, min=1, max=5, initial=1)
        self.rotate = wx.SpinCtrl(self, min=0, max=360, initial=180)
        self.smooth = wx.TextCtrl(self)
        self.resolution = wx.TextCtrl(self)
        self.trim = {}
        for each in 'tbnsew':
            self.trim[each] = wx.TextCtrl(self, size=(40, -1))
        self.trim_tolerance = wx.TextCtrl(self)
        self.interpolate = wx.CheckBox(self, label="Use interpolation instead of binning")
        self.equalize = wx.CheckBox(self, label="Use equalized color table for scan")
        self.ifExport = wx.CheckBox(self, label="")
        self.exportPLY = filebrowse.FileBrowseButton(self, labelText="Export PLY:", fileMode=wx.SAVE,
                                                     startDirectory=initDir, initialValue=path,
                                                     changeCallback=self.OnModelProperties)

        self.elevInput.SetValue(self.scan['elevation'])
        self.regionInput.SetValue(self.scan['region'])
        self.zexag.SetValue(str(self.scan['zexag']))
        self.rotate.SetValue(self.scan['rotation_angle'])
        self.numscans.SetValue(self.scan['numscans'])
        self.interpolate.SetValue(self.scan['interpolate'])
        for i, each in enumerate('nsewtb'):
            self.trim[each].SetValue(self.scan['trim_nsewtb'].split(',')[i])
        self.equalize.SetValue(self.scan['equalize'])
        self.smooth.SetValue(str(self.scan['smooth']))
        self.resolution.SetValue(str(self.scan['resolution']))
        self.trim_tolerance.SetValue(str(self.scan['trim_tolerance']))
        self.ifExport.SetValue(True if self.settings['export']['active'] else False)

        # layout
        mainSizer.Add(hSizer, flag=wx.EXPAND)
        hSizer = wx.BoxSizer(wx.HORIZONTAL)
        hSizer.Add(wx.StaticText(self, label="Name of scanned raster:"), flag=wx.ALL | wx.ALIGN_CENTER_VERTICAL, border=3)
        hSizer.Add(self.scan_name_ctrl, proportion=1, flag=wx.EXPAND | wx.ALL, border=5)
        mainSizer.Add(hSizer, flag=wx.EXPAND)
        # model parameters
        hSizer = wx.BoxSizer(wx.HORIZONTAL)
        hSizer.Add(wx.StaticText(self, label="Reference DEM:"), flag=wx.ALL | wx.ALIGN_CENTER_VERTICAL, border=3)
        hSizer.Add(self.elevInput, proportion=1, flag=wx.ALL | wx.ALIGN_CENTER_VERTICAL, border=3)
        mainSizer.Add(hSizer, flag=wx.EXPAND)
        # region
        hSizer = wx.BoxSizer(wx.HORIZONTAL)
        hSizer.Add(wx.StaticText(self, label="Reference region:"), flag=wx.ALL | wx.ALIGN_CENTER_VERTICAL, border=3)
        hSizer.Add(self.regionInput, proportion=1, flag=wx.ALL | wx.ALIGN_CENTER_VERTICAL, border=3)
        mainSizer.Add(hSizer, flag=wx.EXPAND)
        hSizer = wx.BoxSizer(wx.HORIZONTAL)
        hSizer.Add(wx.StaticText(self, label="Z-exaggeration:"), proportion=1, flag=wx.ALL | wx.ALIGN_CENTER_VERTICAL, border=3)
        hSizer.Add(self.zexag, flag=wx.ALL | wx.ALIGN_CENTER_VERTICAL, border=3)
        mainSizer.Add(hSizer, flag=wx.EXPAND)
        # number of scans
        hSizer = wx.BoxSizer(wx.HORIZONTAL)
        hSizer.Add(wx.StaticText(self, label="Number of scans:"), proportion=1, flag=wx.ALL | wx.ALIGN_CENTER_VERTICAL, border=3)
        hSizer.Add(self.numscans, flag=wx.ALL | wx.ALIGN_CENTER_VERTICAL, border=3)
        mainSizer.Add(hSizer, flag=wx.EXPAND)
        hSizer = wx.BoxSizer(wx.HORIZONTAL)
        hSizer.Add(wx.StaticText(self, label="Rotation angle:"), proportion=1, flag=wx.ALL | wx.ALIGN_CENTER_VERTICAL, border=3)
        hSizer.Add(self.rotate, flag=wx.ALL | wx.ALIGN_CENTER_VERTICAL, border=3)
        mainSizer.Add(hSizer, flag=wx.EXPAND)
        # smooth
        hSizer = wx.BoxSizer(wx.HORIZONTAL)
        hSizer.Add(wx.StaticText(self, label="Smooth value:"), proportion=1, flag=wx.ALL | wx.ALIGN_CENTER_VERTICAL, border=3)
        hSizer.Add(self.smooth, flag=wx.ALL | wx.ALIGN_CENTER_VERTICAL, border=3)
        mainSizer.Add(hSizer, flag=wx.EXPAND)
        # resolution
        hSizer = wx.BoxSizer(wx.HORIZONTAL)
        hSizer.Add(wx.StaticText(self, label="Resolution [mm]:"), proportion=1, flag=wx.ALL | wx.ALIGN_CENTER_VERTICAL, border=3)
        hSizer.Add(self.resolution, flag=wx.ALL | wx.ALIGN_CENTER_VERTICAL, border=3)
        mainSizer.Add(hSizer, flag=wx.EXPAND)
        hSizer = wx.BoxSizer(wx.HORIZONTAL)
        hSizer.Add(wx.StaticText(self, label="Limit scan vertically T, B [cm]:"), proportion=1, flag=wx.ALL | wx.ALIGN_CENTER_VERTICAL, border=3)
        for each in 'tb':
            hSizer.Add(self.trim[each], flag=wx.ALL | wx.ALIGN_CENTER_VERTICAL, border=3)
        mainSizer.Add(hSizer, flag=wx.EXPAND)
        hSizer = wx.BoxSizer(wx.HORIZONTAL)
        hSizer.Add(wx.StaticText(self, label="Trim scan N, S, E, W [cm]:"), proportion=1, flag=wx.ALL | wx.ALIGN_CENTER_VERTICAL, border=3)
        for each in 'nsew':
            hSizer.Add(self.trim[each], flag=wx.ALL | wx.ALIGN_CENTER_VERTICAL, border=3)
        mainSizer.Add(hSizer, flag=wx.EXPAND)
        hSizer = wx.BoxSizer(wx.HORIZONTAL)
        hSizer.Add(wx.StaticText(self, label="Trim tolerance [0-1]:"), proportion=1, flag=wx.ALL | wx.ALIGN_CENTER_VERTICAL, border=3)
        hSizer.Add(self.trim_tolerance, flag=wx.ALL | wx.ALIGN_CENTER_VERTICAL, border=3)
        mainSizer.Add(hSizer, flag=wx.EXPAND)
        hSizer = wx.BoxSizer(wx.HORIZONTAL)
        hSizer.Add(self.interpolate, flag=wx.ALL | wx.ALIGN_CENTER_VERTICAL, border=3)
        mainSizer.Add(hSizer, flag=wx.EXPAND)
        hSizer = wx.BoxSizer(wx.HORIZONTAL)
        hSizer.Add(self.equalize, flag=wx.ALL | wx.ALIGN_CENTER_VERTICAL, border=3)
        mainSizer.Add(hSizer, flag=wx.EXPAND)
        hSizer = wx.BoxSizer(wx.HORIZONTAL)
        hSizer.Add(self.ifExport, flag=wx.ALL | wx.ALIGN_CENTER_VERTICAL | wx.LEFT, border=3)
        hSizer.Add(self.exportPLY, flag=wx.ALL | wx.ALIGN_CENTER_VERTICAL, proportion=1, border=0)
        mainSizer.Add(hSizer, flag=wx.EXPAND)

        self.SetSizer(mainSizer)
        mainSizer.Fit(self)

        self.BindModelProperties()

    def BindModelProperties(self):
        self.scan_name_ctrl.Bind(wx.EVT_TEXT, self.OnModelProperties)
        # model parameters
        self.elevInput.Bind(wx.EVT_TEXT, self.OnModelProperties)
        self.regionInput.Bind(wx.EVT_TEXT, self.OnModelProperties)
        self.zexag.Bind(wx.EVT_TEXT, self.OnModelProperties)
        self.rotate.Bind(wx.EVT_SPINCTRL, self.OnModelProperties)
        self.rotate.Bind(wx.EVT_TEXT, self.OnModelProperties)
        self.numscans.Bind(wx.EVT_SPINCTRL, self.OnModelProperties)
        self.numscans.Bind(wx.EVT_TEXT, self.OnModelProperties)
        self.interpolate.Bind(wx.EVT_CHECKBOX, self.OnModelProperties)
        self.equalize.Bind(wx.EVT_CHECKBOX, self.OnModelProperties)
        self.smooth.Bind(wx.EVT_TEXT, self.OnModelProperties)
        self.resolution.Bind(wx.EVT_TEXT, self.OnModelProperties)
        self.trim_tolerance.Bind(wx.EVT_TEXT, self.OnModelProperties)
        for each in 'nsewtb':
            self.trim[each].Bind(wx.EVT_TEXT, self.OnModelProperties)
        self.ifExport.Bind(wx.EVT_CHECKBOX, self.OnModelProperties)

    def OnModelProperties(self, event):
        self.scan['scan_name'] = self.scan_name_ctrl.GetValue()
        self.scan['elevation'] = self.elevInput.GetValue()
        self.scan['region'] = self.regionInput.GetValue()
        self.scan['rotation_angle'] = self.rotate.GetValue()
        self.scan['numscans'] = self.numscans.GetValue()
        self.scan['interpolate'] = self.interpolate.IsChecked()
        self.scan['equalize'] = self.equalize.IsChecked()
        self.scan['smooth'] = self.smooth.GetValue()
        self.scan['resolution'] = self.resolution.GetValue()
        self.scan['trim_tolerance'] = self.trim_tolerance.GetValue()
        if 'export' not in self.settings:
            self.settings['export'] = {}
        self.settings['export']['active'] = self.ifExport.IsChecked()
        self.settings['export']['file'] = self.exportPLY.GetValue()

        try:
            self.scan['zexag'] = float(self.zexag.GetValue())
            nsewtb_list = []
            for each in 'nsewtb':
                nsewtb_list.append(self.trim[each].GetValue())
            self.scan['trim_nsewtb'] = ','.join(nsewtb_list)
        except ValueError:
            pass
        self.settingsChanged.emit()
Exemplo n.º 56
0
class RLiSetupMapPanel(wx.Panel):
    """Panel with mapwindow used in r.li.setup"""
    def __init__(self, parent, samplingType, icon=None, map_=None):
        wx.Panel.__init__(self, parent=parent)

        self.mapWindowProperties = MapWindowProperties()
        self.mapWindowProperties.setValuesFromUserSettings()
        giface = StandaloneGrassInterface()
        self.samplingtype = samplingType
        self.parent = parent

        if map_:
            self.map_ = map_
        else:
            self.map_ = Map()
        self.map_.region = self.map_.GetRegion()

        self._mgr = wx.aui.AuiManager(self)
        self.mapWindow = BufferedMapWindow(parent=self, giface=giface,
                                           Map=self.map_,
                                           properties=self.mapWindowProperties)
        self._mgr.AddPane(self.mapWindow, wx.aui.AuiPaneInfo().CentrePane().
                          Dockable(True).BestSize((-1, -1)).Name('mapwindow').
                          CloseButton(False).DestroyOnClose(True).
                          Layer(0))
        self._toolSwitcher = ToolSwitcher()
        self._toolSwitcher.toggleToolChanged.connect(self._onToolChanged)
        self.toolbar = RLiSetupToolbar(self, self._toolSwitcher)

        self.catId = 1

        self._mgr.AddPane(self.toolbar,
                          wx.aui.AuiPaneInfo().
                          Name("maptoolbar").Caption(_("Map Toolbar")).
                          ToolbarPane().Left().Name('mapToolbar').
                          CloseButton(False).Layer(1).Gripper(False).
                          BestSize((self.toolbar.GetBestSize())))
        self._mgr.Update()

        if self.samplingtype == SamplingType.REGIONS:
            self.afterRegionDrawn = Signal('RLiSetupMapPanel.afterRegionDrawn')
            self._registeredGraphics = self.mapWindow.RegisterGraphicsToDraw(graphicsType='line')
        elif self.samplingtype in [SamplingType.MUNITSR, SamplingType.MMVWINR]:
            self.sampleFrameChanged = Signal('RLiSetupMapPanel.sampleFrameChanged')
            self._registeredGraphics = self.mapWindow.RegisterGraphicsToDraw(graphicsType='rectangle')
        elif self.samplingtype in [SamplingType.MUNITSC, SamplingType.MMVWINC]:
            self.afterCircleDrawn = Signal('RLiSetupMapPanel.afterCircleDrawn')
            self._registeredGraphics = self.mapWindow.RegisterGraphicsToDraw(graphicsType='line')
        else:
            self.sampleFrameChanged = Signal('RLiSetupMapPanel.sampleFrameChanged')
            self._registeredGraphics = self.mapWindow.RegisterGraphicsToDraw(graphicsType='rectangle')

        self._registeredGraphics.AddPen('rlisetup', wx.Pen(wx.GREEN, width=2,
                                                           style=wx.SOLID))
        self._registeredGraphics.AddItem(coords=[[0, 0], [0, 0]],
                                         penName='rlisetup', hide=True)

        if self.samplingtype != SamplingType.VECT:
            self.toolbar.SelectDefault()

    def GetMap(self):
        return self.map_

    def OnPan(self, event):
        """Panning, set mouse to drag."""
        self.mapWindow.SetModePan()

    def OnZoomIn(self, event):
        """Zoom in the map."""
        self.mapWindow.SetModeZoomIn()

    def OnZoomOut(self, event):
        """Zoom out the map."""
        self.mapWindow.SetModeZoomOut()

    def OnZoomToMap(self, event):
        layers = self.map_.GetListOfLayers()
        self.mapWindow.ZoomToMap(layers=layers, ignoreNulls=False, render=True)

    def OnDrawRadius(self, event):
        """Start draw mode"""
        self.mapWindow.mouse['use'] = "None"
        self.mapWindow.mouse['box'] = "line"
        self.mapWindow.pen = wx.Pen(colour=wx.RED, width=1,
                                    style=wx.SHORT_DASH)
        self.mapWindow.SetNamedCursor('cross')
        self.mapWindow.mouseLeftUp.connect(self._radiusDrawn)

    def OnDigitizeRegion(self, event):
        """Start draw mode"""
        self.mapWindow.mouse['use'] = "None"
        self.mapWindow.mouse['box'] = "line"
        self.mapWindow.pen = wx.Pen(colour=wx.RED, width=1,
                                    style=wx.SHORT_DASH)
        self.mapWindow.SetNamedCursor('cross')
        self.mapWindow.mouseLeftUp.connect(self._lineSegmentDrawn)
        self.mapWindow.mouseDClick.connect(self._mouseDbClick)

        self._registeredGraphics.GetItem(0).SetCoords([])

    def OnDraw(self, event):
        """Start draw mode"""
        self.mapWindow.mouse['use'] = "None"
        self.mapWindow.mouse['box'] = "box"
        self.mapWindow.pen = wx.Pen(colour=wx.RED, width=2,
                                    style=wx.SHORT_DASH)
        self.mapWindow.SetNamedCursor('cross')
        self.mapWindow.mouseLeftUp.connect(self._rectangleDrawn)

    def _lineSegmentDrawn(self, x, y):
        item = self._registeredGraphics.GetItem(0)
        coords = item.GetCoords()
        if len(coords) == 0:
            coords.extend([self.mapWindow.Pixel2Cell(self.mapWindow.mouse['begin'])])
        coords.extend([[x, y]])

        item.SetCoords(coords)
        item.SetPropertyVal('hide', False)
        self.mapWindow.ClearLines()
        self._registeredGraphics.Draw(self.mapWindow.pdcTmp)

    def _mouseDbClick(self, x, y):
        item = self._registeredGraphics.GetItem(0)
        coords = item.GetCoords()
        coords.extend([[x, y]])
        item.SetCoords(coords)
        item.SetPropertyVal('hide', False)
        self.mapWindow.ClearLines()
        self._registeredGraphics.Draw(self.mapWindow.pdc)
        self.createRegion()

    def createRegion(self):
        dlg = wx.TextEntryDialog(None, 'Name of sample region',
                                 'Create region', 'region' + str(self.catId))
        ret = dlg.ShowModal()
        while True:
            if ret == wx.ID_OK:
                raster = dlg.GetValue()
                if checkMapExists(raster):
                    GMessage(parent=self, message=_("The raster file %s already"
                             " exists, please change name") % raster)
                    ret = dlg.ShowModal()
                else:
                    dlg.Destroy()
                    marea = self.writeArea(self._registeredGraphics.GetItem(0).GetCoords(), raster)
                    self.nextRegion(next=True, area=marea)
                    break
            else:
                self.nextRegion(next=False)
                break

    def nextRegion(self, next=True, area=None):
        self.mapWindow.ClearLines()
        item = self._registeredGraphics.GetItem(0)
        item.SetCoords([])
        item.SetPropertyVal('hide', True)

        layers = self.map_.GetListOfLayers()
        self.mapWindow.ZoomToMap(layers=layers, ignoreNulls=False, render=True)
        if next is True:
            self.afterRegionDrawn.emit(marea=area)
        else:
            gcmd.GMessage(parent=self.parent,
                          message=_("Raster map not created. Please redraw region."))

    def writeArea(self, coords, rasterName):
        polyfile = tempfile.NamedTemporaryFile(delete=False)
        polyfile.write("AREA\n")
        for coor in coords:
            east, north = coor
            point = " %s %s\n" % (east, north)
            polyfile.write(point)

        catbuf = "=%d a\n" % self.catId
        polyfile.write(catbuf)
        self.catId = self.catId + 1

        polyfile.close()
        region_settings = grass.parse_command('g.region', flags='p',
                                              delimiter=':')
        pname = polyfile.name.split('/')[-1]
        tmpraster = "rast_" + pname
        tmpvector = "vect_" + pname
        wx.BeginBusyCursor()
        wx.Yield()
        RunCommand('r.in.poly', input=polyfile.name, output=tmpraster,
                   rows=region_settings['rows'], overwrite=True)

        RunCommand('r.to.vect', input=tmpraster, output=tmpvector,
                   type='area', overwrite=True)

        RunCommand('v.to.rast', input=tmpvector, output=rasterName,
                   value=1, use='val')
        wx.EndBusyCursor()
        grass.use_temp_region()
        grass.run_command('g.region', vector=tmpvector)
        region = grass.region()

        marea = MaskedArea(region, rasterName)

        RunCommand('g.remove', flags='f', type='raster', name=tmpraster)
        RunCommand('g.remove', flags='f', type='vector', name=tmpvector)

        os.unlink(polyfile.name)
        return marea

    def _onToolChanged(self):
        """Helper function to disconnect drawing"""
        try:
            self.mapWindow.mouseLeftUp.disconnect(self._rectangleDrawn)
            self.mapWindow.mouseLeftUp.disconnect(self._radiusDrawn)
            self.mapWindow.mouseMoving.disconnect(self._mouseMoving)
            self.mapWindow.mouseLeftDown.disconnect(self._mouseLeftDown)
            self.mapWindow.mouseDClick.disconnect(self._mouseDbClick)
        except DispatcherKeyError:
            pass

    def _radiusDrawn(self, x, y):
        """When drawing finished, get region values"""
        mouse = self.mapWindow.mouse
        item = self._registeredGraphics.GetItem(0)
        p1 = mouse['begin']
        p2 = mouse['end']
        dist, (north, east) = self.mapWindow.Distance(p1, p2, False)
        circle = Circle(p1, dist)
        self.mapWindow.ClearLines()
        self.mapWindow.pdcTmp.SetBrush(wx.Brush(wx.CYAN, wx.TRANSPARENT))
        pen = wx.Pen(colour=wx.RED, width=2)
        self.mapWindow.pdcTmp.SetPen(pen)
        self.mapWindow.pdcTmp.DrawCircle(circle.point[0], circle.point[1],
                                         circle.radius)
        self._registeredGraphics.Draw(self.mapWindow.pdcTmp)
        self.createCricle(circle)

    def createCricle(self, c):
        dlg = wx.TextEntryDialog(None, 'Name of sample circle region',
                                 'Create circle region', 'circle' + str(self.catId))
        ret = dlg.ShowModal()
        while True:        
            if ret == wx.ID_OK:
                raster = dlg.GetValue()
                if checkMapExists(raster):
                    GMessage(parent=self, message=_("The raster file %s already"
                             " exists, please change name") % raster)
                    ret = dlg.ShowModal()
                else:
                    dlg.Destroy()
                    circle = self.writeCircle(c, raster)
                    self.nextCircle(next=True, circle=circle)
                    break
            else:
                self.nextCircle(next=False)
                break

    def nextCircle(self, next=True, circle=None):
        self.mapWindow.ClearLines()
        item = self._registeredGraphics.GetItem(0)
        item.SetPropertyVal('hide', True)
        layers = self.map_.GetListOfLayers()
        self.mapWindow.ZoomToMap(layers=layers, ignoreNulls=False, render=True)
        if next is True:
            self.afterCircleDrawn.emit(region=circle)
        else:
            gcmd.GMessage(parent=self.parent,
                          message=_("Raster map not created. redraw region again."))

    def writeCircle(self, circle, rasterName):
        coords = self.mapWindow.Pixel2Cell(circle.point)
        RunCommand('r.circle', output=rasterName, max=circle.radius,
                   coordinate=coords, flags="b")
        grass.use_temp_region()
        grass.run_command('g.region', zoom=rasterName)
        region = grass.region()
        marea = MaskedArea(region, rasterName, circle.radius)
        return marea

    def _rectangleDrawn(self):
        """When drawing finished, get region values"""
        mouse = self.mapWindow.mouse
        item = self._registeredGraphics.GetItem(0)
        p1 = self.mapWindow.Pixel2Cell(mouse['begin'])
        p2 = self.mapWindow.Pixel2Cell(mouse['end'])
        item.SetCoords([p1, p2])
        region = {'n': max(p1[1], p2[1]),
                  's': min(p1[1], p2[1]),
                  'w': min(p1[0], p2[0]),
                  'e': max(p1[0], p2[0])}
        item.SetPropertyVal('hide', False)
        self.mapWindow.ClearLines()
        self._registeredGraphics.Draw(self.mapWindow.pdcTmp)
        if self.samplingtype in [SamplingType.MUNITSR, SamplingType.MMVWINR]:
            dlg = wx.MessageDialog(self, "Is this area ok?",
                                   "select sampling unit",
                                   wx.YES_NO | wx.ICON_QUESTION)
            ret = dlg.ShowModal()
            if ret == wx.ID_YES:
                grass.use_temp_region()
                grass.run_command('g.region', n=region['n'], s=region['s'],
                                  e=region['e'], w=region['w'])
                tregion = grass.region()
                self.sampleFrameChanged.emit(region=tregion)
                self.mapWindow.ClearLines()
                item = self._registeredGraphics.GetItem(0)
                item.SetPropertyVal('hide', True)
                layers = self.map_.GetListOfLayers()
                self.mapWindow.ZoomToMap(layers=layers, ignoreNulls=False,
                                         render=True)
            else:
                self.nextRegion(next=False)
            dlg.Destroy()

        elif self.samplingtype != SamplingType.WHOLE:
            """When drawing finished, get region values"""
            self.sampleFrameChanged.emit(region=region)