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()
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'))
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"))
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()
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()
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()
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)
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()
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')
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()
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)
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()
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
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:
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()
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()
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)
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()