class Dialog(QtGui.QWidget, Ui_DialogBase):

    def __init__(self):
        QtGui.QWidget.__init__(self)
        self.setupUi(self)
        self.widget = QgsMapCanvas()
        self.show()

    def on_pushButton_clicked(self):
        """Wow - an autoconnected slot!"""
	print 'Click!'
        myPath = os.path.join(
            os.path.dirname(__file__),
            'landsat.tif')
        print myPath
        layer = QgsRasterLayer(myPath, 'A Layer')
        QgsMapLayerRegistry.instance().addMapLayers([layer])
        layer.setGrayBandName(layer.bandName(1))
        layer.setDrawingStyle(QgsRasterLayer.SingleBandPseudoColor)
        layer.setColorShadingAlgorithm(QgsRasterLayer.PseudoColorShader)
        layer.saveDefaultStyle() 
        self.widget.zoomToFullExtent()
        print self.widget.extent().toString()
        print layer.extent().toString()
	self.widget.refresh()
예제 #2
0
    def __init__(self,
                 canvas: QgsMapCanvas,
                 crs: QgsCoordinateReferenceSystem,
                 parent=None):
        QDialog.__init__(self, parent)
        self.setupUi(self)

        extent_gb: QgsExtentGroupBox = self.gb_extent
        extent_gb.setOriginalExtent(canvas.extent(), crs)
        extent_gb.setCurrentExtent(canvas.extent(), crs)
        extent_gb.setOutputCrs(QgsCoordinateReferenceSystem('EPSG:4326'))
예제 #3
0
    def __init__(
        self,
        canvas: QgsMapCanvas,
        crs: QgsCoordinateReferenceSystem,
        parent: Optional[QWidget] = None,
    ) -> None:
        QDialog.__init__(self, parent)
        self.setupUi(self)

        extent_gb: QgsExtentGroupBox = self.gb_extent
        extent_gb.setOriginalExtent(canvas.extent(), crs)
        extent_gb.setCurrentExtent(canvas.extent(), crs)
        extent_gb.setOutputCrs(QgsCoordinateReferenceSystem("EPSG:4326"))
예제 #4
0
def get_bounding_box(canvas: QgsMapCanvas, crs="EPSG:4326"):
    """
    Get the geometry of the bbox in WGS84

    @rtype: QGsRectangle in WGS84
    @return: the extent of the map canvas
    """
    radioButton_extentMapCanvas = True
    # get extend of mapcanvas
    if radioButton_extentMapCanvas:
        geom_extent = canvas.extent()
        if hasattr(canvas, "mapSettings"):
            source_crs = canvas.mapSettings().destinationCrs()
        else:
            source_crs = canvas.mapRenderer().destinationCrs()
    # get extend of layer
    else:
        # layer = self.comboBox_extentLayer.currentLayer()
        # geom_extent = layer.extent()
        # source_crs = layer.crs()
        pass
    geom_extent = QgsGeometry.fromRect(geom_extent)
    dest_crs = QgsCoordinateReferenceSystem(crs)
    crs_transform = QgsCoordinateTransform(source_crs, dest_crs,
                                           QgsProject.instance())
    geom_extent.transform(crs_transform)
    return geom_extent.boundingBox()
예제 #5
0
def make_pdf():
    canvas = QgsMapCanvas()
    # Load our project
    QgsProject.instance().read(QFileInfo(project_path))
    bridge = QgsLayerTreeMapCanvasBridge(QgsProject.instance().layerTreeRoot(),
                                         canvas)
    bridge.setCanvasLayers()

    template_file = file(template_path)
    template_content = template_file.read()
    template_file.close()
    document = QDomDocument()
    document.setContent(template_content)
    composition = QgsComposition(canvas.mapSettings())
    # You can use this to replace any string like this [key]
    # in the template with a new value. e.g. to replace
    # [date] pass a map like this {'date': '1 Jan 2012'}
    substitution_map = {'DATE_TIME_START': 'foo', 'DATE_TIME_END': 'bar'}
    composition.loadFromTemplate(document, substitution_map)
    # You must set the id in the template
    map_item = composition.getComposerItemById('map')
    map_item.setMapCanvas(canvas)
    map_item.zoomToExtent(canvas.extent())
    # You must set the id in the template
    legend_item = composition.getComposerItemById('legend')
    legend_item.updateLegend()
    composition.refreshItems()
    composition.exportAsPDF('report.pdf')
    QgsProject.instance().clear()
예제 #6
0
def make_pdf():
    canvas = QgsMapCanvas()
    # Load our project
    QgsProject.instance().read(QFileInfo(project_path))
    bridge = QgsLayerTreeMapCanvasBridge(
        QgsProject.instance().layerTreeRoot(), canvas)
    bridge.setCanvasLayers()
    if canvas.layerCount() < 1:
        print 'No layers loaded from this project, exiting.'
        return
    print canvas.mapSettings().extent().toString()
    template_file = file(template_path)
    template_content = template_file.read()
    template_file.close()
    document = QDomDocument()
    document.setContent(template_content)
    composition = QgsComposition(canvas.mapSettings())
    # You can use this to replace any string like this [key]
    # in the template with a new value. e.g. to replace
    # [date] pass a map like this {'date': '1 Jan 2012'}
    substitution_map = {
        'DATE_TIME_START': TIME_START,
        'DATE_TIME_END': TIME_STOP}
    composition.loadFromTemplate(document, substitution_map)
    # You must set the id in the template
    map_item = composition.getComposerItemById('map')
    map_item.setMapCanvas(canvas)
    map_item.zoomToExtent(canvas.extent())
    # You must set the id in the template
    legend_item = composition.getComposerItemById('legend')
    legend_item.updateLegend()
    composition.refreshItems()
    composition.exportAsPDF(
        '/home/web/reports/pdf/%s/%s.pdf' % (TIME_SLICE, LABEL))
    QgsProject.instance().clear()
예제 #7
0
def get_canvas_center(canvas: QgsMapCanvas) -> QgsPointXY:
    """ Get canvas center in supported spatial reference system """
    extent: QgsRectangle = canvas.extent()
    center = extent.center()
    # noinspection PyArgumentList
    transformer = QgsCoordinateTransform(canvas.mapSettings().destinationCrs(),
                                         UNFOLDED_CRS, QgsProject.instance())
    return transformer.transform(center)
예제 #8
0
def zoom_out_to_layer(canvas: QgsMapCanvas, layer: QgsVectorLayer) -> None:
    settings = canvas.mapSettings() # type: QgsMapSettings
    new_extent = settings.layerExtentToOutputExtent(layer, layer.extent()) # type: QgsRectangle
    new_extent.scale(1.05)

    old_extent = canvas.extent() # type: QgsRectangle

    if old_extent.contains(new_extent):
        return

    canvas.setExtent(new_extent)
    canvas.refresh()
예제 #9
0
    def testDefaultViewExtentWithCanvas(self):
        p = QgsProject()
        p.setCrs(QgsCoordinateReferenceSystem('EPSG:3857'))

        canvas = QgsMapCanvas()
        canvas.setDestinationCrs(QgsCoordinateReferenceSystem('EPSG:4326'))
        canvas.setFrameStyle(0)
        canvas.resize(600, 400)
        self.assertEqual(canvas.width(), 600)
        self.assertEqual(canvas.height(), 400)
        canvas.setExtent(QgsRectangle(10, 30, 20, 35))
        canvas.show()

        tmpDir = QTemporaryDir()
        tmpFile = "{}/project.qgz".format(tmpDir.path())
        self.assertTrue(p.write(tmpFile))

        QgsProject.instance().read(tmpFile)

        # no default view, extent should not change
        self.assertAlmostEqual(canvas.extent().xMinimum(), 10, 3)
        self.assertAlmostEqual(canvas.extent().yMinimum(), 29.16666, 3)
        self.assertAlmostEqual(canvas.extent().xMaximum(), 20, 3)
        self.assertAlmostEqual(canvas.extent().yMaximum(), 35.833333333, 3)
        self.assertEqual(canvas.mapSettings().destinationCrs().authid(),
                         'EPSG:4326')

        p.viewSettings().setDefaultViewExtent(
            QgsReferencedRectangle(QgsRectangle(1000, 2000, 1500, 2500),
                                   QgsCoordinateReferenceSystem('EPSG:3857')))

        self.assertTrue(p.write(tmpFile))
        QgsProject.instance().read(tmpFile)

        self.assertAlmostEqual(canvas.extent().xMinimum(), 0.0078602, 3)
        self.assertAlmostEqual(canvas.extent().yMinimum(), 0.017966, 3)
        self.assertAlmostEqual(canvas.extent().xMaximum(), 0.01459762, 3)
        self.assertAlmostEqual(canvas.extent().yMaximum(), 0.02245788, 3)
        self.assertEqual(canvas.mapSettings().destinationCrs().authid(),
                         'EPSG:4326')
예제 #10
0
def make_pdf(xmin, ymin, xmax, ymax, filter_expression):
    canvas = QgsMapCanvas()
    # Load our project
    QgsProject.instance().read(QFileInfo(project_path))

    # Set canvas extent
    canvas.setExtent(QgsRectangle(xmin, ymin, xmax, ymax))

    # Load layers here if they are not already in the project
    for layer in QgsMapLayerRegistry.instance().mapLayers().values():
        if layer.name() == 'data':
            lyr = layer
            break
    lyr.setSubsetString(filter_expression)

    # bridge used in standalone script: http://docs.qgis.org/testing/en/docs/pyqgis_developer_cookbook/loadproject.html
    bridge = QgsLayerTreeMapCanvasBridge(QgsProject.instance().layerTreeRoot(),
                                         canvas)
    bridge.setCanvasLayers()

    # Check data providers load ok
    print('Provider List')
    print(QgsProviderRegistry.instance().providerList())

    # Print layer validity - I've had some trouble with some wms layers
    for layer in QgsMapLayerRegistry.instance().mapLayers().values():
        print(layer.name())
        print(layer.isValid())

    template_file = file(template_path)
    template_content = template_file.read()
    template_file.close()
    document = QDomDocument()
    document.setContent(template_content)
    composition = QgsComposition(canvas.mapSettings())
    # You can use this to replace any string like this [key]
    # in the template with a new value. e.g. to replace
    # [date] pass a map like this {'date': '1 Jan 2012'}
    substitution_map = {'title': 'the title of my map'}
    composition.loadFromTemplate(document, substitution_map)
    # You must set the id in the template
    map_item = composition.getComposerItemById('Main Map')
    map_item.setMapCanvas(canvas)
    map_item.zoomToExtent(canvas.extent())
    # You must set the id in the template
    legend_item = composition.getComposerItemById('Legend')
    legend_item.updateLegend()
    composition.refreshItems()
    composition.exportAsPDF('export.pdf')
    QgsProject.instance().clear()
예제 #11
0
class WidgetResult(Ui_widgetResult, QWidget):
    """
    Widget (Panel) for result review
    """
    ''' buffer around clicked point for point in polygon query '''
    SEARCH_BUFFER = 20.0
    ''' supported export formats '''
    EXPORT_FORMATS = {
        get_ui_string("app.extension.shapefile"):
        ExportTypes.Shapefile,
        #get_ui_string("app.extension.kml"):ExportTypes.KML,
        #get_ui_string("app.extension.nrml"):ExportTypes.NRML,
        get_ui_string("app.extension.csv"):
        ExportTypes.CSV,
    }
    ''' enumeration of Layer to be previewed '''
    EXPOSURE, SURVEY, POP_GRID, FOOTPRINT, ZONES = range(5)
    ''' name for Layer to be previewed '''
    LAYER_NAMES = [
        get_ui_string("widget.result.layer.exposure"),
        get_ui_string("widget.result.layer.survey"),
        get_ui_string("widget.result.layer.popgrid"),
        get_ui_string("widget.result.layer.footprint"),
        get_ui_string("widget.result.layer.zones"),
    ]
    LAYER_STYLES = [
        '<!DOCTYPE renderer><renderer-v2 symbollevels="0" type="singleSymbol"><symbols><symbol outputUnit="MM" alpha="1" type="fill" name="0"><layer pass="******" class="SimpleLine" locked="0"><prop k="capstyle" v="square"/><prop k="color" v="0,0,0,255"/><prop k="customdash" v="5;2"/><prop k="joinstyle" v="bevel"/><prop k="offset" v="0"/><prop k="penstyle" v="solid"/><prop k="use_custom_dash" v="0"/><prop k="width" v="0.26"/></layer></symbol></symbols><rotation field=""/><sizescale field=""/></renderer-v2>',
        '<!DOCTYPE renderer><renderer-v2 symbollevels="0" type="singleSymbol"><symbols><symbol outputUnit="MM" alpha="1" type="marker" name="0"><layer pass="******" class="SimpleMarker" locked="0"><prop k="angle" v="0"/><prop k="color" v="0,0,255,255"/><prop k="color_border" v="0,0,255,255"/><prop k="name" v="circle"/><prop k="offset" v="0,0"/><prop k="size" v="2"/></layer></symbol></symbols><rotation field=""/><sizescale field=""/></renderer-v2>',
        '<!DOCTYPE renderer><renderer-v2 symbollevels="0" type="singleSymbol"><symbols><symbol outputUnit="MM" alpha="1" type="marker" name="0"><layer pass="******" class="SimpleMarker" locked="0"><prop k="angle" v="0"/><prop k="color" v="0,255,0,255"/><prop k="color_border" v="0,255,0,255"/><prop k="name" v="rectangle"/><prop k="offset" v="0,0"/><prop k="size" v="4"/></layer></symbol></symbols><rotation field=""/><sizescale field=""/></renderer-v2>',
        '<!DOCTYPE renderer><renderer-v2 symbollevels="0" type="singleSymbol"><symbols><symbol outputUnit="MM" alpha="1" type="fill" name="0"><layer pass="******" class="SimpleFill" locked="0"><prop k="color" v="170,250,170,255"/><prop k="color_border" v="0,0,0,255"/><prop k="offset" v="0,0"/><prop k="style" v="solid"/><prop k="style_border" v="solid"/><prop k="width_border" v="0.26"/></layer></symbol></symbols><rotation field=""/><sizescale field=""/></renderer-v2>',
        '<!DOCTYPE renderer><renderer-v2 symbollevels="0" type="singleSymbol"><symbols><symbol outputUnit="MM" alpha="1" type="fill" name="0"><layer pass="******" class="SimpleFill" locked="0"><prop k="color" v="211,211,158,200"/><prop k="color_border" v="0,0,0,255"/><prop k="offset" v="0,0"/><prop k="style" v="solid"/><prop k="style_border" v="solid"/><prop k="width_border" v="0.26"/></layer></symbol></symbols><rotation field=""/><sizescale field=""/></renderer-v2>',
    ]

    # constructor / destructor
    ###############################

    def __init__(self, app):
        """
        constructor
        - initialize UI elements
        - connect UI elements to callback            
        """
        super(WidgetResult, self).__init__()
        self.ui = Ui_widgetResult()
        self.ui.setupUi(self)

        # create canvas
        self.canvas = QgsMapCanvas(self.ui.widget_map)
        self.canvas.setGeometry(
            0,  # x
            self.ui.widget_map_menu_l.x() +
            self.ui.widget_map_menu_l.height(),  # y  
            self.ui.widget_map.width() - 2 * UI_PADDING,  # width
            self.ui.widget_map.width() - 2 * UI_PADDING  # height
        )

        self.canvas.setCanvasColor(Qt.white)
        self.canvas.enableAntiAliasing(True)
        self.canvas.mapRenderer().setProjectionsEnabled(True)
        self.canvas.mapRenderer().setDestinationCrs(
            QgsCoordinateReferenceSystem(
                4326, QgsCoordinateReferenceSystem.PostgisCrsId))
        self.canvas.zoomNextStatusChanged.connect(self.checkRendering)
        self.canvas.xyCoordinates.connect(self.currentLocation)
        self.registry = QgsMapLayerRegistry.instance()

        self.map_layers = [None] * len(self.LAYER_NAMES)
        self.map_layer_renderer = [None] * len(self.LAYER_NAMES)
        for idx, str_style in enumerate(self.LAYER_STYLES):
            rdoc = QDomDocument("renderer")
            rdoc.setContent(str_style)
            self.map_layer_renderer[idx] = QgsFeatureRendererV2.load(
                rdoc.firstChild().toElement())

        # populate export list
        self.ui.cb_export_format.clear()
        for export_format in self.EXPORT_FORMATS.keys():
            self.ui.cb_export_format.addItem(export_format)

        # style object required for QgsRendererV2PropertiesDialog
        self.style = QgsStyleV2()

        # create the map tools
        self.toolPan = QgsMapToolPan(self.canvas)
        self.toolZoomIn = QgsMapToolZoom(self.canvas, False)  # false = in
        self.toolZoomOut = QgsMapToolZoom(self.canvas, True)  # true = out
        self.toolInfo = QgsMapToolEmitPoint(self.canvas)
        self.toolInfo.canvasClicked.connect(self.showInfo)
        self.canvas.setMapTool(self.toolPan)

        # additional
        self.dlgResultDetail = DialogResult()
        self.dlgResultDetail.setModal(True)

        # set link to application main controller
        self.app = app

        # reset project
        self._project = None

        # default export setting
        self.export_format = ExportTypes.Shapefile

        # connect slots (ui event)
        self.ui.btn_zoom_full.clicked.connect(self.mapZoomFull)
        self.ui.btn_zoom_in.clicked.connect(self.mapZoomIn)
        self.ui.btn_zoom_out.clicked.connect(self.mapZoomOut)
        self.ui.btn_stop.clicked.connect(self.stopRendering)
        self.ui.btn_zoom_layer.clicked.connect(self.mapZoomLayer)
        self.ui.btn_pan.clicked.connect(self.mapPan)
        self.ui.btn_theme.clicked.connect(self.mapEditTheme)
        self.ui.btn_info.clicked.connect(self.mapIdentify)

        self.ui.btn_zoom_to_feature.clicked.connect(self.searchFeature)

        self.ui.cb_export_format.currentIndexChanged[str].connect(
            self.exportFormatChanged)
        self.ui.btn_export.clicked.connect(self.exportData)
        self.ui.btn_export_select_path.clicked.connect(self.selectExportFile)

    @pyqtSlot(QgsPoint)
    def currentLocation(self, point):
        self.app.updateMapLocation(point.x(), point.y())
        #self.canvas.mouseMoveEvent(mouseEvent)

    # UI event handling calls (Qt slots)
    ###############################
    @pyqtSlot(QObject)
    def resizeEvent(self, event):
        """ handle window resize """
        # find left coordinate for right side panels
        x_right_side = self.width() - self.ui.widget_export.width(
        ) - UI_PADDING
        # adjust right side panels
        self.ui.widget_export.move(x_right_side, self.ui.widget_map.y() + 30)
        self.ui.widget_dq_test.move(
            x_right_side,
            self.ui.widget_export.y() + self.ui.widget_export.height() +
            UI_PADDING)
        # adjust map panel (left side)
        self.ui.widget_map.resize(x_right_side - UI_PADDING,
                                  self.height() - 2 * UI_PADDING)
        # adjust map canvas within the map panel
        map_top = self.ui.widget_map_menu_l.x(
        ) + self.ui.widget_map_menu_l.height() + UI_PADDING
        self.canvas.resize(
            x_right_side - UI_PADDING,  # same width as self.ui.widget_map
            self.ui.widget_map.height() - map_top - 2 * UI_PADDING)  # height
        # adjust map menu
        self.ui.widget_map_menu_r.move(
            self.ui.widget_map.width() -
            self.ui.widget_map_menu_r.width(),  # right align with map panel 
            0)
        # logo
        self.ui.lb_gem_logo.move(self.width() - self.ui.lb_gem_logo.width(),
                                 self.ui.lb_gem_logo.y())

    @logUICall
    @pyqtSlot()
    def mapPan(self):
        """ event handler for btn_pan - pan map """
        self.canvas.unsetMapTool(self.toolInfo)
        self.canvas.setMapTool(self.toolPan)

    @logUICall
    @pyqtSlot()
    def mapZoomIn(self):
        """ event handler for btn_zoom_in - zoom in on map """
        self.canvas.unsetMapTool(self.toolInfo)
        self.canvas.setMapTool(self.toolZoomIn)

    @logUICall
    @pyqtSlot()
    def mapZoomOut(self):
        """ event handler for btn_zoom_out - zoom out on map """
        self.canvas.unsetMapTool(self.toolInfo)
        self.canvas.setMapTool(self.toolZoomOut)

    @logUICall
    @pyqtSlot()
    def mapZoomFull(self):
        """ event handler for btn_zoom_full - zoom to full map """
        self.canvas.zoomToFullExtent()

    def checkRendering(self, changed):
        self.canvas.setRenderFlag(True)

    @logUICall
    @pyqtSlot()
    def stopRendering(self):
        self.canvas.setRenderFlag(False)

    @logUICall
    @pyqtSlot()
    def mapZoomLayer(self):
        self.canvas.unsetMapTool(self.toolInfo)
        cur_layer_name = self.ui.cb_layer_selector.currentText()
        if cur_layer_name.isEmpty():
            return
        self.zoomToLayer(
            self.map_layers[self.LAYER_NAMES.index(cur_layer_name)])

    @logUICall
    @pyqtSlot()
    def mapEditTheme(self):
        """ event handler for btn_edit - identify item on map """
        cur_layer_name = self.ui.cb_layer_selector.currentText()
        if cur_layer_name.isEmpty():
            return
        try:
            cur_layer_idx = self.LAYER_NAMES.index(cur_layer_name)

            # build layer render property Dialog for selected layer
            dlg_render = QDialog()
            dlg_render.setWindowTitle(
                get_ui_string('widget.result.renderer.settings'))
            dlg_render.setModal(True)
            dlg_render.setFixedSize(530, 370)
            dlg_render.renderer = QgsRendererV2PropertiesDialog(
                self.map_layers[cur_layer_idx], self.style, True)
            dlg_render.renderer.setParent(dlg_render)
            dlg_render.renderer.setGeometry(QRect(10, 10, 510, 325))
            dlg_render.buttonBox = QDialogButtonBox(dlg_render)
            dlg_render.buttonBox.setGeometry(QRect(10, 335, 510, 25))
            dlg_render.buttonBox.setStandardButtons(QDialogButtonBox.Cancel
                                                    | QDialogButtonBox.Ok)
            dlg_render.buttonBox.accepted.connect(dlg_render.accept)
            dlg_render.buttonBox.accepted.connect(dlg_render.renderer.onOK)
            dlg_render.buttonBox.rejected.connect(dlg_render.reject)
            dlg_render.setVisible(True)

            # get user input and update renderer
            answer = dlg_render.exec_()
            if answer == QDialog.Accepted:
                self.map_layer_renderer[cur_layer_idx] = None
                self.map_layer_renderer[cur_layer_idx] = self.map_layers[
                    cur_layer_idx].rendererV2().clone()
                self.canvas.refresh()
            dlg_render.destroy()
            del dlg_render
        except Exception as err:
            # thematic is not-critical, allow continue on exception
            logUICall.log(str(err), logUICall.WARNING)

    @logUICall
    @pyqtSlot()
    def searchFeature(self):
        cur_layer_name = self.ui.cb_layer_selector.currentText()
        if cur_layer_name.isEmpty():
            return
        try:
            cur_layer_idx = self.LAYER_NAMES.index(cur_layer_name)
            layer = self.map_layers[cur_layer_idx]
            fields = []
            for fidx in layer.dataProvider().fields():
                fields.append(layer.dataProvider().fields()[fidx].name())
            dlg_search = DialogSearchFeature(fields)
            answer = dlg_search.exec_()
            if answer == QDialog.Accepted:
                extent = self.findFeatureExtentByAttribute(
                    layer, dlg_search.attribute, dlg_search.value)
                if extent is not None:
                    self.zoomToExtent(extent)
                else:
                    logUICall.log(get_ui_string("widget.result.info.notfound"),
                                  logUICall.WARNING)
            dlg_search.destroy()
        except Exception as err:
            # thematic is not-critical, allow continue on exception
            logUICall.log(str(err), logUICall.WARNING)

    @logUICall
    @pyqtSlot()
    def mapIdentify(self):
        """ 
        event handler for btn_info 
        This only enables map querying, method connected to canvasClicked signal does
        the actual point-polygon query     
        """
        self.canvas.setMapTool(self.toolInfo)

    @logUICall
    @pyqtSlot()
    def selectExportFile(self):
        """
        event handler for btn_export_select_path 
        - open save file dialog box to select file name for export 
        """
        filename = QFileDialog.getSaveFileName(
            self, get_ui_string("widget.result.export.file.open"), ".",
            self.ui.cb_export_format.currentText())
        if not filename.isNull():
            self.ui.txt_export_select_path.setText(filename)

    @logUICall
    @pyqtSlot(str)
    def exportFormatChanged(self, selected_val):
        """
        event handler for cb_export_format 
        - update selected file after format change
        """
        self.ui.txt_export_select_path.setText("")
        self.export_format = self.EXPORT_FORMATS[str(selected_val)]

    @logUICall
    @pyqtSlot()
    def exportData(self):
        """ 
        event handler for btn_export
        - do export data 
        """
        export_path = str(self.ui.txt_export_select_path.text())
        if export_path == "":
            logUICall.log(get_ui_string("app.error.path.is.null"),
                          logUICall.WARNING)
            return
        self.app.exportResults(self.export_format, export_path)

    @logUICall
    @pyqtSlot(QPoint, QObject)
    def showInfo(self, point, mouseButton):
        """
        event handler for toolInfo
        @see QGIS tutorial for detail
        point-polygon search on currently selected layer  
        """
        cur_layer_name = self.ui.cb_layer_selector.currentText()
        if cur_layer_name.isEmpty():
            return
        try:
            cur_layer_idx = self.LAYER_NAMES.index(cur_layer_name)
            cur_layer = self.map_layers[cur_layer_idx]

            # if layer is not in same projection as map canvas
            # need to project query point
            if cur_layer.crs() != self.canvas.mapRenderer().destinationCrs():
                transform = QgsCoordinateTransform(
                    self.canvas.mapRenderer().destinationCrs(),
                    cur_layer.crs())
                point = transform.transform(point)

            # do query
            provider = cur_layer.dataProvider()
            provider.rewind()
            feature = QgsFeature()
            colonIndexes = provider.attributeIndexes()

            # search using point as center of rectangle polygon
            search_buffer_x = self.canvas.extent().width(
            ) * self.SEARCH_BUFFER / self.canvas.width()
            search_buffer_y = self.canvas.extent().height(
            ) * self.SEARCH_BUFFER / self.canvas.height()
            provider.select(
                colonIndexes,
                QgsRectangle(point.x() - search_buffer_x,
                             point.y() - search_buffer_y,
                             point.x() + search_buffer_x,
                             point.y() + search_buffer_y), True)
            # get selected and display in result detail dialog box
            selected = []
            while provider.nextFeature(feature):
                # for polygons, only show geometry containing query point
                if cur_layer.geometryType() == QGis.Polygon:
                    if feature.geometry(
                    ) is not None and not feature.geometry().contains(point):
                        continue
                selected.append(feature.attributeMap())

            if len(selected) > 0:
                # display result if exists
                if cur_layer_idx == self.EXPOSURE:
                    self.dlgResultDetail.showExposureData(
                        provider.fields(), selected)
                else:
                    self.dlgResultDetail.showInfoData(provider.fields(),
                                                      selected)
                self.dlgResultDetail.exec_()
            else:
                logUICall.log(get_ui_string("widget.result.info.notfound"),
                              logUICall.WARNING)
        except Exception as err:
            # point-in-polygon search is not critical, continue on error
            logUICall.log(str(err), logUICall.WARNING)

    # public methods
    ###############################
    def set_project(self, project):
        ''' set project to preview. force refresh view on set'''
        self._project = project
        if project is None:
            return
        self.refreshView()
        self.canvas.zoomToFullExtent()
        logUICall.log("Project preview initialized sucessfully",
                      logUICall.INFO)

    def get_project(self):
        return self._project

    # property access to project
    project = property(get_project, set_project)

    def refreshView(self):
        ''' reload all QGIS layers in currently defined project '''
        if self._project is None:
            return

        # display layers if exists
        if self._project.fp_file is not None and exists(self._project.fp_file):
            if self.map_layers[self.FOOTPRINT] is None or self.map_layers[
                    self.FOOTPRINT].source() != self._project.fp_file:
                self.showDataLayer(
                    self.FOOTPRINT,
                    load_shapefile(self._project.fp_file, 'footprint'))
        else:
            self.removeDataLayer(self.FOOTPRINT)

        if self._project.zone_file is not None and exists(
                self._project.zone_file):
            if self.map_layers[self.ZONES] is None or self.map_layers[
                    self.ZONES].source() != self._project.zone_file:
                self.showDataLayer(
                    self.ZONES, load_shapefile(self._project.zone_file,
                                               'zones'))
        else:
            self.removeDataLayer(self.ZONES)

        if self._project.survey_file is not None and exists(
                self._project.survey_file):
            if getattr(self._project, 'survey', None) is None:
                self._project.load_survey()
                self.showDataLayer(self.SURVEY, self._project.survey)
        else:
            self.removeDataLayer(self.SURVEY)

        if self._project.popgrid_file is not None and exists(
                self._project.popgrid_file):
            if getattr(self._project, 'popgrid', None) is None:
                self.showDataLayer(
                    self.POP_GRID,
                    load_shapefile(self._project.popgrid_file, 'popgrid'))
        else:
            self.removeDataLayer(self.POP_GRID)

        # set export options
        for idx, export_format in enumerate(self.EXPORT_FORMATS.values()):
            if export_format == self._project.export_type:
                self.ui.cb_export_format.setCurrentIndex(idx)
        self.ui.txt_export_select_path.setText(self._project.export_path)

        # refreshResult contains refresh call to update all layers currently loaded
        self.refreshResult()

    def refreshResult(self):
        ''' reload result QGIS layer and data quality reports in currently defined project '''
        exposure = getattr(self._project, 'exposure', None)
        if exposure is not None:
            self.showDataLayer(self.EXPOSURE, exposure)
            has_result = True
        else:
            self.removeDataLayer(self.EXPOSURE)
            has_result = False

        if has_result:
            # build quality report
            report_lines = []
            if self._project.operator_options.has_key("proc.extrapolation"):
                proc_option = self._project.operator_options[
                    "proc.extrapolation"]
                if proc_option == ExtrapolateOptions.RandomWalk:
                    proc_method = get_ui_string(
                        "widget.result.dq.method",
                        get_ui_string("dlg.options.ep.random"))
                elif proc_option == ExtrapolateOptions.Fraction:
                    proc_method = get_ui_string(
                        "widget.result.dq.method",
                        get_ui_string("dlg.options.ep.fraction"))
                elif proc_option == ExtrapolateOptions.FractionRounded:
                    proc_method = get_ui_string(
                        "widget.result.dq.method",
                        get_ui_string("dlg.options.ep.fraction.rounded"))
            else:
                proc_method = get_ui_string(
                    "widget.result.dq.method",
                    get_ui_string("dlg.options.ep.random"))
            report_lines.append(proc_method)
            report_lines.append('')

            # total tests
            report_lines.append(
                get_ui_string('widget.result.dq.total_tests',
                              len(self._project.quality_reports.keys())))
            report_lines.append('')

            # detail for each test
            for key, report in self._project.quality_reports.iteritems():
                report_lines.append(
                    get_ui_string('widget.result.dq.tests.%s' % key))
                for title, value in report.iteritems():
                    report_lines.append(
                        get_ui_string(
                            'widget.result.dq.tests.%s.%s' % (key, title),
                            value))
                report_lines.append('')
            self.ui.txt_dq_test_details.setText("\n".join(report_lines))

        self.ui.btn_export.setEnabled(has_result)
        self.ui.widget_dq_test.setVisible(has_result)
        self.ui.txt_export_select_path.setEnabled(has_result)
        self.ui.btn_export_select_path.setEnabled(has_result)
        self.ui.cb_export_format.setEnabled(has_result)

        # this call refresh all layers currently loaded
        self.refreshLayers()

    @logUICall
    def closeResult(self):
        ''' remove from map result QGIS layer and reset quality report display '''
        self.canvas.setLayerSet(
            []
        )  # call necessary to remove all layers to avoid disconnect errors
        self.removeDataLayer(self.EXPOSURE)
        self.refreshLayers()
        self.ui.txt_dq_test_details.setText("")

    @logUICall
    def closeAll(self):
        ''' remove from map all QGIS layer in currently defined project '''
        self.ui.cb_layer_selector.clear()
        try:
            self.canvas.setLayerSet(
                []
            )  # call necessary to remove all layers to avoid disconnect errors
            for i in range(5):
                self.removeDataLayer(i)
            self.ui.txt_dq_test_details.setText("")
            self.refreshLayers()
        except:
            pass  # exception will is thrown when registry is empty

    # internal helper methods
    ###############################
    def showDataLayer(self, index, layer):
        """ display given QGIS layer on map """
        try:
            # add to QGIS registry and refresh view
            if self.map_layers[index] is not None:
                self.removeDataLayer(index)
            self.map_layers[index] = layer
            self.registry.addMapLayer(layer)
            layer.setRendererV2(self.map_layer_renderer[index])
        except:
            pass

    def removeDataLayer(self, index):
        """ remove from map the layer identified with index """
        layer = self.map_layers[index]
        self.map_layers[index] = None
        if layer is not None:
            try:
                self.registry.removeMapLayer(layer.getLayerID(), False)
                del layer
            except:
                pass  # do nothing if it fails. probably already deleted

    def findFeatureExtentByAttribute(self, layer, field, value):
        """ 
        find extent of all objects in QGIS layer matching condition "field=value"         
        """
        fidx = layer_field_index(layer, field)
        if fidx == -1:
            return None
        xmin, xmax, ymin, ymax = 180, -180, 90, -90
        extent = QgsRectangle(xmin, ymin, xmax, ymax)
        need_transform = layer.crs() != self.canvas.mapRenderer(
        ).destinationCrs()
        if need_transform:
            transform = QgsCoordinateTransform(
                layer.crs(),
                self.canvas.mapRenderer().destinationCrs())
        for feature in layer_features(layer):
            if str(value) == feature.attributeMap()[fidx].toString():
                f_extent = feature.geometry().boundingBox()
                if need_transform:
                    f_extent = transform.transform(f_extent)
                xmin = min(f_extent.xMinimum(), xmin)
                xmax = max(f_extent.xMaximum(), xmax)
                ymin = min(f_extent.yMinimum(), ymin)
                ymax = max(f_extent.yMaximum(), ymax)
        extent.set(xmin, ymin, xmax, ymax)
        return extent

    def zoomToLayer(self, layer):
        """ zoom canvas to extent of given layer """
        try:
            lyr_extent = layer.extent()
            if layer.crs() != self.canvas.mapRenderer().destinationCrs():
                transform = QgsCoordinateTransform(
                    layer.crs(),
                    self.canvas.mapRenderer().destinationCrs())
                lyr_extent = transform.transform(lyr_extent)
            self.zoomToExtent(lyr_extent)
        except:
            pass

    def zoomToExtent(self, extent):
        """ zoom canvas to given extent """
        try:
            self.canvas.setExtent(extent)
            self.canvas.zoomByFactor(1.1)
        except:
            self.mapZoomFull()

    def refreshLayers(self):
        """ refresh all layers in canvas """
        # add each layer according to order
        layerSet = []
        self.ui.cb_layer_selector.clear()
        for idx, lyr in enumerate(self.map_layers):
            if lyr is not None:
                layerSet.append(QgsMapCanvasLayer(lyr))
                self.ui.cb_layer_selector.addItem(self.LAYER_NAMES[idx])
        if len(layerSet) > 0:
            self.canvas.setLayerSet(layerSet)
예제 #12
0
class MirrorMap(QWidget):
    def __init__(self, parent=None, iface=None):
        QWidget.__init__(self, parent)
        # self.setAttribute(Qt.WA_DeleteOnClose)

        self.iface = iface
        self.layerId2canvasLayer = {}
        self.canvasLayers = []

        self.setupUi()

    def closeEvent(self, event):
        self.scaleFactor.valueChanged.disconnect(self.onExtentsChanged)

        if not self.iface is None:
            self.iface.mapCanvas().extentsChanged.discconnect(
                self.onExtentsChanged)
            self.iface.mapCanvas().mapRenderer(
            ).destinationCrsChanged.disconnect(self.onCrsChanged)
            self.iface.mapCanvas().mapRenderer().mapUnitsChanged.disconnect(
                self.onCrsChanged)
            self.iface.mapCanvas().mapRenderer(
            ).hasCrsTransformEnabled.disconnect(self.onCrsTransformEnabled)
            QgsProject.instance().layerWillBeRemoved.disconnect(self.delLayer)
            self.iface.currentLayerChanged.disconnect(self.refreshLayerButtons)

        self.closed.emit()

        return QWidget.closeEvent(self, event)

    def setupUi(self):
        self.setObjectName("mirrormap")

        gridLayout = QGridLayout(self)
        gridLayout.setContentsMargins(0, 0, gridLayout.verticalSpacing(),
                                      gridLayout.verticalSpacing())

        self.canvas = QgsMapCanvas(self)
        self.canvas.setCanvasColor(QColor(255, 255, 255))
        settings = QSettings()
        gridLayout.addWidget(self.canvas, 0, 0, 1, 5)

        self.addLayerBtn = QToolButton(self)
        # self.addLayerBtn.setToolButtonStyle( Qt.ToolButtonTextBesideIcon )
        # self.addLayerBtn.setText("Add current layer")
        self.addLayerBtn.setIcon(GuiUtils.get_icon("add.png"))

        self.addLayerBtn.clicked.connect(self.tool_add_layer)
        gridLayout.addWidget(self.addLayerBtn, 1, 0, 1, 1)

        self.delLayerBtn = QToolButton(self)
        # self.delLayerBtn.setToolButtonStyle( Qt.ToolButtonTextBesideIcon )
        # self.delLayerBtn.setText("Remove current layer")
        self.delLayerBtn.setIcon(GuiUtils.get_icon("remove.png"))
        self.delLayerBtn.clicked.connect(self.tool_remove_layer)
        gridLayout.addWidget(self.delLayerBtn, 1, 1, 1, 1)

        self.renderCheck = QCheckBox("Render", self)
        self.renderCheck.toggled.connect(self.toggleRender)
        self.renderCheck.setChecked(True)
        gridLayout.addWidget(self.renderCheck, 1, 2, 1, 1)

        self.scaleFactorLabel = QLabel(self)
        self.scaleFactorLabel.setText("Scale factor:")
        self.scaleFactorLabel.setAlignment(Qt.AlignRight | Qt.AlignVCenter)
        gridLayout.addWidget(self.scaleFactorLabel, 1, 3, 1, 1)
        self.scaleFactor = QDoubleSpinBox(self)
        self.scaleFactor.setMinimum(0.0)
        self.scaleFactor.setMaximum(1000.0)
        self.scaleFactor.setDecimals(3)
        self.scaleFactor.setValue(1)
        self.scaleFactor.setObjectName("scaleFactor")
        self.scaleFactor.setSingleStep(.05)
        gridLayout.addWidget(self.scaleFactor, 1, 4, 1, 1)
        self.scaleFactor.valueChanged.connect(self.onExtentsChanged)

        # Add a default pan tool
        self.toolPan = QgsMapToolPan(self.canvas)
        self.canvas.setMapTool(self.toolPan)

        self.scaleFactor.valueChanged.connect(self.onExtentsChanged)
        self.set_iface(self.iface)

    def toggleRender(self, enabled):
        self.canvas.setRenderFlag(enabled)

    def extent(self):
        """
        :return: Current extents of the map canvas view.
        """
        return self.canvas.extent()

    def canvas_layers(self):
        """
        :return: Layers currently in the canvas.
        :rtype: list
        """
        return self.canvasLayers

    def on_canvas_refreshed(self):
        """
        """
        self.refresh_layers()

    def tool_add_layer(self):
        self.addLayer()

    def tool_remove_layer(self):
        self.delLayer()

    def set_iface(self, iface):
        if iface is None:
            return

        self.iface = iface
        self.iface.mapCanvas().extentsChanged.connect(self.onExtentsChanged)
        # self.iface.mapCanvas().mapCanvasRefreshed.connect(self.on_canvas_refreshed)
        self.iface.mapCanvas().destinationCrsChanged.connect(self.onCrsChanged)
        QgsProject.instance().layerWillBeRemoved.connect(self.delLayer)
        self.iface.currentLayerChanged.connect(self.refreshLayerButtons)

        self.refreshLayerButtons()

        self.onExtentsChanged()
        self.onCrsChanged()

    def refresh_layers(self):
        """
        Checks if the layers in the canvas list have already been added.
        If not, then add to the property viewer canvas.
        """
        for ly in self.iface.mapCanvas().layers():
            layer_id = self._layerId(ly)
            if layer_id not in self.layerId2canvasLayer:
                self.addLayer(layer_id)
        # QCoreApplication.processEvents(QEventLoop.ExcludeSocketNotifiers|QEventLoop.ExcludeUserInputEvents)

    def onExtentsChanged(self):
        prevFlag = self.canvas.renderFlag()
        self.canvas.setRenderFlag(False)

        self.canvas.setExtent(self.iface.mapCanvas().extent())
        self.canvas.zoomByFactor(self.scaleFactor.value())
        # self.canvas.refresh()

        self.canvas.setRenderFlag(prevFlag)

    def onCrsChanged(self):
        self.canvas.setDestinationCrs(
            self.iface.mapCanvas().mapSettings().destinationCrs())

    def refreshLayerButtons(self):
        layer = self.iface.activeLayer()

        isLayerSelected = layer != None
        hasLayer = False
        for l in self.canvas.layers():
            if l == layer:
                hasLayer = True
                break

        self.addLayerBtn.setEnabled(isLayerSelected and not hasLayer)
        self.delLayerBtn.setEnabled(isLayerSelected and hasLayer)

    def getLayerSet(self):
        return [self._layerId(x.layer()) for x in self.canvasLayers]

    def setLayerSet(self, layerIds=None):
        prevFlag = self.canvas.renderFlag()
        self.canvas.setRenderFlag(False)

        if layerIds == None:
            self.layerId2canvasLayer = {}
            self.canvasLayers = []
            self.canvas.setLayers([])

        else:
            for lid in layerIds:
                self.addLayer(lid)

        self.refreshLayerButtons()
        self.onExtentsChanged()
        self.canvas.setRenderFlag(prevFlag)

    def addLayer(self, layerId=None):
        if layerId == None:
            layer = self.iface.activeLayer()
        else:
            layer = QgsProject.instance().mapLayer(layerId)

        if layer == None:
            return

        prevFlag = self.canvas.renderFlag()
        self.canvas.setRenderFlag(False)

        # add the layer to the map canvas layer set
        self.canvasLayers = []
        id2cl_dict = {}
        for l in self.iface.mapCanvas().layers():
            lid = self._layerId(l)
            if lid in self.layerId2canvasLayer:  # previously added
                cl = self.layerId2canvasLayer[lid]
            elif l == layer:  # Selected layer
                cl = layer
            else:
                continue

            id2cl_dict[lid] = cl
            self.canvasLayers.append(cl)

        self.layerId2canvasLayer = id2cl_dict
        self.canvas.setLayers(self.canvasLayers)

        self.refreshLayerButtons()
        self.onExtentsChanged()
        self.canvas.setRenderFlag(prevFlag)

    def delLayer(self, layerId=None):
        if layerId == None:
            layer = self.iface.activeLayer()
            if layer == None:
                return
            layerId = self._layerId(layer)

        # remove the layer from the map canvas layer set
        if layerId not in self.layerId2canvasLayer:
            return

        prevFlag = self.canvas.renderFlag()
        self.canvas.setRenderFlag(False)

        cl = self.layerId2canvasLayer[layerId]
        del self.layerId2canvasLayer[layerId]
        self.canvasLayers.remove(cl)
        self.canvas.setLayers(self.canvasLayers)

        self.refreshLayerButtons()
        self.onExtentsChanged()
        self.canvas.setRenderFlag(prevFlag)

    def _layerId(self, layer):
        if hasattr(layer, 'id'):
            return layer.id()

        return layer.getLayerID()
예제 #13
0
def project_info(project_path):
    """Extracts project information and returns it as a dictionary"""

    info = {}
    p = QgsProject()
    canvas = QgsMapCanvas()

    def _readCanvasSettings(xmlDocument):
        canvas.readProject(xmlDocument)

    p.readProject.connect(_readCanvasSettings, Qt.DirectConnection)

    if p.read(project_path):

        # initial extent
        extent = canvas.extent()
        if p.crs().authid() != 4326:
            ct = QgsCoordinateTransform(
                p.crs(), QgsCoordinateReferenceSystem.fromEpsgId(4326),
                p.transformContext())
            extent = ct.transform(extent)

        info['initial_extent'] = [
            extent.xMinimum(),
            extent.yMinimum(),
            extent.xMaximum(),
            extent.yMaximum(),
        ]

        ####################################################
        # Main section

        info['title'] = p.metadata().title()
        if not info['title']:
            info['title'] = QgsServerProjectUtils.owsServiceTitle(p)
        if not info['title']:
            info['title'] = p.title()
        if not info['title']:
            info['title'] = p.baseName()

        info['description'] = p.metadata().abstract()
        if not info['description']:
            info['description'] = QgsServerProjectUtils.owsServiceAbstract(p)

        # Extent, CRS and published WMS layers typenames
        wmsOutputCrsList = QgsServerProjectUtils.wmsOutputCrsList(p)
        info[
            'crs'] = 'EPSG:4326' if 'EPSG:4326' in wmsOutputCrsList else wmsOutputCrsList[
                0]
        extent, info['wms_layers'] = project_wms(p, info['crs'])
        info['extent'] = [
            extent.xMinimum(),
            extent.yMinimum(),
            extent.xMaximum(),
            extent.yMaximum()
        ]
        geographic_extent = extent
        if info['crs'] != 'EPSG:4326':
            extent_crs = QgsCoordinateReferenceSystem.fromEpsgId(
                int(info['crs'].split(':')[1]))
            ct = QgsCoordinateTransform(
                extent_crs, QgsCoordinateReferenceSystem.fromEpsgId(4326),
                p.transformContext())
            geographic_extent = ct.transform(geographic_extent)
        info['geographic_extent'] = [
            geographic_extent.xMinimum(),
            geographic_extent.yMinimum(),
            geographic_extent.xMaximum(),
            geographic_extent.yMaximum()
        ]

        ####################################################
        # Metadata section

        m = p.metadata()
        metadata = {}
        for prop in (
                'title',
                'identifier',
                'parentIdentifier',
                'abstract',
                'author',
                'language',
                'categories',
                'history',
                'type',
        ):
            metadata[prop] = getattr(m, prop)()

        # links array
        metadata['links'] = _read_links(m)
        # contacts array
        metadata['contacts'] = _read_contacts(m)
        metadata['creationDateTime'] = m.creationDateTime().toString(
            Qt.ISODate)
        info['metadata'] = metadata

        ####################################################
        # WMS Service Capabilities section

        capabilities = {}
        for c in ('owsServiceAbstract', 'owsServiceAccessConstraints',
                  'owsServiceCapabilities', 'owsServiceContactMail',
                  'owsServiceContactOrganization', 'owsServiceContactPerson',
                  'owsServiceContactPhone', 'owsServiceContactPosition',
                  'owsServiceFees', 'owsServiceKeywords',
                  'owsServiceOnlineResource', 'owsServiceTitle', 'wcsLayerIds',
                  'wcsServiceUrl', 'wfsLayerIds', 'wfsServiceUrl',
                  'wfstDeleteLayerIds', 'wfstInsertLayerIds',
                  'wfstUpdateLayerIds', 'wmsDefaultMapUnitsPerMm', 'wmsExtent',
                  'wmsFeatureInfoAddWktGeometry',
                  'wmsFeatureInfoDocumentElement',
                  'wmsFeatureInfoDocumentElementNs',
                  'wmsFeatureInfoLayerAliasMap', 'wmsFeatureInfoPrecision',
                  'wmsFeatureInfoSchema',
                  'wmsFeatureInfoSegmentizeWktGeometry', 'wmsImageQuality',
                  'wmsInfoFormatSia2045', 'wmsInspireActivate',
                  'wmsInspireLanguage', 'wmsInspireMetadataDate',
                  'wmsInspireMetadataUrl', 'wmsInspireMetadataUrlType',
                  'wmsInspireTemporalReference', 'wmsMaxAtlasFeatures',
                  'wmsMaxHeight', 'wmsMaxWidth', 'wmsOutputCrsList',
                  'wmsRestrictedComposers', 'wmsRestrictedLayers',
                  'wmsRootName', 'wmsServiceUrl', 'wmsTileBuffer',
                  'wmsUseLayerIds', 'wmtsServiceUrl'):
            capabilities[c] = getattr(QgsServerProjectUtils, c)(p)

        info['capabilities'] = capabilities

        ####################################################
        # WMS Layers section

        info['wms_root_name'] = capabilities['wmsRootName'] if capabilities[
            'wmsRootName'] else p.title()
        restricted_wms = capabilities['wmsRestrictedLayers']
        wms_layers = {}
        use_ids = capabilities['wmsUseLayerIds']
        # Map layer title to layer name (or id if use_ids)
        wms_layers_map = {}
        # Maps a typename to a layer id
        wms_layers_typename_id_map = {}
        wms_layers_searchable = []
        wms_layers_queryable = []

        for l in p.mapLayers().values():
            if l.name() not in restricted_wms:
                wms_layers[l.id()] = layer_info(l)
                name = l.title() if l.title() else l.name()
                short_name = l.shortName() if l.shortName() else l.name()
                wms_layers_typename_id_map[short_name] = l.id()
                wms_layers_map[name] = l.id() if use_ids else short_name
                if bool(l.flags() & QgsMapLayer.Searchable):
                    wms_layers_searchable.append(l.id())
                if bool(l.flags() & QgsMapLayer.Identifiable):
                    wms_layers_queryable.append(l.id())

        info['wms_layers'] = wms_layers
        info['wms_layers_map'] = wms_layers_map
        info['wms_layers_searchable'] = wms_layers_searchable
        info['wms_layers_queryable'] = wms_layers_queryable
        info['wms_layers_typename_id_map'] = wms_layers_typename_id_map

        ####################################################
        # TOC tree (WMS published only)
        info['toc'] = get_toc(p, info)

    return info
예제 #14
0
class WidgetResult(Ui_widgetResult, QWidget):
    """
    Widget (Panel) for result review
    """
    
    ''' buffer around clicked point for point in polygon query ''' 
    SEARCH_BUFFER = 20.0
    ''' supported export formats '''
    EXPORT_FORMATS = {
        get_ui_string("app.extension.shapefile"):ExportTypes.Shapefile,
        #get_ui_string("app.extension.kml"):ExportTypes.KML,
        #get_ui_string("app.extension.nrml"):ExportTypes.NRML,
        get_ui_string("app.extension.csv"):ExportTypes.CSV,
    };
    ''' enumeration of Layer to be previewed '''
    EXPOSURE, SURVEY, POP_GRID, FOOTPRINT, ZONES = range(5);
    ''' name for Layer to be previewed '''
    LAYER_NAMES = [
        get_ui_string("widget.result.layer.exposure"),        
        get_ui_string("widget.result.layer.survey"),
        get_ui_string("widget.result.layer.popgrid"),
        get_ui_string("widget.result.layer.footprint"),        
        get_ui_string("widget.result.layer.zones"),
    ];    
    LAYER_STYLES = [
        '<!DOCTYPE renderer><renderer-v2 symbollevels="0" type="singleSymbol"><symbols><symbol outputUnit="MM" alpha="1" type="fill" name="0"><layer pass="******" class="SimpleLine" locked="0"><prop k="capstyle" v="square"/><prop k="color" v="0,0,0,255"/><prop k="customdash" v="5;2"/><prop k="joinstyle" v="bevel"/><prop k="offset" v="0"/><prop k="penstyle" v="solid"/><prop k="use_custom_dash" v="0"/><prop k="width" v="0.26"/></layer></symbol></symbols><rotation field=""/><sizescale field=""/></renderer-v2>',
        '<!DOCTYPE renderer><renderer-v2 symbollevels="0" type="singleSymbol"><symbols><symbol outputUnit="MM" alpha="1" type="marker" name="0"><layer pass="******" class="SimpleMarker" locked="0"><prop k="angle" v="0"/><prop k="color" v="0,0,255,255"/><prop k="color_border" v="0,0,255,255"/><prop k="name" v="circle"/><prop k="offset" v="0,0"/><prop k="size" v="2"/></layer></symbol></symbols><rotation field=""/><sizescale field=""/></renderer-v2>',
        '<!DOCTYPE renderer><renderer-v2 symbollevels="0" type="singleSymbol"><symbols><symbol outputUnit="MM" alpha="1" type="marker" name="0"><layer pass="******" class="SimpleMarker" locked="0"><prop k="angle" v="0"/><prop k="color" v="0,255,0,255"/><prop k="color_border" v="0,255,0,255"/><prop k="name" v="rectangle"/><prop k="offset" v="0,0"/><prop k="size" v="4"/></layer></symbol></symbols><rotation field=""/><sizescale field=""/></renderer-v2>',
        '<!DOCTYPE renderer><renderer-v2 symbollevels="0" type="singleSymbol"><symbols><symbol outputUnit="MM" alpha="1" type="fill" name="0"><layer pass="******" class="SimpleFill" locked="0"><prop k="color" v="170,250,170,255"/><prop k="color_border" v="0,0,0,255"/><prop k="offset" v="0,0"/><prop k="style" v="solid"/><prop k="style_border" v="solid"/><prop k="width_border" v="0.26"/></layer></symbol></symbols><rotation field=""/><sizescale field=""/></renderer-v2>',
        '<!DOCTYPE renderer><renderer-v2 symbollevels="0" type="singleSymbol"><symbols><symbol outputUnit="MM" alpha="1" type="fill" name="0"><layer pass="******" class="SimpleFill" locked="0"><prop k="color" v="211,211,158,200"/><prop k="color_border" v="0,0,0,255"/><prop k="offset" v="0,0"/><prop k="style" v="solid"/><prop k="style_border" v="solid"/><prop k="width_border" v="0.26"/></layer></symbol></symbols><rotation field=""/><sizescale field=""/></renderer-v2>',
    ]
    
    # constructor / destructor
    ###############################
    
    def __init__(self, app):
        """
        constructor
        - initialize UI elements
        - connect UI elements to callback            
        """
        super(WidgetResult, self).__init__()
        self.ui = Ui_widgetResult()
        self.ui.setupUi(self)
                
        # create canvas
        self.canvas = QgsMapCanvas(self.ui.widget_map)
        self.canvas.setGeometry(
            0,                                                                # x
            self.ui.widget_map_menu_l.x()+self.ui.widget_map_menu_l.height(), # y  
            self.ui.widget_map.width() - 2*UI_PADDING,  # width
            self.ui.widget_map.width() - 2*UI_PADDING   # height
            )
        
        self.canvas.setCanvasColor(Qt.white)
        self.canvas.enableAntiAliasing(True)
        self.canvas.mapRenderer().setProjectionsEnabled(True)
        self.canvas.mapRenderer().setDestinationCrs(QgsCoordinateReferenceSystem(4326, QgsCoordinateReferenceSystem.PostgisCrsId))
        self.canvas.zoomNextStatusChanged.connect(self.checkRendering)
        self.canvas.xyCoordinates.connect(self.currentLocation)
        self.registry = QgsMapLayerRegistry.instance()
        
        self.map_layers = [None] * len(self.LAYER_NAMES)
        self.map_layer_renderer = [None] * len(self.LAYER_NAMES)
        for idx, str_style in enumerate(self.LAYER_STYLES):
            rdoc = QDomDocument("renderer")
            rdoc.setContent(str_style)        
            self.map_layer_renderer[idx] = QgsFeatureRendererV2.load(rdoc.firstChild().toElement())

        # populate export list
        self.ui.cb_export_format.clear()
        for export_format in self.EXPORT_FORMATS.keys():
            self.ui.cb_export_format.addItem(export_format)
                    
        # style object required for QgsRendererV2PropertiesDialog
        self.style = QgsStyleV2()
        
        # create the map tools
        self.toolPan = QgsMapToolPan(self.canvas)
        self.toolZoomIn = QgsMapToolZoom(self.canvas, False) # false = in
        self.toolZoomOut = QgsMapToolZoom(self.canvas, True) # true = out
        self.toolInfo = QgsMapToolEmitPoint(self.canvas)
        self.toolInfo.canvasClicked.connect(self.showInfo)
        self.canvas.setMapTool(self.toolPan)
        
        # additional         
        self.dlgResultDetail = DialogResult()
        self.dlgResultDetail.setModal(True)

        # set link to application main controller
        self.app = app
        
        # reset project
        self._project = None
        
        # default export setting
        self.export_format = ExportTypes.Shapefile
        
        # connect slots (ui event)
        self.ui.btn_zoom_full.clicked.connect(self.mapZoomFull)
        self.ui.btn_zoom_in.clicked.connect(self.mapZoomIn)
        self.ui.btn_zoom_out.clicked.connect(self.mapZoomOut)
        self.ui.btn_stop.clicked.connect(self.stopRendering)
        self.ui.btn_zoom_layer.clicked.connect(self.mapZoomLayer)        
        self.ui.btn_pan.clicked.connect(self.mapPan)
        self.ui.btn_theme.clicked.connect(self.mapEditTheme)
        self.ui.btn_info.clicked.connect(self.mapIdentify)
        
        self.ui.btn_zoom_to_feature.clicked.connect(self.searchFeature)
        
        self.ui.cb_export_format.currentIndexChanged[str].connect(self.exportFormatChanged)
        self.ui.btn_export.clicked.connect(self.exportData)
        self.ui.btn_export_select_path.clicked.connect(self.selectExportFile)

    @pyqtSlot(QgsPoint)
    def currentLocation(self, point):
        self.app.updateMapLocation(point.x(),point.y())
        #self.canvas.mouseMoveEvent(mouseEvent)
        

    # UI event handling calls (Qt slots)
    ###############################
    @pyqtSlot(QObject)
    def resizeEvent(self, event):
        """ handle window resize """ 
        # find left coordinate for right side panels
        x_right_side = self.width()-self.ui.widget_export.width()-UI_PADDING
        # adjust right side panels
        self.ui.widget_export.move(x_right_side, self.ui.widget_map.y()+30)
        self.ui.widget_dq_test.move(x_right_side, self.ui.widget_export.y()+self.ui.widget_export.height()+UI_PADDING)
        # adjust map panel (left side)        
        self.ui.widget_map.resize(x_right_side-UI_PADDING, self.height()-2*UI_PADDING)
        # adjust map canvas within the map panel        
        map_top = self.ui.widget_map_menu_l.x()+self.ui.widget_map_menu_l.height()+UI_PADDING        
        self.canvas.resize(
            x_right_side-UI_PADDING,                            # same width as self.ui.widget_map
            self.ui.widget_map.height()-map_top-2*UI_PADDING)   # height        
        # adjust map menu
        self.ui.widget_map_menu_r.move(
            self.ui.widget_map.width()-self.ui.widget_map_menu_r.width(),   # right align with map panel 
            0)
        # logo
        self.ui.lb_gem_logo.move(self.width()-self.ui.lb_gem_logo.width(), self.ui.lb_gem_logo.y())

    @logUICall
    @pyqtSlot()
    def mapPan(self):
        """ event handler for btn_pan - pan map """
        self.canvas.unsetMapTool(self.toolInfo)
        self.canvas.setMapTool(self.toolPan)

    @logUICall
    @pyqtSlot()
    def mapZoomIn(self):
        """ event handler for btn_zoom_in - zoom in on map """
        self.canvas.unsetMapTool(self.toolInfo)
        self.canvas.setMapTool(self.toolZoomIn)

    @logUICall
    @pyqtSlot()
    def mapZoomOut(self):
        """ event handler for btn_zoom_out - zoom out on map """
        self.canvas.unsetMapTool(self.toolInfo)
        self.canvas.setMapTool(self.toolZoomOut)

    @logUICall
    @pyqtSlot()
    def mapZoomFull(self):
        """ event handler for btn_zoom_full - zoom to full map """
        self.canvas.zoomToFullExtent()

    def checkRendering(self, changed):
        self.canvas.setRenderFlag(True)
        
    @logUICall
    @pyqtSlot()
    def stopRendering(self):        
        self.canvas.setRenderFlag(False)

    @logUICall
    @pyqtSlot()
    def mapZoomLayer(self):
        self.canvas.unsetMapTool(self.toolInfo)
        cur_layer_name = self.ui.cb_layer_selector.currentText()
        if cur_layer_name.isEmpty():
            return
        self.zoomToLayer(self.map_layers[self.LAYER_NAMES.index(cur_layer_name)])
        
    @logUICall
    @pyqtSlot()
    def mapEditTheme(self):
        """ event handler for btn_edit - identify item on map """
        cur_layer_name = self.ui.cb_layer_selector.currentText()
        if cur_layer_name.isEmpty():
            return
        try:
            cur_layer_idx = self.LAYER_NAMES.index(cur_layer_name)
            
            # build layer render property Dialog for selected layer  
            dlg_render = QDialog()
            dlg_render.setWindowTitle(get_ui_string('widget.result.renderer.settings'))
            dlg_render.setModal(True)
            dlg_render.setFixedSize(530, 370)
            dlg_render.renderer = QgsRendererV2PropertiesDialog(self.map_layers[cur_layer_idx], self.style, True)
            dlg_render.renderer.setParent(dlg_render)        
            dlg_render.renderer.setGeometry(QRect(10, 10, 510, 325))
            dlg_render.buttonBox = QDialogButtonBox(dlg_render)
            dlg_render.buttonBox.setGeometry(QRect(10, 335, 510, 25))
            dlg_render.buttonBox.setStandardButtons(QDialogButtonBox.Cancel|QDialogButtonBox.Ok)
            dlg_render.buttonBox.accepted.connect(dlg_render.accept)
            dlg_render.buttonBox.accepted.connect(dlg_render.renderer.onOK)
            dlg_render.buttonBox.rejected.connect(dlg_render.reject)
            dlg_render.setVisible(True)

            # get user input and update renderer
            answer = dlg_render.exec_()
            if answer == QDialog.Accepted:
                self.map_layer_renderer[cur_layer_idx] = None
                self.map_layer_renderer[cur_layer_idx] = self.map_layers[cur_layer_idx].rendererV2().clone()             
                self.canvas.refresh()
            dlg_render.destroy()
            del dlg_render
        except Exception as err:
            # thematic is not-critical, allow continue on exception
            logUICall.log(str(err), logUICall.WARNING)

    @logUICall
    @pyqtSlot()
    def searchFeature(self):        
        cur_layer_name = self.ui.cb_layer_selector.currentText()
        if cur_layer_name.isEmpty():
            return
        try:
            cur_layer_idx = self.LAYER_NAMES.index(cur_layer_name)            
            layer = self.map_layers[cur_layer_idx]
            fields = []
            for fidx in layer.dataProvider().fields():
                fields.append(layer.dataProvider().fields()[fidx].name())
            dlg_search = DialogSearchFeature(fields)           
            answer = dlg_search.exec_()
            if answer == QDialog.Accepted:
                extent = self.findFeatureExtentByAttribute(layer, dlg_search.attribute, dlg_search.value)
                if extent is not None:
                    self.zoomToExtent(extent)
                else:
                    logUICall.log(get_ui_string("widget.result.info.notfound"), logUICall.WARNING)
            dlg_search.destroy()
        except Exception as err:
            # thematic is not-critical, allow continue on exception
            logUICall.log(str(err), logUICall.WARNING)
            
    @logUICall
    @pyqtSlot()
    def mapIdentify(self):
        """ 
        event handler for btn_info 
        This only enables map querying, method connected to canvasClicked signal does
        the actual point-polygon query     
        """
        self.canvas.setMapTool(self.toolInfo)

    @logUICall
    @pyqtSlot()
    def selectExportFile(self):
        """
        event handler for btn_export_select_path 
        - open save file dialog box to select file name for export 
        """
        filename = QFileDialog.getSaveFileName(self,
                                               get_ui_string("widget.result.export.file.open"),
                                               ".", 
                                               self.ui.cb_export_format.currentText())
        if not filename.isNull():
            self.ui.txt_export_select_path.setText(filename) 
                    
    @logUICall
    @pyqtSlot(str)
    def exportFormatChanged(self, selected_val):
        """
        event handler for cb_export_format 
        - update selected file after format change
        """
        self.ui.txt_export_select_path.setText("")
        self.export_format = self.EXPORT_FORMATS[str(selected_val)]
        
    @logUICall
    @pyqtSlot()
    def exportData(self):
        """ 
        event handler for btn_export
        - do export data 
        """
        export_path = str(self.ui.txt_export_select_path.text())
        if export_path == "":
            logUICall.log(get_ui_string("app.error.path.is.null"), logUICall.WARNING)
            return
        self.app.exportResults(self.export_format, export_path)

        
    @logUICall
    @pyqtSlot(QPoint, QObject)
    def showInfo(self, point, mouseButton):
        """
        event handler for toolInfo
        @see QGIS tutorial for detail
        point-polygon search on currently selected layer  
        """
        cur_layer_name = self.ui.cb_layer_selector.currentText()
        if cur_layer_name.isEmpty():
            return
        try:
            cur_layer_idx = self.LAYER_NAMES.index(cur_layer_name)
            cur_layer = self.map_layers[cur_layer_idx]
            
            # if layer is not in same projection as map canvas
            # need to project query point
            if cur_layer.crs() != self.canvas.mapRenderer().destinationCrs():
                transform = QgsCoordinateTransform(self.canvas.mapRenderer().destinationCrs(), cur_layer.crs())
                point = transform.transform(point)
            
            # do query
            provider = cur_layer.dataProvider() 
            provider.rewind()
            feature = QgsFeature()
            colonIndexes = provider.attributeIndexes()
        
            # search using point as center of rectangle polygon
            search_buffer_x = self.canvas.extent().width() * self.SEARCH_BUFFER / self.canvas.width()
            search_buffer_y = self.canvas.extent().height() * self.SEARCH_BUFFER / self.canvas.height()
            provider.select(colonIndexes,
                            QgsRectangle(point.x()-search_buffer_x,
                                         point.y()-search_buffer_y,
                                         point.x()+search_buffer_x,
                                         point.y()+search_buffer_y),
                            True)
            # get selected and display in result detail dialog box 
            selected = []        
            while provider.nextFeature(feature):            
                # for polygons, only show geometry containing query point            
                if cur_layer.geometryType() == QGis.Polygon:                
                    if feature.geometry() is not None and not feature.geometry().contains (point):
                        continue
                selected.append(feature.attributeMap())

            if len(selected)>0:
                # display result if exists
                if cur_layer_idx == self.EXPOSURE:
                    self.dlgResultDetail.showExposureData(provider.fields(), selected)                    
                else:
                    self.dlgResultDetail.showInfoData(provider.fields(), selected)
                self.dlgResultDetail.exec_()
            else:
                logUICall.log(get_ui_string("widget.result.info.notfound"), logUICall.WARNING)
        except Exception as err:
            # point-in-polygon search is not critical, continue on error 
            logUICall.log(str(err), logUICall.WARNING)
        
    # public methods
    ###############################
    def set_project(self, project):
        ''' set project to preview. force refresh view on set'''        
        self._project = project
        if project is None:
            return
        self.refreshView()
        self.canvas.zoomToFullExtent()
        logUICall.log("Project preview initialized sucessfully", logUICall.INFO)
        
    def get_project(self):
        return self._project
    
    # property access to project
    project = property(get_project, set_project)
    
    def refreshView(self):
        ''' reload all QGIS layers in currently defined project '''
        if self._project is None:
            return
        
        # display layers if exists                
        if self._project.fp_file is not None and exists(self._project.fp_file):
            if self.map_layers[self.FOOTPRINT] is None or self.map_layers[self.FOOTPRINT].source() != self._project.fp_file:                            
                self.showDataLayer(self.FOOTPRINT, load_shapefile(self._project.fp_file, 'footprint'))
        else:            
            self.removeDataLayer(self.FOOTPRINT)
        
        if self._project.zone_file is not None and exists(self._project.zone_file):
            if self.map_layers[self.ZONES] is None or self.map_layers[self.ZONES].source() != self._project.zone_file:
                self.showDataLayer(self.ZONES, load_shapefile(self._project.zone_file, 'zones'))
        else:            
            self.removeDataLayer(self.ZONES)
            
        if self._project.survey_file is not None and exists(self._project.survey_file):
            if getattr(self._project, 'survey', None) is None:
                self._project.load_survey()
                self.showDataLayer(self.SURVEY, self._project.survey)
        else:            
            self.removeDataLayer(self.SURVEY)
        
        if self._project.popgrid_file is not None and exists(self._project.popgrid_file):
            if getattr(self._project, 'popgrid', None) is None:
                self.showDataLayer(self.POP_GRID, load_shapefile(self._project.popgrid_file, 'popgrid'))
        else:            
            self.removeDataLayer(self.POP_GRID)
        
        # set export options
        for idx, export_format in enumerate(self.EXPORT_FORMATS.values()):
            if export_format == self._project.export_type:
                self.ui.cb_export_format.setCurrentIndex(idx)
        self.ui.txt_export_select_path.setText(self._project.export_path)
        
        # refreshResult contains refresh call to update all layers currently loaded
        self.refreshResult()

    def refreshResult(self):
        ''' reload result QGIS layer and data quality reports in currently defined project '''
        exposure = getattr(self._project, 'exposure', None)        
        if exposure is not None:
            self.showDataLayer(self.EXPOSURE, exposure)
            has_result = True            
        else:
            self.removeDataLayer(self.EXPOSURE)
            has_result = False
        
        if has_result:
            # build quality report 
            report_lines = []
            if self._project.operator_options.has_key("proc.extrapolation"):
                proc_option = self._project.operator_options["proc.extrapolation"]
                if proc_option == ExtrapolateOptions.RandomWalk:
                    proc_method = get_ui_string("widget.result.dq.method", get_ui_string("dlg.options.ep.random"))
                elif proc_option == ExtrapolateOptions.Fraction:
                    proc_method = get_ui_string("widget.result.dq.method", get_ui_string("dlg.options.ep.fraction"))
                elif proc_option == ExtrapolateOptions.FractionRounded:
                    proc_method = get_ui_string("widget.result.dq.method", get_ui_string("dlg.options.ep.fraction.rounded"))
            else:
                proc_method = get_ui_string("widget.result.dq.method", get_ui_string("dlg.options.ep.random")) 
            report_lines.append(proc_method)
            report_lines.append('')
            
            # total tests
            report_lines.append(get_ui_string('widget.result.dq.total_tests', len(self._project.quality_reports.keys())))
            report_lines.append('')
            
            # detail for each test
            for key, report in self._project.quality_reports.iteritems():
                report_lines.append(get_ui_string('widget.result.dq.tests.%s' % key))            
                for title, value in report.iteritems():
                    report_lines.append( get_ui_string('widget.result.dq.tests.%s.%s' % (key, title), value) )
                report_lines.append('')                    
            self.ui.txt_dq_test_details.setText("\n".join(report_lines))


        self.ui.btn_export.setEnabled(has_result)
        self.ui.widget_dq_test.setVisible(has_result)
        self.ui.txt_export_select_path.setEnabled(has_result)
        self.ui.btn_export_select_path.setEnabled(has_result)
        self.ui.cb_export_format.setEnabled(has_result)        

        # this call refresh all layers currently loaded        
        self.refreshLayers()      

    @logUICall
    def closeResult(self):
        ''' remove from map result QGIS layer and reset quality report display '''
        self.canvas.setLayerSet([]) # call necessary to remove all layers to avoid disconnect errors  
        self.removeDataLayer(self.EXPOSURE)
        self.refreshLayers()
        self.ui.txt_dq_test_details.setText("")
        
    @logUICall
    def closeAll(self):
        ''' remove from map all QGIS layer in currently defined project '''
        self.ui.cb_layer_selector.clear()
        try:
            self.canvas.setLayerSet([]) # call necessary to remove all layers to avoid disconnect errors 
            for i in range(5):
                self.removeDataLayer(i)            
            self.ui.txt_dq_test_details.setText("")
            self.refreshLayers()
        except:            
            pass    # exception will is thrown when registry is empty
    
    # internal helper methods
    ###############################
    def showDataLayer(self, index, layer):
        """ display given QGIS layer on map """
        try:
            # add to QGIS registry and refresh view
            if self.map_layers[index] is not None:
                self.removeDataLayer(index)
            self.map_layers[index] = layer
            self.registry.addMapLayer(layer)
            layer.setRendererV2(self.map_layer_renderer[index])            
        except:
            pass

    def removeDataLayer(self, index):
        """ remove from map the layer identified with index """
        layer = self.map_layers[index]
        self.map_layers[index] = None    
        if layer is not None:                
            try:
                self.registry.removeMapLayer(layer.getLayerID(), False)
                del layer      
            except:
                pass # do nothing if it fails. probably already deleted

    def findFeatureExtentByAttribute(self, layer, field, value):
        """ 
        find extent of all objects in QGIS layer matching condition "field=value"         
        """
        fidx = layer_field_index(layer, field)
        if fidx == -1:
            return None
        xmin, xmax, ymin, ymax = 180, -180, 90, -90
        extent = QgsRectangle(xmin, ymin, xmax, ymax)
        need_transform = layer.crs() != self.canvas.mapRenderer().destinationCrs()
        if need_transform:
            transform = QgsCoordinateTransform(layer.crs(), self.canvas.mapRenderer().destinationCrs())
        for feature in layer_features(layer):
            if str(value) == feature.attributeMap()[fidx].toString():
                f_extent = feature.geometry().boundingBox()
                if need_transform:
                    f_extent = transform.transform(f_extent)
                xmin = min(f_extent.xMinimum(), xmin)
                xmax = max(f_extent.xMaximum(), xmax)
                ymin = min(f_extent.yMinimum(), ymin)
                ymax = max(f_extent.yMaximum(), ymax)
        extent.set (xmin, ymin, xmax, ymax)
        return extent

    def zoomToLayer(self, layer):
        """ zoom canvas to extent of given layer """
        try:
            lyr_extent = layer.extent()            
            if layer.crs() != self.canvas.mapRenderer().destinationCrs():
                transform = QgsCoordinateTransform(layer.crs(), self.canvas.mapRenderer().destinationCrs())
                lyr_extent = transform.transform(lyr_extent)
            self.zoomToExtent(lyr_extent)
        except:
            pass
    
    def zoomToExtent(self, extent):
        """ zoom canvas to given extent """
        try:
            self.canvas.setExtent(extent)
            self.canvas.zoomByFactor(1.1)
        except:
            self.mapZoomFull()
    
    def refreshLayers(self):
        """ refresh all layers in canvas """
        # add each layer according to order
        layerSet = []
        self.ui.cb_layer_selector.clear()
        for idx, lyr in enumerate(self.map_layers):
            if lyr is not None:
                layerSet.append(QgsMapCanvasLayer(lyr))
                self.ui.cb_layer_selector.addItem(self.LAYER_NAMES[idx])
        if len(layerSet) > 0:            
예제 #15
0
class ExploreMapWindow(QMainWindow):
    """This class offers a canvas and tools to preview and explore data 
        provided by Geocubes. Preview raster layers are fetched from the Geocubes
        cached WMTS server. The user can simply view the data or get legend info
        on a single point."""

    # the window is initiated with the Geocubes url base defined on the main plugin
    # this means that the base doesn't have to be manually changed here if it changes
    def __init__(self, url_base):
        QMainWindow.__init__(self)

        # creating map canvas, which draws the maplayers
        # setting up features like canvas color
        self.canvas = QgsMapCanvas()
        self.canvas.setMinimumSize(550, 700)
        self.canvas.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)
        self.canvas.setCanvasColor(Qt.white)
        #self.canvas.enableAntiAliasing(True)

        self.url_base = url_base

        # Qmainwindow requires a central widget. Canvas is placed
        self.setCentralWidget(self.canvas)
        """'tile widths' refer to the Map proxy WMTS server's settings for displaying
        data of different resolutions. If I understood correctly, these values 
        (got by examining properties of a layer from that server in QGIS) are
        the thresholds at which a different resolution is loaded on the GRIDI-FIN
        tileset. The values represent the tile size in map units (meters). 
        
        Each tile widths is tied to the corresponding resolution, which is used
        to get the correct resolution legend info. The method is only an estimation,
        but ought to produce good enough results for this purpose.
        Smallest resolutions (1, 2, 5) are omitted since only some layers 
        have them. """
        self.tile_widths = {
            2560: 10,
            5120: 20,
            12800: 50,
            25600: 100,
            51200: 200,
            128000: 500,
            256000: 1000
        }

        # get all keys i.e. widths
        self.all_widths = [i for i in self.tile_widths]

        # creating background layer box and housing it with the hardcoded options
        self.bg_layer_box = QComboBox()
        # if ortokuva ever updates to newer versions, just change the year here
        bg_layers = ['Taustakartta', 'Ortokuva_2018', 'No reference layer']

        # set 'No reference layer' as the default option
        self.bg_layer_box.addItems(layer for layer in bg_layers)
        self.bg_layer_box.setCurrentIndex(2)
        self.bg_layer_box.currentIndexChanged.connect(self.addBackgroundLayer)

        # initialize the slider that will control BG layer opacity/transparency
        self.opacity_slider = QSlider(Qt.Horizontal)
        self.opacity_slider.setMinimum(0)
        self.opacity_slider.setMaximum(100)
        self.opacity_slider.setSingleStep(1)
        self.opacity_slider.setMaximumWidth(100)
        self.opacity_slider.valueChanged.connect(self.setBackgroundMapOpacity)

        self.legend_checkbox = QCheckBox("Get attribute info on all layers")

        # explanatory texts for the different widgets are stored as label widgets
        bg_layer_label = QLabel(" Background: ")
        bg_opacity_label = QLabel(" BG opacity: ")
        data_label = QLabel("Data: ")
        spacing = QLabel(" ")

        # all of the data layers are housed in this combobox
        self.layer_box = QComboBox()
        self.layer_box.currentIndexChanged.connect(self.addLayer)

        # creating each desired action
        self.actionPan = QAction("Pan tool", self)
        self.actionLegend = QAction("Attribute info tool", self)
        self.actionCancel = QAction("Close window", self)
        self.actionZoom = QAction("Zoom to full extent", self)

        # these two work as on/off. the rest are clickable
        self.actionPan.setCheckable(True)
        self.actionLegend.setCheckable(True)

        # when actions are clicked, do corresponding function
        self.actionPan.triggered.connect(self.pan)
        self.actionLegend.triggered.connect(self.info)
        self.actionCancel.triggered.connect(self.cancel)
        self.actionZoom.triggered.connect(self.zoomToExtent)

        # defining two toolbars: first one houses layer and opacity selection
        # the other has all the tools and functions
        self.layers_toolbar = self.addToolBar("Select layers")
        self.layers_toolbar.setContextMenuPolicy(Qt.PreventContextMenu)
        self.layers_toolbar.setMovable(False)
        self.addToolBarBreak()
        self.tools_toolbar = self.addToolBar("Tools")
        self.tools_toolbar.setContextMenuPolicy(Qt.PreventContextMenu)
        self.tools_toolbar.setMovable(False)

        # change order here to change their placement on window
        # starting with the layer widgets and the corresponding label texts
        self.layers_toolbar.addWidget(data_label)
        self.layers_toolbar.addWidget(self.layer_box)
        self.layers_toolbar.addWidget(bg_layer_label)
        self.layers_toolbar.addWidget(self.bg_layer_box)
        self.layers_toolbar.addWidget(bg_opacity_label)
        self.layers_toolbar.addWidget(self.opacity_slider)
        self.layers_toolbar.addWidget(spacing)
        self.layers_toolbar.addWidget(self.legend_checkbox)

        # then setting all the canvas tools on the second toolbar
        self.tools_toolbar.addAction(self.actionLegend)
        self.tools_toolbar.addAction(self.actionPan)
        self.tools_toolbar.addAction(self.actionZoom)
        self.tools_toolbar.addAction(self.actionCancel)

        # a large text box that will house the legend info
        self.text_browser = QTextEdit("Legend will be shown here")
        self.text_browser.setReadOnly(True)

        # a dock widget is required for the text browser. Docked to main window
        dock_widget = QDockWidget()
        dock_widget.setFeatures(QDockWidget.NoDockWidgetFeatures)
        dock_widget.setWindowTitle("Legend")
        dock_widget.setWidget(self.text_browser)

        self.addDockWidget(Qt.RightDockWidgetArea, dock_widget)

        # link actions to premade map tools
        self.toolPan = QgsMapToolPan(self.canvas)
        self.toolPan.setAction(self.actionPan)

        self.toolClick = QgsMapToolEmitPoint(self.canvas)
        self.toolClick.canvasClicked.connect(self.getLegendInfo)

        # this is to ensure that the map isn't zoomed out everytime the layer changes
        self.first_start = True

        # this boolean is true while there is no active background layer
        # needed to ensure that e.g. opacity isn't attempted to be set on a nonexisting layer
        self.no_bg_layer_flag = True

        # set pantool as default
        self.pan()

    def pan(self):
        """Simply activates the tool and deactivates the other tool if active"""
        self.canvas.setMapTool(self.toolPan)
        # make sure the other button isn't checked to avoid confusion
        self.actionLegend.setChecked(False)

    def info(self):
        self.canvas.setMapTool(self.toolClick)
        self.actionLegend.setChecked(True)
        self.actionPan.setChecked(False)

    def zoomToExtent(self):
        """zooms out/in so that the raster layer is centered"""
        self.canvas.setExtent(self.layer.extent())
        self.canvas.refresh()

    def setBackgroundMapOpacity(self):
        if self.no_bg_layer_flag:
            return
        else:
            self.bg_layer.renderer().setOpacity(self.getBackgroundMapOpacity())
            self.canvas.refresh()

    def getBackgroundMapOpacity(self):
        """Returns the current BG layer opacity as a double [0, 1]. Slider only
            accepts integers, therefore the initial value is divided by hundred."""
        return (self.opacity_slider.value() / 100)

    def showCanvas(self, all_datasets):
        """Called to activate the the window. Input is all of the datasets on 
            the Geocubes server as a dictionary (see main plugin py-file). First a
            default layer (background map, which is on the WMTS server
            but not on Geocubes files) is inserted to the combobox. Then the 
            keys of the dictionary (which are in format layer_name;year) are inserted."""

        # empty box on restart
        self.layer_box.clear()
        self.all_datasets = all_datasets
        self.no_bg_layer_flag = True

        for key in self.all_datasets:
            self.layer_box.addItem(key)

        # zoom to the full extent of the current map
        self.zoomToExtent()

        # default values set
        self.text_browser.setText("Legend will be shown here")
        self.bg_layer_box.setCurrentIndex(2)
        self.opacity_slider.setValue(50)
        self.show()

    def getLegendInfo(self, point):
        """Activated when the canvas is clicked. The click returns a point, which
            is parsed to a string of X and Y coordinates separated by a comma.
            An url to get legend info on this point is formed and used.
            If the request is succesful, the response string is decoded and passed
            to be inserted to the text browser."""
        formatted_point = str(int(point.x())) + "," + str(int(point.y()))

        url = self.formLegendUrl(formatted_point)

        if not url:
            return

        response = requests.get(url, timeout=6)

        # 200 = succesful request
        # the field won't be updated in case of a failed request
        if response.status_code == 200:
            legend_string = response.content.decode("utf-8")
            self.setTextToBrowser(legend_string)

    def setTextToBrowser(self, string):
        """Formats and inserts legend text to the browser. Input is string of 
        raw text data. This is split at semicolons if there are multiple features."""

        # empty on multiple clicks
        self.text_browser.clear()
        strings = string.split(';')

        # no need for a loop if there's only one line
        if len(strings) == 1:
            self.text_browser.setText(string)
        else:
            for text_string in strings:
                # appending allows to insert multi-line texts
                self.text_browser.append(text_string)

    def formLegendUrl(self, formatted_point):
        """Forms an url for querying legend data on a specific coordinate point.
            Data is queried either from the currently selected layer or, if selected
            by the user, from all available layers."""
        key = self.layer_box.currentText()
        resolution = self.getResolutionFromExtent()

        if not key:
            return

        if not resolution:
            resolution = 100

        if self.legend_checkbox.isChecked():
            layer_name = "all"
            year = "2015"
        else:
            value = self.all_datasets[key]
            layer_name, year = value[0], value[3]

        url = (self.url_base + "/legend/" + str(resolution) + "/" +
               layer_name + "/" + formatted_point + "/" + year)

        return url

    def getResolutionFromExtent(self):
        """Estimates the resolution of the imagery currently viewed by user
         based on the width of the canvas. Returns said resolution. Used by
         the legend tool to get info of the correct dataset."""
        # extent as a QgsRectangle
        canvas_extent = self.canvas.extent()

        # width (in meters, since CRS is EPSG:3067) of the current canvas view
        width = canvas_extent.xMaximum() - canvas_extent.xMinimum()

        # find the width threshold closest to the current one
        try:
            closest_width = min(self.all_widths, key=lambda x: abs(x - width))
        except Exception:
            return

        # use the width key to get the corrensponding resolution
        closest_resolution = self.tile_widths[closest_width]

        return closest_resolution

    def addLayer(self):
        """Adds a new layer on the map canvas based on the selection on the combobox.
            Everything else is hardcoded, but the layer name of course changes.
            Layers are identified by name and year (i.e. km2_2018). These type
            of strings are formed first, then the whole url"""
        # often a layer already exists. If so, remove
        try:
            QgsProject.instance().removeMapLayer(self.layer)
        except Exception:
            pass

        key = self.layer_box.currentText()

        if not key:
            return
        # background map doesn't have a specific year attached to it
        if key == "Taustakartta":
            layer_name = key
        else:
            # the desired parameters are housed in the dictionary. Luckily the
            # combobox houses the keys to it. gets a tuple with four values
            value = self.all_datasets[key]
            # name is first value, year last. separated with an underscore
            layer_name = value[0] + "_" + value[3]

        url = ("https://vm0160.kaj.pouta.csc.fi/ogiir_cache/wmts/1.0.0/" +
               "WMTSCapabilities.xml&crs=EPSG:3067&dpiMode=7&format=image/" +
               "png&layers=" + layer_name +
               "&styles=default&tileMatrixSet=GRIDI-FIN")

        self.layer = QgsRasterLayer("url=" + url,
                                    'GEOCUBES DATALAYER - TEMPORARY', 'wms')

        if self.layer.isValid():
            QgsProject.instance().addMapLayer(self.layer, False)
            # if layer is valid and added to the instance, insert it to the canvas
            self.setMapLayers()
            # zoom to the full extent of the map if canvas is started for the first time
            if self.first_start:
                self.zoomToExtent()
                self.first_start = False

    def addBackgroundLayer(self):
        """Adds a background layer to help user locating what they want.
            This layer will be either background map (taustakarta), ortographic
            imagery or nothing at all. BG layer has an opacity value that set by
            the user. Function is called when user selects a layer on the
            combobox."""
        layer_name = self.bg_layer_box.currentText()

        # remove the old background layer, if one exists
        try:
            QgsProject.instance().removeMapLayer(self.bg_layer)
        except Exception:
            pass

        # if user wants no background layer, return without setting a new layer
        if not layer_name or layer_name == 'No reference layer':
            self.no_bg_layer_flag = True
            self.canvas.refresh()
            return
        else:
            self.bg_layer = QgsRasterLayer(
                "url=https://vm0160.kaj.pouta.csc.fi/ogiir_cache/wmts/1.0.0/" +
                "WMTSCapabilities.xml&crs=EPSG:3067&dpiMode=7&format=image/" +
                "png&layers=" + layer_name.lower() +
                "&styles=default&tileMatrixSet=GRIDI-FIN",
                'GEOCUBES BG-LAYER - TEMPORARY', 'wms')

            if self.bg_layer.isValid():
                self.no_bg_layer_flag = False
                QgsProject.instance().addMapLayer(self.bg_layer, False)
                self.bg_layer.renderer().setOpacity(
                    self.getBackgroundMapOpacity())
                self.setMapLayers()

    def setMapLayers(self):
        """Called anytime a new layer is added to the project instance.
        Setting layers to canvas decides what's shown to the user and in which
        order. If there's a background layer, it must be set before the data layer."""
        if self.no_bg_layer_flag:
            self.canvas.setLayers([self.layer])
        else:
            self.canvas.setLayers([self.bg_layer, self.layer])

        self.canvas.refresh()

    def cancel(self):
        self.close()

    def closeEvent(self, event):
        """Activated anytime Mapwindow is closed either by buttons given or
            if the user finds some other way to close the window. 
            Deletes scrap maplayers."""
        try:
            QgsProject.instance().removeMapLayer(self.layer)
            QgsProject.instance().removeMapLayer(self.bg_layer)
        except Exception:
            pass
        QMainWindow.closeEvent(self, event)
class AuxiliaryWindow(QMainWindow):
  
  closed = pyqtSignal( int )
  
  def __init__(self, parent, geometryWin, numWin):
    
    def populateStatusBar():
      statusBar = self.statusBar()

      w = QCheckBox( "Render", self )
      w.setObjectName( 'renderCheck')
      w.setToolTip( "Toggle map rendering" )
      w.setChecked( True )
      statusBar.addPermanentWidget( w )

      w = QCheckBox( "Marker", self )
      w.setObjectName( 'markerCheck')
      w.setToolTip( "Toggle marker with cursor position from main map" )
      w.setChecked( False )
      statusBar.addPermanentWidget( w, 1 )

      w = QCheckBox( "Extent", self )
      w.setObjectName( 'extentCheck')
      w.setToolTip( "Show extent of main map" )
      w.setChecked( False )
      statusBar.addPermanentWidget( w, 1 )

      w = QToolButton(self)
      w.setObjectName( 'highlightBtn')
      w.setToolTip( "Highlight extent in main map" )
      w.setText("Highlight")
      statusBar.addPermanentWidget( w, 1 )

      w = QLabel( "Scale factor:", self )
      w.setObjectName( 'scaleFactorLabel')
      w.setAlignment(Qt.AlignRight | Qt.AlignVCenter)
      statusBar.addPermanentWidget( w, 1 )

      w = QDoubleSpinBox(self)
      w.setObjectName( 'scaleFactorSpin')
      w.setToolTip( "Current scale factor of main map" )
      w.setMinimum(0.0)
      w.setMaximum(1000.0)
      w.setDecimals(3)
      w.setValue(1)
      w.setSingleStep(.05)
      statusBar.addPermanentWidget( w, 1 )

      w = QToolButton(self)
      w.setObjectName( 'scaleBtn')
      w.setToolTip( "Set scale for main map" )
      w.setText("Scale: ")
      statusBar.addPermanentWidget( w, 1 )

    def setupUi():
      self.setObjectName( "AuxiliaryWindow" )
      self.setGeometry( geometryWin )
      self.addDockWidget ( Qt.LeftDockWidgetArea, self.dockLegend )
      self.actLegend = self.menuBar().addAction("")
      self.actLegend.triggered.connect( self.onActionLegend )
      self.canvas.setMapTool( self.toolPan )
      self.canvas.setCanvasColor( QColor(255,255,255) )
      self.canvas.enableAntiAliasing( False )
      self.canvas.useImageToRender( False )
      self.canvas.setWheelAction( QgsMapCanvas.WheelZoom )
      self.setCentralWidget( centralWidget )
      self.messageBar.setSizePolicy( QSizePolicy.Minimum, QSizePolicy.Fixed )
      layout = QGridLayout()
      layout.setContentsMargins( 0, 0, 0, 0 )
      layout.addWidget( self.canvas, 0, 0, 2, 1 )
      layout.addWidget( self.messageBar, 0, 0, 1, 1 )
      centralWidget.setLayout( layout )

    super( AuxiliaryWindow, self ).__init__( parent )

    centralWidget = QWidget( self )
    self.canvas = QgsMapCanvas( centralWidget )
    self.messageBar = QgsMessageBar( centralWidget )
    self.toolPan = QgsMapToolPan( self.canvas )
    self.qgisCanvas = qgis.utils.iface.mapCanvas()
    self.qgisTView = qgis.utils.iface.layerTreeView()
    self.qgisSyncGroup = None
    self.numWin = numWin

    self.ltg = QgsLayerTreeGroup('', Qt.Unchecked)
    self.dockLegend = AuxiliaryLegend( self, numWin )
    self.root = QgsProject.instance().layerTreeRoot()
    

    self.extent = self.actLegend = None
    self.marker = MarkerWindow( self.canvas )

    setupUi()
    populateStatusBar()

    self.onCurrentLayerChanged( None )
    self.onDestinationCrsChanged_MapUnitsChanged()
    self.onHasCrsTransformEnabledChanged( self.qgisCanvas.hasCrsTransformEnabled() )
    
  def _connect(self, isConnect = True):
    widgets = {
     'scaleBtn': self.findChild( QToolButton, 'scaleBtn'),
     'renderCheck': self.findChild( QCheckBox, 'renderCheck'),
     'markerCheck': self.findChild( QCheckBox, 'markerCheck'),
     'extentCheck': self.findChild( QCheckBox, 'extentCheck'),
     'highlightBtn': self.findChild( QToolButton, 'highlightBtn'),
     'scaleFactorSpin': self.findChild( QDoubleSpinBox, 'scaleFactorSpin')
    }
    signal_slot = (
      { 'signal': widgets['scaleBtn'].clicked, 'slot': self.onClickedScale },
      { 'signal': widgets['renderCheck'].toggled, 'slot': self.onToggledRender },
      { 'signal': widgets['markerCheck'].toggled, 'slot': self.onToggledMarker },
      { 'signal': widgets['extentCheck'].toggled, 'slot': self.onToggledExtent },
      { 'signal': widgets['highlightBtn'].clicked, 'slot': self.onClickedHighlight },
      { 'signal': widgets['scaleFactorSpin'].valueChanged, 'slot': self.onValueChangedScale },
      { 'signal': self.dockLegend.currentLayerChanged, 'slot': self.onCurrentLayerChanged },
      { 'signal': self.dockLegend.currentLayerQgis, 'slot': self.onCurrentLayerQgis },
      { 'signal': self.dockLegend.syncGroupLayer, 'slot': self.onSyncGroupAddLayersQgis },
      { 'signal': self.dockLegend.addSelectedLayersQgis, 'slot': self.onAddSelectedLayersQgis },
      { 'signal': self.dockLegend.removeLayer, 'slot': self.onRemoveLayers },
      { 'signal': self.dockLegend.needSelectLayer, 'slot': self.onNeedSelectLayer },
      { 'signal': self.dockLegend.closed, 'slot': self.onClosedLegend },
      { 'signal': self.canvas.extentsChanged, 'slot': self.onExtentsChangedMirror },
      { 'signal': self.qgisCanvas.extentsChanged, 'slot': self.onExtentsChangedQgisCanvas },
      { 'signal': self.qgisCanvas.xyCoordinates, 'slot': self.marker.onXYCoordinates },
      { 'signal': self.qgisCanvas.destinationCrsChanged, 'slot': self.onDestinationCrsChanged_MapUnitsChanged },
      { 'signal': self.qgisCanvas.mapUnitsChanged, 'slot': self.onDestinationCrsChanged_MapUnitsChanged },
      { 'signal': self.qgisCanvas.hasCrsTransformEnabledChanged, 'slot': self.onHasCrsTransformEnabledChanged },
      { 'signal': self.root.removedChildren, 'slot': self.onRemovedChildrenQgisRoot },
      { 'signal': QgsMapLayerRegistry.instance().layersWillBeRemoved, 'slot': self.onLayersWillBeRemoved }
    )
    if isConnect:
      for item in signal_slot:
        item['signal'].connect( item['slot'] )
    else:
      for item in signal_slot:
        item['signal'].disconnect( item['slot'] )

  def _extentsChanged(self, canvasOrigin, originSlot, canvasDest, scaleFactor=None):
    canvasOrigin.extentsChanged.disconnect( originSlot )

    if scaleFactor is None:
      scale = canvasOrigin.scale()
      canvasOrigin.setExtent( canvasDest.extent() )
      canvasOrigin.zoomScale( scale )
    else:
      canvasOrigin.setExtent( canvasDest.extent() )
      canvasOrigin.zoomScale( scaleFactor * canvasDest.scale() )

    canvasOrigin.extentsChanged.connect( originSlot )

  def _textScaleBtnChanched(self):
    scale = locale.format( "%.0f", self.canvas.scale(), True ) 
    w = self.findChild( QToolButton, 'scaleBtn' )
    w.setText("Scale 1:%s" % scale )

  def _extent(self):
   rect = self.qgisCanvas.extent()
   p1 = QgsPoint( rect.xMinimum() , rect.yMinimum() )
   p2 = QgsPoint( rect.xMinimum() , rect.yMaximum() )
   p3 = QgsPoint( rect.xMaximum() , rect.yMaximum() )
   p4 = QgsPoint( rect.xMaximum() , rect.yMinimum() )
   p5 = QgsPoint( rect.xMinimum() , rect.yMinimum() )
   points = [ p1, p2, p3, p4, p5 ]
   self.extent.setToGeometry(QgsGeometry.fromPolyline (points), None)

  def _execFunction( self, func, arg, signal, slot):
   signal.disconnect( slot )
   func( arg )
   signal.connect( slot )

  def _connectVectorRefresh(self, layer, isConnect=True):
    if isinstance( layer, QgsVectorLayer ):
      f = layer.editCommandEnded.connect if isConnect else layer.editCommandEnded.disconnect
      f( self.canvas.refresh )

  def _addLayersQgis( self, layersQgis, needMsg=True ):
    
    self.dockLegend.clearBridge()

    l1 = set( layersQgis )
    l2 = set( map( lambda item: item.layer(), self.ltg.findLayers() ) )
    layers = list( l1 - l2 )
    if len( layers ) == 0:
      if needMsg:
        self.messageBar.pushMessage("Need select new layer(s) in main map", QgsMessageBar.WARNING, 2 )
    else:
      # Get order by layersQgis
      for item in layersQgis:
        if item in layers:
          self.ltg.addLayer( item )
          self._connectVectorRefresh( item )

    self.dockLegend.setBridge( self.canvas )

  def _syncGroupAddLayersQgis( self, ltg ):
    layersQgis = map( lambda item: item.layer(), ltg.findLayers() )
    if len( layersQgis ) == 0:
      return False

    name = ltg.name()
    if self.qgisSyncGroup == ltg:
      msg = "Already synchronized group (main map) -> '%s'" % name
      self.messageBar.pushMessage( msg, QgsMessageBar.INFO, 4 )
      return True
    
    if not self.qgisSyncGroup is None:
      self.qgisSyncGroup.addedChildren.disconnect( self.addedChildrenLayer )
      
    self.qgisSyncGroup = ltg
    self.qgisSyncGroup.addedChildren.connect( self.addedChildrenLayer )
    
    self.dockLegend.addNameSyncGroup( name )
    msg = "Changed synchronized group (main map) -> '%s'" % name
    self.messageBar.pushMessage( msg, QgsMessageBar.INFO, 4 )
    
    self._addLayersQgis( layersQgis )
    return True

  def run(self):
    
    if len( self.qgisTView.selectedLayerNodes() ) > 0:
      self.onAddSelectedLayersQgis()
    else:
      ltn = self.qgisTView.currentNode()
      if not isinstance( ltn, QgsLayerTreeGroup ):
        return False
      else:
        if ltn == self.root:
          return False
        else:
          if not self._syncGroupAddLayersQgis( ltn ):
            return False

    self.dockLegend.setBridge( self.canvas)

    self.canvas.setRenderFlag( False )
    self.show() # Need show before self._connect()
    self._connect()
    self.canvas.setExtent( self.qgisCanvas.extent() )
    w = self.findChild( QDoubleSpinBox, 'scaleFactorSpin')
    w.setValue( 1 )
    self.canvas.setRenderFlag( True )

    return True

  def getLayersCanvas(self):
    layerIds = map(lambda x: x.layerId(), self.ltg.findLayers() )
    layerChecks = map(lambda x: str( x.isVisible() ), self.ltg.findLayers() )

    return ( layerIds, layerChecks )

  def setLayersCanvas(self, layerIds, layerChecks ):
    prevFlag = self.canvas.renderFlag()
    self.canvas.setRenderFlag( False )

    lyrRegs = QgsMapLayerRegistry.instance()
    for id in range( len( layerIds ) ):
      layer = lyrRegs.mapLayer(  layerIds[id] )
      isVisible = int( layerChecks[id] )
      if not layer is None:
        self.ltg.addLayer( layer ).setVisible( isVisible )

    self.canvas.setRenderFlag( prevFlag )

  def getWindowSetting(self):
    g = self.geometry()
    r = self.canvas.extent()
    nodes = self.ltg.findLayers()
    currentLayer = self.dockLegend.tview.currentLayer()
    currentLayerId = currentLayer.id() if not currentLayer is None else "None"
    
    windowSetting =  {
      'numWin': self.numWin,
      'geometryWin': { 'x': g.x(), 'y': g.y(), 'width': g.width(), 'height': g.height() },
      'extentCanvas': { 'xmin': r.xMinimum(), 'ymin': r.yMinimum(), 'xmax': r.xMaximum(), 'ymax': r.yMaximum() },
      'currentLayerId': currentLayerId,
      'layerIds' : ' '.join( map(lambda item: item.layerId(), nodes ) ),
      'visibles': ' '.join( map(lambda item: str( int( item.isVisible() ) ), nodes ) )
    }
    for item in ( 'render', 'marker', 'extent' ):
      nameGui = "%sCheck" % item
      windowSetting[ item ] = int( self.findChild( QCheckBox, nameGui).isChecked() )

    return windowSetting

  def setWindowSetting(self, windowSetting):
    self.numWin = windowSetting['numWin']

    # Populate with layers and set Bridge for legend
    layerIds = windowSetting['layerIds'].split(' ')
    visibles = map( lambda item: bool( int( item ) ), windowSetting['visibles'].split(' ') )
    ltg = self.qgisTView.layerTreeModel().rootGroup()
    for id in range( len( layerIds ) ):
      node = ltg.findLayer( layerIds[ id ] )
      if node is None:
        continue
      layer = node.layer()
      visible = Qt.Checked if visibles[ id ] else Qt.Unchecked
      self._connectVectorRefresh( layer )
      self.ltg.addLayer( layer ).setVisible( visible )
    self.dockLegend.setBridge( self.canvas)
    
    self.show() # Need show before self._connect()
    self._connect()
    node = ltg.findLayer( windowSetting['currentLayerId'] )
    if not node is None:
      layer = node.layer()
      self.dockLegend.tview.setCurrentLayer( layer )
    w = windowSetting['extentCanvas']
    self.canvas.setExtent( QgsRectangle( w['xmin'], w['ymin'], w['xmax'], w['ymax'] ) )
    for item in ( 'render', 'marker', 'extent' ):
      value = bool( windowSetting[ item ] )
      nameGui = "%sCheck" % item
      self.findChild( QCheckBox, nameGui ).setChecked( value )

  def closeEvent(self, event):
    self._connect( False )
    event.accept()
    self.closed.emit( self.numWin )

  @pyqtSlot(int)
  def onValueChangedScale(self, scaleFactor):
    w = self.findChild( QCheckBox, 'renderCheck')
    if not w.isChecked():
      return
    self._execFunction(
      self.canvas.zoomScale, scaleFactor * self.qgisCanvas.scale(),
      self.canvas.extentsChanged, self.onExtentsChangedMirror
    )
    self._textScaleBtnChanched()

  @pyqtSlot()
  def onClickedScale(self):
    self._execFunction( 
        self.qgisCanvas.zoomScale, self.canvas.scale(),
        self.qgisCanvas.extentsChanged, self.onExtentsChangedQgisCanvas
    )
    w = self.findChild( QDoubleSpinBox, 'scaleFactorSpin' )
    self._execFunction( w.setValue, 1.0, w.valueChanged, self.onValueChangedScale )

  @pyqtSlot()
  def onClickedHighlight(self):
    def removeRB():
      rb.reset( True )
      self.qgisCanvas.scene().removeItem( rb )
    
    rb = QgsRubberBand( self.qgisCanvas, QGis.Polygon)
    rb.setBorderColor( QColor( 255,  0, 0 ) )
    rb.setWidth( 2 )
    rb.setToGeometry( QgsGeometry.fromRect( self.canvas.extent() ), None )
    QTimer.singleShot( 2000, removeRB )

  @pyqtSlot(bool)
  def onToggledRender(self, enabled):
    if enabled:
      self.canvas.setMapTool(self.toolPan)
      w = self.findChild( QDoubleSpinBox, 'scaleFactorSpin' )
      self._extentsChanged( self.canvas, self.onExtentsChangedMirror, self.qgisCanvas, w.value() )
      self._textScaleBtnChanched()
      self.canvas.setWheelAction( QgsMapCanvas.WheelZoom )
    else:
      self.canvas.unsetMapTool(self.toolPan)
      self.canvas.setWheelAction( QgsMapCanvas.WheelNothing )
    self.canvas.setRenderFlag( enabled )

  @pyqtSlot(bool)
  def onToggledMarker(self, enabled):
    self.marker.add() if enabled else self.marker.remove() 

  @pyqtSlot(bool)
  def onToggledExtent(self, enabled):
    def setExtent():
      if not self.extent is None:
        self.canvas.scene().removeItem( self.extent )
      self.extent = QgsRubberBand( self.canvas, QGis.Polygon )
      self.extent.setBorderColor( QColor( 255, 0 , 0 ) )
      self.extent.setWidth( 2 )
      self._extent()

    if enabled:
      setExtent()
    else:
      if not self.extent is None:
        self.canvas.scene().removeItem( self.extent )
        self.extent = None

  @pyqtSlot()
  def onExtentsChangedMirror(self):
    w = self.findChild( QCheckBox, 'renderCheck')
    if not w.isChecked():
      return
    self._extentsChanged( self.qgisCanvas, self.onExtentsChangedQgisCanvas, self.canvas )
    self._textScaleBtnChanched()
    w = self.findChild( QDoubleSpinBox, 'scaleFactorSpin' )
    self._execFunction(
        w.setValue, self.canvas.scale() / self.qgisCanvas.scale(),
        w.valueChanged, self.onValueChangedScale
    )
    if not self.extent is None:
      self._extent()

  @pyqtSlot()
  def onExtentsChangedQgisCanvas(self):
    w = self.findChild( QCheckBox, 'renderCheck')
    if not w.isChecked():
      return
    w = self.findChild( QDoubleSpinBox, 'scaleFactorSpin' )
    self._extentsChanged( self.canvas, self.onExtentsChangedMirror, self.qgisCanvas, w.value() )
    self._textScaleBtnChanched()
    if not self.extent is None:
      self._extent()

  @pyqtSlot()
  def onDestinationCrsChanged_MapUnitsChanged(self):
    prevFlag = self.canvas.renderFlag()
    self.canvas.setRenderFlag( False )

    self.canvas.setDestinationCrs( self.qgisCanvas.mapRenderer().destinationCrs()  )
    self.canvas.setMapUnits( self.qgisCanvas.mapUnits() )

    self.canvas.setRenderFlag( prevFlag )

  @pyqtSlot(bool)
  def onHasCrsTransformEnabledChanged(self, enabled):
    prevFlag = self.canvas.renderFlag()
    self.canvas.setRenderFlag( False )
    self.canvas.mapRenderer().setProjectionsEnabled( enabled )
    self.canvas.setRenderFlag( prevFlag )

  @pyqtSlot(list)
  def onLayersWillBeRemoved( self, theLayerIds ):
    ids = list( set( self.ltg.findLayerIds() ) & set( theLayerIds ) ) # intersection
    nodes = map( lambda item: self.ltg.findLayer( item ), ids )
    for item in nodes:
      self._connectVectorRefresh( item.layer(), False )
      self.ltg.removeChildNode( item )

  @pyqtSlot()
  def onAddSelectedLayersQgis( self ):
    layersQgis = map( lambda item: item.layer(), self.qgisTView.selectedLayerNodes() )
    self._addLayersQgis( layersQgis )

  @pyqtSlot('QgsLayerTreeNode', int, int)
  def addedChildrenLayer(self, ltg, indexFrom, indexTo):
    layersQgis = map( lambda item: item.layer(), ltg.findLayers() )
    self._addLayersQgis( layersQgis, False )

  @pyqtSlot('QgsLayerTreeNode', int, int)
  def onRemovedChildrenQgisRoot(self, ltg, indexFrom, indexTo):
    if not self.qgisSyncGroup is None and not self.qgisSyncGroup in self.root.children():
      self.qgisSyncGroup = None
      self.dockLegend.addNameSyncGroup( "None" )
      msg = "Removed synchronized group (main map)"
      self.messageBar.pushMessage( msg, QgsMessageBar.INFO, 4 )
      
      
  @pyqtSlot()
  def onSyncGroupAddLayersQgis( self):
    
    msg = "Need active a group in main map with new layers"
    ltn = self.qgisTView.currentNode()
    if not isinstance( ltn, QgsLayerTreeGroup ) or ltn == self.root:
      self.messageBar.pushMessage( msg, QgsMessageBar.WARNING, 3 )
      return

    if not self._syncGroupAddLayersQgis( ltn ):
      self.messageBar.pushMessage( msg, QgsMessageBar.WARNING, 3 )

  @pyqtSlot( 'QgsMapLayer' )
  def onRemoveLayers( self, layer ):
    self._connectVectorRefresh(layer, False)

  @pyqtSlot()
  def onNeedSelectLayer(self):
    self.messageBar.pushMessage("Need select layer(s)", QgsMessageBar.WARNING, 2 )    

  @pyqtSlot('QgsMapLayer')
  def onCurrentLayerQgis(self, layer ):
    if layer is None:
      self.messageBar.pushMessage("Need active layer", QgsMessageBar.WARNING, 2 )
    else:
      self.qgisTView.setCurrentLayer( layer )

  @pyqtSlot('QgsMapLayer')
  def onCurrentLayerChanged(self, layer ):
    hasLayer = True if not layer is None else False
    selectName = "Select layer '%s'" % layer.name() if hasLayer else "None select layer"
    title = "#%d - %s" % ( self.numWin, selectName )
    self.setWindowTitle( title )

  @pyqtSlot()
  def onClosedLegend(self):
    self.actLegend.setText( "Show layers" )

  @pyqtSlot()
  def onActionLegend(self):
    self.actLegend.setText( "" )
    self.dockLegend.show()
예제 #17
0
class RenderWidget(QWidget):
    def __init__(self, parent=None):
        QWidget.__init__(self, parent)
        self.setupUi()
        self.active_layers = None  # instances of active layers
        self.crs = None

    def setupUi(self):
        gridLayout = QGridLayout(self)
        gridLayout.setContentsMargins(0, 0, 0, 0)
        self.canvas = QgsMapCanvas()
        self.canvas.setCanvasColor(QColor(255, 255, 255))
        self.canvas.setStyleSheet("border: 0px;")
        settings = QSettings()
        self.canvas.enableAntiAliasing(
            settings.value("/qgis/enable_anti_aliasing", False, type=bool))
        self.setMinimumSize(15, 15)
        # action pan and zoom
        self.default_point_tool = QgsMapToolPan(self.canvas)
        self.canvas.setMapTool(self.default_point_tool, clean=True)

        gridLayout.addWidget(self.canvas)

    def refresh(self):
        if self.active_layers is not None:
            [
                active_layer.layer.reload()
                for active_layer in self.active_layers
                if active_layer.is_active
            ]
            [
                active_layer.layer.triggerRepaint()
                for active_layer in self.active_layers
                if active_layer.is_active
            ]
        self.canvas.refreshAllLayers()

    def set_crs(self, crs):
        self.crs = crs
        self.update_render_layers()

    def update_render_layers(self):
        with block_signals_to(self):
            # set the CRS of the canvas view
            if self.crs:
                # use the crs of thematic raster to edit
                self.canvas.setDestinationCrs(self.crs)
            else:
                # use the crs set in Qgis
                self.canvas.setDestinationCrs(
                    iface.mapCanvas().mapSettings().destinationCrs())
            # get all valid activated layers
            valid_layers = [
                active_layer.layer for active_layer in self.active_layers
                if active_layer.is_active
            ]
            if len(valid_layers) == 0:
                self.canvas.setLayers([])
                self.refresh()
                return
            # set to canvas
            self.canvas.setLayers(valid_layers)
            # set init extent from other view if any is activated else set layer extent
            from ThRasE.gui.main_dialog import ThRasEDialog
            others_extents = [
                view_widget.render_widget.canvas.extent()
                for view_widget in ThRasEDialog.view_widgets
                if view_widget.is_active and view_widget.render_widget != self
                and not view_widget.render_widget.canvas.extent().isEmpty()
            ]
            if others_extents:
                # set extent using the extent of the other valid view (or self) with at least one layer
                extent = others_extents[0]
                self.update_canvas_to(extent)
            elif self.canvas.extent().isEmpty():
                # first layer to render
                # set the extent using the extent of the Qgis project but first transform the crs if it is different
                new_layer = valid_layers[0]
                transform = QgsCoordinateTransform(
                    new_layer.crs(),
                    self.canvas.mapSettings().destinationCrs(),
                    QgsProject.instance())
                new_extent = transform.transformBoundingBox(new_layer.extent())
                self.canvas.setExtent(new_extent)

            self.refresh()

    def update_canvas_to(self, new_extent):
        with block_signals_to(self.canvas):
            self.canvas.setExtent(new_extent)
            self.refresh()
class PdfMaker(object):
    """A generator that takes a QGIS project file and a layout template and makes a pdf."""
    def __init__(self, template_path, debug=False):
        """Constructor.

        :param template_path: Absolute path to a QGIS composer template file.
        :type template_path: str
        """
        gui_flag = True
        self.app = QgsApplication(sys.argv, gui_flag)

        # Make sure QGIS_PREFIX_PATH is set in your env if needed!
        self.app.initQgis()

        if debug:
            print QgsProviderRegistry.instance().pluginList()

        self.canvas = QgsMapCanvas()
        self.canvas.enableAntiAliasing(True)

        self.template_path = template_path

    def __del__(self):
        """Destructor."""
        del self.app

    def _load_template(self):
        """Load the template.

        :return: QgsComposition containing the loaded template.
        :rtype: QgsComposition
        """
        template_file = file(self.template_path)
        template_content = template_file.read()
        template_file.close()
        document = QDomDocument()
        document.setContent(template_content)
        composition = QgsComposition(self.canvas.mapSettings())
        # You can use this to replace any string like this [key]
        # in the template with a new value. e.g. to replace
        # [date] pass a map like this {'date': '1 Jan 2012'}
        substitution_map = {'DATE_TIME_START': 'foo', 'DATE_TIME_END': 'bar'}
        composition.loadFromTemplate(document, substitution_map)
        return composition

    def _load_layers(self):
        """Manually load all the layers for our project.

        :return: A list of QgsMapLayer instances.
        :rtype: list
        """
        layers = []

        # First the RW layer

        host = 'db'
        port = '5432'
        user = '******'
        password = '******'
        dbname = 'gis'
        uri = QgsDataSourceURI()
        uri.setConnection(host, port, dbname, user, password)

        schema = 'public'
        table = 'flood_mapper_rw'
        geometry_column = 'geometry'
        where_clause = ''
        title = 'RW'
        uri.setDataSource(schema, table, geometry_column, where_clause)
        layer = QgsVectorLayer(uri.uri(), title, 'postgres')

        QgsMapLayerRegistry.instance().addMapLayer(layer, False)
        canvas_layer = QgsMapCanvasLayer(layer)
        layers.append(canvas_layer)

        # Now the JK layer
        path = './data/jk.shp'
        title = 'JK'
        layer = QgsVectorLayer(path, title, 'ogr')
        QgsMapLayerRegistry.instance().addMapLayer(layer, False)
        canvas_layer = QgsMapCanvasLayer(layer)
        layers.append(canvas_layer)

        return layers

    def make_pdf(self, pdf_path):
        """Generate a pdf for the given project and template files.

        :param pdf_path: Absolute path for the output PDF file.
        :type pdf_path: str

        """

        layers = self._load_layers()
        self.canvas.setLayerSet(layers)

        if self.canvas.layerCount() < 1:
            print 'No layers loaded from this project, exiting.'
            return
        self.canvas.setDestinationCrs(
            QgsCoordinateReferenceSystem('EPSG:3857'))
        self.canvas.setCrsTransformEnabled(True)
        self.canvas.zoomToFullExtent()

        print 'Extent: %s' % self.canvas.mapSettings().extent().toString()
        # self._load_project()
        composition = self._load_template()
        # You must set the id in the template
        map_item = composition.getComposerItemById('map')
        map_item.setMapCanvas(self.canvas)
        map_item.zoomToExtent(self.canvas.extent())
        # You must set the id in the template
        legend_item = composition.getComposerItemById('legend')
        legend_item.updateLegend()
        composition.refreshItems()
        composition.exportAsPDF(pdf_path)
        QgsProject.instance().clear()
class AuxiliaryWindow(QMainWindow):
    """Creates a New Canvas."""

    closed = pyqtSignal(int)

    def __init__(self, parent, geometryWin, numWin):
        def populateStatusBar():
            statusBar = self.statusBar()

            w = QCheckBox("Render", self)
            w.setObjectName('renderCheck')
            w.setToolTip("Toggle map rendering")
            w.setChecked(True)
            statusBar.addPermanentWidget(w)

            w = QCheckBox("Marker", self)
            w.setObjectName('markerCheck')
            w.setToolTip("Toggle marker with cursor position from main map")
            w.setChecked(False)
            statusBar.addPermanentWidget(w, 1)

            w = QCheckBox("Extent", self)
            w.setObjectName('extentCheck')
            w.setToolTip("Show extent of main map")
            w.setChecked(False)
            statusBar.addPermanentWidget(w, 1)

            w = QToolButton(self)
            w.setObjectName('highlightBtn')
            w.setToolTip("Highlight extent in main map")
            w.setText("Highlight")
            statusBar.addPermanentWidget(w, 1)

            w = QLabel("Scale factor:", self)
            w.setObjectName('scaleFactorLabel')
            w.setAlignment(Qt.AlignRight | Qt.AlignVCenter)
            statusBar.addPermanentWidget(w, 1)

            w = QDoubleSpinBox(self)
            w.setObjectName('scaleFactorSpin')
            w.setToolTip("Current scale factor of main map")
            w.setMinimum(0.0)
            w.setMaximum(1000.0)
            w.setDecimals(3)
            w.setValue(1)
            w.setSingleStep(.05)
            statusBar.addPermanentWidget(w, 1)

            w = QToolButton(self)
            w.setObjectName('scaleBtn')
            w.setToolTip("Set scale for main map")
            w.setText("Scale: ")
            statusBar.addPermanentWidget(w, 1)

        def setupUi():
            self.setObjectName("AuxiliaryWindow")
            self.setGeometry(geometryWin)
            self.addDockWidget(Qt.LeftDockWidgetArea, self.dockLegend)
            self.actLegend = self.menuBar().addAction("")
            self.actLegend.triggered.connect(self.onActionLegend)
            self.canvas.setMapTool(self.toolPan)
            self.canvas.setCanvasColor(QColor(255, 255, 255))
            self.canvas.enableAntiAliasing(False)
            self.canvas.useImageToRender(False)
            self.canvas.setWheelAction(QgsMapCanvas.WheelZoom)
            self.setCentralWidget(centralWidget)
            self.messageBar.setSizePolicy(QSizePolicy.Minimum,
                                          QSizePolicy.Fixed)
            layout = QGridLayout()
            layout.setContentsMargins(0, 0, 0, 0)
            layout.addWidget(self.canvas, 0, 0, 2, 1)
            layout.addWidget(self.messageBar, 0, 0, 1, 1)
            centralWidget.setLayout(layout)

        super(AuxiliaryWindow, self).__init__(parent)

        centralWidget = QWidget(self)
        self.canvas = QgsMapCanvas(centralWidget)
        self.messageBar = QgsMessageBar(centralWidget)
        self.toolPan = QgsMapToolPan(self.canvas)
        self.qgisCanvas = qgis.utils.iface.mapCanvas()
        self.qgisTView = qgis.utils.iface.layerTreeView()
        self.qgisSyncGroup = None
        self.numWin = numWin

        self.ltg = QgsLayerTreeGroup('', Qt.Unchecked)
        self.dockLegend = AuxiliaryLegend(self, numWin)
        self.root = QgsProject.instance().layerTreeRoot()

        self.extent = self.actLegend = None
        self.marker = MarkerWindow(self.canvas)

        setupUi()
        populateStatusBar()

        self.onCurrentLayerChanged(None)
        self.onDestinationCrsChanged_MapUnitsChanged()
        self.onHasCrsTransformEnabledChanged(
            self.qgisCanvas.hasCrsTransformEnabled())

    def _connect(self, isConnect=True):
        widgets = {
            'scaleBtn': self.findChild(QToolButton, 'scaleBtn'),
            'renderCheck': self.findChild(QCheckBox, 'renderCheck'),
            'markerCheck': self.findChild(QCheckBox, 'markerCheck'),
            'extentCheck': self.findChild(QCheckBox, 'extentCheck'),
            'highlightBtn': self.findChild(QToolButton, 'highlightBtn'),
            'scaleFactorSpin': self.findChild(QDoubleSpinBox,
                                              'scaleFactorSpin')
        }
        signal_slot = ({
            'signal': widgets['scaleBtn'].clicked,
            'slot': self.onClickedScale
        }, {
            'signal': widgets['renderCheck'].toggled,
            'slot': self.onToggledRender
        }, {
            'signal': widgets['markerCheck'].toggled,
            'slot': self.onToggledMarker
        }, {
            'signal': widgets['extentCheck'].toggled,
            'slot': self.onToggledExtent
        }, {
            'signal': widgets['highlightBtn'].clicked,
            'slot': self.onClickedHighlight
        }, {
            'signal': widgets['scaleFactorSpin'].valueChanged,
            'slot': self.onValueChangedScale
        }, {
            'signal': self.dockLegend.currentLayerChanged,
            'slot': self.onCurrentLayerChanged
        }, {
            'signal': self.dockLegend.currentLayerQgis,
            'slot': self.onCurrentLayerQgis
        }, {
            'signal': self.dockLegend.syncGroupLayer,
            'slot': self.onSyncGroupAddLayersQgis
        }, {
            'signal': self.dockLegend.addSelectedLayersQgis,
            'slot': self.onAddSelectedLayersQgis
        }, {
            'signal': self.dockLegend.removeLayer,
            'slot': self.onRemoveLayers
        }, {
            'signal': self.dockLegend.needSelectLayer,
            'slot': self.onNeedSelectLayer
        }, {
            'signal': self.dockLegend.closed,
            'slot': self.onClosedLegend
        }, {
            'signal': self.canvas.extentsChanged,
            'slot': self.onExtentsChangedMirror
        }, {
            'signal': self.qgisCanvas.extentsChanged,
            'slot': self.onExtentsChangedQgisCanvas
        }, {
            'signal': self.qgisCanvas.xyCoordinates,
            'slot': self.marker.onXYCoordinates
        }, {
            'signal': self.qgisCanvas.destinationCrsChanged,
            'slot': self.onDestinationCrsChanged_MapUnitsChanged
        }, {
            'signal': self.qgisCanvas.mapUnitsChanged,
            'slot': self.onDestinationCrsChanged_MapUnitsChanged
        }, {
            'signal': self.qgisCanvas.hasCrsTransformEnabledChanged,
            'slot': self.onHasCrsTransformEnabledChanged
        }, {
            'signal': self.root.removedChildren,
            'slot': self.onRemovedChildrenQgisRoot
        }, {
            'signal': QgsMapLayerRegistry.instance().layersWillBeRemoved,
            'slot': self.onLayersWillBeRemoved
        })
        if isConnect:
            for item in signal_slot:
                item['signal'].connect(item['slot'])
        else:
            for item in signal_slot:
                item['signal'].disconnect(item['slot'])

    def _extentsChanged(self,
                        canvasOrigin,
                        originSlot,
                        canvasDest,
                        scaleFactor=None):
        canvasOrigin.extentsChanged.disconnect(originSlot)

        if scaleFactor is None:
            scale = canvasOrigin.scale()
            canvasOrigin.setExtent(canvasDest.extent())
            canvasOrigin.zoomScale(scale)
        else:
            canvasOrigin.setExtent(canvasDest.extent())
            canvasOrigin.zoomScale(scaleFactor * canvasDest.scale())

        canvasOrigin.extentsChanged.connect(originSlot)

    def _textScaleBtnChanched(self):
        scale = locale.format("%.0f", self.canvas.scale(), True)
        w = self.findChild(QToolButton, 'scaleBtn')
        w.setText("Scale 1:%s" % scale)

    def _extent(self):
        rect = self.qgisCanvas.extent()
        p1 = QgsPoint(rect.xMinimum(), rect.yMinimum())
        p2 = QgsPoint(rect.xMinimum(), rect.yMaximum())
        p3 = QgsPoint(rect.xMaximum(), rect.yMaximum())
        p4 = QgsPoint(rect.xMaximum(), rect.yMinimum())
        p5 = QgsPoint(rect.xMinimum(), rect.yMinimum())
        points = [p1, p2, p3, p4, p5]
        self.extent.setToGeometry(QgsGeometry.fromPolyline(points), None)

    def _execFunction(self, func, arg, signal, slot):
        signal.disconnect(slot)
        func(arg)
        signal.connect(slot)

    def _connectVectorRefresh(self, layer, isConnect=True):
        if isinstance(layer, QgsVectorLayer):
            f = layer.editCommandEnded.connect if isConnect else layer.editCommandEnded.disconnect
            f(self.canvas.refresh)

    def _addLayersQgis(self, layersQgis, needMsg=True):

        self.dockLegend.clearBridge()

        l1 = set(layersQgis)
        l2 = set(map(lambda item: item.layer(), self.ltg.findLayers()))
        layers = list(l1 - l2)
        if len(layers) == 0:
            if needMsg:
                self.messageBar.pushMessage(
                    "Need select new layer(s) in main map",
                    QgsMessageBar.WARNING, 2)
        else:
            # Get order by layersQgis
            for item in layersQgis:
                if item in layers:
                    self.ltg.addLayer(item)
                    self._connectVectorRefresh(item)

        self.dockLegend.setBridge(self.canvas)

    def _syncGroupAddLayersQgis(self, ltg):
        layersQgis = map(lambda item: item.layer(), ltg.findLayers())
        if len(layersQgis) == 0:
            return False

        name = ltg.name()
        if self.qgisSyncGroup == ltg:
            msg = "Already synchronized group (main map) -> '%s'" % name
            self.messageBar.pushMessage(msg, QgsMessageBar.INFO, 4)
            return True

        if not self.qgisSyncGroup is None:
            self.qgisSyncGroup.addedChildren.disconnect(
                self.addedChildrenLayer)

        self.qgisSyncGroup = ltg
        self.qgisSyncGroup.addedChildren.connect(self.addedChildrenLayer)

        self.dockLegend.addNameSyncGroup(name)
        msg = "Changed synchronized group (main map) -> '%s'" % name
        self.messageBar.pushMessage(msg, QgsMessageBar.INFO, 4)

        self._addLayersQgis(layersQgis)
        return True

    def run(self):

        if len(self.qgisTView.selectedLayerNodes()) > 0:
            self.onAddSelectedLayersQgis()
        else:
            ltn = self.qgisTView.currentNode()
            if not isinstance(ltn, QgsLayerTreeGroup):
                return False
            else:
                if ltn == self.root:
                    return False
                else:
                    if not self._syncGroupAddLayersQgis(ltn):
                        return False

        self.dockLegend.setBridge(self.canvas)

        self.canvas.setRenderFlag(False)
        self.show()  # Need show before self._connect()
        self._connect()
        self.canvas.setExtent(self.qgisCanvas.extent())
        w = self.findChild(QDoubleSpinBox, 'scaleFactorSpin')
        w.setValue(1)
        self.canvas.setRenderFlag(True)

        return True

    def getLayersCanvas(self):
        layerIds = map(lambda x: x.layerId(), self.ltg.findLayers())
        layerChecks = map(lambda x: str(x.isVisible()), self.ltg.findLayers())

        return (layerIds, layerChecks)

    def setLayersCanvas(self, layerIds, layerChecks):
        prevFlag = self.canvas.renderFlag()
        self.canvas.setRenderFlag(False)

        lyrRegs = QgsMapLayerRegistry.instance()
        for id in range(len(layerIds)):
            layer = lyrRegs.mapLayer(layerIds[id])
            isVisible = int(layerChecks[id])
            if not layer is None:
                self.ltg.addLayer(layer).setVisible(isVisible)

        self.canvas.setRenderFlag(prevFlag)

    def getWindowSetting(self):
        g = self.geometry()
        r = self.canvas.extent()
        nodes = self.ltg.findLayers()
        currentLayer = self.dockLegend.tview.currentLayer()
        currentLayerId = currentLayer.id(
        ) if not currentLayer is None else "None"

        windowSetting = {
            'numWin':
            self.numWin,
            'geometryWin': {
                'x': g.x(),
                'y': g.y(),
                'width': g.width(),
                'height': g.height()
            },
            'extentCanvas': {
                'xmin': r.xMinimum(),
                'ymin': r.yMinimum(),
                'xmax': r.xMaximum(),
                'ymax': r.yMaximum()
            },
            'currentLayerId':
            currentLayerId,
            'layerIds':
            ' '.join(map(lambda item: item.layerId(), nodes)),
            'visibles':
            ' '.join(map(lambda item: str(int(item.isVisible())), nodes))
        }
        for item in ('render', 'marker', 'extent'):
            nameGui = "%sCheck" % item
            windowSetting[item] = int(
                self.findChild(QCheckBox, nameGui).isChecked())

        return windowSetting

    def setWindowSetting(self, windowSetting):
        self.numWin = windowSetting['numWin']

        # Populate with layers and set Bridge for legend
        layerIds = windowSetting['layerIds'].split(' ')
        visibles = map(lambda item: bool(int(item)),
                       windowSetting['visibles'].split(' '))
        ltg = self.qgisTView.layerTreeModel().rootGroup()
        for id in range(len(layerIds)):
            node = ltg.findLayer(layerIds[id])
            if node is None:
                continue
            layer = node.layer()
            visible = Qt.Checked if visibles[id] else Qt.Unchecked
            self._connectVectorRefresh(layer)
            self.ltg.addLayer(layer).setVisible(visible)
        self.dockLegend.setBridge(self.canvas)

        self.show()  # Need show before self._connect()
        self._connect()
        node = ltg.findLayer(windowSetting['currentLayerId'])
        if not node is None:
            layer = node.layer()
            self.dockLegend.tview.setCurrentLayer(layer)
        w = windowSetting['extentCanvas']
        self.canvas.setExtent(
            QgsRectangle(w['xmin'], w['ymin'], w['xmax'], w['ymax']))
        for item in ('render', 'marker', 'extent'):
            value = bool(windowSetting[item])
            nameGui = "%sCheck" % item
            self.findChild(QCheckBox, nameGui).setChecked(value)

    def closeEvent(self, event):
        self._connect(False)
        event.accept()
        self.closed.emit(self.numWin)

    @pyqtSlot(int)
    def onValueChangedScale(self, scaleFactor):
        w = self.findChild(QCheckBox, 'renderCheck')
        if not w.isChecked():
            return
        self._execFunction(self.canvas.zoomScale,
                           scaleFactor * self.qgisCanvas.scale(),
                           self.canvas.extentsChanged,
                           self.onExtentsChangedMirror)
        self._textScaleBtnChanched()

    @pyqtSlot()
    def onClickedScale(self):
        self._execFunction(self.qgisCanvas.zoomScale, self.canvas.scale(),
                           self.qgisCanvas.extentsChanged,
                           self.onExtentsChangedQgisCanvas)
        w = self.findChild(QDoubleSpinBox, 'scaleFactorSpin')
        self._execFunction(w.setValue, 1.0, w.valueChanged,
                           self.onValueChangedScale)

    @pyqtSlot()
    def onClickedHighlight(self):
        def removeRB():
            rb.reset(True)
            self.qgisCanvas.scene().removeItem(rb)

        rb = QgsRubberBand(self.qgisCanvas, QGis.Polygon)
        rb.setBorderColor(QColor(255, 0, 0))
        rb.setWidth(2)
        rb.setToGeometry(QgsGeometry.fromRect(self.canvas.extent()), None)
        QTimer.singleShot(2000, removeRB)

    @pyqtSlot(bool)
    def onToggledRender(self, enabled):
        if enabled:
            self.canvas.setMapTool(self.toolPan)
            w = self.findChild(QDoubleSpinBox, 'scaleFactorSpin')
            self._extentsChanged(self.canvas, self.onExtentsChangedMirror,
                                 self.qgisCanvas, w.value())
            self._textScaleBtnChanched()
            self.canvas.setWheelAction(QgsMapCanvas.WheelZoom)
        else:
            self.canvas.unsetMapTool(self.toolPan)
            self.canvas.setWheelAction(QgsMapCanvas.WheelNothing)
        self.canvas.setRenderFlag(enabled)

    @pyqtSlot(bool)
    def onToggledMarker(self, enabled):
        self.marker.add() if enabled else self.marker.remove()

    @pyqtSlot(bool)
    def onToggledExtent(self, enabled):
        def setExtent():
            if not self.extent is None:
                self.canvas.scene().removeItem(self.extent)
            self.extent = QgsRubberBand(self.canvas, QGis.Polygon)
            self.extent.setBorderColor(QColor(255, 0, 0))
            self.extent.setWidth(2)
            self._extent()

        if enabled:
            setExtent()
        else:
            if not self.extent is None:
                self.canvas.scene().removeItem(self.extent)
                self.extent = None

    @pyqtSlot()
    def onExtentsChangedMirror(self):
        w = self.findChild(QCheckBox, 'renderCheck')
        if not w.isChecked():
            return
        self._extentsChanged(self.qgisCanvas, self.onExtentsChangedQgisCanvas,
                             self.canvas)
        self._textScaleBtnChanched()
        w = self.findChild(QDoubleSpinBox, 'scaleFactorSpin')
        self._execFunction(w.setValue,
                           self.canvas.scale() / self.qgisCanvas.scale(),
                           w.valueChanged, self.onValueChangedScale)
        if not self.extent is None:
            self._extent()

    @pyqtSlot()
    def onExtentsChangedQgisCanvas(self):
        w = self.findChild(QCheckBox, 'renderCheck')
        if not w.isChecked():
            return
        w = self.findChild(QDoubleSpinBox, 'scaleFactorSpin')
        self._extentsChanged(self.canvas, self.onExtentsChangedMirror,
                             self.qgisCanvas, w.value())
        self._textScaleBtnChanched()
        if not self.extent is None:
            self._extent()

    @pyqtSlot()
    def onDestinationCrsChanged_MapUnitsChanged(self):
        prevFlag = self.canvas.renderFlag()
        self.canvas.setRenderFlag(False)

        self.canvas.setDestinationCrs(
            self.qgisCanvas.mapRenderer().destinationCrs())
        self.canvas.setMapUnits(self.qgisCanvas.mapUnits())

        self.canvas.setRenderFlag(prevFlag)

    @pyqtSlot(bool)
    def onHasCrsTransformEnabledChanged(self, enabled):
        prevFlag = self.canvas.renderFlag()
        self.canvas.setRenderFlag(False)
        self.canvas.mapRenderer().setProjectionsEnabled(enabled)
        self.canvas.setRenderFlag(prevFlag)

    @pyqtSlot(list)
    def onLayersWillBeRemoved(self, theLayerIds):
        ids = list(set(self.ltg.findLayerIds())
                   & set(theLayerIds))  # intersection
        nodes = map(lambda item: self.ltg.findLayer(item), ids)
        for item in nodes:
            self._connectVectorRefresh(item.layer(), False)
            self.ltg.removeChildNode(item)

    @pyqtSlot()
    def onAddSelectedLayersQgis(self):
        layersQgis = map(lambda item: item.layer(),
                         self.qgisTView.selectedLayerNodes())
        self._addLayersQgis(layersQgis)

    @pyqtSlot('QgsLayerTreeNode', int, int)
    def addedChildrenLayer(self, ltg, indexFrom, indexTo):
        layersQgis = map(lambda item: item.layer(), ltg.findLayers())
        self._addLayersQgis(layersQgis, False)

    @pyqtSlot('QgsLayerTreeNode', int, int)
    def onRemovedChildrenQgisRoot(self, ltg, indexFrom, indexTo):
        if not self.qgisSyncGroup is None and not self.qgisSyncGroup in self.root.children(
        ):
            self.qgisSyncGroup = None
            self.dockLegend.addNameSyncGroup("None")
            msg = "Removed synchronized group (main map)"
            self.messageBar.pushMessage(msg, QgsMessageBar.INFO, 4)

    @pyqtSlot()
    def onSyncGroupAddLayersQgis(self):

        msg = "Need active a group in main map with new layers"
        ltn = self.qgisTView.currentNode()
        if not isinstance(ltn, QgsLayerTreeGroup) or ltn == self.root:
            self.messageBar.pushMessage(msg, QgsMessageBar.WARNING, 3)
            return

        if not self._syncGroupAddLayersQgis(ltn):
            self.messageBar.pushMessage(msg, QgsMessageBar.WARNING, 3)

    @pyqtSlot('QgsMapLayer')
    def onRemoveLayers(self, layer):
        self._connectVectorRefresh(layer, False)

    @pyqtSlot()
    def onNeedSelectLayer(self):
        self.messageBar.pushMessage("Need select layer(s)",
                                    QgsMessageBar.WARNING, 2)

    @pyqtSlot('QgsMapLayer')
    def onCurrentLayerQgis(self, layer):
        if layer is None:
            self.messageBar.pushMessage("Need active layer",
                                        QgsMessageBar.WARNING, 2)
        else:
            self.qgisTView.setCurrentLayer(layer)

    @pyqtSlot('QgsMapLayer')
    def onCurrentLayerChanged(self, layer):
        hasLayer = True if not layer is None else False
        selectName = "Select layer '%s'" % layer.name(
        ) if hasLayer else "None select layer"
        title = "#%d - %s" % (self.numWin, selectName)
        self.setWindowTitle(title)

    @pyqtSlot()
    def onClosedLegend(self):
        self.actLegend.setText("Show layers")

    @pyqtSlot()
    def onActionLegend(self):
        self.actLegend.setText("")
        self.dockLegend.show()
예제 #20
0
    def update_summary_sheet(self,lyr=None, force=None):
        '''
        Creates a summary sheet with thumbnail, layer metadata and online view link
        '''
        #create a layer snapshot and upload it to google drive

        if not lyr:
            lyr = self.lyr

        mapbox_style = self.service_sheet.sheet_cell('settings!A5')
        if not mapbox_style:
            logger("migrating mapbox style")
            self.service_sheet.set_style_mapbox(self.layer_style_to_json(self.lyr))
        
        if not force and not self.dirty and not self.restyled:
            return

        if self.restyled:
            self.service_sheet.set_style_qgis(self.layer_style_to_xml(self.lyr))
            self.service_sheet.set_style_sld(self.SLD_to_xml(self.lyr))
            self.service_sheet.set_style_mapbox(self.layer_style_to_json(self.lyr))
            self.saveMetadataState()
        
        canvas = QgsMapCanvas()
        canvas.resize(QSize(600,600))
        canvas.setCanvasColor(Qt.white)
        canvas.setExtent(lyr.extent())
        canvas.setLayers([lyr])
        canvas.refresh()
        canvas.update()
        settings = canvas.mapSettings()
        settings.setLayers([lyr])
        job = QgsMapRendererParallelJob(settings)
        job.start()
        job.waitForFinished()
        image = job.renderedImage()

        transparent_image = QImage(image.width(), image.height(), QImage.Format_ARGB32)
        transparent_image.fill(Qt.transparent)
        p = QPainter(transparent_image)
        mask = image.createMaskFromColor(QColor(255, 255, 255).rgb(), Qt.MaskInColor)
        p.setClipRegion(QRegion(QBitmap(QPixmap.fromImage(mask))))
        p.drawPixmap(0, 0, QPixmap.fromImage(image))
        p.end()

        tmp_path = os.path.join(self.parent.plugin_dir,self.service_sheet.name+".png")
        transparent_image.save(tmp_path,"PNG")
        image_istances = self.service_drive.list_files(mimeTypeFilter='image/png',filename=self.service_sheet.name+".png")
        for imagename, image_props in image_istances.items():
            self.service_drive.delete_file(image_props['id'])
        result = self.service_drive.upload_image(tmp_path)
        self.service_drive.add_permission(result['id'],'anyone','reader')
        webLink = result['webContentLink'] #'https://drive.google.com/uc?export=view&id='+result['id']
        logger("webLink:" + webLink)
        canvas.setDestinationCrs(QgsCoordinateReferenceSystem(4326))
        worldfile = QgsMapSettingsUtils.worldFileContent(settings)
        lonlat_min = self.transformToWGS84(QgsPointXY(canvas.extent().xMinimum(), canvas.extent().yMinimum()))
        lonlat_max = self.transformToWGS84(QgsPointXY(canvas.extent().xMaximum(), canvas.extent().yMaximum()))
        keymap_extent = [lonlat_min.x(),lonlat_min.y(),lonlat_max.x(),lonlat_max.y()]
        
        os.remove(tmp_path)
        #update layer metadata
        summary_id = self.service_sheet.add_sheet('summary', no_grid=True)
        appPropsUpdate = [
            ["keymap",webLink],
            ["worldfile",pack(worldfile)],
            ["keymap_extent", json.dumps(keymap_extent)]
        ]
        res = self.service_sheet.update_appProperties(self.spreadsheet_id,appPropsUpdate)
        
        self.saveMetadataState(metadata=self.get_layer_metadata())
        self.parent.public_db.setKey(self.spreadsheet_id, dict(self.get_layer_metadata()+appPropsUpdate),only_update=True)
        #merge cells to visualize snapshot and aaply image snapshot
        request_body = {
            'requests': [{
                'mergeCells': {
                    "range": {
                        "sheetId": summary_id,
                        "startRowIndex": 9,
                        "endRowIndex": 32,
                        "startColumnIndex": 0,
                        "endColumnIndex": 9,
                    },
                "mergeType": 'MERGE_ALL'
                }
            }]
        }
        self.service_sheet.service.spreadsheets().batchUpdate(spreadsheetId=self.spreadsheet_id, body=request_body).execute()
        self.service_sheet.set_sheet_cell('summary!A10','=IMAGE("%s",3)' % webLink)

        permissions = self.service_drive.file_property(self.spreadsheet_id,'permissions')
        for permission in permissions:
            if permission['type'] == 'anyone':
                public = True
                break
            else:
                public = False
        if public:
            update_range = 'summary!A9:B9'
            update_body = {
                "range": update_range,
                "values": [['public link', "https://enricofer.github.io/gdrive_provider/weblink/converter.html?spreadsheet_id="+self.spreadsheet_id]]
            }
            self.service_sheet.service.spreadsheets().values().update(spreadsheetId=self.spreadsheet_id,range=update_range, body=update_body, valueInputOption='USER_ENTERED').execute()

        #hide worksheets except summary
        sheets = self.service_sheet.get_sheets()
        #self.service_sheet.toggle_sheet('summary', sheets['summary'], hidden=None)
        for sheet_name,sheet_id in sheets.items():
            if not sheet_name == 'summary':
                self.service_sheet.toggle_sheet(sheet_name, sheet_id, hidden=True)
예제 #21
0
class MirrorMap(QWidget):

    def __init__(self, parent, iface):
        QWidget.__init__(self, parent)
        self.setAttribute(Qt.WA_DeleteOnClose)

        self.iface = iface
        self.layerId2canvasLayer = {}
        self.canvasLayers = []

        self.setupUi()

    def closeEvent(self, event):
        # self.scaleFactor.valueChanged.disconnect(self.onExtentsChanged)
        QObject.disconnect(self.iface.mapCanvas(), SIGNAL("extentsChanged()"), self.onExtentsChanged)
        QObject.disconnect(self.iface.mapCanvas().mapRenderer(), SIGNAL("destinationCrsChanged()"), self.onCrsChanged)
        QObject.disconnect(self.iface.mapCanvas().mapRenderer(), SIGNAL("mapUnitsChanged()"), self.onCrsChanged)
        QObject.disconnect(self.iface.mapCanvas().mapRenderer(), SIGNAL("hasCrsTransformEnabled(bool)"), self.onCrsTransformEnabled)
        QObject.disconnect(QgsMapLayerRegistry.instance(), SIGNAL("layerWillBeRemoved(QString)"), self.delLayer)
        QObject.disconnect(self.iface, SIGNAL("currentLayerChanged(QgsMapLayer *)"), self.refreshLayerButtons)

        self.emit(SIGNAL("closed(PyQt_PyObject)"), self)
        return QWidget.closeEvent(self, event)

    def setupUi(self):
        self.setObjectName("dockablemirrormap_mirrormap")

        gridLayout = QGridLayout(self)
        gridLayout.setContentsMargins(0, 0, gridLayout.verticalSpacing(), gridLayout.verticalSpacing())

        self.canvas = QgsMapCanvas(self)
        self.canvas.setCanvasColor(QColor(255, 255, 255))
        settings = QSettings()
        self.canvas.enableAntiAliasing(settings.value("/qgis/enable_anti_aliasing", False, type=bool))
        self.canvas.useImageToRender(settings.value("/qgis/use_qimage_to_render", False, type=bool))
        self.canvas.setWheelAction(3)
        gridLayout.addWidget(self.canvas, 0, 0, 1, 5)

        QObject.connect(self.iface.mapCanvas(), SIGNAL("extentsChanged()"), self.onExtentsChanged)
        QObject.connect(self.iface.mapCanvas().mapRenderer(), SIGNAL("destinationCrsChanged()"), self.onCrsChanged)
        QObject.connect(self.iface.mapCanvas().mapRenderer(), SIGNAL("mapUnitsChanged()"), self.onCrsChanged)
        QObject.connect(self.iface.mapCanvas().mapRenderer(), SIGNAL("hasCrsTransformEnabled(bool)"), self.onCrsTransformEnabled)
        QObject.connect(QgsMapLayerRegistry.instance(), SIGNAL("layerWillBeRemoved(QString)"), self.delLayer)

        self.onExtentsChanged()
        self.onCrsChanged()
        self.onCrsTransformEnabled(self.iface.mapCanvas().hasCrsTransformEnabled())

    def toggleRender(self, enabled):
        self.canvas.setRenderFlag(enabled)
        self.canvas.refresh()
        self.canvas.repaint()

    def onExtentsChanged(self):
        try :
            prevFlag = self.canvas.renderFlag()
            self.canvas.setRenderFlag(False)

            self.canvas.setExtent(self.iface.mapCanvas().extent())
            # self.canvas.zoomByFactor( self.scaleFactor.value() )

            self.canvas.setRenderFlag(prevFlag)
            self.canvas.repaint()
            self.canvas.refresh()
        except Exception:
            pass

    def mirror_extent_changed(self):
        logger.debug(self.canvas.extent())
        logger.debug(self.iface.mapCanvas().extent())
        if self.canvas.extent() != self.iface.mapCanvas().extent():
            self.emit(SIGNAL("extentChanged( QgsRectangle )"), self.canvas.extent())

    def onCrsChanged(self):
        try:
            prevFlag = self.canvas.renderFlag()
            self.canvas.setRenderFlag(False)

            renderer = self.iface.mapCanvas().mapRenderer()
            self._setRendererCrs(self.canvas.mapRenderer(), self._rendererCrs(renderer))
            self.canvas.mapRenderer().setMapUnits(renderer.mapUnits())

            self.canvas.setRenderFlag(prevFlag)
            self.canvas.repaint()
            self.canvas.refresh()

        except Exception:
            pass

    def onCrsTransformEnabled(self, enabled):
        try:
            prevFlag = self.canvas.renderFlag()
            self.canvas.setRenderFlag(False)

            self.canvas.mapRenderer().setProjectionsEnabled(enabled)

            self.canvas.setRenderFlag(prevFlag)
            self.canvas.repaint()
            self.canvas.refresh()
        except Exception:
            pass

    def getLayerSet(self):
        return map(lambda x: self._layerId(x.layer()), self.canvasLayers)

    def setLayerSet(self, layerIds=None):
        prevFlag = self.canvas.renderFlag()
        self.canvas.setRenderFlag(False)

        if layerIds is None:
            self.layerId2canvasLayer = {}
            self.canvasLayers = []
            self.canvas.setLayerSet([])

        else:
            for lid in layerIds:
                self.addLayer(lid)

        self.onExtentsChanged()

        self.canvas.setRenderFlag(prevFlag)
        self.canvas.repaint()
        self.canvas.refresh()

    def addLayer(self, layerId = None):
        if layerId is None:
            layer = self.iface.activeLayer()
        else:
            layer = QgsMapLayerRegistry.instance().mapLayer(layerId)

        if layer is None:
            return

        prevFlag = self.canvas.renderFlag()
        self.canvas.setRenderFlag(False)

        # add the layer to the map canvas layer set
        self.canvasLayers = []
        id2cl_dict = {}
        for l in self.iface.legendInterface().layers():
            lid = self._layerId(l)
            if self.layerId2canvasLayer.has_key(lid):  # previously added
                cl = self.layerId2canvasLayer[ lid ]
            elif l == layer:  # selected layer
                cl = QgsMapCanvasLayer(layer)
            else:
                continue

            id2cl_dict[ lid ] = cl
            self.canvasLayers.append(cl)

        self.layerId2canvasLayer = id2cl_dict
        self.canvas.setLayerSet(self.canvasLayers)

        self.onExtentsChanged()
        self.canvas.setRenderFlag(prevFlag)
        self.canvas.repaint()
        self.canvas.refresh()

    def delLayer(self, layerId=None):
        if layerId is None:
            layer = self.iface.activeLayer()
            if layer is None:
                return
            layerId = self._layerId(layer)

        # remove the layer from the map canvas layer set
        if layerId not in self.layerId2canvasLayer:
            return

        prevFlag = self.canvas.renderFlag()
        self.canvas.setRenderFlag(False)

        cl = self.layerId2canvasLayer[ layerId ]
        if cl is not None:
            del self.layerId2canvasLayer[ layerId ]
        self.canvasLayers.remove(cl)
        self.canvas.setLayerSet(self.canvasLayers)
        del cl

        self.onExtentsChanged()
        self.canvas.setRenderFlag(prevFlag)
        self.canvas.repaint()
        self.canvas.refresh()

    def _layerId(self, layer):
        if hasattr(layer, 'id'):
            return layer.id()
        return layer.getLayerID()

    def _rendererCrs(self, renderer):
        if hasattr(renderer, 'destinationCrs'):
            return renderer.destinationCrs()
        return renderer.destinationSrs()

    def _setRendererCrs(self, renderer, crs):
        if hasattr(renderer, 'setDestinationCrs'):
            return renderer.setDestinationCrs(crs)
        return renderer.setDestinationSrs(crs)
class PdfMaker(object):
    """A generator that takes a QGIS project file and a layout template and makes a pdf."""

    def __init__(self, template_path, debug=False):
        """Constructor.

        :param template_path: Absolute path to a QGIS composer template file.
        :type template_path: str
        """
        gui_flag = True
        self.app = QgsApplication(sys.argv, gui_flag)

        # Make sure QGIS_PREFIX_PATH is set in your env if needed!
        self.app.initQgis()

        if debug:
            print QgsProviderRegistry.instance().pluginList()

        self.canvas = QgsMapCanvas()
        self.canvas.enableAntiAliasing(True)

        self.template_path = template_path

    def __del__(self):
        """Destructor."""
        del self.app

    def _load_template(self):
        """Load the template.

        :return: QgsComposition containing the loaded template.
        :rtype: QgsComposition
        """
        template_file = file(self.template_path)
        template_content = template_file.read()
        template_file.close()
        document = QDomDocument()
        document.setContent(template_content)
        composition = QgsComposition(self.canvas.mapSettings())
        # You can use this to replace any string like this [key]
        # in the template with a new value. e.g. to replace
        # [date] pass a map like this {'date': '1 Jan 2012'}
        substitution_map = {
            'DATE_TIME_START': 'foo',
            'DATE_TIME_END': 'bar'}
        composition.loadFromTemplate(document, substitution_map)
        return composition

    def _load_layers(self):
        """Manually load all the layers for our project.

        :return: A list of QgsMapLayer instances.
        :rtype: list
        """
        layers = []

        # First the RW layer

        host = 'db'
        port = '5432'
        user = '******'
        password = '******'
        dbname = 'gis'
        uri = QgsDataSourceURI()
        uri.setConnection(host, port, dbname, user, password)

        schema = 'public'
        table = 'flood_mapper_rw'
        geometry_column = 'geometry'
        where_clause = ''
        title = 'RW'
        uri.setDataSource(schema, table, geometry_column, where_clause)
        layer = QgsVectorLayer(uri.uri(), title, 'postgres')

        QgsMapLayerRegistry.instance().addMapLayer(layer, False)
        canvas_layer = QgsMapCanvasLayer(layer)
        layers.append(canvas_layer)

        # Now the JK layer
        path = './data/jk.shp'
        title = 'JK'
        layer = QgsVectorLayer(path, title, 'ogr')
        QgsMapLayerRegistry.instance().addMapLayer(layer, False)
        canvas_layer = QgsMapCanvasLayer(layer)
        layers.append(canvas_layer)

        return layers

    def make_pdf(self, pdf_path):
        """Generate a pdf for the given project and template files.

        :param pdf_path: Absolute path for the output PDF file.
        :type pdf_path: str

        """

        layers = self._load_layers()
        self.canvas.setLayerSet(layers)

        if self.canvas.layerCount() < 1:
            print 'No layers loaded from this project, exiting.'
            return
        self.canvas.setDestinationCrs(QgsCoordinateReferenceSystem('EPSG:3857'))
        self.canvas.setCrsTransformEnabled(True)
        self.canvas.zoomToFullExtent()

        print 'Extent: %s' % self.canvas.mapSettings().extent().toString()
        # self._load_project()
        composition = self._load_template()
        # You must set the id in the template
        map_item = composition.getComposerItemById('map')
        map_item.setMapCanvas(self.canvas)
        map_item.zoomToExtent(self.canvas.extent())
        # You must set the id in the template
        legend_item = composition.getComposerItemById('legend')
        legend_item.updateLegend()
        composition.refreshItems()
        composition.exportAsPDF(pdf_path)
        QgsProject.instance().clear()