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 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 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, 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() 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 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, 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 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 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() == QgsPointXY(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 create_grid_from_bounds(bounds: QgsRectangle) -> List[QgsGeometry]: """Creates a regular grid of size 1x1 within specified bounds""" grid_bboxes = [] # loop through x and y range and create the grid min_x, min_y = [floor(x) for x in (bounds.xMinimum(), bounds.yMinimum())] max_x, max_y = [ ceil(x) for x in ( bounds.xMinimum() + bounds.width(), bounds.yMinimum() + bounds.height(), ) ] for x in range(min_x, max_x): for y in range(min_y, max_y): grid_bboxes.append( QgsGeometry.fromRect(QgsRectangle(x, y, x + 1, y + 1))) return grid_bboxes
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 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)
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 processAlgorithm(self, parameters, context, feedback): feedback.setProgress(1) extent = self.parameterAsExtent(parameters, self.EXTENT, context) min_zoom = self.parameterAsInt(parameters, self.ZOOM_MIN, context) max_zoom = self.parameterAsInt(parameters, self.ZOOM_MAX, context) dpi = self.parameterAsInt(parameters, self.DPI, context) tile_format = self.formats[self.parameterAsEnum(parameters, self.TILE_FORMAT, context)] output_format = self.outputs[self.parameterAsEnum(parameters, self.OUTPUT_FORMAT, context)] if output_format == 'Directory': output_dir = self.parameterAsString(parameters, self.OUTPUT_DIRECTORY, context) if not output_dir: raise QgsProcessingException(self.tr('You need to specify output directory.')) else: # MBTiles output_file = self.parameterAsString(parameters, self.OUTPUT_FILE, context) if not output_file: raise QgsProcessingException(self.tr('You need to specify output filename.')) tile_width = 256 tile_height = 256 wgs_crs = QgsCoordinateReferenceSystem('EPSG:4326') dest_crs = QgsCoordinateReferenceSystem('EPSG:3857') project = context.project() src_to_wgs = QgsCoordinateTransform(project.crs(), wgs_crs, context.transformContext()) wgs_to_dest = QgsCoordinateTransform(wgs_crs, dest_crs, context.transformContext()) settings = QgsMapSettings() settings.setOutputImageFormat(QImage.Format_ARGB32_Premultiplied) settings.setDestinationCrs(dest_crs) settings.setLayers(self.layers) settings.setOutputDpi(dpi) wgs_extent = src_to_wgs.transformBoundingBox(extent) wgs_extent = [wgs_extent.xMinimum(), wgs_extent.yMinimum(), wgs_extent.xMaximum(), wgs_extent.yMaximum()] metatiles_by_zoom = {} metatiles_count = 0 for zoom in range(min_zoom, max_zoom + 1): metatiles = get_metatiles(wgs_extent, zoom, 4) metatiles_by_zoom[zoom] = metatiles metatiles_count += len(metatiles) lab_buffer_px = 100 progress = 0 tile_params = { 'format': tile_format, 'quality': 75, 'width': tile_width, 'height': tile_height } if output_format == 'Directory': writer = DirectoryWriter(output_dir, tile_params) else: writer = MBTilesWriter(output_file, tile_params, wgs_extent, min_zoom, max_zoom) for zoom in range(min_zoom, max_zoom + 1): feedback.pushConsoleInfo('Generating tiles for zoom level: %s' % zoom) for i, metatile in enumerate(metatiles_by_zoom[zoom]): size = QSize(tile_width * metatile.rows(), tile_height * metatile.columns()) extent = QgsRectangle(*metatile.extent()) settings.setExtent(wgs_to_dest.transformBoundingBox(extent)) settings.setOutputSize(size) label_area = QgsRectangle(settings.extent()) lab_buffer = label_area.width() * (lab_buffer_px / size.width()) label_area.set( label_area.xMinimum() + lab_buffer, label_area.yMinimum() + lab_buffer, label_area.xMaximum() - lab_buffer, label_area.yMaximum() - lab_buffer ) settings.setLabelBoundaryGeometry(QgsGeometry.fromRect(label_area)) image = QImage(size, QImage.Format_ARGB32_Premultiplied) image.fill(Qt.transparent) dpm = settings.outputDpi() / 25.4 * 1000 image.setDotsPerMeterX(dpm) image.setDotsPerMeterY(dpm) painter = QPainter(image) job = QgsMapRendererCustomPainterJob(settings, painter) job.renderSynchronously() painter.end() # For analysing metatiles (labels, etc.) # metatile_dir = os.path.join(output_dir, str(zoom)) # os.makedirs(metatile_dir, exist_ok=True) # image.save(os.path.join(metatile_dir, 'metatile_%s.png' % i)) for r, c, tile in metatile.tiles: tile_img = image.copy(tile_width * r, tile_height * c, tile_width, tile_height) writer.writeTile(tile, tile_img) progress += 1 feedback.setProgress(100 * (progress / metatiles_count)) writer.close() results = {} if output_format == 'Directory': results['OUTPUT_DIRECTORY'] = output_dir else: # MBTiles results['OUTPUT_FILE'] = output_file return results
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.""" # 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
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)
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
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
class DEMBlock: def __init__(self, grid_width, grid_height, grid_values, plane_width, plane_height, offsetX, offsetY): self.grid_width = grid_width self.grid_height = grid_height self.grid_values = grid_values self.plane_width = plane_width self.plane_height = plane_height self.offsetX = offsetX self.offsetY = offsetY self.orig_stats = {"max": max(grid_values), "min": min(grid_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.clip_geometry = None def setClipGeometry(self, geometry): self.clip_geometry = geometry def zShift(self, shift): if shift != 0: self.grid_values = [x + shift for x in self.grid_values] def zScale(self, scale): if scale != 1: self.grid_values = [x * scale for x in self.grid_values] def export(self, extFileUrl=None): """extFileUrl: should be specified when the grid values are written to an extenal binary file.""" g = {"width": self.grid_width, "height": self.grid_height} #"csv": ",".join(map(gdal2threejs.formatValue, self.grid_values))} if extFileUrl is None: g["array"] = self.grid_values else: g["url"] = extFileUrl return { "grid": g, "width": self.plane_width, "height": self.plane_height, "translate": [self.offsetX, self.offsetY, 0] } def write(self, filepath): """write grid values to an external binary file""" with open(filepath, "wb") as f: f.write( struct.pack("{}f".format(self.grid_width * self.grid_height), *self.grid_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.grid_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.grid_width, self.grid_height)) polygons = [] for polygon in geom.polygons: bnds = [] for boundary in polygon: bnds.append([[pt.x, pt.y] for pt in 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 = [[[pt.x, pt.y] for pt in bnd] for bnd in polygon] polygons.append(bnds) vf = { "v": [[pt.x, pt.y] for pt in 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.grid_values[gx + self.grid_width * gy] if 0 <= x and x <= self.grid_width - 1 and 0 <= y and y <= self.grid_height - 1: ix, iy = int(x), int(y) sx, sy = x - ix, y - iy z11 = _getValue(ix, iy) z21 = 0 if x == self.grid_width - 1 else _getValue(ix + 1, iy) z12 = 0 if y == self.grid_height - 1 else _getValue(ix, iy + 1) z22 = 0 if x == self.grid_width - 1 or y == self.grid_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.grid_width - 1) * x y = self.rect.yMaximum() - self.rect.height() / (self.grid_height - 1) * y return x, y def pointToGridPoint(self, x, y): x = (x - self.rect.xMinimum()) / self.rect.width() * (self.grid_width - 1) y = (self.rect.yMaximum() - y) / self.rect.height() * (self.grid_height - 1) return x, y
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)
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): feedback.setProgress(1) extent = self.parameterAsExtent(parameters, self.EXTENT, context) min_zoom = self.parameterAsInt(parameters, self.ZOOM_MIN, context) max_zoom = self.parameterAsInt(parameters, self.ZOOM_MAX, context) dpi = self.parameterAsInt(parameters, self.DPI, context) tile_format = self.formats[self.parameterAsEnum(parameters, self.TILE_FORMAT, context)] output_format = self.outputs[self.parameterAsEnum(parameters, self.OUTPUT_FORMAT, context)] if output_format == 'Directory': output_dir = self.parameterAsString(parameters, self.OUTPUT_DIRECTORY, context) if not output_dir: raise QgsProcessingException(self.tr('You need to specify output directory.')) else: # MBTiles output_file = self.parameterAsString(parameters, self.OUTPUT_FILE, context) if not output_file: raise QgsProcessingException(self.tr('You need to specify output filename.')) tile_width = 256 tile_height = 256 wgs_crs = QgsCoordinateReferenceSystem('EPSG:4326') dest_crs = QgsCoordinateReferenceSystem('EPSG:3857') project = context.project() src_to_wgs = QgsCoordinateTransform(project.crs(), wgs_crs, context.transformContext()) wgs_to_dest = QgsCoordinateTransform(wgs_crs, dest_crs, context.transformContext()) settings = QgsMapSettings() settings.setOutputImageFormat(QImage.Format_ARGB32_Premultiplied) settings.setDestinationCrs(dest_crs) settings.setLayers(self.layers) settings.setOutputDpi(dpi) if tile_format == 'PNG': settings.setBackgroundColor(QColor(Qt.transparent)) wgs_extent = src_to_wgs.transformBoundingBox(extent) wgs_extent = [wgs_extent.xMinimum(), wgs_extent.yMinimum(), wgs_extent.xMaximum(), wgs_extent.yMaximum()] metatiles_by_zoom = {} metatiles_count = 0 for zoom in range(min_zoom, max_zoom + 1): metatiles = get_metatiles(wgs_extent, zoom, 4) metatiles_by_zoom[zoom] = metatiles metatiles_count += len(metatiles) lab_buffer_px = 100 progress = 0 tile_params = { 'format': tile_format, 'quality': 75, 'width': tile_width, 'height': tile_height } if output_format == 'Directory': writer = DirectoryWriter(output_dir, tile_params) else: writer = MBTilesWriter(output_file, tile_params, wgs_extent, min_zoom, max_zoom) for zoom in range(min_zoom, max_zoom + 1): feedback.pushConsoleInfo('Generating tiles for zoom level: %s' % zoom) for i, metatile in enumerate(metatiles_by_zoom[zoom]): size = QSize(tile_width * metatile.rows(), tile_height * metatile.columns()) extent = QgsRectangle(*metatile.extent()) settings.setExtent(wgs_to_dest.transformBoundingBox(extent)) settings.setOutputSize(size) label_area = QgsRectangle(settings.extent()) lab_buffer = label_area.width() * (lab_buffer_px / size.width()) label_area.set( label_area.xMinimum() + lab_buffer, label_area.yMinimum() + lab_buffer, label_area.xMaximum() - lab_buffer, label_area.yMaximum() - lab_buffer ) settings.setLabelBoundaryGeometry(QgsGeometry.fromRect(label_area)) image = QImage(size, QImage.Format_ARGB32_Premultiplied) image.fill(Qt.transparent) dpm = settings.outputDpi() / 25.4 * 1000 image.setDotsPerMeterX(dpm) image.setDotsPerMeterY(dpm) painter = QPainter(image) job = QgsMapRendererCustomPainterJob(settings, painter) job.renderSynchronously() painter.end() # For analysing metatiles (labels, etc.) # metatile_dir = os.path.join(output_dir, str(zoom)) # os.makedirs(metatile_dir, exist_ok=True) # image.save(os.path.join(metatile_dir, 'metatile_%s.png' % i)) for r, c, tile in metatile.tiles: tile_img = image.copy(tile_width * r, tile_height * c, tile_width, tile_height) writer.writeTile(tile, tile_img) progress += 1 feedback.setProgress(100 * (progress / metatiles_count)) writer.close() results = {} if output_format == 'Directory': results['OUTPUT_DIRECTORY'] = output_dir else: # MBTiles results['OUTPUT_FILE'] = output_file return results
def processAlgorithm(self, context, 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, context) 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 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 UpdateLayers(packet, parent=None, mosaic=False, group=None): ''' Update Layers Values ''' gv.setGroupName(group) groupName = group # frameCenterLat = packet.FrameCenterLatitude # frameCenterLon = packet.FrameCenterLongitude gv.setFrameCenterElevation(packet.FrameCenterElevation) gv.setSensorLatitude(packet.SensorLatitude) gv.setSensorLongitude(packet.SensorLongitude) sensorTrueAltitude = packet.SensorTrueAltitude gv.setSensorTrueAltitude(sensorTrueAltitude) sensorRelativeElevationAngle = packet.SensorRelativeElevationAngle slantRange = packet.SlantRange OffsetLat1 = packet.OffsetCornerLatitudePoint1 LatitudePoint1Full = packet.CornerLatitudePoint1Full UpdatePlatformData(packet, hasElevationModel()) UpdateTrajectoryData(packet, hasElevationModel()) frameCenterPoint = [ packet.FrameCenterLatitude, packet.FrameCenterLongitude, packet.FrameCenterElevation ] # If no framcenter (f.i. horizontal target) don't comptute footprint, # beams and frame center if (frameCenterPoint[0] is None and frameCenterPoint[1] is None): gv.setTransform(None) return True # No framecenter altitude if (frameCenterPoint[2] is None): if (sensorRelativeElevationAngle is not None and slantRange is not None): frameCenterPoint[2] = sensorTrueAltitude - \ sin(sensorRelativeElevationAngle) * slantRange else: frameCenterPoint[2] = 0.0 # qgsu.showUserAndLogMessage("", "FC Alt:"+str(frameCenterPoint[2]), onlyLog=True) if OffsetLat1 is not None and LatitudePoint1Full is None: if hasElevationModel(): frameCenterPoint = GetLine3DIntersectionWithDEM( GetSensor(), frameCenterPoint) CornerEstimationWithOffsets(packet) if mosaic: georeferencingVideo(parent) elif OffsetLat1 is None and LatitudePoint1Full is None: if hasElevationModel(): frameCenterPoint = GetLine3DIntersectionWithDEM( GetSensor(), frameCenterPoint) CornerEstimationWithoutOffsets(packet) if mosaic: georeferencingVideo(parent) else: cornerPointUL = [ packet.CornerLatitudePoint1Full, packet.CornerLongitudePoint1Full ] if None in cornerPointUL: return False cornerPointUR = [ packet.CornerLatitudePoint2Full, packet.CornerLongitudePoint2Full ] if None in cornerPointUR: return False cornerPointLR = [ packet.CornerLatitudePoint3Full, packet.CornerLongitudePoint3Full ] if None in cornerPointLR: return False cornerPointLL = [ packet.CornerLatitudePoint4Full, packet.CornerLongitudePoint4Full ] if None in cornerPointLL: return False UpdateFootPrintData(packet, cornerPointUL, cornerPointUR, cornerPointLR, cornerPointLL, hasElevationModel()) UpdateBeamsData(packet, cornerPointUL, cornerPointUR, cornerPointLR, cornerPointLL, hasElevationModel()) SetGCPsToGeoTransform(cornerPointUL, cornerPointUR, cornerPointLR, cornerPointLL, frameCenterPoint[1], frameCenterPoint[0], hasElevationModel()) if mosaic: georeferencingVideo(parent) UpdateFrameCenterData(frameCenterPoint, hasElevationModel()) UpdateFrameAxisData(packet.ImageSourceSensor, GetSensor(), frameCenterPoint, hasElevationModel()) # detect if we need a recenter or not. If Footprint and Platform fits in # 80% of the map, do not trigger recenter. f_lyr = qgsu.selectLayerByName(Footprint_lyr, groupName) p_lyr = qgsu.selectLayerByName(Platform_lyr, groupName) t_lyr = qgsu.selectLayerByName(FrameCenter_lyr, groupName) iface = gv.getIface() centerMode = gv.getCenterMode() if f_lyr is not None and p_lyr is not None and t_lyr is not None: f_lyr_out_extent = f_lyr.extent() p_lyr_out_extent = p_lyr.extent() t_lyr_out_extent = t_lyr.extent() # Default EPSG is 4326, f_lyr.crs().authid () # Disable transform if we have the same projection wit layers anf # canvas epsg4326 = "EPSG:4326" curAuthId = iface.mapCanvas().mapSettings().destinationCrs().authid() if (curAuthId != epsg4326): xform = QgsCoordinateTransform( QgsCoordinateReferenceSystem(epsg4326), QgsCoordinateReferenceSystem(curAuthId), QgsProject().instance()) transP = xform.transform( QgsPointXY( list(p_lyr.getFeatures())[0].geometry().asPoint().x(), list(p_lyr.getFeatures())[0].geometry().asPoint().y())) transT = xform.transform( QgsPointXY( list(t_lyr.getFeatures())[0].geometry().asPoint().x(), list(t_lyr.getFeatures())[0].geometry().asPoint().y())) rect = list(f_lyr.getFeatures())[0].geometry().boundingBox() rectLL = xform.transform( QgsPointXY(rect.xMinimum(), rect.yMinimum())) rectUR = xform.transform( QgsPointXY(rect.xMaximum(), rect.yMaximum())) f_lyr_out_extent = QgsRectangle(rectLL, rectUR) t_lyr_out_extent = QgsRectangle(transT.x(), transT.y(), transT.x(), transT.y()) p_lyr_out_extent = QgsRectangle(transP.x(), transP.y(), transP.x(), transP.y()) bValue = iface.mapCanvas().extent().xMaximum() - iface.mapCanvas( ).center().x() # create a detection buffer map_detec_buffer = iface.mapCanvas().extent().buffered(bValue * -0.7) # recenter map on platform if not map_detec_buffer.contains(p_lyr_out_extent) and centerMode == 1: # recenter map on platform iface.mapCanvas().setExtent(p_lyr_out_extent) # recenter map on footprint elif not map_detec_buffer.contains( f_lyr_out_extent) and centerMode == 2: # zoom a bit wider than the footprint itself iface.mapCanvas().setExtent( f_lyr_out_extent.buffered(f_lyr_out_extent.width() * 0.5)) # recenter map on target elif not map_detec_buffer.contains( t_lyr_out_extent) and centerMode == 3: iface.mapCanvas().setExtent(t_lyr_out_extent) # Refresh Canvas iface.mapCanvas().refresh() return True
def processAlgorithm(self, 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 = [ 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) 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 = 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 - 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 = 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 + 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 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 # load composition object composition = QgsComposition(qgis_composition_context.map_settings) # 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() 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_item(composition, item_id, QgsComposerPicture) """:type: qgis.core.QgsComposerPicture""" 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') composer_item = composition.getComposerItemById(item_id) try: html_element = composition.getComposerHtmlByItem(composer_item) except: pass """: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) original_crs = impact_report.impact_function.crs destination_crs = qgis_composition_context.map_settings.destinationCrs() coord_transform = QgsCoordinateTransform(original_crs, destination_crs) # 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 = composition_item(composition, item_id, QgsComposerMap) for index, layer in enumerate(layers): # we need to check whether the layer is registered or not registered_layer = ( QgsMapLayerRegistry.instance().mapLayer(layer.id())) if registered_layer: if not registered_layer == layer: layers[index] = registered_layer else: QgsMapLayerRegistry.instance().addMapLayer(layer) """:type: qgis.core.QgsComposerMap""" 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.id() for l in layers if isinstance(l, QgsMapLayer)] composer_map.setLayerSet(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.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 = [ 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 = composition_item(composition, item_id, QgsComposerLegend) """:type: qgis.core.QgsComposerLegend""" 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.modelV2().rootGroup() for layer in layers: # we need to check whether the layer is registered or not registered_layer = ( QgsMapLayerRegistry.instance().mapLayer(layer.id())) if registered_layer: if not registered_layer == layer: layer = registered_layer else: QgsMapLayerRegistry.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, 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( impact_report, each_path, composition, 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( impact_report, each_path, composition, 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( impact_report, component_output_path, composition, 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 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 generate(self, writer, parameters, context, feedback): feedback.setProgress(1) extent = self.parameterAsExtent(parameters, self.EXTENT, context) self.min_zoom = self.parameterAsInt(parameters, self.ZOOM_MIN, context) self.max_zoom = self.parameterAsInt(parameters, self.ZOOM_MAX, context) dpi = self.parameterAsInt(parameters, self.DPI, context) self.tile_format = self.formats[self.parameterAsEnum(parameters, self.TILE_FORMAT, context)] tile_width = 256 tile_height = 256 wgs_crs = QgsCoordinateReferenceSystem('EPSG:4326') dest_crs = QgsCoordinateReferenceSystem('EPSG:3857') project = context.project() src_to_wgs = QgsCoordinateTransform(project.crs(), wgs_crs, context.transformContext()) wgs_to_dest = QgsCoordinateTransform(wgs_crs, dest_crs, context.transformContext()) settings = QgsMapSettings() settings.setOutputImageFormat(QImage.Format_ARGB32_Premultiplied) settings.setDestinationCrs(dest_crs) settings.setLayers(self.layers) settings.setOutputDpi(dpi) if self.tile_format == 'PNG': settings.setBackgroundColor(QColor(Qt.transparent)) self.wgs_extent = src_to_wgs.transformBoundingBox(extent) self.wgs_extent = [self.wgs_extent.xMinimum(), self.wgs_extent.yMinimum(), self.wgs_extent.xMaximum(), self.wgs_extent.yMaximum()] metatiles_by_zoom = {} metatiles_count = 0 for zoom in range(self.min_zoom, self.max_zoom + 1): metatiles = get_metatiles(self.wgs_extent, zoom, 4) metatiles_by_zoom[zoom] = metatiles metatiles_count += len(metatiles) lab_buffer_px = 100 progress = 0 tile_params = { 'format': self.tile_format, 'quality': 75, 'width': tile_width, 'height': tile_height, 'min_zoom': self.min_zoom, 'max_zoom': self.max_zoom, 'extent': self.wgs_extent, } writer.set_parameters(tile_params) for zoom in range(self.min_zoom, self.max_zoom + 1): feedback.pushConsoleInfo('Generating tiles for zoom level: %s' % zoom) for i, metatile in enumerate(metatiles_by_zoom[zoom]): if feedback.isCanceled(): break size = QSize(tile_width * metatile.rows(), tile_height * metatile.columns()) extent = QgsRectangle(*metatile.extent()) settings.setExtent(wgs_to_dest.transformBoundingBox(extent)) settings.setOutputSize(size) if hasattr(settings, 'setLabelBoundaryGeometry'): label_area = QgsRectangle(settings.extent()) lab_buffer = label_area.width() * (lab_buffer_px / size.width()) label_area.set( label_area.xMinimum() + lab_buffer, label_area.yMinimum() + lab_buffer, label_area.xMaximum() - lab_buffer, label_area.yMaximum() - lab_buffer ) settings.setLabelBoundaryGeometry(QgsGeometry.fromRect(label_area)) image = QImage(size, QImage.Format_ARGB32_Premultiplied) image.fill(Qt.transparent) dpm = settings.outputDpi() / 25.4 * 1000 image.setDotsPerMeterX(dpm) image.setDotsPerMeterY(dpm) painter = QPainter(image) job = QgsMapRendererCustomPainterJob(settings, painter) job.renderSynchronously() painter.end() # For analysing metatiles (labels, etc.) # metatile_dir = os.path.join(output_dir, str(zoom)) # os.makedirs(metatile_dir, exist_ok=True) # image.save(os.path.join(metatile_dir, 'metatile_%s.png' % i)) for r, c, tile in metatile.tiles: tile_img = image.copy(tile_width * r, tile_height * c, tile_width, tile_height) writer.write_tile(tile, tile_img) progress += 1 feedback.setProgress(100 * (progress / metatiles_count)) writer.close()
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 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"))