Exemple #1
0
class PlotWidget(Widgets.WidgetBase):
    def __init__(self, plot, width=500, height=500):
        super(PlotWidget, self).__init__()

        self.widget = FigureCanvas(plot.get_figure())
        self.widget._resizeEvent = self.widget.resizeEvent
        self.widget.resizeEvent = self.resize_event
        self.plot = plot
        self.logger = plot.logger

    def set_plot(self, plot):
        self.plot = plot
        self.logger = plot.logger
        self.logger.debug("set_plot called")

    def configure_window(self, wd, ht):
        fig = self.plot.get_figure()
        fig.set_size_inches(float(wd) / fig.dpi, float(ht) / fig.dpi)

    def resize_event(self, event):
        rect = self.widget.geometry()
        x1, y1, x2, y2 = rect.getCoords()
        width = x2 - x1
        height = y2 - y1

        if width > 0 and height > 0:
            self.configure_window(width, height)
            self.widget._resizeEvent(event)
Exemple #2
0
class PlotWidget(Widgets.WidgetBase):

    def __init__(self, plot, width=500, height=500):
        super(PlotWidget, self).__init__()

        self.widget = FigureCanvas(plot.get_figure())
        self.widget._resizeEvent = self.widget.resizeEvent
        self.widget.resizeEvent = self.resize_event
        self.plot = plot
        self.logger = plot.logger

    def set_plot(self, plot):
        self.plot = plot
        self.logger = plot.logger
        self.logger.debug("set_plot called")

    def configure_window(self, wd, ht):
        fig = self.plot.get_figure()
        fig.set_size_inches(float(wd) / fig.dpi, float(ht) / fig.dpi)

    def resize_event(self, event):
        rect = self.widget.geometry()
        x1, y1, x2, y2 = rect.getCoords()
        width = x2 - x1
        height = y2 - y1

        if width > 0 and height > 0:
            self.configure_window(width, height)
            self.widget._resizeEvent(event)
Exemple #3
0
    def __init__(self, fig):
        super().__init__()
        _widget = QtWidgets.QWidget()
        self.setCentralWidget(_widget)
        layout = QtWidgets.QVBoxLayout(_widget)
        layout.setContentsMargins(0, 0, 0, 0)
        layout.setSpacing(0)

        canvas = FigureCanvas(fig)
        self.addToolBar(NavigationToolbar(canvas, self))
        scroll = QtWidgets.QScrollArea()
        scroll.setWidget(canvas)
        self._fig_dpi = canvas.geometry().width() / \
            fig.get_size_inches()[0]
        layout.addWidget(scroll)
class MatplotGraph(QMainWindow):
    def __init__(self, parent=None):
        super(MatplotGraph, self).__init__(parent)
        self.setAttribute(Qt.WA_DeleteOnClose)
        self.setWindowTitle("Matplot Graph")
        screen_x_pos, screen_y_pos, screen_width, screen_height = QDesktopWidget(
        ).geometry().getRect()
        self.window_width = screen_width * 0.5
        self.window_height = screen_height * 0.5
        self.resize(self.window_width, self.window_height)
        self.move((screen_width - self.window_width) / 2,
                  (screen_height - self.window_height) / 2)
        # Note that this winodw should be modaless
        #self.setWindowModality(Qt.ApplicationModaless)
        # Destructed when closed
        self.setAttribute(Qt.WA_DeleteOnClose)
        # Menu bar
        file_menu = self.menuBar().addMenu("File(&F)")
        resize_action = QAction("resize image", self)
        resize_action.triggered.connect(self.resizeImage)
        file_menu.addAction(resize_action)
        export_action = QAction("export image", self)
        export_action.triggered.connect(self.exportImage)
        file_menu.addAction(export_action)
        # Create the matplotlib widget
        self.plot_widget = FigureCanvas(Figure())
        self.setCentralWidget(self.plot_widget)
        self.plot_widget.setParent(self)
        # make the figure square
        cen_x_pos, cen_y_pos, cen_x_len, cen_y_len = self.centralWidget(
        ).geometry().getRect()
        ratio_tmp = (self.window_width - self.window_height) / (cen_x_len -
                                                                cen_y_len)
        self.window_width -= (cen_x_len - cen_y_len) / 2 * ratio_tmp
        self.window_height += (cen_x_len - cen_y_len) / 2 * ratio_tmp
        self.resize(self.window_width, self.window_height)
        self.move((screen_width - self.window_width) / 2,
                  (screen_height - self.window_height) / 2)
        #FigureCanvas.setSizePolicy(self.plot_widget, QSizePolicy.Expanding, QSizePolicy.Expanding)
        #FigureCanvas.updateGeometry(self.plot_widget)
        # Status Bar
        self.statusBar()

    def printMsg(self, msg_str):
        self.statusBar().showMessage(msg_str)

    def getPlotWidget(self):
        return self.plot_widget

    def show(self):
        self.plot_widget.draw()
        super(MatplotGraph, self).show()

    def resizeImage(self):
        main_x_pos, main_y_pox, main_x_len, main_y_len = self.geometry(
        ).getRect()
        fig_x_pos, fig_y_pos, fig_x_len, fig_y_len = self.plot_widget.geometry(
        ).getRect()
        get_size_dialog = ImageSizeDialog(self, fig_x_len, fig_y_len)
        if (get_size_dialog.exec()):
            fig_x, fig_y = get_size_dialog.getXYText()
            try:
                fig_x = int(fig_x)
                fig_y = int(fig_y)
            except ValueError:
                self.printMsg("Should input integer.")
            else:
                if (fig_x != 0 and fig_y != 0):
                    # set size of figure
                    self.setGeometry(main_x_pos, main_y_pox,
                                     main_x_len + (fig_x - fig_x_len),
                                     main_y_len + (fig_y - fig_y_len))
                    self.plot_widget.setGeometry(fig_x_pos, fig_y_pos, fig_x,
                                                 fig_y)

    def exportImage(self):
        file_path, file_type = QFileDialog.getSaveFileName(
            self, "Output Figure", "./",
            "PNG Image Files (*.png);;All Files (*)")
        if file_path != '':
            self.plot_widget.figure.savefig(file_path)
Exemple #5
0
class MapWidget(QtWidgets.QWidget):
    """
    Widget with the background map

    This widget works based on a WMTS, which contains background tiles
    within and around the axes limits. Each action can update the bounding
    box, and thus the tile set.
    """

    element_dct = {
        'harborarea': {
            'kwargs': {'facecolor': 'C0', 'alpha': 0.4, 'edgecolor': 'C0'},
            'handle': 'patch',
            'label': 'haventerrein'
        },
        'breakwaters': {
            'kwargs': {'colors': 'C1', 'linewidths': 2, 'alpha': 1.0},
            'handle': 'element',
            'label': 'havendam(men)'
        },
        'flooddefence': {
            'kwargs': {'colors': 'C3', 'linewidths': 2, 'alpha': 1.0},
            'handle': 'element',
            'label': 'waterkering'
        },
        'support_locations': {
            'kwargs': {'color': '0.1', 'marker': 'x', 'ms': 6, 'mew': 2, 'alpha': 1.0},
            'handle': 'element',
            'label': 'HRD-locaties'
        },
        'result_locations': {
            'kwargs': {'color': '0.1', 'marker': 'o', 'ms': 2, 'alpha': 1.0},
            'handle': 'element',
            'label': 'uitvoerlocaties'
        },
        'entrance': {
            'kwargs': {'lw': 1.5, 'color': '0.1', 'dashes': (2,2), 'alpha': 1.0},
            'handle': 'element',
            'label': 'haveningang'
        },
        'inner': {
            'kwargs': {'color': 'grey', 'alpha': 0.3},
            'handle': 'patch',
            'label': 'binnengaats gebied'
        },
        'support_location': {
            'kwargs': {'color': '0.1', 'marker': 'o', 'ms': 10, 'alpha': 1.0, 'mfc': 'none'},
            'handle': 'element',
            'label': 'steunpuntlocatie'
        }
    }

    def __init__(self, mainmodel, maintab):
        """
        Constructor of the tab
        """

        # Create child class
        QtWidgets.QWidget.__init__(self)

        self.landboundary = io.geometry.read_landboundary()
            
        # Dictionaries for legend and toggling items
        self.plot_elements = {}
        
        self.bbox = (0., 300000., 280000., 620000.)
        self.labels = {}
        self.legend = InteractiveLegend(self, self.plot_elements)

        self.mainmodel = mainmodel
        self.main = maintab
        self.schematisation = mainmodel.schematisation
        self.init_widget()
        self.load_project()

        self.update_tiles_thread = threads.UpdateTilesThread(self)


    def init_widget(self):
        # Aangepast Svasek 31/10/2018 - Ander gebruik van figure, waardoor er in Spyder geen extra figuur opent
        # Create figure
        self.figure = Figure(figsize=(self.geometry().width() / 100., self.geometry().height() / 100.))

        # Add canvas
        self.canvas = FigureCanvasQTAgg(self.figure)
        self.canvas.mpl_connect('pick_event', self.legend._onpick)

        # this is the Navigation widget
        # it takes the Canvas widget and a parent
        self.toolbar = widgets.CustomNavigationToolbar(canvas=self.canvas, widget=self, update_func=self._update_background)

        # Add checkbox for showing labels
        hbox = QtWidgets.QHBoxLayout()
        self.label_checkbox = QtWidgets.QCheckBox('Laat labels zien')
        self.label_checkbox.setChecked(False)
        self.label_checkbox.stateChanged.connect(self.show_labels)
        hbox.addWidget(self.label_checkbox, 0, QtCore.Qt.AlignLeft)

        # Create WMTS selection
        label = QtWidgets.QLabel('Achtergrondkaartlaag:')
        hbox.addWidget(label, 0, QtCore.Qt.AlignRight)
        self.wmts_combobox = QtWidgets.QComboBox()

        self.comboboxitems = [
            ('Geen achtergrond', '', ''),
            ('OpenTopo Achtergrondkaart', 'layer.png', 'opentopoachtergrondkaart'),
            ('Luchtfoto PDOK', 'layer.png', '2017_ortho25'),
            ('AHN2 5m DTM', 'layer.png', 'ahn2_05m_ruw'),
            ('BRT achtergrondkaart Grijs', 'layer.png', 'brtachtergrondkaartgrijs')
        ]

        # Get path to icon data
        for text, iconname, userdata in self.comboboxitems:
            # Get path to icon
            iconpath = os.path.join(self.mainmodel.datadir,'icons',iconname)
            # Add item to combobox with icon
            self.wmts_combobox.addItem(QtGui.QIcon(iconpath), text, userdata)

        self.wmts_combobox.setCurrentIndex(0)
        hbox.addWidget(self.wmts_combobox)
        self.wmts_combobox.currentIndexChanged.connect(self._set_WMTS_layer)

        # create an axis
        self.ax = self.figure.add_axes([0, 0, 1, 1])
        self.bbox = self._get_filled_bbox(self.bbox)
        self.ax.set_xlim(self.bbox[0], self.bbox[2])
        self.ax.set_ylim(self.bbox[1], self.bbox[3])

        # Add WMTS
        self.WMTS = None
        self._set_WMTS_layer(0)

        self.landboundary, = self.ax.plot(*self.landboundary, color='0.8', zorder=-20, lw=0.75)
        if self.WMTSlayer == '':
            self.landboundary.set_visible(True)
        self.ax.axis('off')

        layout = QtWidgets.QVBoxLayout()
        layout.addWidget(self.toolbar)
        layout.addWidget(self.canvas)
        layout.addLayout(hbox)

        self.setLayout(layout)

    
    def load_project(self):
        """
        Set project elements visible
        """
        if not self.schematisation.result_locations.empty:
            self.set_visible('result_locations')
        if not self.schematisation.harborarea.empty:
            self.set_visible('harborarea')
        if not self.schematisation.breakwaters.empty:
            self.set_visible('breakwaters')
            if len(self.schematisation.breakwaters) == 2 or hasattr(self.schematisation, 'entrance_coordinate'):
                self.set_visible('inner')
                self.set_visible('entrance')
        if not self.schematisation.flooddefence.empty:
            self.set_visible('flooddefence')
        if not self.schematisation.support_locations.empty:
            self.set_visible('support_locations')
        if not pd.isnull(self.schematisation.support_location).all():
            self.set_visible('support_location')

    def show_labels(self, state=QtCore.Qt.Checked):
        """
        Update labels on canvas

        Parameters
        ----------
        state : QtCore.Qt.[state]
            State of the checkbox
        """
        if state == QtCore.Qt.Checked:
            # Both result and support locations
            for locations, column in zip(['result_locations', 'support_locations'], ['Naam', 'Name']):
                # If not in visible elements, continue
                if locations not in self.plot_elements.keys():
                    continue
                
                # Plot each label
                for row in getattr(self.schematisation, locations).itertuples():
                    labelkey = locations[0] + getattr(row, column)
                    if labelkey not in self.labels.keys():
                        self.labels[labelkey] = self.ax.text(*row.geometry.coords[0], s=getattr(row, column), ha='left', va='bottom')

        else:
            # Remove all labels
            for key, label in self.labels.items():
                label.remove()
            self.labels.clear()
                
        self.canvas.draw()


    def _set_WMTS_layer(self, idx):
        """
        Change the WMTS layer
        """
        try:
            activated_idx = idx
            if idx == -1:
                return None
    
            item = self.comboboxitems[idx]
            if not item:
                return None
    
            matched_idx = self.wmts_combobox.findData(item[2])
            assert activated_idx == matched_idx
    
            # Get layer name from combobox
            self.WMTSlayer = item[2]
    
            # Remove the present WMTS
            if self.WMTS is not None:
                self.WMTS.clean_all()
                self.WMTS = None
                self.landboundary.set_visible(True)
    
            # Skip if no background
            if self.WMTSlayer != '':
                self.landboundary.set_visible(False)
                # Add Web Mapping Tile Service
                self.WMTS = WMTS(
                    'http://geodata.nationaalgeoregister.nl/tiles/service',
                    'EPSG:28992',
                    self.WMTSlayer
                )
    
            self._update_background(None)
        except Exception as e:
            print(e)

    def _get_filled_bbox(self, limits):
        # Get canvas size
        geo = self.canvas.geometry()
        
        canvas_width = (geo.width()) / 100.
        canvas_height = (geo.height()) / 100.
        canvas_ratio = max(1, canvas_width) / max(1, canvas_height)

        # Get axis size
        left, lower, right, upper = limits

        ax_width = right - left
        ax_height = upper - lower
        ax_ratio = (ax_width / ax_height)

        # Als de assen te hoog zijn, corrigeer de breedte
        if canvas_ratio > ax_ratio:
            ax_width = ax_width / (ax_ratio / canvas_ratio)
            center = 0.5 * (left + right)
            left = center - 0.5 * ax_width
            right = center + 0.5 * ax_width

        # Als de assen te breed zijn
        elif canvas_ratio < ax_ratio:
            ax_height = ax_height / (canvas_ratio / ax_ratio)
            center = 0.5 * (upper + lower)
            lower = center - 0.5 * ax_height
            upper = center + 0.5 * ax_height

        # Return bbox
        return (left, lower, right, upper)

    def _update_background(self, event, limits=None):
        """
        Update the bounding box
        """
        # Get the limits of the frame for the bouding box
        if not limits:
            limits = (self.ax.get_xlim()[0], self.ax.get_ylim()[0], self.ax.get_xlim()[1], self.ax.get_ylim()[1])
        self.bbox = self._get_filled_bbox(limits)
        # Reset the axis limits
        self.ax.set_xlim(self.bbox[0], self.bbox[2])
        self.ax.set_ylim(self.bbox[1], self.bbox[3])

        # Scale markers in plotter
        self.main.plotter.set_location_values()
        self.canvas.draw_idle()

        # Skip if no layer
        if self.WMTSlayer == '':
            return None

        # Check if the thread is still running from an older background update
        if self.update_tiles_thread.isRunning():
            self.update_tiles_thread.quit()
            self.update_tiles_thread.wait()
            logger.debug('Tile update thread interrupted.')

        # Start the thread
        self.update_tiles_thread.start()

    def remove_plot_element(self, element):
        """
        Remove plotted element from view

        Parameters
        ----------
        element : str
            element that is removed
        """
        # Remove element if present
        if element in self.plot_elements.keys():
            self.plot_elements[element].remove()
            del self.plot_elements[element]
            self.legend.remove(element)
            
    def set_visible(self, element, ax=None):
        """
        Plot a given element

        Parameters
        ----------
        element : str
            element to be plotted
        """
        
        if ax is None:
            ax = self.ax

        # Remove the element if it is already plotted
        self.remove_plot_element(element)

        # Get plotting properties
        plot_props = self.element_dct[element]
        kwargs = plot_props['kwargs']

        # If element is geodataframe
        geometry = getattr(self.schematisation, element)
        if isinstance(geometry, (gpd.GeoDataFrame, gpd.GeoSeries)):
            if geometry.empty:
                return None
            self.plot_elements[element] = self._plot_gdf(gdf=geometry, kwargs=kwargs, ax=ax)
        elif isinstance(geometry, LineString):
            self.plot_elements[element], = ax.plot(*np.vstack(geometry.coords[:]).T, **kwargs)
        elif isinstance(geometry, (Polygon, MultiPolygon)):
            self.plot_elements[element] = ax.add_collection(PatchCollection([PolygonPatch(geometry)], **kwargs))
        else:
            raise TypeError('Geometry type not recognized.')

        # Add legend handles
        if plot_props['handle'] == 'patch':
            self.legend.add_item(element, handle=PolygonPatch(Polygon([(0,0), (0,1), (1,1), (1,0)]), **kwargs), label=plot_props['label'])
        else:
            self.legend.add_item(element, handle=self.plot_elements[element], label=plot_props['label'])

        # Update legend
        self.legend._update_legend()
        # Update labels
        if self.label_checkbox.isChecked() and element in ['support_locations', 'result_locations']:
            self.show_labels()
        self.canvas.draw_idle()


    def _plot_gdf(self, gdf, kwargs={}, ax=None):
        """
        Plot geodataframe to axis
        This function will plot a default style for all geometry types.

        Paramaters
        ----------
        gdf : geopandas.GeoDataFrame
            geodataframe with data to plot
        kwargs : dictionary
            dictionary with keyword arguments for plotting
        ax : matplotlib.pyplot.axes
            axes to plot to
        """  
        if ax is None:
            ax = self.ax

        # get geometries
        if isinstance(gdf, gpd.GeoSeries):
            geometries = [gdf['geometry']]
        else:
            geometries = gdf['geometry'].values.tolist()

        # for line and pts
        if isinstance(geometries[0], LineString):
            # Create collection
            segments = [np.vstack(geo.coords[:]) for geo in geometries]
            collection = ax.add_collection(LineCollection(segments, **kwargs))

        if isinstance(geometries[0], Polygon):
            # Create collection
            polygons = [PolygonPatch(geo) for geo in geometries]
            collection = ax.add_collection(PatchCollection(polygons, **kwargs))

        if isinstance(geometries[0], Point):
            kwargs['linestyle'] = ''
            crds = np.vstack([pt.coords[0] for pt in geometries])
            collection, = ax.plot(*crds.T, **kwargs)

        return collection