def processAlgorithm(self, progress): idx = self.getParameterValue(self.TYPE) extent = self.getParameterValue(self.EXTENT).split(',') hSpacing = self.getParameterValue(self.HSPACING) vSpacing = self.getParameterValue(self.VSPACING) crs = QgsCoordinateReferenceSystem(self.getParameterValue(self.CRS)) bbox = QgsRectangle(float(extent[0]), float(extent[2]), float(extent[1]), float(extent[3])) width = bbox.width() height = bbox.height() centerX = bbox.center().x() centerY = bbox.center().y() #~ originX = centerX - width / 2.0 #~ originY = centerY - height / 2.0 originX = bbox.xMinimum() originY = bbox.yMaximum() if hSpacing <= 0 or vSpacing <= 0: raise GeoAlgorithmExecutionException( self.tr('Invalid grid spacing: %s/%s' % (hSpacing, vSpacing))) if width < hSpacing: raise GeoAlgorithmExecutionException( self.tr('Horizontal spacing is too small for the covered area')) if height < vSpacing: raise GeoAlgorithmExecutionException( self.tr('Vertical spacing is too small for the covered area')) #if self.types[idx].find(self.tr('polygon')) >= 0: if idx != 0: geometryType = QGis.WKBPolygon else: geometryType = QGis.WKBLineString fields = [QgsField('left', QVariant.Double, '', 24, 16), QgsField('top', QVariant.Double, '', 24, 16), QgsField('right', QVariant.Double, '', 24, 16), QgsField('bottom', QVariant.Double, '', 24, 16) ] writer = self.getOutputFromName(self.OUTPUT).getVectorWriter(fields, geometryType, crs) if idx == 0: self._rectangleGridLine( writer, width, height, originX, originY, hSpacing, vSpacing) elif idx == 1: self._rectangleGridPoly( writer, width, height, originX, originY, hSpacing, vSpacing) elif idx == 2: self._diamondGrid( writer, width, height, originX, originY, hSpacing, vSpacing) elif idx == 3: self._hexagonGrid( writer, width, height, originX, originY, hSpacing, vSpacing) del writer
def processAlgorithm(self, feedback): extent = str(self.getParameterValue(self.EXTENT)).split(',') spacing = float(self.getParameterValue(self.SPACING)) inset = float(self.getParameterValue(self.INSET)) randomize = self.getParameterValue(self.RANDOMIZE) isSpacing = self.getParameterValue(self.IS_SPACING) crsId = self.getParameterValue(self.CRS) crs = QgsCoordinateReferenceSystem() crs.createFromUserInput(crsId) extent = QgsRectangle(float(extent[0]), float(extent[2]), float(extent[1]), float(extent[3])) fields = QgsFields() fields.append(QgsField('id', QVariant.Int, '', 10, 0)) writer = self.getOutputFromName(self.OUTPUT).getVectorWriter( fields, QgsWkbTypes.Point, crs) if randomize: seed() area = extent.width() * extent.height() if isSpacing: pSpacing = spacing else: pSpacing = sqrt(area / spacing) f = QgsFeature() f.initAttributes(1) f.setFields(fields) count = 0 total = 100.0 / (area / pSpacing) y = extent.yMaximum() - inset extent_geom = QgsGeometry.fromRect(extent) extent_engine = QgsGeometry.createGeometryEngine(extent_geom.geometry()) extent_engine.prepareGeometry() while y >= extent.yMinimum(): x = extent.xMinimum() + inset while x <= extent.xMaximum(): if randomize: geom = QgsGeometry().fromPoint(QgsPoint( uniform(x - (pSpacing / 2.0), x + (pSpacing / 2.0)), uniform(y - (pSpacing / 2.0), y + (pSpacing / 2.0)))) else: geom = QgsGeometry().fromPoint(QgsPoint(x, y)) if extent_engine.intersects(geom.geometry()): f.setAttribute('id', count) f.setGeometry(geom) writer.addFeature(f) x += pSpacing count += 1 feedback.setProgress(int(count * total)) y = y - pSpacing del writer
def rotation_test(self): # We will create a polygon layer with a rotated rectangle. # Then we will make it the object layer for the atlas, # rotate the map and test that the bounding rectangle # is smaller than the bounds without rotation. polygonLayer = QgsVectorLayer('Polygon', 'test_polygon', 'memory') poly = QgsFeature(polygonLayer.fields()) points = [(10, 15), (15, 10), (45, 40), (40, 45)] poly.setGeometry(QgsGeometry.fromPolygonXY([[QgsPointXY(x[0], x[1]) for x in points]])) polygonLayer.dataProvider().addFeatures([poly]) QgsProject.instance().addMapLayer(polygonLayer) # Recreating the layout locally composition = QgsPrintLayout(QgsProject.instance()) composition.initializeDefaults() # the atlas map atlasMap = QgsLayoutItemMap(composition) atlasMap.attemptSetSceneRect(QRectF(20, 20, 130, 130)) atlasMap.setFrameEnabled(True) atlasMap.setLayers([polygonLayer]) atlasMap.setExtent(QgsRectangle(0, 0, 100, 50)) composition.addLayoutItem(atlasMap) # the atlas atlas = composition.atlas() atlas.setCoverageLayer(polygonLayer) atlas.setEnabled(True) atlasMap.setAtlasDriven(True) atlasMap.setAtlasScalingMode(QgsLayoutItemMap.Auto) atlasMap.setAtlasMargin(0.0) # Testing atlasMap.setMapRotation(0.0) atlas.beginRender() atlas.first() nonRotatedExtent = QgsRectangle(atlasMap.extent()) atlasMap.setMapRotation(45.0) atlas.first() rotatedExtent = QgsRectangle(atlasMap.extent()) self.assertLess(rotatedExtent.width(), nonRotatedExtent.width() * 0.9) self.assertLess(rotatedExtent.height(), nonRotatedExtent.height() * 0.9) QgsProject.instance().removeMapLayer(polygonLayer)
def rotation_test(self): # We will create a polygon layer with a rotated rectangle. # Then we will make it the object layer for the atlas, # rotate the map and test that the bounding rectangle # is smaller than the bounds without rotation. polygonLayer = QgsVectorLayer('Polygon', 'test_polygon', 'memory') poly = QgsFeature(polygonLayer.pendingFields()) points = [(10, 15), (15, 10), (45, 40), (40, 45)] poly.setGeometry(QgsGeometry.fromPolygon([[QgsPointXY(x[0], x[1]) for x in points]])) polygonLayer.dataProvider().addFeatures([poly]) QgsProject.instance().addMapLayer(polygonLayer) # Recreating the composer locally composition = QgsComposition(QgsProject.instance()) composition.setPaperSize(297, 210) # the atlas map atlasMap = QgsComposerMap(composition, 20, 20, 130, 130) atlasMap.setFrameEnabled(True) atlasMap.setLayers([polygonLayer]) atlasMap.setNewExtent(QgsRectangle(0, 0, 100, 50)) composition.addComposerMap(atlasMap) # the atlas atlas = composition.atlasComposition() atlas.setCoverageLayer(polygonLayer) atlas.setEnabled(True) composition.setAtlasMode(QgsComposition.ExportAtlas) atlasMap.setAtlasDriven(True) atlasMap.setAtlasScalingMode(QgsComposerMap.Auto) atlasMap.setAtlasMargin(0.0) # Testing atlasMap.setMapRotation(0.0) atlas.firstFeature() nonRotatedExtent = QgsRectangle(atlasMap.currentMapExtent()) atlasMap.setMapRotation(45.0) atlas.firstFeature() rotatedExtent = QgsRectangle(atlasMap.currentMapExtent()) assert rotatedExtent.width() < nonRotatedExtent.width() * 0.9 assert rotatedExtent.height() < nonRotatedExtent.height() * 0.9 QgsProject.instance().removeMapLayer(polygonLayer)
def _rect(self, startPoint, endPoint): if startPoint is None or endPoint is None: return None p0 = self.toCanvasCoordinates(startPoint) p1 = self.toCanvasCoordinates(endPoint) canvas_rect = QgsRectangle(QgsPoint(p0.x(), p0.y()), QgsPoint(p1.x(), p1.y())) center = QgsPoint((startPoint.x() + endPoint.x()) / 2, (startPoint.y() + endPoint.y()) / 2) return RotatedRect(center, self.mupp * canvas_rect.width(), self.mupp * canvas_rect.height()).rotate(self.rotation, center)
def testDimensions(self): rect = QgsRectangle(0.0, 0.0, 10.0, 10.0) myMessage = "Expected: %s\nGot: %s\n" % (10.0, rect.width()) assert rect.width() == 10.0, myMessage myMessage = "Expected: %s\nGot: %s\n" % (10.0, rect.height()) assert rect.height() == 10.0, myMessage myMessage = "Expected: %s\nGot: %s\n" % ("5.0, 5.0", rect.center().toString()) assert rect.center() == QgsPoint(5.0, 5.0), myMessage rect.scale(2.0) myMessage = "Expected: %s\nGot: %s\n" % (20.0, rect.width()) assert rect.width() == 20.0, myMessage myMessage = "Expected: %s\nGot: %s\n" % (20.0, rect.height()) assert rect.height() == 20.0, myMessage
def processAlgorithm(self, progress): extent = str(self.getParameterValue(self.EXTENT)).split(",") spacing = float(self.getParameterValue(self.SPACING)) inset = float(self.getParameterValue(self.INSET)) randomize = self.getParameterValue(self.RANDOMIZE) isSpacing = self.getParameterValue(self.IS_SPACING) extent = QgsRectangle(float(extent[0]), float(extent[2]), float(extent[1]), float(extent[3])) fields = QgsFields() fields.append(QgsField("id", QVariant.Int, "", 10, 0)) mapCRS = iface.mapCanvas().mapSettings().destinationCrs() writer = self.getOutputFromName(self.OUTPUT).getVectorWriter(fields, QGis.WKBPoint, mapCRS) if randomize: seed() area = extent.width() * extent.height() if isSpacing: pSpacing = spacing else: pSpacing = sqrt(area / spacing) f = QgsFeature() f.initAttributes(1) f.setFields(fields) count = 0 total = 100.00 / (area / pSpacing) y = extent.yMaximum() - inset while y >= extent.yMinimum(): x = extent.xMinimum() + inset while x <= extent.xMaximum(): if randomize: geom = QgsGeometry().fromPoint( QgsPoint( uniform(x - (pSpacing / 2.0), x + (pSpacing / 2.0)), uniform(y - (pSpacing / 2.0), y + (pSpacing / 2.0)), ) ) else: geom = QgsGeometry().fromPoint(QgsPoint(x, y)) if geom.intersects(extent): f.setAttribute("id", count) f.setGeometry(geom) writer.addFeature(f) x += pSpacing count += 1 progress.setPercentage(int(count * total)) y = y - pSpacing del writer
def processAlgorithm(self, feedback): idx = self.getParameterValue(self.TYPE) extent = self.getParameterValue(self.EXTENT).split(',') hSpacing = self.getParameterValue(self.HSPACING) vSpacing = self.getParameterValue(self.VSPACING) hOverlay = self.getParameterValue(self.HOVERLAY) vOverlay = self.getParameterValue(self.VOVERLAY) crs = QgsCoordinateReferenceSystem(self.getParameterValue(self.CRS)) bbox = QgsRectangle(float(extent[0]), float(extent[2]), float(extent[1]), float(extent[3])) width = bbox.width() height = bbox.height() originX = bbox.xMinimum() originY = bbox.yMaximum() if hSpacing <= 0 or vSpacing <= 0: raise GeoAlgorithmExecutionException( self.tr('Invalid grid spacing: %s/%s' % (hSpacing, vSpacing))) if width < hSpacing: raise GeoAlgorithmExecutionException( self.tr('Horizontal spacing is too small for the covered area')) if hSpacing <= hOverlay or vSpacing <= vOverlay: raise GeoAlgorithmExecutionException( self.tr('Invalid overlay: %s/%s' % (hOverlay, vOverlay))) if height < vSpacing: raise GeoAlgorithmExecutionException( self.tr('Vertical spacing is too small for the covered area')) fields = [QgsField('left', QVariant.Double, '', 24, 16), QgsField('top', QVariant.Double, '', 24, 16), QgsField('right', QVariant.Double, '', 24, 16), QgsField('bottom', QVariant.Double, '', 24, 16), QgsField('id', QVariant.Int, '', 10, 0) ] writer = self.getOutputFromName(self.OUTPUT).getVectorWriter(fields, QgsWkbTypes.Polygon, crs) if idx == 0: self._rectangleGrid( writer, width, height, originX, originY, hSpacing, vSpacing, hOverlay, vOverlay, feedback) elif idx == 1: self._diamondGrid( writer, width, height, originX, originY, hSpacing, vSpacing, hOverlay, vOverlay, feedback) elif idx == 2: self._hexagonGrid( writer, width, height, originX, originY, hSpacing, vSpacing, hOverlay, vOverlay, feedback) del writer
def subrectangle(self, norm_rect, y_inverted=False): """ args: norm_rect -- QgsRectangle (0 <= xmin, 0 <= ymin, xmax <= 1, ymax <= 1) y_inverted -- If True, lower-left is (0, 1) and upper-right is (1, 0). Or else lower-left is (0, 0) and upper-right is (1, 1). """ ur_rect = self._unrotated_rect xmin = ur_rect.xMinimum() + norm_rect.xMinimum() * ur_rect.width() xmax = ur_rect.xMinimum() + norm_rect.xMaximum() * ur_rect.width() if y_inverted: ymin = ur_rect.yMaximum() - norm_rect.yMaximum() * ur_rect.height() ymax = ur_rect.yMaximum() - norm_rect.yMinimum() * ur_rect.height() else: ymin = ur_rect.yMinimum() + norm_rect.yMinimum() * ur_rect.height() ymax = ur_rect.yMinimum() + norm_rect.yMaximum() * ur_rect.height() rect = QgsRectangle(xmin, ymin, xmax, ymax) return RotatedRect(rect.center(), rect.width(), rect.height()).rotate(self._rotation, self._center)
def processAlgorithm(self, progress): extent = self.getParameterValue(self.EXTENT).split(',') hSpacing = self.getParameterValue(self.HSPACING) vSpacing = self.getParameterValue(self.VSPACING) crs = QgsCoordinateReferenceSystem(self.getParameterValue(self.CRS)) bbox = QgsRectangle(float(extent[0]), float(extent[2]), float(extent[1]), float(extent[3])) width = bbox.width() height = bbox.height() originX = bbox.xMinimum() originY = bbox.yMaximum() if hSpacing <= 0 or vSpacing <= 0: raise GeoAlgorithmExecutionException( self.tr('Invalid grid spacing: %s/%s' % (hSpacing, vSpacing))) if width < hSpacing: raise GeoAlgorithmExecutionException( self.tr('Horizontal spacing is too small for the covered area')) if height < vSpacing: raise GeoAlgorithmExecutionException( self.tr('Vertical spacing is too small for the covered area')) fields = [QgsField('left', QVariant.Double, '', 24, 16), QgsField('top', QVariant.Double, '', 24, 16), QgsField('right', QVariant.Double, '', 24, 16), QgsField('bottom', QVariant.Double, '', 24, 16) ] writer = self.getOutputFromName(self.OUTPUT).getVectorWriter(fields, QgsWkbTypes.LineString, crs) self._rectangleGridLine( writer, width, height, originX, originY, hSpacing, vSpacing)
def qgis_composer_renderer(impact_report, component): """Default Map Report Renderer using QGIS Composer. Render using qgis composer for a given impact_report data and component context. :param impact_report: ImpactReport contains data about the report that is going to be generated. :type impact_report: safe.report.impact_report.ImpactReport :param component: Contains the component metadata and context for rendering the output. :type component: safe.report.report_metadata.QgisComposerComponentsMetadata :return: Whatever type of output the component should be. .. versionadded:: 4.0 """ context = component.context qgis_composition_context = impact_report.qgis_composition_context # load composition object layout = QgsPrintLayout(QgsProject.instance()) # load template main_template_folder = impact_report.metadata.template_folder # we do this condition in case custom template was found if component.template.startswith('../qgis-composer-templates/'): template_path = os.path.join(main_template_folder, component.template) else: template_path = component.template with open(template_path) as template_file: template_content = template_file.read() document = QtXml.QDomDocument() # Replace for k, v in context.substitution_map.items(): template_content = template_content.replace('[{}]'.format(k), v) document.setContent(template_content) rwcontext = QgsReadWriteContext() load_status = layout.loadFromTemplate( document, rwcontext) if not load_status: raise TemplateLoadingError( tr('Error loading template: %s') % template_path) # replace image path for img in context.image_elements: item_id = img.get('id') path = img.get('path') image = layout_item(layout, item_id, QgsLayoutItemPicture) if image and path: image.setPicturePath(path) # replace html frame for html_el in context.html_frame_elements: item_id = html_el.get('id') mode = html_el.get('mode') html_element = layout_item(layout, item_id, QgsLayoutItemHtml) if html_element: if mode == 'text': text = html_el.get('text') text = text if text else '' html_element.setContentMode(QgsLayoutItemHtml.ManualHtml) html_element.setHtml(text) html_element.loadHtml() elif mode == 'url': url = html_el.get('url') html_element.setContentMode(QgsLayoutItemHtml.Url) qurl = QUrl.fromLocalFile(url) html_element.setUrl(qurl) original_crs = impact_report.impact_function.crs destination_crs = qgis_composition_context.map_settings.destinationCrs() coord_transform = QgsCoordinateTransform(original_crs, destination_crs, QgsProject.instance()) # resize map extent for map_el in context.map_elements: item_id = map_el.get('id') split_count = map_el.get('grid_split_count') layers = [ layer for layer in map_el.get('layers') if isinstance( layer, QgsMapLayer) ] map_extent_option = map_el.get('extent') composer_map = layout_item(layout, item_id, QgsLayoutItemMap) for index, layer in enumerate(layers): # we need to check whether the layer is registered or not registered_layer = ( QgsProject.instance().mapLayer(layer.id())) if registered_layer: if not registered_layer == layer: layers[index] = registered_layer else: QgsProject.instance().addMapLayer(layer) """:type: qgis.core.QgsLayoutItemMap""" if composer_map: # Search for specified map extent in the template. min_x = composer_map.extent().xMinimum() if ( impact_report.use_template_extent) else None min_y = composer_map.extent().yMinimum() if ( impact_report.use_template_extent) else None max_x = composer_map.extent().xMaximum() if ( impact_report.use_template_extent) else None max_y = composer_map.extent().yMaximum() if ( impact_report.use_template_extent) else None composer_map.setKeepLayerSet(True) layer_set = [l for l in layers if isinstance(l, QgsMapLayer)] composer_map.setLayers(layer_set) map_overview_extent = None if map_extent_option and isinstance( map_extent_option, QgsRectangle): # use provided map extent extent = coord_transform.transform(map_extent_option) for l in [layer for layer in layers if isinstance(layer, QgsMapLayer)]: layer_extent = coord_transform.transform(l.extent()) if l.name() == map_overview['id']: map_overview_extent = layer_extent else: # if map extent not provided, try to calculate extent # from list of given layers. Combine it so all layers were # shown properly extent = QgsRectangle() extent.setMinimal() for l in [layer for layer in layers if isinstance(layer, QgsMapLayer)]: # combine extent if different layer is provided. layer_extent = coord_transform.transform(l.extent()) extent.combineExtentWith(layer_extent) if l.name() == map_overview['id']: map_overview_extent = layer_extent width = extent.width() height = extent.height() longest_width = width if width > height else height half_length = longest_width / 2 margin = half_length / 5 center = extent.center() min_x = min_x or (center.x() - half_length - margin) max_x = max_x or (center.x() + half_length + margin) min_y = min_y or (center.y() - half_length - margin) max_y = max_y or (center.y() + half_length + margin) # noinspection PyCallingNonCallable square_extent = QgsRectangle(min_x, min_y, max_x, max_y) if component.key == 'population-infographic' and ( map_overview_extent): square_extent = map_overview_extent composer_map.zoomToExtent(square_extent) composer_map.invalidateCache() actual_extent = composer_map.extent() # calculate intervals for grid x_interval = actual_extent.width() / split_count composer_map.grid().setIntervalX(x_interval) y_interval = actual_extent.height() / split_count composer_map.grid().setIntervalY(y_interval) # calculate legend element for leg_el in context.map_legends: item_id = leg_el.get('id') title = leg_el.get('title') layers = [ layer for layer in leg_el.get('layers') if isinstance( layer, QgsMapLayer) ] symbol_count = leg_el.get('symbol_count') column_count = leg_el.get('column_count') legend = layout_item(layout, item_id, QgsLayoutItemLegend) """:type: qgis.core.QgsLayoutItemLegend""" if legend: # set column count if column_count: legend.setColumnCount(column_count) elif symbol_count <= 7: legend.setColumnCount(1) else: legend.setColumnCount(symbol_count / 7 + 1) # set legend title if title is not None and not impact_report.legend_layers: legend.setTitle(title) # set legend root_group = legend.model().rootGroup() for layer in layers: # we need to check whether the layer is registered or not registered_layer = ( QgsProject.instance().mapLayer(layer.id())) if registered_layer: if not registered_layer == layer: layer = registered_layer else: QgsProject.instance().addMapLayer(layer) # used for customizations tree_layer = root_group.addLayer(layer) if impact_report.legend_layers or ( not impact_report.multi_exposure_impact_function): QgsLegendRenderer.setNodeLegendStyle( tree_layer, QgsLegendStyle.Hidden) legend.adjustBoxSize() legend.updateFilterByMap(False) # process to output # in case output folder not specified if impact_report.output_folder is None: impact_report.output_folder = mkdtemp(dir=temp_dir()) output_format = component.output_format component_output_path = impact_report.component_absolute_output_path( component.key) component_output = None doc_format = QgisComposerComponentsMetadata.OutputFormat.DOC_OUTPUT template_format = QgisComposerComponentsMetadata.OutputFormat.QPT if isinstance(output_format, list): component_output = [] for i in range(len(output_format)): each_format = output_format[i] each_path = component_output_path[i] if each_format in doc_format: result_path = create_qgis_pdf_output( impact_report, each_path, layout, each_format, component) component_output.append(result_path) elif each_format == template_format: result_path = create_qgis_template_output( each_path, layout) component_output.append(result_path) elif isinstance(output_format, dict): component_output = {} for key, each_format in list(output_format.items()): each_path = component_output_path[key] if each_format in doc_format: result_path = create_qgis_pdf_output( impact_report, each_path, layout, each_format, component) component_output[key] = result_path elif each_format == template_format: result_path = create_qgis_template_output( each_path, layout) component_output[key] = result_path elif (output_format in QgisComposerComponentsMetadata.OutputFormat.SUPPORTED_OUTPUT): component_output = None if output_format in doc_format: result_path = create_qgis_pdf_output( impact_report, component_output_path, layout, output_format, component) component_output = result_path elif output_format == template_format: result_path = create_qgis_template_output( component_output_path, layout) component_output = result_path component.output = component_output return component.output
def load_template(self): """Load a QgsComposer map from a template and render it. .. note:: THIS METHOD IS EXPERIMENTAL """ self.setup_composition() template_file = QtCore.QFile(self.template) template_file.open(QtCore.QIODevice.ReadOnly | QtCore.QIODevice.Text) template_content = template_file.readAll() template_file.close() document = QtXml.QDomDocument() document.setContent(template_content) # get information for substitutions # date, time and plugin version date_time = self.keyword_io.read_keywords(self.layer, 'time_stamp') tokens = date_time.split('_') date = tokens[0] time = tokens[1] long_version = get_version() tokens = long_version.split('.') version = '%s.%s.%s' % (tokens[0], tokens[1], tokens[2]) # map title LOGGER.debug('InaSAFE Map getMapTitle called') try: title = self.keyword_io.read_keywords(self.layer, 'map_title') except KeywordNotFoundError: title = None except Exception: title = None if not title: title = '' substitution_map = { 'impact-title': title, 'date': date, 'time': time, 'safe-version': version } LOGGER.debug(substitution_map) load_ok = self.composition.loadFromTemplate(document, substitution_map) if not load_ok: raise ReportCreationError( self.tr('Error loading template %s') % self.template) self.page_width = self.composition.paperWidth() self.page_height = self.composition.paperHeight() # set logo image = self.composition.getComposerItemById('safe-logo') image.setPictureFile(self.logo) # Get the main map canvas on the composition and set # its extents to the event. map = self.composition.getComposerItemById('impact-map') if map is not None: # Recenter the composer map on the center of the canvas # Note that since the composer map is square and the canvas may be # arbitrarily shaped, we center based on the longest edge canvas_extent = self.iface.mapCanvas().extent() width = canvas_extent.width() height = canvas_extent.height() longest_width = width if width < height: longest_width = height half_length = longest_width / 2 center = canvas_extent.center() min_x = center.x() - half_length max_x = center.x() + half_length min_y = center.y() - half_length max_y = center.y() + half_length square_extent = QgsRectangle(min_x, min_y, max_x, max_y) map.setNewExtent(square_extent) # calculate intervals for grid split_count = 5 x_interval = square_extent.width() / split_count map.setGridIntervalX(x_interval) y_interval = square_extent.height() / split_count map.setGridIntervalY(y_interval) else: raise ReportCreationError( self.tr('Map "impact-map" could not be found')) legend = self.composition.getComposerItemById('impact-legend') legend_attributes = self.map_legend_attributes() LOGGER.debug(legend_attributes) #legend_notes = mapLegendAttributes.get('legend_notes', None) #legend_units = mapLegendAttributes.get('legend_units', None) legend_title = legend_attributes.get('legend_title', None) if legend_title is None: legend_title = "" legend.setTitle(legend_title) legend.updateLegend()
def load_template(self): """Load a QgsComposer map from a template and render it. .. note:: THIS METHOD IS EXPERIMENTAL """ self.setup_composition() template_file = QtCore.QFile(self.template) template_file.open(QtCore.QIODevice.ReadOnly | QtCore.QIODevice.Text) template_content = template_file.readAll() template_file.close() document = QtXml.QDomDocument() document.setContent(template_content) # get information for substitutions # date, time and plugin version date_time = self.keyword_io.read_keywords(self.layer, 'time_stamp') tokens = date_time.split('_') date = tokens[0] time = tokens[1] long_version = get_version() tokens = long_version.split('.') version = '%s.%s.%s' % (tokens[0], tokens[1], tokens[2]) # map title LOGGER.debug('InaSAFE Map getMapTitle called') try: title = self.keyword_io.read_keywords(self.layer, 'map_title') except KeywordNotFoundError: title = None except Exception: title = None if not title: title = '' substitution_map = { 'impact-title': title, 'date': date, 'time': time, 'safe-version': version } LOGGER.debug(substitution_map) load_ok = self.composition.loadFromTemplate(document, substitution_map) if not load_ok: raise ReportCreationError( self.tr('Error loading template %s') % self.template) self.page_width = self.composition.paperWidth() self.page_height = self.composition.paperHeight() # set logo image = self.composition.getComposerItemById('safe-logo') image.setPictureFile(self.logo) # Get the main map canvas on the composition and set # its extents to the event. map = self.composition.getComposerItemById('impact-map') if map is not None: # Recenter the composer map on the center of the canvas # Note that since the composer map is square and the canvas may be # arbitrarily shaped, we center based on the longest edge canvas_extent = self.iface.mapCanvas().extent() width = canvas_extent.width() height = canvas_extent.height() longest_width = width if width < height: longest_width = height half_length = longest_width / 2 center = canvas_extent.center() min_x = center.x() - half_length max_x = center.x() + half_length min_y = center.y() - half_length max_y = center.y() + half_length square_extent = QgsRectangle(min_x, min_y, max_x, max_y) map.setNewExtent(square_extent) # calculate intervals for grid split_count = 5 x_interval = square_extent.width() / split_count map.setGridIntervalX(x_interval) y_interval = square_extent.height() / split_count map.setGridIntervalY(y_interval) else: raise ReportCreationError(self.tr( 'Map "impact-map" could not be found')) legend = self.composition.getComposerItemById('impact-legend') legend_attributes = self.map_legend_attributes() LOGGER.debug(legend_attributes) #legend_notes = mapLegendAttributes.get('legend_notes', None) #legend_units = mapLegendAttributes.get('legend_units', None) legend_title = legend_attributes.get('legend_title', None) if legend_title is None: legend_title = "" legend.setTitle(legend_title) legend.updateLegend()
def qgis_composer_renderer(impact_report, component): """Default Map Report Renderer using QGIS Composer. Render using qgis composer for a given impact_report data and component context :param impact_report: ImpactReport contains data about the report that is going to be generated :type impact_report: safe.report.impact_report.ImpactReport :param component: Contains the component metadata and context for rendering the output :type component: safe.report.report_metadata.QgisComposerComponentsMetadata :return: whatever type of output the component should be .. versionadded:: 4.0 """ context = component.context """:type: safe.report.extractors.composer.QGISComposerContext""" qgis_composition_context = impact_report.qgis_composition_context inasafe_context = impact_report.inasafe_context # load composition object composition = QgsComposition(qgis_composition_context.map_settings) # load template main_template_folder = impact_report.metadata.template_folder template_path = os.path.join(main_template_folder, component.template) with open(template_path) as template_file: template_content = template_file.read() document = QtXml.QDomDocument() document.setContent(template_content) load_status = composition.loadFromTemplate( document, context.substitution_map) if not load_status: raise TemplateLoadingError( tr('Error loading template: %s') % template_path) # replace image path for img in context.image_elements: item_id = img.get('id') path = img.get('path') image = composition.getComposerItemById(item_id) """:type: qgis.core.QgsComposerPicture""" if image is not None and path is not None: try: image.setPicturePath(path) except: pass # replace html frame for html_el in context.html_frame_elements: item_id = html_el.get('id') mode = html_el.get('mode') html_element = composition.getComposerItemById(item_id) """:type: qgis.core.QgsComposerHtml""" if html_element: if mode == 'text': text = html_el.get('text') text = text if text else '' html_element.setContentMode(QgsComposerHtml.ManualHtml) html_element.setHtml(text) html_element.loadHtml() elif mode == 'url': url = html_el.get('url') html_element.setContentMode(QgsComposerHtml.Url) qurl = QUrl.fromLocalFile(url) html_element.setUrl(qurl) # resize map extent for map_el in context.map_elements: item_id = map_el.get('id') split_count = map_el.get('grid_split_count') layers = map_el.get('layers') map_extent_option = map_el.get('extent') composer_map = composition.getComposerItemById(item_id) """:type: qgis.core.QgsComposerMap""" if isinstance(composer_map, QgsComposerMap): composer_map.setKeepLayerSet(True) layer_set = [l.id() for l in layers if isinstance(l, QgsMapLayer)] composer_map.setLayerSet(layer_set) if map_extent_option and isinstance( map_extent_option, QgsRectangle): # use provided map extent extent = map_extent_option else: # if map extent not provided, try to calculate extent # from list of given layers. Combine it so all layers were # shown properly extent = QgsRectangle() extent.setMinimal() for l in layers: # combine extent if different layer is provided. extent.combineExtentWith(l.extent()) width = extent.width() height = extent.height() longest_width = width if width > height else height half_length = longest_width / 2 margin = half_length / 5 center = extent.center() min_x = center.x() - half_length - margin max_x = center.x() + half_length + margin min_y = center.y() - half_length - margin max_y = center.y() + half_length + margin # noinspection PyCallingNonCallable square_extent = QgsRectangle(min_x, min_y, max_x, max_y) composer_map.zoomToExtent(square_extent) composer_map.renderModeUpdateCachedImage() actual_extent = composer_map.extent() # calculate intervals for grid x_interval = actual_extent.width() / split_count composer_map.grid().setIntervalX(x_interval) y_interval = actual_extent.height() / split_count composer_map.grid().setIntervalY(y_interval) # calculate legend element for leg_el in context.map_legends: item_id = leg_el.get('id') title = leg_el.get('title') layers = leg_el.get('layers') symbol_count = leg_el.get('symbol_count') column_count = leg_el.get('column_count') legend = composition.getComposerItemById(item_id) """:type: qgis.core.QgsComposerLegend""" if isinstance(legend, QgsComposerLegend): # set column count if column_count: legend.setColumnCount(column_count) elif symbol_count <= 5: legend.setColumnCount(1) else: legend.setColumnCount(symbol_count / 5 + 1) # set legend title if title is not None: legend.setTitle(title) # set legend root_group = legend.modelV2().rootGroup() for l in layers: # used for customizations tree_layer = root_group.addLayer(l) QgsLegendRenderer.setNodeLegendStyle( tree_layer, QgsComposerLegendStyle.Hidden) legend.synchronizeWithModel() # process to output # in case output folder not specified if impact_report.output_folder is None: impact_report.output_folder = mkdtemp(dir=temp_dir()) output_format = component.output_format component_output_path = impact_report.component_absolute_output_path( component.key) component_output = None doc_format = QgisComposerComponentsMetadata.OutputFormat.DOC_OUTPUT template_format = QgisComposerComponentsMetadata.OutputFormat.QPT if isinstance(output_format, list): component_output = [] for i in range(len(output_format)): each_format = output_format[i] each_path = component_output_path[i] if each_format in doc_format: result_path = create_qgis_pdf_output( each_path, composition, impact_report.qgis_composition_context, each_format, component) component_output.append(result_path) elif each_format == template_format: result_path = create_qgis_template_output( each_path, composition) component_output.append(result_path) elif isinstance(output_format, dict): component_output = {} for key, each_format in output_format.iteritems(): each_path = component_output_path[key] if each_format in doc_format: result_path = create_qgis_pdf_output( each_path, composition, impact_report.qgis_composition_context, each_format, component) component_output[key] = result_path elif each_format == template_format: result_path = create_qgis_template_output( each_path, composition) component_output[key] = result_path elif (output_format in QgisComposerComponentsMetadata.OutputFormat.SUPPORTED_OUTPUT): component_output = None if output_format in doc_format: result_path = create_qgis_pdf_output( component_output_path, composition, impact_report.qgis_composition_context, output_format, component) component_output = result_path elif output_format == template_format: result_path = create_qgis_template_output( component_output_path, composition) component_output = result_path component.output = component_output return component.output
def load_template(self): """Load a QgsComposer map from a template. """ self.setup_composition() template_file = QtCore.QFile(self.template) template_file.open(QtCore.QIODevice.ReadOnly | QtCore.QIODevice.Text) template_content = template_file.readAll() template_file.close() document = QtXml.QDomDocument() document.setContent(template_content) # get information for substitutions # date, time and plugin version date_time = self.keyword_io.read_keywords(self.layer, 'time_stamp') if date_time is None: date = '' time = '' else: tokens = date_time.split('_') date = tokens[0] time = tokens[1] long_version = get_version() tokens = long_version.split('.') version = '%s.%s.%s' % (tokens[0], tokens[1], tokens[2]) title = self.map_title() if not title: title = '' substitution_map = { 'impact-title': title, 'date': date, 'time': time, 'safe-version': version, 'disclaimer': self.disclaimer } LOGGER.debug(substitution_map) load_ok = self.composition.loadFromTemplate( document, substitution_map) if not load_ok: raise ReportCreationError( self.tr('Error loading template %s') % self.template) self.page_width = self.composition.paperWidth() self.page_height = self.composition.paperHeight() # set InaSAFE logo image = self.composition.getComposerItemById('safe-logo') if image is not None: image.setPictureFile(self.safe_logo) else: raise ReportCreationError(self.tr( 'Image "safe-logo" could not be found')) # set north arrow image = self.composition.getComposerItemById('north-arrow') if image is not None: image.setPictureFile(self.north_arrow) else: raise ReportCreationError(self.tr( 'Image "north arrow" could not be found')) # set organisation logo image = self.composition.getComposerItemById('organisation-logo') if image is not None: image.setPictureFile(self.org_logo) else: raise ReportCreationError(self.tr( 'Image "organisation-logo" could not be found')) # set impact report table table = self.composition.getComposerItemById('impact-report') if table is not None: text = self.keyword_io.read_keywords(self.layer, 'impact_summary') if text is None: text = '' table.setText(text) table.setHtmlState(1) else: LOGGER.debug('"impact-report" element not found.') # Get the main map canvas on the composition and set # its extents to the event. composer_map = self.composition.getComposerItemById('impact-map') if composer_map is not None: # Recenter the composer map on the center of the extent # Note that since the composer map is square and the canvas may be # arbitrarily shaped, we center based on the longest edge canvas_extent = self.extent width = canvas_extent.width() height = canvas_extent.height() longest_width = width if width < height: longest_width = height half_length = longest_width / 2 center = canvas_extent.center() min_x = center.x() - half_length max_x = center.x() + half_length min_y = center.y() - half_length max_y = center.y() + half_length square_extent = QgsRectangle(min_x, min_y, max_x, max_y) composer_map.setNewExtent(square_extent) # calculate intervals for grid split_count = 5 x_interval = square_extent.width() / split_count composer_map.setGridIntervalX(x_interval) y_interval = square_extent.height() / split_count composer_map.setGridIntervalY(y_interval) else: raise ReportCreationError(self.tr( 'Map "impact-map" could not be found')) legend = self.composition.getComposerItemById('impact-legend') legend_attributes = self.map_legend_attributes() LOGGER.debug(legend_attributes) #legend_notes = mapLegendAttributes.get('legend_notes', None) #legend_units = mapLegendAttributes.get('legend_units', None) legend_title = legend_attributes.get('legend_title', None) symbol_count = 1 if self.layer.type() == QgsMapLayer.VectorLayer: renderer = self.layer.rendererV2() if renderer.type() in ['', '']: symbol_count = len(self.layer.legendSymbologyItems()) else: renderer = self.layer.renderer() if renderer.type() in ['']: symbol_count = len(self.layer.legendSymbologyItems()) if symbol_count <= 5: legend.setColumnCount(1) else: legend.setColumnCount(symbol_count / 5 + 1) if legend_title is None: legend_title = "" legend.setTitle(legend_title) legend.updateLegend() # remove from legend all layers, except impact one model = legend.model() if model.rowCount() > 0 and model.columnCount() > 0: impact_item = model.findItems(self.layer.name())[0] row = impact_item.index().row() model.removeRows(row + 1, model.rowCount() - row) if row > 0: model.removeRows(0, row)
class DEMBlock: def __init__(self, dem_width, dem_height, dem_values, plane_width, plane_height, offsetX, offsetY): self.dem_width = dem_width self.dem_height = dem_height self.dem_values = dem_values self.plane_width = plane_width self.plane_height = plane_height self.offsetX = offsetX self.offsetY = offsetY self.orig_stats = {"max": max(dem_values), "min": min(dem_values)} self.rect = QgsRectangle(offsetX - plane_width * 0.5, offsetY - plane_height * 0.5, offsetX + plane_width * 0.5, offsetY + plane_height * 0.5) self.properties = {"width": dem_width, "height": dem_height} self.properties["plane"] = { "width": plane_width, "height": plane_height, "offsetX": offsetX, "offsetY": offsetY } self.clip_geometry = None def set(self, key, value): """set property""" self.properties[key] = value def setClipGeometry(self, geometry): self.clip_geometry = geometry def zShift(self, shift): if shift != 0: self.dem_values = map(lambda x: x + shift, self.dem_values) def zScale(self, scale): if scale != 1: self.dem_values = map(lambda x: x * scale, self.dem_values) def write(self, writer): mapTo3d = writer.settings.mapTo3d() writer.write("bl = lyr.addBlock({0}, {1});\n".format( pyobj2js(self.properties), pyobj2js(bool(self.clip_geometry)))) writer.write("bl.data = [{0}];\n".format(",".join( map(gdal2threejs.formatValue, self.dem_values)))) # clipped with polygon layer if self.clip_geometry: z_func = lambda x, y: 0 transform_func = lambda x, y, z: mapTo3d.transform(x, y, z) geom = PolygonGeometry.fromQgsGeometry(self.clip_geometry, z_func, transform_func) geom.splitPolygon( writer.triangleMesh(self.dem_width, self.dem_height)) polygons = [] for polygon in geom.polygons: bnds = [] for boundary in polygon: bnds.append(map(lambda pt: [pt.x, pt.y], boundary)) polygons.append(bnds) writer.write("bl.clip = {};\n") writer.write("bl.clip.polygons = {0};\n".format( pyobj2js(polygons))) triangles = Triangles() polygons = [] for polygon in geom.split_polygons: boundary = polygon[0] if len(polygon) == 1 and len(boundary) == 4: # vertex order should be counter-clockwise triangles.addTriangle(boundary[0], boundary[2], boundary[1]) else: bnds = [ map(lambda pt: [pt.x, pt.y], bnd) for bnd in polygon ] polygons.append(bnds) vf = { "v": map(lambda pt: [pt.x, pt.y], triangles.vertices), "f": triangles.faces } writer.write("bl.clip.triangles = {0};\n".format(pyobj2js(vf))) writer.write("bl.clip.split_polygons = {0};\n".format( pyobj2js(polygons))) def getValue(self, x, y): def _getValue(gx, gy): return self.dem_values[gx + self.dem_width * gy] if 0 <= x and x <= self.dem_width - 1 and 0 <= y and y <= self.dem_height - 1: ix, iy = int(x), int(y) sx, sy = x - ix, y - iy z11 = _getValue(ix, iy) z21 = 0 if x == self.dem_width - 1 else _getValue(ix + 1, iy) z12 = 0 if y == self.dem_height - 1 else _getValue(ix, iy + 1) z22 = 0 if x == self.dem_width - 1 or y == self.dem_height - \ 1 else _getValue(ix + 1, iy + 1) return (1 - sx) * ((1 - sy) * z11 + sy * z12) + sx * \ ((1 - sy) * z21 + sy * z22) # bilinear interpolation return 0 # as safe null value def gridPointToPoint(self, x, y): x = self.rect.xMinimum() + self.rect.width() / (self.dem_width - 1) * x y = self.rect.yMaximum() - self.rect.height() / (self.dem_height - 1) * y return x, y def pointToGridPoint(self, x, y): x = (x - self.rect.xMinimum()) / \ self.rect.width() * (self.dem_width - 1) y = (self.rect.yMaximum() - y) / \ self.rect.height() * (self.dem_height - 1) return x, y
def processAlgorithm(self, parameters, context, feedback): extent = self.getParameterValue(self.EXTENT).split(',') hSpacing = self.getParameterValue(self.HSPACING) vSpacing = self.getParameterValue(self.VSPACING) hOverlay = self.getParameterValue(self.HOVERLAY) vOverlay = self.getParameterValue(self.VOVERLAY) crs = QgsCoordinateReferenceSystem(self.getParameterValue(self.CRS)) bbox = QgsRectangle(float(extent[0]), float(extent[2]), float(extent[1]), float(extent[3])) width = bbox.width() height = bbox.height() if hSpacing <= 0 or vSpacing <= 0: raise GeoAlgorithmExecutionException( self.tr('Invalid grid spacing: {0}/{1}').format( hSpacing, vSpacing)) if hSpacing <= hOverlay or vSpacing <= vOverlay: raise GeoAlgorithmExecutionException( self.tr('Invalid overlay: {0}/{1}').format(hOverlay, vOverlay)) if width < hSpacing: raise GeoAlgorithmExecutionException( self.tr( 'Horizontal spacing is too small for the covered area')) if height < vSpacing: raise GeoAlgorithmExecutionException( self.tr('Vertical spacing is too small for the covered area')) fields = QgsFields() fields.append(QgsField('left', QVariant.Double, '', 24, 16)) fields.append(QgsField('top', QVariant.Double, '', 24, 16)) fields.append(QgsField('right', QVariant.Double, '', 24, 16)) fields.append(QgsField('bottom', QVariant.Double, '', 24, 16)) fields.append(QgsField('id', QVariant.Int, '', 10, 0)) fields.append(QgsField('coord', QVariant.Double, '', 24, 15)) writer = self.getOutputFromName(self.OUTPUT).getVectorWriter( fields, QgsWkbTypes.LineString, crs, context) if hOverlay > 0: hSpace = [hSpacing - hOverlay, hOverlay] else: hSpace = [hSpacing, hSpacing] if vOverlay > 0: vSpace = [vSpacing - vOverlay, vOverlay] else: vSpace = [vSpacing, vSpacing] feat = QgsFeature() feat.initAttributes(len(fields)) count = 0 id = 1 # latitude lines count_max = height / vSpacing count_update = count_max * 0.10 y = bbox.yMaximum() while y >= bbox.yMinimum(): pt1 = QgsPoint(bbox.xMinimum(), y) pt2 = QgsPoint(bbox.xMaximum(), y) line = QgsLineString() line.setPoints([pt1, pt2]) feat.setGeometry(QgsGeometry(line)) feat.setAttributes([bbox.xMinimum(), y, bbox.xMaximum(), y, id, y]) writer.addFeature(feat, QgsFeatureSink.FastInsert) y = y - vSpace[count % 2] id += 1 count += 1 if int(math.fmod(count, count_update)) == 0: feedback.setProgress(int(count / count_max * 50)) feedback.setProgress(50) # longitude lines # counters for progressbar - update every 5% count = 0 count_max = width / hSpacing count_update = count_max * 0.10 x = bbox.xMinimum() while x <= bbox.xMaximum(): pt1 = QgsPoint(x, bbox.yMaximum()) pt2 = QgsPoint(x, bbox.yMinimum()) line = QgsLineString() line.setPoints([pt1, pt2]) feat.setGeometry(QgsGeometry(line)) feat.setAttributes([x, bbox.yMaximum(), x, bbox.yMinimum(), id, x]) writer.addFeature(feat, QgsFeatureSink.FastInsert) x = x + hSpace[count % 2] id += 1 count += 1 if int(math.fmod(count, count_update)) == 0: feedback.setProgress(50 + int(count / count_max * 50)) del writer
def processAlgorithm(self, progress): extent = self.getParameterValue(self.EXTENT).split(",") hSpacing = self.getParameterValue(self.HSPACING) vSpacing = self.getParameterValue(self.VSPACING) crs = QgsCoordinateReferenceSystem(self.getParameterValue(self.CRS)) bbox = QgsRectangle(float(extent[0]), float(extent[2]), float(extent[1]), float(extent[3])) width = bbox.width() height = bbox.height() if hSpacing <= 0 or vSpacing <= 0: raise GeoAlgorithmExecutionException(self.tr("Invalid grid spacing: %s/%s" % (hSpacing, vSpacing))) if width < hSpacing: raise GeoAlgorithmExecutionException(self.tr("Horizontal spacing is too small for the covered area")) if height < vSpacing: raise GeoAlgorithmExecutionException(self.tr("Vertical spacing is too small for the covered area")) fields = [ QgsField("left", QVariant.Double, "", 24, 16), QgsField("top", QVariant.Double, "", 24, 16), QgsField("right", QVariant.Double, "", 24, 16), QgsField("bottom", QVariant.Double, "", 24, 16), QgsField("id", QVariant.Int, "", 10, 0), QgsField("coord", QVariant.Double, "", 24, 15), ] writer = self.getOutputFromName(self.OUTPUT).getVectorWriter(fields, QgsWkbTypes.LineString, crs) feat = QgsFeature() feat.initAttributes(len(fields)) count = 0 id = 1 # latitude lines count_max = height / vSpacing count_update = count_max * 0.10 y = bbox.yMaximum() while y >= bbox.yMinimum(): pt1 = QgsPointV2(bbox.xMinimum(), y) pt2 = QgsPointV2(bbox.xMaximum(), y) line = QgsLineString() line.setPoints([pt1, pt2]) feat.setGeometry(QgsGeometry(line)) feat.setAttributes([bbox.xMinimum(), y, bbox.xMaximum(), y, id, y]) writer.addFeature(feat) y = y - vSpacing id += 1 count += 1 if int(math.fmod(count, count_update)) == 0: progress.setPercentage(int(count / count_max * 50)) progress.setPercentage(50) # longitude lines # counters for progressbar - update every 5% count = 0 count_max = width / hSpacing count_update = count_max * 0.10 x = bbox.xMinimum() while x <= bbox.xMaximum(): pt1 = QgsPointV2(x, bbox.yMaximum()) pt2 = QgsPointV2(x, bbox.yMinimum()) line = QgsLineString() line.setPoints([pt1, pt2]) feat.setGeometry(QgsGeometry(line)) feat.setAttributes([x, bbox.yMaximum(), x, bbox.yMinimum(), id, x]) writer.addFeature(feat) x = x + hSpacing id += 1 count += 1 if int(math.fmod(count, count_update)) == 0: progress.setPercentage(50 + int(count / count_max * 50)) del writer
def processAlgorithm(self, progress): idx = self.getParameterValue(self.TYPE) extent = self.getParameterValue(self.EXTENT).split(',') hSpacing = self.getParameterValue(self.HSPACING) vSpacing = self.getParameterValue(self.VSPACING) crs = QgsCoordinateReferenceSystem(self.getParameterValue(self.CRS)) bbox = QgsRectangle(float(extent[0]), float(extent[2]), float(extent[1]), float(extent[3])) width = bbox.width() height = bbox.height() centerX = bbox.center().x() centerY = bbox.center().y() #~ originX = centerX - width / 2.0 #~ originY = centerY - height / 2.0 originX = bbox.xMinimum() originY = bbox.yMaximum() if hSpacing <= 0 or vSpacing <= 0: raise GeoAlgorithmExecutionException( self.tr('Invalid grid spacing: %s/%s' % (hSpacing, vSpacing))) if width < hSpacing: raise GeoAlgorithmExecutionException( self.tr( 'Horizontal spacing is too small for the covered area')) if height < vSpacing: raise GeoAlgorithmExecutionException( self.tr('Vertical spacing is too small for the covered area')) #if self.types[idx].find(self.tr('polygon')) >= 0: if idx != 0: geometryType = QGis.WKBPolygon else: geometryType = QGis.WKBLineString fields = [ QgsField('left', QVariant.Double, '', 24, 16), QgsField('top', QVariant.Double, '', 24, 16), QgsField('right', QVariant.Double, '', 24, 16), QgsField('bottom', QVariant.Double, '', 24, 16) ] writer = self.getOutputFromName(self.OUTPUT).getVectorWriter( fields, geometryType, crs) if idx == 0: self._rectangleGridLine(writer, width, height, originX, originY, hSpacing, vSpacing) elif idx == 1: self._rectangleGridPoly(writer, width, height, originX, originY, hSpacing, vSpacing) elif idx == 2: self._diamondGrid(writer, width, height, originX, originY, hSpacing, vSpacing) elif idx == 3: self._hexagonGrid(writer, width, height, originX, originY, hSpacing, vSpacing) del writer
class HeatmapPixelSizeWidget(BASE, WIDGET): def __init__(self): super(HeatmapPixelSizeWidget, self).__init__(None) self.setupUi(self) self.layer_bounds = QgsRectangle() self.layer = None self.raster_bounds = QgsRectangle() self.radius = 100 self.radius_field = None self.mCellXSpinBox.setShowClearButton(False) self.mCellYSpinBox.setShowClearButton(False) self.mRowsSpinBox.setShowClearButton(False) self.mColumnsSpinBox.setShowClearButton(False) self.mCellYSpinBox.valueChanged.connect(self.mCellXSpinBox.setValue) self.mCellXSpinBox.valueChanged.connect(self.pixelSizeChanged) self.mRowsSpinBox.valueChanged.connect(self.rowsChanged) self.mColumnsSpinBox.valueChanged.connect(self.columnsChanged) def setRadius(self, radius): self.radius = radius self.recalculate_bounds() def setRadiusField(self, radius_field): self.radius_field = radius_field self.recalculate_bounds() def setLayer(self, layer): if not layer: return bounds = layer.extent() if bounds.isNull(): return self.layer = layer self.layer_bounds = bounds self.recalculate_bounds() def recalculate_bounds(self): self.raster_bounds = QgsRectangle(self.layer_bounds) if not self.layer: return max_radius = self.radius if self.radius_field: idx = self.layer.fields().lookupField(self.radius_field) try: max_radius = float(self.layer.maximumValue(idx)) except: pass self.raster_bounds.setXMinimum(self.raster_bounds.xMinimum() - max_radius) self.raster_bounds.setYMinimum(self.raster_bounds.yMinimum() - max_radius) self.raster_bounds.setXMaximum(self.raster_bounds.xMaximum() + max_radius) self.raster_bounds.setYMaximum(self.raster_bounds.yMaximum() + max_radius) self.pixelSizeChanged() def pixelSizeChanged(self): cell_size = self.mCellXSpinBox.value() if cell_size <= 0: return self.mCellYSpinBox.blockSignals(True) self.mCellYSpinBox.setValue(cell_size) self.mCellYSpinBox.blockSignals(False) rows = max(round(self.raster_bounds.height() / cell_size) + 1, 1) cols = max(round(self.raster_bounds.width() / cell_size) + 1, 1) self.mRowsSpinBox.blockSignals(True) self.mRowsSpinBox.setValue(rows) self.mRowsSpinBox.blockSignals(False) self.mColumnsSpinBox.blockSignals(True) self.mColumnsSpinBox.setValue(cols) self.mColumnsSpinBox.blockSignals(False) def rowsChanged(self): rows = self.mRowsSpinBox.value() if rows <= 0: return cell_size = self.raster_bounds.height() / rows cols = max(round(self.raster_bounds.width() / cell_size) + 1, 1) self.mColumnsSpinBox.blockSignals(True) self.mColumnsSpinBox.setValue(cols) self.mColumnsSpinBox.blockSignals(False) for w in [self.mCellXSpinBox, self.mCellYSpinBox]: w.blockSignals(True) w.setValue(cell_size) w.blockSignals(False) def columnsChanged(self): cols = self.mColumnsSpinBox.value() if cols < 2: return cell_size = self.raster_bounds.width() / (cols - 1) rows = max(round(self.raster_bounds.height() / cell_size), 1) self.mRowsSpinBox.blockSignals(True) self.mRowsSpinBox.setValue(rows) self.mRowsSpinBox.blockSignals(False) for w in [self.mCellXSpinBox, self.mCellYSpinBox]: w.blockSignals(True) w.setValue(cell_size) w.blockSignals(False) def setValue(self, value): try: numeric_value = float(value) except: return False self.mCellXSpinBox.setValue(numeric_value) self.mCellYSpinBox.setValue(numeric_value) return True def value(self): return self.mCellXSpinBox.value()
def draw_composition(self): """Draw all the components in the composition.""" # This is deprecated - use inasafe-logo-<colour> rather safe_logo = self.composition.getComposerItemById( 'safe-logo') # Next two options replace safe logo in 3.2 black_inasafe_logo = self.composition.getComposerItemById( 'black-inasafe-logo') white_inasafe_logo = self.composition.getComposerItemById( 'white-inasafe-logo') north_arrow = self.composition.getComposerItemById( 'north-arrow') organisation_logo = self.composition.getComposerItemById( 'organisation-logo') supporters_logo = self.composition.getComposerItemById( 'supporters-logo') if qgis_version() < 20600: if safe_logo is not None: # its deprecated so just use black_inasafe_logo safe_logo.setPictureFile(self.inasafe_logo) if black_inasafe_logo is not None: black_inasafe_logo.setPictureFile(self._black_inasafe_logo) if white_inasafe_logo is not None: white_inasafe_logo.setPictureFile(self._white_inasafe_logo) if north_arrow is not None: north_arrow.setPictureFile(self.north_arrow) if organisation_logo is not None: organisation_logo.setPictureFile(self.organisation_logo) if supporters_logo is not None: supporters_logo.setPictureFile(self.supporters_logo) else: if safe_logo is not None: # its deprecated so just use black_inasafe_logo safe_logo.setPicturePath(self.inasafe_logo) if black_inasafe_logo is not None: black_inasafe_logo.setPicturePath(self._black_inasafe_logo) if white_inasafe_logo is not None: white_inasafe_logo.setPicturePath(self._white_inasafe_logo) if north_arrow is not None: north_arrow.setPicturePath(self.north_arrow) if organisation_logo is not None: organisation_logo.setPicturePath(self.organisation_logo) if supporters_logo is not None: supporters_logo.setPicturePath(self.supporters_logo) # Set impact report table table = self.composition.getComposerItemById('impact-report') if table is not None: text = self._keyword_io.read_keywords(self.layer, 'impact_summary') if text is None: text = '' table.setText(text) table.setHtmlState(1) # Get the main map canvas on the composition and set its extents to # the event. composer_map = self.composition.getComposerItemById('impact-map') if composer_map is not None: # Recenter the composer map on the center of the extent # Note that since the composer map is square and the canvas may be # arbitrarily shaped, we center based on the longest edge canvas_extent = self.extent width = canvas_extent.width() height = canvas_extent.height() longest_width = width if width > height else height half_length = longest_width / 2 center = canvas_extent.center() min_x = center.x() - half_length max_x = center.x() + half_length min_y = center.y() - half_length max_y = center.y() + half_length # noinspection PyCallingNonCallable square_extent = QgsRectangle(min_x, min_y, max_x, max_y) composer_map.setNewExtent(square_extent) # calculate intervals for grid split_count = 5 x_interval = square_extent.width() / split_count composer_map.setGridIntervalX(x_interval) y_interval = square_extent.height() / split_count composer_map.setGridIntervalY(y_interval) legend = self.composition.getComposerItemById('impact-legend') if legend is not None: symbol_count = ImpactReport.symbol_count(self.layer) # add legend symbol count from extra_layers for l in self.extra_layers: symbol_count += ImpactReport.symbol_count(l) if symbol_count <= 5: legend.setColumnCount(1) else: legend.setColumnCount(symbol_count / 5 + 1) # Set back to blank to #2409 legend.setTitle("") # Set Legend # Since QGIS 2.6, legend.model() is obsolete if qgis_version() < 20600: layer_set = [self.layer.id()] layer_set += [l.id() for l in self.extra_layers] legend.model().setLayerSet(layer_set) legend.synchronizeWithModel() else: root_group = legend.modelV2().rootGroup() root_group.addLayer(self.layer) for l in self.extra_layers: root_group.addLayer(l) legend.synchronizeWithModel()
def processAlgorithm(self, progress): extent = self.getParameterValue(self.EXTENT).split(',') hSpacing = self.getParameterValue(self.HSPACING) vSpacing = self.getParameterValue(self.VSPACING) crs = QgsCoordinateReferenceSystem(self.getParameterValue(self.CRS)) bbox = QgsRectangle(float(extent[0]), float(extent[2]), float(extent[1]), float(extent[3])) width = bbox.width() height = bbox.height() if hSpacing <= 0 or vSpacing <= 0: raise GeoAlgorithmExecutionException( self.tr('Invalid grid spacing: %s/%s' % (hSpacing, vSpacing))) if width < hSpacing: raise GeoAlgorithmExecutionException( self.tr('Horizontal spacing is too small for the covered area')) if height < vSpacing: raise GeoAlgorithmExecutionException( self.tr('Vertical spacing is too small for the covered area')) fields = [QgsField('left', QVariant.Double, '', 24, 16), QgsField('top', QVariant.Double, '', 24, 16), QgsField('right', QVariant.Double, '', 24, 16), QgsField('bottom', QVariant.Double, '', 24, 16), QgsField('id', QVariant.Int, '', 10, 0), QgsField('coord', QVariant.Double, '', 24, 15) ] writer = self.getOutputFromName(self.OUTPUT).getVectorWriter(fields, QgsWkbTypes.LineString, crs) feat = QgsFeature() feat.initAttributes(len(fields)) count = 0 id = 1 # latitude lines count_max = height / vSpacing count_update = count_max * 0.10 y = bbox.yMaximum() while y >= bbox.yMinimum(): pt1 = QgsPointV2(bbox.xMinimum(), y) pt2 = QgsPointV2(bbox.xMaximum(), y) line = QgsLineString() line.setPoints([pt1, pt2]) feat.setGeometry(QgsGeometry(line)) feat.setAttributes([bbox.xMinimum(), y, bbox.xMaximum(), y, id, y]) writer.addFeature(feat) y = y - vSpacing id += 1 count += 1 if int(math.fmod(count, count_update)) == 0: progress.setPercentage(int(count / count_max * 50)) progress.setPercentage(50) # longitude lines # counters for progressbar - update every 5% count = 0 count_max = width / hSpacing count_update = count_max * 0.10 x = bbox.xMinimum() while x <= bbox.xMaximum(): pt1 = QgsPointV2(x, bbox.yMaximum()) pt2 = QgsPointV2(x, bbox.yMinimum()) line = QgsLineString() line.setPoints([pt1, pt2]) feat.setGeometry(QgsGeometry(line)) feat.setAttributes([x, bbox.yMaximum(), x, bbox.yMinimum(), id, x]) writer.addFeature(feat) x = x + hSpacing id += 1 count += 1 if int(math.fmod(count, count_update)) == 0: progress.setPercentage(50 + int(count / count_max * 50)) del writer
class DEMBlock: def __init__(self, dem_width, dem_height, dem_values, plane_width, plane_height, offsetX, offsetY): self.dem_width = dem_width self.dem_height = dem_height self.dem_values = dem_values self.plane_width = plane_width self.plane_height = plane_height self.offsetX = offsetX self.offsetY = offsetY self.orig_stats = {"max": max(dem_values), "min": min(dem_values)} self.rect = QgsRectangle(offsetX - plane_width * 0.5, offsetY - plane_height * 0.5, offsetX + plane_width * 0.5, offsetY + plane_height * 0.5) self.properties = {"width": dem_width, "height": dem_height} self.properties["plane"] = {"width": plane_width, "height": plane_height, "offsetX": offsetX, "offsetY": offsetY} self.clip_geometry = None def set(self, key, value): """set property""" self.properties[key] = value def setClipGeometry(self, geometry): self.clip_geometry = geometry def zShift(self, shift): if shift != 0: self.dem_values = map(lambda x: x + shift, self.dem_values) def zScale(self, scale): if scale != 1: self.dem_values = map(lambda x: x * scale, self.dem_values) def write(self, writer): mapTo3d = writer.settings.mapTo3d writer.write("bl = lyr.addBlock({0}, {1});\n".format(pyobj2js(self.properties), pyobj2js(bool(self.clip_geometry)))) writer.write("bl.data = [{0}];\n".format(",".join(map(gdal2threejs.formatValue, self.dem_values)))) # clipped with polygon layer if self.clip_geometry: z_func = lambda x, y: 0 transform_func = lambda x, y, z: mapTo3d.transform(x, y, z) geom = PolygonGeometry.fromQgsGeometry(self.clip_geometry, z_func, transform_func) geom.splitPolygon(writer.triangleMesh(self.dem_width, self.dem_height)) polygons = [] for polygon in geom.polygons: bnds = [] for boundary in polygon: bnds.append(map(lambda pt: [pt.x, pt.y], boundary)) polygons.append(bnds) writer.write("bl.clip = {};\n") writer.write("bl.clip.polygons = {0};\n".format(pyobj2js(polygons))) triangles = Triangles() polygons = [] for polygon in geom.split_polygons: boundary = polygon[0] if len(polygon) == 1 and len(boundary) == 4: triangles.addTriangle(boundary[0], boundary[2], boundary[1]) # vertex order should be counter-clockwise else: bnds = [map(lambda pt: [pt.x, pt.y], bnd) for bnd in polygon] polygons.append(bnds) vf = {"v": map(lambda pt: [pt.x, pt.y], triangles.vertices), "f": triangles.faces} writer.write("bl.clip.triangles = {0};\n".format(pyobj2js(vf))) writer.write("bl.clip.split_polygons = {0};\n".format(pyobj2js(polygons))) def getValue(self, x, y): def _getValue(gx, gy): return self.dem_values[gx + self.dem_width * gy] if 0 <= x and x <= self.dem_width - 1 and 0 <= y and y <= self.dem_height - 1: ix, iy = int(x), int(y) sx, sy = x - ix, y - iy z11 = _getValue(ix, iy) z21 = 0 if x == self.dem_width - 1 else _getValue(ix + 1, iy) z12 = 0 if y == self.dem_height - 1 else _getValue(ix, iy + 1) z22 = 0 if x == self.dem_width - 1 or y == self.dem_height - 1 else _getValue(ix + 1, iy + 1) return (1 - sx) * ((1 - sy) * z11 + sy * z12) + sx * ((1 - sy) * z21 + sy * z22) # bilinear interpolation return 0 # as safe null value def gridPointToPoint(self, x, y): x = self.rect.xMinimum() + self.rect.width() / (self.dem_width - 1) * x y = self.rect.yMaximum() - self.rect.height() / (self.dem_height - 1) * y return x, y def pointToGridPoint(self, x, y): x = (x - self.rect.xMinimum()) / self.rect.width() * (self.dem_width - 1) y = (self.rect.yMaximum() - y) / self.rect.height() * (self.dem_height - 1) return x, y
def draw_composition(self): """Draw all the components in the composition.""" safe_logo = self.composition.getComposerItemById('safe-logo') north_arrow = self.composition.getComposerItemById('north-arrow') organisation_logo = self.composition.getComposerItemById( 'organisation-logo') if qgis_version() < 20600: if safe_logo is not None: safe_logo.setPictureFile(self.safe_logo) if north_arrow is not None: north_arrow.setPictureFile(self.north_arrow) if organisation_logo is not None: organisation_logo.setPictureFile(self.organisation_logo) else: if safe_logo is not None: safe_logo.setPicturePath(self.safe_logo) if north_arrow is not None: north_arrow.setPicturePath(self.north_arrow) if organisation_logo is not None: organisation_logo.setPicturePath(self.organisation_logo) # Set impact report table table = self.composition.getComposerItemById('impact-report') if table is not None: text = self._keyword_io.read_keywords(self.layer, 'impact_summary') if text is None: text = '' table.setText(text) table.setHtmlState(1) # Get the main map canvas on the composition and set its extents to # the event. composer_map = self.composition.getComposerItemById('impact-map') if composer_map is not None: # Recenter the composer map on the center of the extent # Note that since the composer map is square and the canvas may be # arbitrarily shaped, we center based on the longest edge canvas_extent = self.extent width = canvas_extent.width() height = canvas_extent.height() longest_width = width if width < height: longest_width = height half_length = longest_width / 2 center = canvas_extent.center() min_x = center.x() - half_length max_x = center.x() + half_length min_y = center.y() - half_length max_y = center.y() + half_length # noinspection PyCallingNonCallable square_extent = QgsRectangle(min_x, min_y, max_x, max_y) composer_map.setNewExtent(square_extent) # calculate intervals for grid split_count = 5 x_interval = square_extent.width() / split_count composer_map.setGridIntervalX(x_interval) y_interval = square_extent.height() / split_count composer_map.setGridIntervalY(y_interval) legend = self.composition.getComposerItemById('impact-legend') if legend is not None: legend_attributes = self.map_legend_attributes legend_title = legend_attributes.get('legend_title', None) symbol_count = 1 # noinspection PyUnresolvedReferences if self.layer.type() == QgsMapLayer.VectorLayer: renderer = self.layer.rendererV2() if renderer.type() in ['', '']: symbol_count = len(self.layer.legendSymbologyItems()) else: renderer = self.layer.renderer() if renderer.type() in ['']: symbol_count = len(self.layer.legendSymbologyItems()) if symbol_count <= 5: legend.setColumnCount(1) else: legend.setColumnCount(symbol_count / 5 + 1) if legend_title is None: legend_title = "" legend.setTitle(legend_title) # Set Legend # Since QGIS 2.6, legend.model() is obsolete if qgis_version() < 20600: legend.model().setLayerSet([self.layer.id()]) legend.synchronizeWithModel() else: root_group = legend.modelV2().rootGroup() root_group.addLayer(self.layer) legend.synchronizeWithModel()
def processAlgorithm(self, parameters, context, feedback): source = self.parameterAsSource(parameters, self.INPUT, context) field_name = self.parameterAsString(parameters, self.FIELD, context) type = self.parameterAsEnum(parameters, self.TYPE, context) use_field = bool(field_name) field_index = -1 fields = QgsFields() fields.append(QgsField('id', QVariant.Int, '', 20)) if use_field: # keep original field type, name and parameters field_index = source.fields().lookupField(field_name) if field_index >= 0: fields.append(source.fields()[field_index]) if type == 0: # envelope fields.append(QgsField('width', QVariant.Double, '', 20, 6)) fields.append(QgsField('height', QVariant.Double, '', 20, 6)) fields.append(QgsField('area', QVariant.Double, '', 20, 6)) fields.append(QgsField('perimeter', QVariant.Double, '', 20, 6)) elif type == 1: # oriented rect fields.append(QgsField('width', QVariant.Double, '', 20, 6)) fields.append(QgsField('height', QVariant.Double, '', 20, 6)) fields.append(QgsField('angle', QVariant.Double, '', 20, 6)) fields.append(QgsField('area', QVariant.Double, '', 20, 6)) fields.append(QgsField('perimeter', QVariant.Double, '', 20, 6)) elif type == 2: # circle fields.append(QgsField('radius', QVariant.Double, '', 20, 6)) fields.append(QgsField('area', QVariant.Double, '', 20, 6)) elif type == 3: # convex hull fields.append(QgsField('area', QVariant.Double, '', 20, 6)) fields.append(QgsField('perimeter', QVariant.Double, '', 20, 6)) (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, fields, QgsWkbTypes.Polygon, source.sourceCrs()) if field_index >= 0: geometry_dict = {} bounds_dict = {} total = 50.0 / source.featureCount() if source.featureCount( ) else 1 features = source.getFeatures( QgsFeatureRequest().setSubsetOfAttributes([field_index])) for current, f in enumerate(features): if feedback.isCanceled(): break if not f.hasGeometry(): continue if type == 0: # bounding boxes - calculate on the fly for efficiency if not f.attributes()[field_index] in bounds_dict: bounds_dict[f.attributes()[field_index]] = f.geometry( ).boundingBox() else: bounds_dict[f.attributes() [field_index]].combineExtentWith( f.geometry().boundingBox()) else: if not f.attributes()[field_index] in geometry_dict: geometry_dict[f.attributes()[field_index]] = [ f.geometry() ] else: geometry_dict[f.attributes()[field_index]].append( f.geometry()) feedback.setProgress(int(current * total)) if type == 0: # bounding boxes current = 0 total = 50.0 / len(bounds_dict) if bounds_dict else 1 for group, rect in bounds_dict.items(): if feedback.isCanceled(): break # envelope feature = QgsFeature() feature.setGeometry(QgsGeometry.fromRect(rect)) feature.setAttributes([ current, group, rect.width(), rect.height(), rect.area(), rect.perimeter() ]) sink.addFeature(feature, QgsFeatureSink.FastInsert) geometry_dict[group] = None feedback.setProgress(50 + int(current * total)) current += 1 else: current = 0 total = 50.0 / len(geometry_dict) if geometry_dict else 1 for group, geometries in geometry_dict.items(): if feedback.isCanceled(): break feature = self.createFeature(feedback, current, type, geometries, group) sink.addFeature(feature, QgsFeatureSink.FastInsert) geometry_dict[group] = None feedback.setProgress(50 + int(current * total)) current += 1 else: total = 80.0 / source.featureCount() if source.featureCount( ) else 1 features = source.getFeatures( QgsFeatureRequest().setSubsetOfAttributes([])) geometry_queue = [] bounds = QgsRectangle() for current, f in enumerate(features): if feedback.isCanceled(): break if not f.hasGeometry(): continue if type == 0: # bounding boxes, calculate on the fly for efficiency bounds.combineExtentWith(f.geometry().boundingBox()) else: geometry_queue.append(f.geometry()) feedback.setProgress(int(current * total)) if not feedback.isCanceled(): if type == 0: feature = QgsFeature() feature.setGeometry(QgsGeometry.fromRect(bounds)) feature.setAttributes([ 0, bounds.width(), bounds.height(), bounds.area(), bounds.perimeter() ]) else: feature = self.createFeature(feedback, 0, type, geometry_queue) sink.addFeature(feature, QgsFeatureSink.FastInsert) return {self.OUTPUT: dest_id}
def processAlgorithm(self, feedback): extent = str(self.getParameterValue(self.EXTENT)).split(',') spacing = float(self.getParameterValue(self.SPACING)) inset = float(self.getParameterValue(self.INSET)) randomize = self.getParameterValue(self.RANDOMIZE) isSpacing = self.getParameterValue(self.IS_SPACING) extent = QgsRectangle(float(extent[0]), float(extent[2]), float(extent[1]), float(extent[3])) fields = QgsFields() fields.append(QgsField('id', QVariant.Int, '', 10, 0)) mapCRS = iface.mapCanvas().mapSettings().destinationCrs() writer = self.getOutputFromName(self.OUTPUT).getVectorWriter( fields, QgsWkbTypes.Point, mapCRS) if randomize: seed() area = extent.width() * extent.height() if isSpacing: pSpacing = spacing else: pSpacing = sqrt(area / spacing) f = QgsFeature() f.initAttributes(1) f.setFields(fields) count = 0 total = 100.0 / (area / pSpacing) y = extent.yMaximum() - inset extent_geom = QgsGeometry.fromRect(extent) extent_engine = QgsGeometry.createGeometryEngine( extent_geom.geometry()) extent_engine.prepareGeometry() while y >= extent.yMinimum(): x = extent.xMinimum() + inset while x <= extent.xMaximum(): if randomize: geom = QgsGeometry().fromPoint( QgsPoint( uniform(x - (pSpacing / 2.0), x + (pSpacing / 2.0)), uniform(y - (pSpacing / 2.0), y + (pSpacing / 2.0)))) else: geom = QgsGeometry().fromPoint(QgsPoint(x, y)) if extent_engine.intersects(geom.geometry()): f.setAttribute('id', count) f.setGeometry(geom) writer.addFeature(f) x += pSpacing count += 1 feedback.setProgress(int(count * total)) y = y - pSpacing del writer
def processAlgorithm(self, parameters, context, feedback): extent = self.getParameterValue(self.EXTENT).split(',') hSpacing = self.getParameterValue(self.HSPACING) vSpacing = self.getParameterValue(self.VSPACING) hOverlay = self.getParameterValue(self.HOVERLAY) vOverlay = self.getParameterValue(self.VOVERLAY) crs = QgsCoordinateReferenceSystem(self.getParameterValue(self.CRS)) bbox = QgsRectangle(float(extent[0]), float(extent[2]), float(extent[1]), float(extent[3])) width = bbox.width() height = bbox.height() if hSpacing <= 0 or vSpacing <= 0: raise GeoAlgorithmExecutionException( self.tr('Invalid grid spacing: {0}/{1}').format(hSpacing, vSpacing)) if hSpacing <= hOverlay or vSpacing <= vOverlay: raise GeoAlgorithmExecutionException( self.tr('Invalid overlay: {0}/{1}').format(hOverlay, vOverlay)) if width < hSpacing: raise GeoAlgorithmExecutionException( self.tr('Horizontal spacing is too small for the covered area')) if height < vSpacing: raise GeoAlgorithmExecutionException( self.tr('Vertical spacing is too small for the covered area')) fields = QgsFields() fields.append(QgsField('left', QVariant.Double, '', 24, 16)) fields.append(QgsField('top', QVariant.Double, '', 24, 16)) fields.append(QgsField('right', QVariant.Double, '', 24, 16)) fields.append(QgsField('bottom', QVariant.Double, '', 24, 16)) fields.append(QgsField('id', QVariant.Int, '', 10, 0)) fields.append(QgsField('coord', QVariant.Double, '', 24, 15)) writer = self.getOutputFromName(self.OUTPUT).getVectorWriter(fields, QgsWkbTypes.LineString, crs, context) if hOverlay > 0: hSpace = [hSpacing - hOverlay, hOverlay] else: hSpace = [hSpacing, hSpacing] if vOverlay > 0: vSpace = [vSpacing - vOverlay, vOverlay] else: vSpace = [vSpacing, vSpacing] feat = QgsFeature() feat.initAttributes(len(fields)) count = 0 id = 1 # latitude lines count_max = height / vSpacing count_update = count_max * 0.10 y = bbox.yMaximum() while y >= bbox.yMinimum(): pt1 = QgsPoint(bbox.xMinimum(), y) pt2 = QgsPoint(bbox.xMaximum(), y) line = QgsLineString() line.setPoints([pt1, pt2]) feat.setGeometry(QgsGeometry(line)) feat.setAttributes([bbox.xMinimum(), y, bbox.xMaximum(), y, id, y]) writer.addFeature(feat, QgsFeatureSink.FastInsert) y = y - vSpace[count % 2] id += 1 count += 1 if int(math.fmod(count, count_update)) == 0: feedback.setProgress(int(count / count_max * 50)) feedback.setProgress(50) # longitude lines # counters for progressbar - update every 5% count = 0 count_max = width / hSpacing count_update = count_max * 0.10 x = bbox.xMinimum() while x <= bbox.xMaximum(): pt1 = QgsPoint(x, bbox.yMaximum()) pt2 = QgsPoint(x, bbox.yMinimum()) line = QgsLineString() line.setPoints([pt1, pt2]) feat.setGeometry(QgsGeometry(line)) feat.setAttributes([x, bbox.yMaximum(), x, bbox.yMinimum(), id, x]) writer.addFeature(feat, QgsFeatureSink.FastInsert) x = x + hSpace[count % 2] id += 1 count += 1 if int(math.fmod(count, count_update)) == 0: feedback.setProgress(50 + int(count / count_max * 50)) del writer
def processAlgorithm(self, parameters, context, feedback): source = self.parameterAsSource(parameters, self.INPUT, context) if source is None: raise QgsProcessingException(self.invalidSourceError(parameters, self.INPUT)) field_name = self.parameterAsString(parameters, self.FIELD, context) type = self.parameterAsEnum(parameters, self.TYPE, context) use_field = bool(field_name) field_index = -1 fields = QgsFields() fields.append(QgsField('id', QVariant.Int, '', 20)) if use_field: # keep original field type, name and parameters field_index = source.fields().lookupField(field_name) if field_index >= 0: fields.append(source.fields()[field_index]) if type == 0: # envelope fields.append(QgsField('width', QVariant.Double, '', 20, 6)) fields.append(QgsField('height', QVariant.Double, '', 20, 6)) fields.append(QgsField('area', QVariant.Double, '', 20, 6)) fields.append(QgsField('perimeter', QVariant.Double, '', 20, 6)) elif type == 1: # oriented rect fields.append(QgsField('width', QVariant.Double, '', 20, 6)) fields.append(QgsField('height', QVariant.Double, '', 20, 6)) fields.append(QgsField('angle', QVariant.Double, '', 20, 6)) fields.append(QgsField('area', QVariant.Double, '', 20, 6)) fields.append(QgsField('perimeter', QVariant.Double, '', 20, 6)) elif type == 2: # circle fields.append(QgsField('radius', QVariant.Double, '', 20, 6)) fields.append(QgsField('area', QVariant.Double, '', 20, 6)) elif type == 3: # convex hull fields.append(QgsField('area', QVariant.Double, '', 20, 6)) fields.append(QgsField('perimeter', QVariant.Double, '', 20, 6)) (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, fields, QgsWkbTypes.Polygon, source.sourceCrs()) if sink is None: raise QgsProcessingException(self.invalidSinkError(parameters, self.OUTPUT)) if field_index >= 0: geometry_dict = {} bounds_dict = {} total = 50.0 / source.featureCount() if source.featureCount() else 1 features = source.getFeatures(QgsFeatureRequest().setSubsetOfAttributes([field_index])) for current, f in enumerate(features): if feedback.isCanceled(): break if not f.hasGeometry(): continue if type == 0: # bounding boxes - calculate on the fly for efficiency if not f.attributes()[field_index] in bounds_dict: bounds_dict[f.attributes()[field_index]] = f.geometry().boundingBox() else: bounds_dict[f.attributes()[field_index]].combineExtentWith(f.geometry().boundingBox()) else: if not f.attributes()[field_index] in geometry_dict: geometry_dict[f.attributes()[field_index]] = [f.geometry()] else: geometry_dict[f.attributes()[field_index]].append(f.geometry()) feedback.setProgress(int(current * total)) if type == 0: # bounding boxes current = 0 total = 50.0 / len(bounds_dict) if bounds_dict else 1 for group, rect in bounds_dict.items(): if feedback.isCanceled(): break # envelope feature = QgsFeature() feature.setGeometry(QgsGeometry.fromRect(rect)) feature.setAttributes([current, group, rect.width(), rect.height(), rect.area(), rect.perimeter()]) sink.addFeature(feature, QgsFeatureSink.FastInsert) geometry_dict[group] = None feedback.setProgress(50 + int(current * total)) current += 1 else: current = 0 total = 50.0 / len(geometry_dict) if geometry_dict else 1 for group, geometries in geometry_dict.items(): if feedback.isCanceled(): break feature = self.createFeature(feedback, current, type, geometries, group) sink.addFeature(feature, QgsFeatureSink.FastInsert) geometry_dict[group] = None feedback.setProgress(50 + int(current * total)) current += 1 else: total = 80.0 / source.featureCount() if source.featureCount() else 1 features = source.getFeatures(QgsFeatureRequest().setSubsetOfAttributes([])) geometry_queue = [] bounds = QgsRectangle() for current, f in enumerate(features): if feedback.isCanceled(): break if not f.hasGeometry(): continue if type == 0: # bounding boxes, calculate on the fly for efficiency bounds.combineExtentWith(f.geometry().boundingBox()) else: geometry_queue.append(f.geometry()) feedback.setProgress(int(current * total)) if not feedback.isCanceled(): if type == 0: feature = QgsFeature() feature.setGeometry(QgsGeometry.fromRect(bounds)) feature.setAttributes([0, bounds.width(), bounds.height(), bounds.area(), bounds.perimeter()]) else: feature = self.createFeature(feedback, 0, type, geometry_queue) sink.addFeature(feature, QgsFeatureSink.FastInsert) return {self.OUTPUT: dest_id}
def import_params(self): setting_file = QFileDialog.getOpenFileName( self, self.tr("Open settings file"), self.lastSavingPath, "*.txt") if setting_file[0] != '': with open(setting_file[0]) as json_file: try: parameters = json.load(json_file) param_crs = QgsCoordinateReferenceSystem() param_crs.createFromProj4(parameters["crs_map"]) if (self.map_crs != param_crs): # do traslation transform = QgsCoordinateTransform( param_crs, self.map_crs, QgsProject.instance()) pointMin = transform.transform(parameters["roi_x_min"], parameters["roi_y_min"]) pointMax = transform.transform(parameters["roi_x_max"], parameters["roi_y_max"]) self.roi_x_max = pointMax.x() self.roi_y_min = pointMin.y() self.roi_x_min = pointMin.x() self.roi_y_max = pointMax.y() else: self.roi_x_max = parameters["roi_x_max"] self.roi_y_min = parameters["roi_y_min"] self.roi_x_min = parameters["roi_x_min"] self.roi_y_max = parameters["roi_y_max"] self.ui.XMaxLineEdit.setText(str(round(self.roi_x_max, 3))) self.ui.YMinLineEdit.setText(str(round(self.roi_y_min, 3))) self.ui.XMinLineEdit.setText(str(round(self.roi_x_min, 3))) self.ui.YMaxLineEdit.setText(str(round(self.roi_y_max, 3))) if "roi_rect_Param" in parameters: self.rect_Params = parameters["roi_rect_Param"] else: rec = QgsRectangle(self.roi_x_min, self.roi_y_min, self.roi_x_max, self.roi_y_max) self.rect_Params = { 'center': [rec.center().x(), rec.center().y()], 'width': rec.width(), 'height': rec.height(), 'rotation': 0 } self.ui.WidthGeoLineEdit.setText( str(round(self.rect_Params["width"], 3))) self.ui.HeightGeoLineEdit.setText( str(round(self.rect_Params["height"], 3))) self.ui.SpacingLineEdit.setText( str(round(parameters["spacing_mm"], 2))) self.scale = parameters['scale'] self.scale_h = parameters['scale_h'] self.scale_w = parameters['scale_w'] self.ui.ScaleLineEdit.setScale(int(parameters["scale"])) self.upload_size_from_scale() self.ui.ZScaleDoubleSpinBox.setValue(parameters["z_scale"]) self.get_z_max_z_min() self.ui.BaseHeightLineEdit.setText( str(round(parameters["z_base"], 3))) self.ui.RevereseZCheckBox.setChecked(parameters["z_inv"]) self.get_height_model() if "divideRow" in parameters: self.ui.RowPartsSpinBox.setValue( int(parameters["divideRow"])) if "divideCols" in parameters: self.ui.ColPartsSpinBox.setValue( int(parameters["divideCols"])) self.paint_extent(self.rect_Params) except: QMessageBox.warning(self, self.tr("Attention"), self.tr("Wrong file"))