def runThis(self, iface): """This is called when user clicks on "CZML Prism Map" :param iface: An interface instance that will be passed to this class which provides the hook by which you can manipulate the QGIS application at run time. :type iface: QgsInterface """ # store iface self.iface = iface # clear lists self.layerList.clear() self.twTimesAttrs.clear() self.twTimesAttrs.setRowCount(0) # place to store legend settings self.legendSamples = None # collect currently loaded polygon layers to "layerList" comboBox #layers=iface.legendInterface().layers() layers = [ tree_layer.layer() for tree_layer in QgsProject.instance().layerTreeRoot().findLayers() ] ll = [] for layer in layers: if (layer.type() == layer.VectorLayer and layer.geometryType() == QgsWkbTypes.PolygonGeometry): ll.append(layer.name()) self.layerList.addItems(ll) # die if no suitable layers found if (len(ll) == 0): QMessageBox.warning(self, "Error", "No suitable layers found.") return # show dialog self.show() if self.exec_(): # OK clicked, let's create the CZML file # get filename filename = self.leFileName.text() # set html legend filename htmlFn = filename + ".legend.html" # find layer by chosen name name = self.layerList.currentText() for layer in layers: if (layer.name() == name): alayer = layer break # get chosen attribute name aName = self.attrList.currentText() # create projection transformer object crsDest = QgsCoordinateReferenceSystem(4326) # WGS 84 crsSrc = alayer.crs() xform = QgsCoordinateTransform(crsSrc, crsDest, QgsProject.instance()) # get colors c1 = self.cb1.color() c2 = self.cb2.color() R1 = c1.red() G1 = c1.green() B1 = c1.blue() A1 = c1.alpha() dR = c2.red() - R1 dG = c2.green() - G1 dB = c2.blue() - B1 dA = c2.alpha() - A1 # find minimum/maximum time and attribute value self.minT = None self.maxT = None usedAttrs = [] for row in range(self.twTimesAttrs.rowCount()): t = self.twTimesAttrs.item(row, 0).text() usedAttrs.append(self.twTimesAttrs.item(row, 1).text()) if self.minT == None or self.minT > t: self.minT = t if self.maxT == None or self.maxT < t: self.maxT = t self.minV = None self.maxV = None for aName in usedAttrs: if self.minV == None or self.minV > self.amin[aName]: self.minV = self.amin[aName] if self.maxV == None or self.maxV < self.amax[aName]: self.maxV = self.amax[aName] # open output file ofile = codecs.open(filename, 'w', 'utf-8') # leading [ and document packet ofile.write('[\n{"id":"document","version":"1.0"}') # iterate over feaures polyN = 0 for f in alayer.getFeatures(): # calculate animated values (height, color) heights = [] colors = [] for row in range(self.twTimesAttrs.rowCount()): aTime = self.twTimesAttrs.item(row, 0).text() aName = self.twTimesAttrs.item(row, 1).text() # calculate height value = f[aName] if (not value): value = 0 if (self.rbLin.isChecked()): h = value elif (self.rbSqrt.isChecked()): h = math.sqrt(value) else: h = math.log(value) h = h * self.sf heights.append('"' + aTime + '",' + str(h)) # calculate color rh = 1.0 * (value - self.minV) / (self.maxV - self.minV) R = R1 + dR * rh G = G1 + dG * rh B = B1 + dB * rh A = A1 + dA * rh rgba = str(R) + ',' + str(G) + ',' + str(B) + ',' + str(A) colors.append('"' + aTime + '",' + rgba) # polygon/multipolygon? if len(f.geometry().asMultiPolygon()) > 0: mpg = f.geometry().asMultiPolygon() else: mpg = [f.geometry().asPolygon()] for pg in mpg: coords = "" # iterate over rings of the polygon for ring in pg: for p in ring: p1 = xform.transform(p) if (coords != ""): coords = coords + "," coords = coords + '\n\t\t\t\t' + str( p1.x()) + "," + str(p1.y()) + ",0" # add element name if checked if (self.cbAddName.isChecked()): nameString = ',\n\t"name":"' + f[ self.strAttrList.currentText()] + '"' else: nameString = '' packetString = '{\n\t"id":"poly' + str( polyN ) + '"' + nameString + ',"availability":"' + self.minT + '/' + self.maxT + '",\n\t"polygon":{\n\t\t"material":{"solidColor":{"color":{"rgba":[' + ( ','.join(colors) ) + ']}}},\n\t\t"positions":{\n\t\t\t"cartographicDegrees":[' + coords + ']},\n\t\t"extrudedHeight":{"number":[' + ( ','.join(heights)) + ']}}}' ofile.write(",\n" + packetString) polyN = polyN + 1 # trailing ] and close flie ofile.write('\n]') ofile.close() # create legend if checked if (self.cbCreateLegend.isChecked()): # open html legend file hfile = codecs.open(htmlFn, 'w', 'utf-8') hfile.write( '<style>\n.czmlLegendSample { width: 40px; height: 20px; border-radius:3px; border: solid thin black; display: inline-block; }\n' ) hfile.write( 'h3.czmlLegend { margin:0; padding-bottom:10px; }\n') if self.legendSamples != None: # we have custom settings for legend for i in range(len(self.legendSamples)): rv = (self.legendSamples[i] - self.minV) / (self.maxV - self.minV) R = int(R1 + dR * rv) G = int(G1 + dG * rv) B = int(B1 + dB * rv) hfile.write('.czmlLegendSample' + str(i) + ' { background: rgb(' + str(R) + ',' + str(G) + ',' + str(B) + '); }\n') else: for i in range(4): R = int(R1 + dR * i / 3.0) G = int(G1 + dG * i / 3.0) B = int(B1 + dB * i / 3.0) hfile.write('.czmlLegendSample' + str(i) + ' { background: rgb(' + str(R) + ',' + str(G) + ',' + str(B) + '); }\n') hfile.write('</style>\n<h3 class="czmlLegend">' + self.leLegendAttrName.text() + '</h3>') if self.legendSamples != None: # we have custom settings for legend for i in range(len(self.legendSamples)): value = self.legendSamples[i] hfile.write( '<span class="czmlLegendSample czmlLegendSample' + str(i) + '"></span> ' + str(value) + '<br/>') else: for i in range(4): value = int(self.amin[aName] + (self.amax[aName] - self.amin[aName]) * i / 3.0) hfile.write( '<span class="czmlLegendSample czmlLegendSample' + str(i) + '"></span> ' + str(value) + '<br/>') hfile.close() # farewell message msg = "CZML file (" + filename + ")" if (self.cbCreateLegend.isChecked()): msg = msg + " and legend (" + htmlFn + ")" msg = msg + " successfully created." QMessageBox.information(self, "Information", msg)
def processAlgorithm(self, parameters, context, feedback): source = self.parameterAsSource(parameters, self.INPUT, context) if source is None: raise QgsProcessingException(self.invalidSourceError(parameters, self.INPUT)) extent = self.parameterAsExtent(parameters, self.TARGET_AREA, context) target_crs = self.parameterAsExtentCrs(parameters, self.TARGET_AREA, context) if self.TARGET_AREA_CRS in parameters: c = self.parameterAsCrs(parameters, self.TARGET_AREA_CRS, context) if c.isValid(): target_crs = c target_geom = QgsGeometry.fromRect(extent) fields = QgsFields() fields.append(QgsField('auth_id', QVariant.String, '', 20)) (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, fields, QgsWkbTypes.NoGeometry, QgsCoordinateReferenceSystem()) if sink is None: raise QgsProcessingException(self.invalidSinkError(parameters, self.OUTPUT)) # make intersection tests nice and fast engine = QgsGeometry.createGeometryEngine(target_geom.constGet()) engine.prepareGeometry() layer_bounds = QgsGeometry.fromRect(source.sourceExtent()) crses_to_check = QgsCoordinateReferenceSystem.validSrsIds() total = 100.0 / len(crses_to_check) found_results = 0 transform_context = QgsCoordinateTransformContext() for current, srs_id in enumerate(crses_to_check): if feedback.isCanceled(): break candidate_crs = QgsCoordinateReferenceSystem.fromSrsId(srs_id) if not candidate_crs.isValid(): continue transform_candidate = QgsCoordinateTransform(candidate_crs, target_crs, transform_context) transformed_bounds = QgsGeometry(layer_bounds) try: if not transformed_bounds.transform(transform_candidate) == 0: continue except: continue try: if engine.intersects(transformed_bounds.constGet()): feedback.pushInfo(self.tr('Found candidate CRS: {}').format(candidate_crs.authid())) f = QgsFeature(fields) f.setAttributes([candidate_crs.authid()]) sink.addFeature(f, QgsFeatureSink.FastInsert) found_results += 1 except: continue feedback.setProgress(int(current * total)) if found_results == 0: feedback.reportError(self.tr('No matching projections found')) return {self.OUTPUT: dest_id}
def processAlgorithm(self, parameters, context, feedback): source = self.parameterAsSource(parameters, self.INPUT, context) method = self.parameterAsEnum(parameters, self.METHOD, context) wkb_type = source.wkbType() fields = source.fields() if QgsWkbTypes.geometryType(wkb_type) == QgsWkbTypes.PolygonGeometry: areaName = vector.createUniqueFieldName('area', fields) fields.append(QgsField(areaName, QVariant.Double)) perimeterName = vector.createUniqueFieldName('perimeter', fields) fields.append(QgsField(perimeterName, QVariant.Double)) elif QgsWkbTypes.geometryType(wkb_type) == QgsWkbTypes.LineGeometry: lengthName = vector.createUniqueFieldName('length', fields) fields.append(QgsField(lengthName, QVariant.Double)) else: xName = vector.createUniqueFieldName('xcoord', fields) fields.append(QgsField(xName, QVariant.Double)) yName = vector.createUniqueFieldName('ycoord', fields) fields.append(QgsField(yName, QVariant.Double)) if QgsWkbTypes.hasZ(source.wkbType()): self.export_z = True zName = vector.createUniqueFieldName('zcoord', fields) fields.append(QgsField(zName, QVariant.Double)) if QgsWkbTypes.hasM(source.wkbType()): self.export_m = True zName = vector.createUniqueFieldName('mvalue', fields) fields.append(QgsField(zName, QVariant.Double)) (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, fields, wkb_type, source.sourceCrs()) coordTransform = None # Calculate with: # 0 - layer CRS # 1 - project CRS # 2 - ellipsoidal self.distance_area = QgsDistanceArea() if method == 2: self.distance_area.setSourceCrs(source.sourceCrs()) self.distance_area.setEllipsoid(context.project().ellipsoid()) elif method == 1: coordTransform = QgsCoordinateTransform(source.sourceCrs(), context.project().crs()) features = source.getFeatures() total = 100.0 / source.featureCount() if source.featureCount() else 0 for current, f in enumerate(features): if feedback.isCanceled(): break outFeat = f attrs = f.attributes() inGeom = f.geometry() if inGeom: if coordTransform is not None: inGeom.transform(coordTransform) if inGeom.type() == QgsWkbTypes.PointGeometry: attrs.extend(self.point_attributes(inGeom)) elif inGeom.type() == QgsWkbTypes.PolygonGeometry: attrs.extend(self.polygon_attributes(inGeom)) else: attrs.extend(self.line_attributes(inGeom)) outFeat.setAttributes(attrs) sink.addFeature(outFeat, QgsFeatureSink.FastInsert) feedback.setProgress(int(current * total)) return {self.OUTPUT: dest_id}
def processAlgorithm(self, parameters, context, feedback): layer = QgsProcessingUtils.mapLayerFromString( self.getParameterValue(self.INPUT), context) method = self.getParameterValue(self.METHOD) geometryType = layer.geometryType() fields = layer.fields() export_z = False export_m = False if geometryType == QgsWkbTypes.PolygonGeometry: areaName = vector.createUniqueFieldName('area', fields) fields.append(QgsField(areaName, QVariant.Double)) perimeterName = vector.createUniqueFieldName('perimeter', fields) fields.append(QgsField(perimeterName, QVariant.Double)) elif geometryType == QgsWkbTypes.LineGeometry: lengthName = vector.createUniqueFieldName('length', fields) fields.append(QgsField(lengthName, QVariant.Double)) else: xName = vector.createUniqueFieldName('xcoord', fields) fields.append(QgsField(xName, QVariant.Double)) yName = vector.createUniqueFieldName('ycoord', fields) fields.append(QgsField(yName, QVariant.Double)) if QgsWkbTypes.hasZ(layer.wkbType()): export_z = True zName = vector.createUniqueFieldName('zcoord', fields) fields.append(QgsField(zName, QVariant.Double)) if QgsWkbTypes.hasM(layer.wkbType()): export_m = True zName = vector.createUniqueFieldName('mvalue', fields) fields.append(QgsField(zName, QVariant.Double)) writer = self.getOutputFromName(self.OUTPUT).getVectorWriter( fields, layer.wkbType(), layer.crs(), context) ellips = None crs = None coordTransform = None # Calculate with: # 0 - layer CRS # 1 - project CRS # 2 - ellipsoidal if method == 2: ellips = QgsProject.instance().ellipsoid() crs = layer.crs().srsid() elif method == 1: mapCRS = iface.mapCanvas().mapSettings().destinationCrs() layCRS = layer.crs() coordTransform = QgsCoordinateTransform(layCRS, mapCRS) outFeat = QgsFeature() outFeat.initAttributes(len(fields)) outFeat.setFields(fields) features = QgsProcessingUtils.getFeatures(layer, context) total = 100.0 / QgsProcessingUtils.featureCount(layer, context) for current, f in enumerate(features): inGeom = f.geometry() if method == 1: inGeom.transform(coordTransform) (attr1, attr2) = vector.simpleMeasure(inGeom, method, ellips, crs) outFeat.setGeometry(inGeom) attrs = f.attributes() attrs.append(attr1) if attr2 is not None: attrs.append(attr2) # add point z/m if export_z: attrs.append(inGeom.geometry().z()) if export_m: attrs.append(inGeom.geometry().m()) outFeat.setAttributes(attrs) writer.addFeature(outFeat) feedback.setProgress(int(current * total)) del writer
def processPoly(source, sink, feedback): layercrs = source.sourceCrs() if layercrs != epsg4326: transto4326 = QgsCoordinateTransform(layercrs, epsg4326, QgsProject.instance()) transfrom4326 = QgsCoordinateTransform(epsg4326, layercrs, QgsProject.instance()) total = 100.0 / source.featureCount() if source.featureCount() else 0 iterator = source.getFeatures() num_bad = 0 maxseglen = settings.maxSegLength * 1000.0 maxSegments = settings.maxSegments for cnt, feature in enumerate(iterator): if feedback.isCanceled(): break try: if not feature.geometry().isMultipart(): poly = feature.geometry().asPolygon() numpolygons = len(poly) if numpolygons < 1: continue ptset = [] for points in poly: numpoints = len(points) if numpoints < 2: continue # If the input is not 4326 we need to convert it to that and then back to the output CRS ptStart = QgsPointXY(points[0][0], points[0][1]) if layercrs != epsg4326: # Convert to 4326 ptStart = transto4326.transform(ptStart) pts = [ptStart] for x in range(1, numpoints): ptEnd = QgsPointXY(points[x][0], points[x][1]) if layercrs != epsg4326: # Convert to 4326 ptEnd = transto4326.transform(ptEnd) l = geod.InverseLine(ptStart.y(), ptStart.x(), ptEnd.y(), ptEnd.x()) n = int(math.ceil(l.s13 / maxseglen)) if n > maxSegments: n = maxSegments seglen = l.s13 / n for i in range(1, n): s = seglen * i g = l.Position( s, Geodesic.LATITUDE | Geodesic.LONGITUDE | Geodesic.LONG_UNROLL) pts.append(QgsPointXY(g['lon2'], g['lat2'])) pts.append(ptEnd) ptStart = ptEnd if layercrs != epsg4326: # Convert each point to the output CRS for x, pt in enumerate(pts): pts[x] = transfrom4326.transform(pt) ptset.append(pts) if len(ptset) > 0: featureout = QgsFeature() featureout.setGeometry(QgsGeometry.fromPolygonXY(ptset)) featureout.setAttributes(feature.attributes()) sink.addFeature(featureout) else: multipoly = feature.geometry().asMultiPolygon() multiset = [] for poly in multipoly: ptset = [] for points in poly: numpoints = len(points) if numpoints < 2: continue # If the input is not 4326 we need to convert it to that and then back to the output CRS ptStart = QgsPointXY(points[0][0], points[0][1]) if layercrs != epsg4326: # Convert to 4326 ptStart = transto4326.transform(ptStart) pts = [ptStart] for x in range(1, numpoints): ptEnd = QgsPointXY(points[x][0], points[x][1]) if layercrs != epsg4326: # Convert to 4326 ptEnd = transto4326.transform(ptEnd) l = geod.InverseLine(ptStart.y(), ptStart.x(), ptEnd.y(), ptEnd.x()) n = int(math.ceil(l.s13 / maxseglen)) if n > maxSegments: n = maxSegments seglen = l.s13 / n for i in range(1, n): s = seglen * i g = l.Position( s, Geodesic.LATITUDE | Geodesic.LONGITUDE | Geodesic.LONG_UNROLL) pts.append(QgsPointXY(g['lon2'], g['lat2'])) pts.append(ptEnd) ptStart = ptEnd if layercrs != epsg4326: # Convert each point to the output CRS for x, pt in enumerate(pts): pts[x] = transfrom4326.transform(pt) ptset.append(pts) multiset.append(ptset) if len(multiset) > 0: featureout = QgsFeature() featureout.setGeometry( QgsGeometry.fromMultiPolygonXY(multiset)) featureout.setAttributes(feature.attributes()) sink.addFeature(featureout) except: num_bad += 1 #traceback.print_exc() pass feedback.setProgress(int(cnt * total)) return num_bad
def processAlgorithm(self, parameters, context, feedback): expression = self.parameterAsString(parameters, self.EXPRESSION, context) layers = self.parameterAsLayerList(parameters, self.LAYERS, context) layersDict = {} if layers: layersDict = { os.path.basename(lyr.source().split(".")[0]): lyr for lyr in layers } crs = self.parameterAsCrs(parameters, self.CRS, context) if not layers and not crs.isValid(): raise QgsProcessingException( self.tr("No reference layer selected nor CRS provided")) if not crs.isValid() and layers: crs = list(layersDict.values())[0].crs() bbox = self.parameterAsExtent(parameters, self.EXTENT, context) if not layers and bbox.isNull(): raise QgsProcessingException( self.tr("No reference layer selected nor extent box provided")) if not bbox.isNull(): bboxCrs = self.parameterAsExtentCrs(parameters, self.EXTENT, context) if bboxCrs != crs: transform = QgsCoordinateTransform(bboxCrs, crs, context.project()) bbox = transform.transformBoundingBox(bbox) if bbox.isNull() and layers: bbox = QgsProcessingUtils.combineLayerExtents(layers, crs) cellsize = self.parameterAsDouble(parameters, self.CELLSIZE, context) if not layers and cellsize == 0: raise QgsProcessingException( self.tr( "No reference layer selected nor cellsize value provided")) def _cellsize(layer): ext = layer.extent() if layer.crs() != crs: transform = QgsCoordinateTransform(layer.crs(), crs, context.project()) ext = transform.transformBoundingBox(ext) return (ext.xMaximum() - ext.xMinimum()) / layer.width() if cellsize == 0: cellsize = min([_cellsize(lyr) for lyr in layersDict.values()]) for lyr in QgsProcessingUtils.compatibleRasterLayers( context.project()): name = lyr.name() if (name + "@") in expression: layersDict[name] = lyr entries = [] for name, lyr in layersDict.items(): for n in range(lyr.bandCount()): ref = '{:s}@{:d}'.format(name, n + 1) if ref in expression: entry = QgsRasterCalculatorEntry() entry.ref = ref entry.raster = lyr entry.bandNumber = n + 1 entries.append(entry) output = self.parameterAsOutputLayer(parameters, self.OUTPUT, context) width = math.floor((bbox.xMaximum() - bbox.xMinimum()) / cellsize) height = math.floor((bbox.yMaximum() - bbox.yMinimum()) / cellsize) driverName = GdalUtils.getFormatShortNameFromFilename(output) calc = QgsRasterCalculator(expression, output, driverName, bbox, crs, width, height, entries) res = calc.processCalculation(feedback) if res == QgsRasterCalculator.ParserError: raise QgsProcessingException(self.tr("Error parsing formula")) return {self.OUTPUT: output}
def __init__(self, iface, parent=None, extent=None, crs=None): """Constructor for the dialog. :param iface: A Quantum GIS QGisAppInterface instance. :type iface: QGisAppInterface :param parent: Parent widget of this dialog :type parent: QWidget :param extent: Extent of the user's preferred analysis area. :type extent: QgsRectangle :param crs: Coordinate reference system for user defined analysis extent. :type crs: QgsCoordinateReferenceSystem """ QDialog.__init__(self, parent) self.setupUi(self) self.iface = iface self.parent = parent self.canvas = iface.mapCanvas() self.previous_map_tool = None # Prepare the map tool self.tool = RectangleMapTool(self.canvas) self.previous_map_tool = self.canvas.mapTool() if extent is None and crs is None: # Use the current map canvas extents as a starting point self.tool.set_rectangle(self.canvas.extent()) else: # Ensure supplied extent is in current canvas crs transform = QgsCoordinateTransform( crs, self.canvas.mapRenderer().destinationCrs()) transformed_extent = transform.transformBoundingBox(extent) self.tool.set_rectangle(transformed_extent) self._populate_coordinates() # Observe inputs for changes self.x_minimum.valueChanged.connect(self._coordinates_changed) self.y_minimum.valueChanged.connect(self._coordinates_changed) self.x_maximum.valueChanged.connect(self._coordinates_changed) self.y_maximum.valueChanged.connect(self._coordinates_changed) # Draw the rubberband self._coordinates_changed() # Wire up button events self.capture_button.clicked.connect(self.start_capture) # Handle cancel cancel_button = self.button_box.button(QtGui.QDialogButtonBox.Cancel) cancel_button.clicked.connect(self.reject) # Make sure to reshow this dialog when rectangle is captured self.tool.rectangle_created.connect(self.stop_capture) # Setup ok button self.ok_button = self.button_box.button(QtGui.QDialogButtonBox.Ok) self.ok_button.clicked.connect(self.accept) # Set up context help self.help_button = self.button_box.button(QtGui.QDialogButtonBox.Help) # Allow toggling the help button self.help_button.setCheckable(True) self.help_button.toggled.connect(self.help_toggled) self.main_stacked_widget.setCurrentIndex(1) # Reset / Clear button clear_button = self.button_box.button(QtGui.QDialogButtonBox.Reset) clear_button.setText(self.tr('Clear')) clear_button.clicked.connect(self.clear) # Populate the bookmarks list and connect the combobox self._populate_bookmarks_list() self.bookmarks_list.currentIndexChanged.connect( self.bookmarks_index_changed) # Reinstate the last used radio button settings = QSettings() mode = settings.value('inasafe/analysis_extents_mode', 'HazardExposureView') if mode == 'HazardExposureView': self.hazard_exposure_view_extent.setChecked(True) elif mode == 'HazardExposure': self.hazard_exposure_only.setChecked(True) elif mode == 'HazardExposureBookmark': self.hazard_exposure_bookmark.setChecked(True) elif mode == 'HazardExposureBoundingBox': self.hazard_exposure_user_extent.setChecked(True) show_warnings = settings.value('inasafe/show_extent_warnings', True, type=bool) if show_warnings: self.show_warnings.setChecked(True) else: self.show_warnings.setChecked(False) show_confirmations = settings.value( 'inasafe/show_extent_confirmations', True, type=bool) if show_confirmations: self.show_confirmations.setChecked(True) else: self.show_confirmations.setChecked(False)
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 = [ _layer for _layer in layers if isinstance(_layer, 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 layer in layer_set: layer_extent = coord_transform.transform(layer.extent()) if layer.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 layer in layer_set: # combine extent if different layer is provided. layer_extent = coord_transform.transform(layer.extent()) extent.combineExtentWith(layer_extent) if layer.name() == map_overview['id']: map_overview_extent = layer_extent width = extent.width() height = extent.height() longest_width = width if width > height else height half_length = longest_width / 2 margin = half_length / 5 center = extent.center() min_x = min_x or (center.x() - half_length - margin) max_x = max_x or (center.x() + half_length + margin) min_y = min_y or (center.y() - half_length - margin) max_y = max_y or (center.y() + half_length + margin) # noinspection PyCallingNonCallable square_extent = QgsRectangle(min_x, min_y, max_x, max_y) if component.key == 'population-infographic' and ( map_overview_extent): square_extent = map_overview_extent composer_map.zoomToExtent(square_extent) composer_map.invalidateCache() actual_extent = composer_map.extent() # calculate intervals for grid x_interval = actual_extent.width() / split_count composer_map.grid().setIntervalX(x_interval) y_interval = actual_extent.height() / split_count composer_map.grid().setIntervalY(y_interval) # calculate legend element for leg_el in context.map_legends: item_id = leg_el.get('id') title = leg_el.get('title') layers = [ _layer for _layer in leg_el.get('layers') if isinstance(_layer, QgsMapLayer) ] symbol_count = leg_el.get('symbol_count') column_count = leg_el.get('column_count') legend = layout_item(layout, item_id, QgsLayoutItemLegend) """:type: qgis.core.QgsLayoutItemLegend""" if legend: # set column count if column_count: legend.setColumnCount(column_count) elif symbol_count <= 7: legend.setColumnCount(1) else: legend.setColumnCount(symbol_count / 7 + 1) # set legend title if title is not None and not impact_report.legend_layers: legend.setTitle(title) # set legend root_group = legend.model().rootGroup() for _layer in layers: # we need to check whether the layer is registered or not registered_layer = (QgsProject.instance().mapLayer( _layer.id())) if registered_layer: if not registered_layer == _layer: _layer = registered_layer else: QgsProject.instance().addMapLayer(_layer) # used for customizations tree_layer = root_group.addLayer(_layer) if impact_report.legend_layers or ( not impact_report.multi_exposure_impact_function): QgsLegendRenderer.setNodeLegendStyle( tree_layer, QgsLegendStyle.Hidden) legend.adjustBoxSize() legend.updateFilterByMap(False) # process to output # in case output folder not specified if impact_report.output_folder is None: impact_report.output_folder = mkdtemp(dir=temp_dir()) output_format = component.output_format component_output_path = impact_report.component_absolute_output_path( component.key) component_output = None doc_format = QgisComposerComponentsMetadata.OutputFormat.DOC_OUTPUT template_format = QgisComposerComponentsMetadata.OutputFormat.QPT if isinstance(output_format, list): component_output = [] for i in range(len(output_format)): each_format = output_format[i] each_path = component_output_path[i] if each_format in doc_format: result_path = create_qgis_pdf_output(impact_report, each_path, layout, each_format, component) component_output.append(result_path) elif each_format == template_format: result_path = create_qgis_template_output(each_path, layout) component_output.append(result_path) elif isinstance(output_format, dict): component_output = {} for key, each_format in list(output_format.items()): each_path = component_output_path[key] if each_format in doc_format: result_path = create_qgis_pdf_output(impact_report, each_path, layout, each_format, component) component_output[key] = result_path elif each_format == template_format: result_path = create_qgis_template_output(each_path, layout) component_output[key] = result_path elif (output_format in QgisComposerComponentsMetadata.OutputFormat.SUPPORTED_OUTPUT): component_output = None if output_format in doc_format: result_path = create_qgis_pdf_output(impact_report, component_output_path, layout, output_format, component) component_output = result_path elif output_format == template_format: result_path = create_qgis_template_output(component_output_path, layout) component_output = result_path component.output = component_output return component.output
def __init__(self, iface, parent=None, extent=None, crs=None): """Constructor for the dialog. :param iface: A Quantum GIS QgisAppInterface instance. :type iface: QgisAppInterface :param parent: Parent widget of this dialog. :type parent: QWidget :param extent: Extent of the user's preferred analysis area. :type extent: QgsRectangle :param crs: CRS for user defined analysis extent. :type crs: QgsCoordinateReferenceSystem """ QDialog.__init__(self, parent) self.setupUi(self) icon = resources_path('img', 'icons', 'set-extents-tool.svg') self.setWindowIcon(QIcon(icon)) self.iface = iface self.parent = parent self.canvas = iface.mapCanvas() self.previous_map_tool = None # Prepare the map tool self.tool = RectangleMapTool(self.canvas) self.previous_map_tool = self.canvas.mapTool() if extent is None: # Use the current map canvas extents as a starting point self.tool.set_rectangle(self.canvas.extent()) else: if isinstance(extent, QgsGeometry): # In InaSAFE V4, the extent is a QgsGeometry. # This like a hack to transform a geometry to a rectangle. extent = wkt_to_rectangle(extent.asWkt()) # Ensure supplied extent is in current canvas crs transform = QgsCoordinateTransform( crs, self.canvas.mapSettings().destinationCrs(), QgsProject.instance()) transformed_extent = transform.transformBoundingBox(extent) self.tool.set_rectangle(transformed_extent) self._populate_coordinates() # Observe inputs for changes self.x_minimum.valueChanged.connect(self._coordinates_changed) self.y_minimum.valueChanged.connect(self._coordinates_changed) self.x_maximum.valueChanged.connect(self._coordinates_changed) self.y_maximum.valueChanged.connect(self._coordinates_changed) # Draw the rubberband self._coordinates_changed() # Wire up button events self.capture_button.clicked.connect(self.start_capture) # Handle cancel cancel_button = self.button_box.button( QtWidgets.QDialogButtonBox.Cancel) cancel_button.clicked.connect(self.reject) # Make sure to reshow this dialog when rectangle is captured self.tool.rectangle_created.connect(self.stop_capture) # Setup ok button self.ok_button = self.button_box.button(QtWidgets.QDialogButtonBox.Ok) self.ok_button.clicked.connect(self.accept) # Set up context help self.help_button = self.button_box.button( QtWidgets.QDialogButtonBox.Help) # Allow toggling the help button self.help_button.setCheckable(True) self.help_button.toggled.connect(self.help_toggled) self.main_stacked_widget.setCurrentIndex(1) # Reset / Clear button clear_button = self.button_box.button(QtWidgets.QDialogButtonBox.Reset) clear_button.setText(self.tr('Clear')) clear_button.clicked.connect(self.clear) # Populate the bookmarks list and connect the combobox self._populate_bookmarks_list() self.bookmarks_list.currentIndexChanged.connect( self.bookmarks_index_changed) # Reinstate the last used radio button mode = setting('analysis_extents_mode', HAZARD_EXPOSURE_VIEW) if mode == HAZARD_EXPOSURE_VIEW: self.hazard_exposure_view_extent.setChecked(True) elif mode == EXPOSURE: self.exposure_only.setChecked(True) elif mode == HAZARD_EXPOSURE: self.hazard_exposure_only.setChecked(True) elif mode == HAZARD_EXPOSURE_BOOKMARK: self.hazard_exposure_bookmark.setChecked(True) elif mode == HAZARD_EXPOSURE_BOUNDINGBOX: self.hazard_exposure_user_extent.setChecked(True) self.show_warnings.setChecked( setting('show_extent_warnings', True, bool)) self.show_confirmations.setChecked( setting('show_extent_confirmations', True, bool))
def matrix_dem_builder(self, dem_dataset, height, width, scale, spacing_mm, roi_x_max, roi_x_min, roi_y_min, h_base, z_scale, projected): # Calculate DEM parameters dem_col = dem_dataset.RasterXSize dem_row = dem_dataset.RasterYSize geotransform = dem_dataset.GetGeoTransform() dem_x_min = geotransform[0] dem_y_max = geotransform[3] dem_y_min = dem_y_max + dem_row * geotransform[5] dem_x_max = dem_x_min + dem_col * geotransform[1] rectParam = self.parameters["roi_rect_Param"] rotation = rectParam["rotation"] if not projected: spacing_deg = spacing_mm * rectParam["width"] / width row_stl = int(math.ceil(height / spacing_mm) + 1) col_stl = int(math.ceil(width / spacing_mm) + 1) matrix_dem = [list(range(col_stl)) for i in range(row_stl)] source = self.parameters["crs_map"] target = self.parameters["crs_layer"] if source != target: transform = QgsCoordinateTransform(source, target, QgsProject.instance()) # RECORRIDO # 0 ---------------------------> 1 # ---------------------------> # ^ ---------------------------> # | ---------------------------> # | ---------------------------> # X ---------------------------> 2 var_y = height for i in range(row_stl): self.updateProgress.emit() var_x = 0 for j in range(col_stl): # Model coordinate x(mm), y(mm) x_model = round(var_x, 2) y_model = round(var_y, 2) # Model maps geo_coordinates if projected: x0, y0 = getPolarPoint(roi_x_min, roi_y_min, rotation, x_model * scale / 1000) x, y = getPolarPoint(x0, y0, rotation + math.pi * 0.5, y_model * scale / 1000) else: x0, y0 = getPolarPoint(roi_x_min, roi_y_min, rotation, x_model * spacing_deg / spacing_mm) x, y = getPolarPoint(x0, y0, rotation + math.pi * 0.5, y_model * spacing_deg / spacing_mm) # Model layer geo_coordinates to query z value point = QgsPoint(x, y) if source != target: point = transform.transform(point) x = point.x() y = point.y() # From x(m) get Column in DEM file col_dem = (x - dem_x_min) * dem_col / (dem_x_max - dem_x_min) col_dem = int(math.floor(col_dem)) if col_dem == dem_col: col_dem -= 1 # From y(m) get Row in DEM file row_dem = (dem_y_max - y) * dem_row / (dem_y_max - dem_y_min) row_dem = int(math.floor(row_dem)) if row_dem == dem_row: row_dem -= 1 # Model coordinate z(mm) if col_dem < 0 or row_dem < 0: z_model = self.baseModel elif self.get_dem_z(dem_dataset, col_dem, row_dem, 1, 1)[0] <= h_base: z_model = self.baseModel elif math.isnan(self.get_dem_z(dem_dataset, col_dem, row_dem, 1, 1)[0]): z_model = self.baseModel else: z_model = round((self.get_dem_z(dem_dataset, col_dem, row_dem, 1, 1)[0] - h_base) / scale * 1000 * z_scale, 2) + self.baseModel matrix_dem[i][j] = self.pto(x=x_model, y=y_model, z=z_model) var_x += spacing_mm if var_x > width: var_x = width var_y = spacing_mm * (row_stl - (i + 2)) if self.quit: return 0 return matrix_dem
def build_layer_table(layer_list=None, only_raster_boundingbox=True): """Build a table of layer properties. Can be used in conjunction with selecting layers to exclude from mapcomboboxes Layer_list: default None if None then it will build the table from all layers in the QGIS project otherwise it will use the list. only_raster_boundingbox: default False create a bounding box from the raster data ie removing nodata from polygon. This will slow it down if large numbers of rasters are present. """ dest_crs = QgsProject.instance().crs() gdf_layers = gpd.GeoDataFrame(columns=[ 'layer', 'layer_name', 'layer_id', 'layer_type', 'source', 'format', 'epsg', 'crs_name', 'is_projected', 'extent', 'provider', 'geometry' ], geometry='geometry', crs=dest_crs.authid()) # pd.DataFrame() if layer_list is None or len(layer_list) == 0: layermap = QgsProject.instance().mapLayers().values() else: layermap = layer_list new_rows = [] for layer in layermap: if layer.type() not in [ QgsMapLayer.VectorLayer, QgsMapLayer.RasterLayer ]: continue if layer.providerType() not in ['ogr', 'gdal', 'delimitedtext']: continue if layer.type() == QgsMapLayer.VectorLayer: format = layer.dataProvider().storageType() else: format = None if layer.crs().isValid() and layer.crs().authid() == '': # Try and convert older style coordinates systems # were correctly definied in QGIS 2 as GDA94 / MGA zone 54 # but get interpreted in QGIS 3 as Unknown CRS: BOUNDCRS[SOURCECRS[PROJCRS["GDA94 / MGA zone 54",..... layer_crs = QgsCoordinateReferenceSystem() if not layer_crs.createFromProj(layer.crs().toWkt()): #print('Could not match a coordinate system for {}'.format(layer.id())) layer_crs = layer.crs() # could apply to the layer, but what if it's wrong.... #layer.setCrs(layer_crs) else: layer_crs = layer.crs() # project the bounding box extents to be the same as the qgis project. if layer_crs.authid() != dest_crs.authid(): transform = QgsCoordinateTransform(layer_crs, dest_crs, QgsProject.instance()) prj_ext = transform.transformBoundingBox(layer.extent()) else: prj_ext = layer.extent() row_dict = { 'layer': layer, 'layer_name': layer.name(), 'layer_id': layer.id(), 'layer_type': layerTypes[layer.type()], 'format': format, 'source': get_layer_source(layer), 'epsg': layer_crs.authid(), 'crs_name': layer_crs.description(), 'is_projected': not layer_crs.isGeographic(), 'provider': layer.providerType(), 'geometry': wkt.loads(prj_ext.asWktPolygon()) } # 'extent': prj_ext.asWktPolygon(), if layer.type() == QgsMapLayer.RasterLayer: pixel_size = get_pixel_size(layer) if not only_raster_boundingbox: with rasterio.open(get_layer_source(layer)) as src: msk = src.dataset_mask() # 0 = nodata 255=valid rast_shapes = rasterio.features.shapes( np.ma.masked_equal(np.where(msk > 0, 1, 0), 0), transform=src.transform) try: results = ({ 'properties': { 'raster_val': v }, 'geometry': s } for i, (s, v) in enumerate(rast_shapes)) geoms = list(results) gpd_rPoly = gpd.GeoDataFrame.from_features( geoms, crs=layer.crs().authid()) dest_crs.authid().replace('epgs:', '') gpd_rPoly.to_crs(dest_crs.authid().replace('epgs:', ''), inplace=True) row_dict.update({'geometry': gpd_rPoly.unary_union}) del gpd_rPoly, results, msk, rast_shapes except: pass row_dict.update({ 'bandcount': layer.bandCount(), 'datatype': dataTypes[layer.dataProvider().dataType(1)], 'pixel_size': pixel_size[0], 'pixel_text': '{} {}'.format(*pixel_size), }) new_rows.append(row_dict) # gdf_layers = gpd.GeoDataFrame(new_rows, geometry='extent') if len(new_rows) == 0: return gdf_layers # for pandas 0.23.4 add sort=False to prevent row and column orders to change. try: gdf_layers = gdf_layers.append(new_rows, ignore_index=True, sort=False) except: gdf_layers = gdf_layers.append(new_rows, ignore_index=True) #df_layers.set_geometry('geometry') return gdf_layers
def matrix_dem_builder_interpolation(self, dem_dataset, height, width, scale, scale_h, scale_w, spacing_mm, roi_x_max, roi_x_min, roi_y_min, z_base, z_scale, projected): # Calculate DEM parameters columns = dem_dataset.RasterXSize rows = dem_dataset.RasterYSize geotransform = dem_dataset.GetGeoTransform() dem_x_min = geotransform[0] # Limit pixel (not center) dem_y_max = geotransform[3] # Limit pixel (not center) # dem_y_min = dem_y_max + rows * geotransform[5] # dem_x_max = dem_x_min + columns * geotransform[1] spacing_deg = 0 rectParam = self.parameters["roi_rect_Param"] rotation = rectParam["rotation"] if not projected: spacing_deg = spacing_mm * rectParam["width"] / width row_stl = int(math.ceil(height / spacing_mm) + 1) col_stl = int(math.ceil(width / spacing_mm) + 1) matrix_dem = [list(range(col_stl)) for i in range(row_stl)] # RECORRIDO # 0 ---------------------------> 1 # ---------------------------> # ^ ---------------------------> # | ---------------------------> # | ---------------------------> # X ---------------------------> 2 var_y = height for i in range(row_stl): self.updateProgress.emit() var_x = 0 for j in range(col_stl): # Model coordinate x(mm), y(mm) x_model = round(var_x, 2) y_model = round(var_y, 2) # Model maps geo_coordinates if projected: x0, y0 = getPolarPoint(roi_x_min, roi_y_min, rotation, x_model * scale / 1000) x, y = getPolarPoint(x0, y0, rotation + math.pi * 0.5, y_model * scale / 1000) else: x0, y0 = getPolarPoint(roi_x_min, roi_y_min, rotation, x_model * spacing_deg / spacing_mm) x, y = getPolarPoint(x0, y0, rotation + math.pi * 0.5, y_model * spacing_deg / spacing_mm) # print('punto cuajado (row - col - x - y)', i, j, x_model, y_model, round(x, 3), round(y, 3), sep=" - ") # Model layer geo_coordinates to query z value # point = QgsPoint(x, y) source = self.parameters["crs_map"] target = self.parameters["crs_layer"] if source != target: transform = QgsCoordinateTransform(source, target, QgsProject.instance()) point = transform.transform(x, y) x = point.x() y = point.y() # From x(m) get Column in DEM file col_dem = (x - dem_x_min) / geotransform[1] if col_dem >= columns: col_dem -= 1 # From y(m) get Row in DEM file row_dem = (y - dem_y_max) / geotransform[5] if row_dem >= rows: row_dem -= 1 # region nearest neighbours interpolation # row_dem = int(math.floor(row_dem)) # col_dem = int(math.floor(col_dem)) # # # Model coordinate z(mm) # if col_dem < 0 or row_dem < 0: # z_model = self.baseModel # elif self.get_dem_z(dem_dataset, col_dem, row_dem, 1, 1)[0] <= h_base: # z_model = self.baseModel # elif math.isnan(self.get_dem_z(dem_dataset, col_dem, row_dem, 1, 1)[0]): # z_model = self.baseModel # else: # z_model = round((self.get_dem_z(dem_dataset, col_dem, row_dem, 1, 1)[0] - h_base) / # scale * 1000 * z_scale, 2) + self.baseModel # # matrix_dem[i][j] = self.pto(x=x_model, y=y_model, z=z_model) # endregion # region Lineal interpolation if 0 < col_dem < columns - 1 and 0 < row_dem < rows - 1: min_col = int(math.floor(col_dem)) max_col = int(math.ceil(col_dem)) min_row = int(math.floor(row_dem)) max_row = int(math.ceil(row_dem)) # - From geographic coordinates calculate pixel coordinates # - round up and down to see the 4 pixels neighbours integer xP1 = dem_x_min + min_col * geotransform[1] yP1 = dem_y_max + min_row * geotransform[5] zP1 = self.get_z(min_col, min_row, dem_dataset, z_base, scale, z_scale) xP2 = dem_x_min + max_col * geotransform[1] yP2 = dem_y_max + min_row * geotransform[5] zP2 = self.get_z(max_col, min_row, dem_dataset, z_base, scale, z_scale) xP3 = dem_x_min + min_col * geotransform[1] yP3 = dem_y_max + max_row * geotransform[5] zP3 = self.get_z(min_col, max_row, dem_dataset, z_base, scale, z_scale) xP4 = dem_x_min + max_col * geotransform[1] yP4 = dem_y_max + max_row * geotransform[5] zP4 = self.get_z(max_col, max_row, dem_dataset, z_base, scale, z_scale) p = self.pto(x=x, y=y, z=0) p1 = self.pto(x=xP1, y=yP1, z=zP1) p2 = self.pto(x=xP2, y=yP2, z=zP2) p3 = self.pto(x=xP3, y=yP3, z=zP3) p4 = self.pto(x=xP4, y=yP4, z=zP4) z_model = self.interp_line(p, p1, p2, p3, p4) matrix_dem[i][j] = self.pto(x=x_model, y=y_model, z=z_model) else: # Solution for boundaries when col = 0 or col = Nº cols # Manage Boundary limits: if (col_dem == 0 or col_dem >= columns - 1) and (row_dem == 0 or row_dem >= rows - 1): # Corners: col_dem = int(col_dem) row_dem = int(row_dem) z_model = self.get_z(col_dem, row_dem, dem_dataset, z_base, scale, z_scale) matrix_dem[i][j] = self.pto(x=x_model, y=y_model, z=z_model) elif (col_dem == 0 or col_dem >= columns - 1) and 0 < row_dem < rows - 1: # First and last column min_row = int(math.floor(row_dem)) max_row = int(math.ceil(row_dem)) col_dem = int(col_dem) if min_row == max_row: z_model = self.get_z(col_dem, max_row, dem_dataset, z_base, scale, z_scale) matrix_dem[i][j] = self.pto(x=x_model, y=y_model, z=z_model) else: yP1 = dem_y_max + min_row * geotransform[5] zP1 = self.get_z(col_dem, min_row, dem_dataset, z_base, scale, z_scale) yP2 = dem_y_max + max_row * geotransform[5] zP2 = self.get_z(col_dem, max_row, dem_dataset, z_base, scale, z_scale) z_model = zP2 + math.fabs(yP2 - y) * (zP1 - zP2) / math.fabs(yP2 - yP1) matrix_dem[i][j] = self.pto(x=x_model, y=y_model, z=z_model) elif 0 < col_dem < columns - 1 and (row_dem == 0 or row_dem >= rows - 1): # First and last row min_col = int(math.floor(col_dem)) max_col = int(math.ceil(col_dem)) row_dem = int(row_dem) if min_col == max_col: z_model = self.get_z(min_col, row_dem, dem_dataset, z_base, scale, z_scale) matrix_dem[i][j] = self.pto(x=x_model, y=y_model, z=z_model) else: xP1 = dem_x_min + min_col * geotransform[1] zP1 = self.get_z(min_col, row_dem, dem_dataset, z_base, scale, z_scale) xP2 = dem_x_min + max_col * geotransform[1] zP2 = self.get_z(max_col, row_dem, dem_dataset, z_base, scale, z_scale) z_model = zP1 + math.fabs(xP1 - x) * (zP2 - zP1) / math.fabs(xP2 - xP1) matrix_dem[i][j] = self.pto(x=x_model, y=y_model, z=z_model) else: # print('punto cuajado', x_model, y_model, sep=" ") matrix_dem[i][j] = self.pto(x=x_model, y=y_model, z=0) # endregion var_x += spacing_mm if var_x > width: var_x = width var_y = spacing_mm * (row_stl - (i + 2)) if self.quit: return 0 return matrix_dem
def canvasPointXY(self, lat, lon): canvasCrs = self.canvas.mapSettings().destinationCrs() transform = QgsCoordinateTransform(self.settings.epsg4326, canvasCrs) x, y = transform.transform(float(lon), float(lat)) pt = QgsPointXY(x, y) return pt
def exportRaster(layer, count, layersFolder, feedback, iface, matchCRS): feedback.showFeedback("Exporting %s to PNG..." % layer.name()) name_ts = (safeName(layer.name()) + unicode(count) + unicode(int(time.time()))) # We need to create a new file to export style piped_file = os.path.join(tempfile.gettempdir(), name_ts + '_piped.tif') piped_extent = layer.extent() piped_width = layer.height() piped_height = layer.width() piped_crs = layer.crs() piped_renderer = layer.renderer() piped_provider = layer.dataProvider() pipe = QgsRasterPipe() pipe.set(piped_provider.clone()) pipe.set(piped_renderer.clone()) file_writer = QgsRasterFileWriter(piped_file) file_writer.writeRaster(pipe, piped_height, -1, piped_extent, piped_crs) # Export layer as PNG out_raster = os.path.join( layersFolder, safeName(layer.name()) + "_" + unicode(count) + ".png") projectCRS = iface.mapCanvas().mapSettings().destinationCrs() if not (matchCRS and layer.crs() == projectCRS): # Extent of the layer in EPSG:3857 crsSrc = layer.crs() crsDest = QgsCoordinateReferenceSystem(3857) try: xform = QgsCoordinateTransform(crsSrc, crsDest, QgsProject.instance()) except: xform = QgsCoordinateTransform(crsSrc, crsDest) extentRep = xform.transformBoundingBox(layer.extent()) extentRepNew = ','.join([ unicode(extentRep.xMinimum()), unicode(extentRep.xMaximum()), unicode(extentRep.yMinimum()), unicode(extentRep.yMaximum()) ]) # Reproject in 3857 piped_3857 = os.path.join(tempfile.gettempdir(), name_ts + '_piped_3857.tif') qgis_version = Qgis.QGIS_VERSION old_stdout = sys.stdout sys.stdout = mystdout = StringIO() try: processing.algorithmHelp("gdal:warpreproject") except: pass sys.stdout = old_stdout params = { "INPUT": piped_file, "SOURCE_CRS": layer.crs().authid(), "TARGET_CRS": "EPSG:3857", "NODATA": 0, "TARGET_RESOLUTION": 0, "RESAMPLING": 2, "TARGET_EXTENT": extentRepNew, "EXT_CRS": "EPSG:3857", "TARGET_EXTENT_CRS": "EPSG:3857", "DATA_TYPE": 0, "COMPRESS": 4, "JPEGCOMPRESSION": 75, "ZLEVEL": 6, "PREDICTOR": 1, "TILED": False, "BIGTIFF": 0, "TFW": False, "MULTITHREADING": False, "COPY_SUBDATASETS": False, "EXTRA": "", "OUTPUT": piped_3857 } warpArgs = {} lines = mystdout.getvalue() for count, line in enumerate(lines.split("\n")): if count != 0 and ":" in line: try: k = line.split(":")[0] warpArgs[k] = params[k] except: pass try: processing.run("gdal:warpreproject", warpArgs) except: shutil.copyfile(piped_file, piped_3857) try: processing.run( "gdal:translate", { "INPUT": piped_3857, "OUTSIZE": 100, "OUTSIZE_PERC": True, "NODATA": 0, "EXPAND": 0, "TARGET_CRS": "", "PROJWIN": extentRepNew, "SDS": False, "DATA_TYPE": 0, "COMPRESS": 4, "JPEGCOMPRESSION": 75, "ZLEVEL": 6, "PREDICTOR": 1, "TILED": False, "BIGTIFF": 0, "TFW": False, "COPY_SUBDATASETS": False, "OPTIONS": "", "OUTPUT": out_raster }) except: shutil.copyfile(piped_3857, out_raster) else: srcExtent = ','.join([ unicode(piped_extent.xMinimum()), unicode(piped_extent.xMaximum()), unicode(piped_extent.yMinimum()), unicode(piped_extent.yMaximum()) ]) processing.run( "gdal:translate", { "INPUT": piped_file, "OUTSIZE": 100, "OUTSIZE_PERC": True, "NODATA": 0, "EXPAND": 0, "TARGET_CRS": "", "PROJWIN": srcExtent, "SDS": False, "DATA_TYPE": 0, "COMPRESS": 4, "JPEGCOMPRESSION": 75, "ZLEVEL": 6, "PREDICTOR": 1, "TILED": False, "BIGTIFF": 0, "TFW": False, "COPY_SUBDATASETS": False, "OPTIONS": "", "OUTPUT": out_raster })
def processAlgorithm(self, parameters, context, feedback): source = self.parameterAsSource(parameters, self.INPUT, context) if source is None: raise QgsProcessingException(self.invalidSourceError(parameters, self.INPUT)) method = self.parameterAsEnum(parameters, self.METHOD, context) wkb_type = source.wkbType() fields = source.fields() new_fields = QgsFields() if QgsWkbTypes.geometryType(wkb_type) == QgsWkbTypes.PolygonGeometry: new_fields.append(QgsField('area', QVariant.Double)) new_fields.append(QgsField('perimeter', QVariant.Double)) elif QgsWkbTypes.geometryType(wkb_type) == QgsWkbTypes.LineGeometry: new_fields.append(QgsField('length', QVariant.Double)) if not QgsWkbTypes.isMultiType(source.wkbType()): new_fields.append(QgsField('straightdis', QVariant.Double)) new_fields.append(QgsField('sinuosity', QVariant.Double)) else: new_fields.append(QgsField('xcoord', QVariant.Double)) new_fields.append(QgsField('ycoord', QVariant.Double)) if QgsWkbTypes.hasZ(source.wkbType()): self.export_z = True new_fields.append(QgsField('zcoord', QVariant.Double)) if QgsWkbTypes.hasM(source.wkbType()): self.export_m = True new_fields.append(QgsField('mvalue', QVariant.Double)) fields = QgsProcessingUtils.combineFields(fields, new_fields) (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, fields, wkb_type, source.sourceCrs()) if sink is None: raise QgsProcessingException(self.invalidSinkError(parameters, self.OUTPUT)) coordTransform = None # Calculate with: # 0 - layer CRS # 1 - project CRS # 2 - ellipsoidal self.distance_area = QgsDistanceArea() if method == 2: self.distance_area.setSourceCrs(source.sourceCrs(), context.transformContext()) self.distance_area.setEllipsoid(context.project().ellipsoid()) elif method == 1: coordTransform = QgsCoordinateTransform(source.sourceCrs(), context.project().crs(), context.project()) features = source.getFeatures() total = 100.0 / source.featureCount() if source.featureCount() else 0 for current, f in enumerate(features): if feedback.isCanceled(): break outFeat = f attrs = f.attributes() inGeom = f.geometry() if inGeom: if coordTransform is not None: inGeom.transform(coordTransform) if inGeom.type() == QgsWkbTypes.PointGeometry: attrs.extend(self.point_attributes(inGeom)) elif inGeom.type() == QgsWkbTypes.PolygonGeometry: attrs.extend(self.polygon_attributes(inGeom)) else: attrs.extend(self.line_attributes(inGeom)) # ensure consistent count of attributes - otherwise null # geometry features will have incorrect attribute length # and provider may reject them if len(attrs) < len(fields): attrs += [NULL] * (len(fields) - len(attrs)) outFeat.setAttributes(attrs) sink.addFeature(outFeat, QgsFeatureSink.FastInsert) feedback.setProgress(int(current * total)) return {self.OUTPUT: dest_id}
def processAlgorithm(self, parameters, context, feedback): """ Here is where the processing itself takes place. """ ueberhoehung = self.parameterAsInt(parameters, self.INPUTZFACTOR, context) rasterLayer = self.parameterAsRasterLayer(parameters, self.INPUTRASTER, context) baseLineLayer = self.parameterAsVectorLayer(parameters, self.INPUTBASELINE, context) polygonLayer = self.parameterAsVectorLayer(parameters, self.INPUTINTERSECTIONLAYER, context) outputGeomType = self.parameterAsEnum(parameters, self.OUTPUTGEOMTYPE, context) baseLine = None #Basline Layer must have only 1 Feature if baseLineLayer.featureCount() == 1: #baseLine must be the first feature baseLineFeature = next( baseLineLayer.getFeatures(QgsFeatureRequest().setLimit(1))) baseLine = baseLineFeature.geometry() elif len(baseLineLayer.selectedFeatures()) == 1: selection = baseLineLayer.selectedFeatures() #baseLine must be the first feature selFeats = [f for f in selection] baseLineFeature = selFeats[0] baseLine = baseLineFeature.geometry() else: msg = self.tr( "Error: BaseLine layer needs exactly one line feature! " + str(baseLineLayer.featureCount()) + " Just select one feature!") feedback.reportError(msg) raise QgsProcessingException(msg) #take CRS from Rasterlayer crsProject = QgsProject.instance().crs() #check if layers have the same crs if not baseLineLayer.crs().authid() == crsProject.authid(): # if not, transform to raster crs() trafo1 = QgsCoordinateTransform(baseLineLayer.crs(), crsProject, QgsProject.instance()) #transform BaseLine opResult1 = baseLine.transform( trafo1, QgsCoordinateTransform.ForwardTransform, False) if not polygonLayer.crs().authid() == crsProject.authid(): # if not, transform to raster crs() trafo2 = QgsCoordinateTransform(polygonLayer.crs(), crsProject, QgsProject.instance()) #transform BaseLine opResult2 = baseLine.transform( trafo2, QgsCoordinateTransform.ForwardTransform, False) layerZFieldId = -1 #init Terrain tm = TerrainModel(rasterLayer, feedback) #init LaengsProfil lp = LaengsProfil(baseLine, tm, crsProject, feedback) try: total = 100.0 / len(lp.linearRef.lineSegments) except: msg = self.tr("Keine Basislinie") feedback.reportError(msg) raise QgsProcessingException(msg) bufferWidth = 10 #10 m, we make an area to intersect #get candidates featuresOnLine=[] featuresOnLine = lp.linearRef.getFeaturesOnBaseLine( polygonLayer, bufferWidth) #Falls Linien, dann ermittle Schnittpunkte mit Laengsprofil schnittpunkte = [] #Liste von Features if self.isPolygonType(polygonLayer): #get intersection point features #hier erfolgt ggf. auch die umwandlung der Projektion schnittpunkte, schnittLinien = self.getSchnittpunkteAusPolygonen( featuresOnLine, polygonLayer.crs(), lp, feedback ) #Um Attribute der geschnittenen Objekte zu uebernehmen, muss hier mehr uebergeben werden featuresWithZ = [] if outputGeomType == 1: #'Points': #calculate Z-Values for Points featuresWithZ = tm.addZtoPointFeatures(schnittpunkte, crsProject, layerZFieldId) else: #0 Lines if schnittLinien is None or len(schnittLinien) == 0: msg = self.tr( "No polygon feature is intersecting this baseline!") feedback.reportError(msg) else: #calculate Z-Values for SchnittLines for schnittLineFeat in schnittLinien: featZ = QgsFeature(schnittLineFeat) linRef = LinearReferencingMaschine( schnittLineFeat.geometry(), crsProject, feedback) line3D = self.calc3DProfile(linRef, tm, crsProject) featZ.setGeometry(line3D) featZ.setAttributes(schnittLineFeat.attributes()) featuresWithZ.append(featZ) if not featuresWithZ is None: if len(featuresWithZ) == 0: feedback.reportError( "No Features was added with z values.") return {self.OUTPUT: 0} else: newFields = [] try: newFields = featuresWithZ[0].fields() wkbTyp = featuresWithZ[0].geometry().wkbType() except IndexError: msg = self.tr( "No Z values could be assigned to the Geometries.") feedback.reportError(msg) raise QgsProcessingException(msg) #newFields.append( QgsField( "profil_id" , QVariant.Int ) ) newFields.append(QgsField("z_factor", QVariant.Int)) #config Output (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, newFields, wkbTyp, crsProject) #create geometries as profil coordinates profilFeatures = [] iFeat = 0 for current, srcFeat in enumerate(featuresWithZ): # Stop the algorithm if cancel button has been clicked if feedback.isCanceled(): return {self.OUTPUT: dest_id} #break srcGeom = srcFeat.geometry() if srcGeom.isGeosValid(): profilGeometries = lp.extractProfilGeom( srcGeom, ueberhoehung, lp.srcProfilLine) #feedback.pushInfo("b " + str(srcFeat.attributes())+ ""+ str(profilGeometries)) for profilGeom in profilGeometries: # build a Feature profilFeat = QgsFeature(newFields) profilFeat.setGeometry(profilGeom) attrs = srcFeat.attributes() attrs.append(ueberhoehung) profilFeat.setAttributes(attrs) # Add a feature in the sink sink.addFeature(profilFeat, QgsFeatureSink.FastInsert) iFeat = iFeat + 1 #feedback.pushInfo(str(profilFeat.attributes())) else: feedback.reportError( str(srcFeat.attributes()) + srcGeom.asWkt()) # Update the progress bar feedback.setProgress(int(current * total)) msgInfo = self.tr( "{0} intersection features where transformed to profile coordinates:" ).format(iFeat) feedback.pushInfo(msgInfo) # Return the results of the algorithm. In this case our only result is return {self.OUTPUT: dest_id}
def testContextSingle(self): """ Various tests to ensure that datum transforms are correctly set respecting context """ context = QgsCoordinateTransformContext() context.addSourceDatumTransform( QgsCoordinateReferenceSystem('EPSG:28356'), 1) context.addDestinationDatumTransform( QgsCoordinateReferenceSystem('EPSG:4283'), 2) context.addSourceDestinationDatumTransform( QgsCoordinateReferenceSystem('EPSG:28356'), QgsCoordinateReferenceSystem('EPSG:4283'), 3, 4) transform = QgsCoordinateTransform( QgsCoordinateReferenceSystem('EPSG:28354'), QgsCoordinateReferenceSystem('EPSG:28353'), context) # should be no datum transforms self.assertEqual(transform.sourceDatumTransformId(), -1) self.assertEqual(transform.destinationDatumTransformId(), -1) # matching source transform = QgsCoordinateTransform( QgsCoordinateReferenceSystem('EPSG:28356'), QgsCoordinateReferenceSystem('EPSG:28353'), context) self.assertEqual(transform.sourceDatumTransformId(), 1) self.assertEqual(transform.destinationDatumTransformId(), -1) # matching dest transform = QgsCoordinateTransform( QgsCoordinateReferenceSystem('EPSG:28354'), QgsCoordinateReferenceSystem('EPSG:4283'), context) self.assertEqual(transform.sourceDatumTransformId(), -1) self.assertEqual(transform.destinationDatumTransformId(), 2) # matching src/dest pair transform = QgsCoordinateTransform( QgsCoordinateReferenceSystem('EPSG:28356'), QgsCoordinateReferenceSystem('EPSG:4283'), context) self.assertEqual(transform.sourceDatumTransformId(), 3) self.assertEqual(transform.destinationDatumTransformId(), 4) # test manual overwriting transform.setSourceDatumTransform(11) transform.setDestinationDatumTransform(13) self.assertEqual(transform.sourceDatumTransformId(), 11) self.assertEqual(transform.destinationDatumTransformId(), 13) # test that auto datum setting occurs when updating src/dest crs transform.setSourceCrs(QgsCoordinateReferenceSystem('EPSG:28356')) self.assertEqual(transform.sourceDatumTransformId(), 3) self.assertEqual(transform.destinationDatumTransformId(), 4) transform.setSourceDatumTransform(11) transform.setDestinationDatumTransform(13) transform.setDestinationCrs(QgsCoordinateReferenceSystem('EPSG:4283')) self.assertEqual(transform.sourceDatumTransformId(), 3) self.assertEqual(transform.destinationDatumTransformId(), 4) transform.setSourceDatumTransform(11) transform.setDestinationDatumTransform(13) # delayed context set transform = QgsCoordinateTransform() self.assertEqual(transform.sourceDatumTransformId(), -1) self.assertEqual(transform.destinationDatumTransformId(), -1) transform.setSourceCrs(QgsCoordinateReferenceSystem('EPSG:28356')) transform.setDestinationCrs(QgsCoordinateReferenceSystem('EPSG:4283')) self.assertEqual(transform.sourceDatumTransformId(), -1) self.assertEqual(transform.destinationDatumTransformId(), -1) transform.setContext(context) self.assertEqual(transform.sourceDatumTransformId(), 3) self.assertEqual(transform.destinationDatumTransformId(), 4)
def getSchnittpunkteAusPolygonen(self, overlapFeats, featureCrs, laengsProfil, feedback): schnittpunktFeatures = [] schnittLinesFeatures = [] ioFeat = 0 countPoints = 0 try: for feat in overlapFeats: #Feature bekommt neues Attribut Station if ioFeat == 0: fields = feat.fields() fields.append(QgsField("station", QVariant.Double)) schnittpunktFeaturesOfThisPolygon = [] schnittpunktListOfPolygon = [ ] #Liste [points, stations, feature.id's] #check if Multipolygon iMulti = 0 iRing = 0 tempGeom = QgsGeometry() tempGeom.fromWkb(feat.geometry().asWkb()) #transform geom to Project.crs() if crs a different if not featureCrs.authid() == QgsProject.instance().crs( ).authid(): trafo = QgsCoordinateTransform(featureCrs, QgsProject.instance().crs(), QgsProject.instance()) #transform Geom to Project.crs() tempGeom.transform(trafo, QgsCoordinateTransform.ForwardTransform, False) if tempGeom.isMultipart(): multiGeom = tempGeom.asMultiPolygon() #Schleife zum Auflösen des Multiparts for polygon in multiGeom: for ring in polygon: points = [] for pxy in ring: #feedback.pushInfo(str(iMulti)+" "+str(iRing)+" "+str(pxy) + " " + str(type(pxy))) points.append(QgsPoint(pxy.x(), pxy.y())) singlePolygon = QgsGeometry().fromPolyline(points) iRing = iRing + 1 #feedback.pushInfo(str(iMulti) + str(type(singlePolygon)) + str(polygon)) schnittpunktFeaturesOfThisPolygonItem = self.makeIntersectionFeatures( feat, singlePolygon, laengsProfil, fields, feedback) for spfotp in schnittpunktFeaturesOfThisPolygonItem: schnittpunktFeaturesOfThisPolygon.append( spfotp) #Liste [points, stations, feature.id's] schnittpunktListOfPolygonItem = self.makeIntersectionPointsStationList( feat, singlePolygon, laengsProfil, fields, feedback) for sp in schnittpunktListOfPolygonItem: schnittpunktListOfPolygon.append(sp) iMulti = iMulti + 1 else: # single Geometry schnittpunktFeaturesOfThisPolygon = self.makeIntersectionFeatures( feat, tempGeom, laengsProfil, fields, feedback) schnittpunktListOfPolygon = self.makeIntersectionPointsStationList( feat, tempGeom, laengsProfil, fields, feedback) #create LineFeatures schnittLinesFeats = self.makeLineFeaturesFromPointStationList( feat, schnittpunktListOfPolygon, laengsProfil, feedback) for f in schnittLinesFeats: schnittLinesFeatures.append(f) #add to list for schnittFeat in schnittpunktFeaturesOfThisPolygon: #Intersection Feature in project.crs() schnittpunktFeatures.append(schnittFeat) #feedback.pushInfo(str(schnittFeat.attributes())) ioFeat = ioFeat + 1 #count Intersections countPoints = countPoints + len( schnittpunktFeaturesOfThisPolygon) except: msg = self.tr( "Error: Creating Intersections Geometry {0} Feature {1}" ).format(str(type(feat.geometry())), str(feat.attributes())) feedback.reportError(msg) raise QgsProcessingException(msg) msgInfo = self.tr("Intersected Lines: {0} Intersections: {1}").format( ioFeat, countPoints) feedback.pushInfo(msgInfo) return schnittpunktFeatures, schnittLinesFeatures
def matrix_dem_build(self, dem_dataset, height, width, scale, spacing_mm, roi_x_max, roi_x_min, roi_y_min, h_base, z_scale, projected): # Calculate DEM parameters dem_col = dem_dataset.RasterXSize dem_row = dem_dataset.RasterYSize geotransform = dem_dataset.GetGeoTransform() dem_x_min = geotransform[0] dem_y_max = geotransform[3] dem_y_min = dem_y_max + dem_row * geotransform[5] dem_x_max = dem_x_min + dem_col * geotransform[1] spacing_deg = spacing_mm * (roi_x_max - roi_x_min) / width row_stl = int(math.ceil(height / spacing_mm) + 1) col_stl = int(math.ceil(width / spacing_mm) + 1) matrix_dem = [list(range(col_stl)) for i in range(row_stl)] var_y = height for i in range(row_stl): self.updateProgress.emit() QApplication.processEvents() var_x = 0 for j in range(col_stl): # Model coordinate x(mm), y(mm) x_model = round(var_x, 2) y_model = round(var_y, 2) # Model maps geo_coordinates if projected: x = x_model * scale / 1000 + roi_x_min y = y_model * scale / 1000 + roi_y_min else: x = x_model * spacing_deg / spacing_mm + roi_x_min y = y_model * spacing_deg / spacing_mm + roi_y_min # Model layer geo_coordinates to query z value point = QgsPoint(x, y) source = self.parameters["crs_map"] target = self.parameters["crs_layer"] if source != target: transform = QgsCoordinateTransform(source, target) point = transform.transform(point) x = point.x() y = point.y() # From x(m) get Column in DEM file col_dem = (x - dem_x_min) * dem_col / (dem_x_max - dem_x_min) col_dem = int(math.floor(col_dem)) if col_dem == dem_col: col_dem -= 1 # From y(m) get Row in DEM file row_dem = (dem_y_max - y) * dem_row / (dem_y_max - dem_y_min) row_dem = int(math.floor(row_dem)) if row_dem == dem_row: row_dem -= 1 # Model coordinate z(mm) if col_dem < 0 or row_dem < 0: z_model = 2 elif self.get_dem_z(dem_dataset, col_dem, row_dem, 1, 1)[0] <= h_base: z_model = 2 elif math.isnan( self.get_dem_z(dem_dataset, col_dem, row_dem, 1, 1)[0]): z_model = 2 else: z_model = round( (self.get_dem_z(dem_dataset, col_dem, row_dem, 1, 1)[0] - h_base) / scale * 1000 * z_scale, 2) + 2 matrix_dem[i][j] = self.pto(x=x_model, y=y_model, z=z_model) var_x += spacing_mm if var_x > width: var_x = width var_y = spacing_mm * (row_stl - (i + 2)) if self.quit: return 0 return matrix_dem
def btn_calculate(self): ret = super(DlgCalculateMQI, self).btn_calculate() if not ret: return self.close() crosses_180th, geojsons = self.aoi.bounding_box_gee_geojson() val = [] n = 1 if self.area_tab.area_fromfile.isChecked(): for f in self.aoi.get_layer_wgs84().getFeatures(): # Get an OGR geometry from the QGIS geometry geom = f.geometry() val.append(geom) n += 1 # stringify json object val_string = '{}'.format(json.loads(val[0].asJson())) # create ogr geometry val_geom = ogr.CreateGeometryFromJson(val_string) # simplify polygon to tolerance of 0.003 val_geom_simplified = val_geom.Simplify(0.003) # fetch coordinates from json coords = json.loads( val_geom_simplified.ExportToJson())['coordinates'] geometries = json.dumps([{ "coordinates": coords, "type": "Polygon" }]) elif self.area_tab.area_fromadmin.isChecked(): geometries = json.dumps([{ "coordinates": self.get_admin_poly_geojson()['geometry']['coordinates'][0], "type": "Polygon" }]) elif self.area_tab.area_frompoint.isChecked(): point = QgsPointXY( float(self.area_tab.area_frompoint_point_x.text()), float(self.area_tab.area_frompoint_point_y.text())) crs_src = QgsCoordinateReferenceSystem( self.area_tab.canvas.mapSettings().destinationCrs().authid()) point = QgsCoordinateTransform( crs_src, self.aoi.crs_dst, QgsProject.instance()).transform(point) geometries = json.dumps( json.loads(QgsGeometry.fromPointXY(point).asJson())) payload = { 'year': self.mqi_setup_tab.use_esa_tg_year.date().year(), 'lu_matrix': self.mqi_setup_tab.dlg_lu_agg.get_agg_as_list()[1], 'geojsons': geometries, 'crs': self.aoi.get_crs_dst_wkt(), 'crosses_180th': crosses_180th, 'task_name': self.options_tab.task_name.text(), 'task_notes': self.options_tab.task_notes.toPlainText() } resp = run_script(get_script_slug('management-quality'), payload) if resp: mb.pushMessage( QtWidgets.QApplication.translate("MISLAND", "Submitted"), QtWidgets.QApplication.translate( "MISLAND", "Management Quality task submitted to Google Earth Engine." ), level=0, duration=5) else: mb.pushMessage( QtWidgets.QApplication.translate("MISLAND", "Error"), QtWidgets.QApplication.translate( "MISLAND", "Unable to submit management quality task to Google Earth Engine." ), level=0, duration=5)
def testReprojectionErrorsWhileRendering(self): # WKT of a polygon which causes reprojection errors while rendering # (apologies for the ridiculously complex wkt, but I can't find a way to reproduce with simplifiction) wkt = 'MultiPolygon (((16.93392988400009358 42.77094147300012139, 16.88493899800005238 42.72939687700012712, ' \ '16.80298912900011032 42.76349518400014915, 16.85816491000014139 42.78400299700011544, ' \ '16.93392988400009358 42.77094147300012139)),((17.38200931100010393 42.79783763200002511, ' \ '17.65894616000011297 42.74298737200008702, 17.74887129000009622 42.69456614800010641, ' \ '17.32374108200008322 42.79083893400003547, 17.38200931100010393 42.79783763200002511)),' \ '((16.768565300000148 42.97223541900014254, 17.03207441500009622 42.98261139500014849, ' \ '17.13184655000009116 42.96954987200014386, 17.20020592500009116 42.92177969000012183, ' \ '16.85141035200010151 42.90070221600008438, 16.65544681100004709 42.92625560099999404, ' \ '16.70679772200014668 42.96954987200014386, 16.63168379000003938 42.98261139500014849, ' \ '16.768565300000148 42.97223541900014254)),((17.05567467500011958 43.02895742400001211, ' \ '17.24024498800011429 43.02277252800014651, 17.74146569100011561 42.83926015800001608, ' \ '17.70736738400009358 42.88703034100014122, 17.65334906206413734 42.8909283361407887, ' \ '17.70158573400010482 42.91950022500007833, 17.81175988700005064 42.909862570000044, ' \ '17.85847538200005147 42.81697418200012351, 18.22413781700009849 42.62807098500009317, ' \ '18.43735477700010961 42.55921213800017711, 18.4371480710000526 42.4934022020000981, ' \ '18.49642988400009358 42.41632721600008438, 18.23894290500010129 42.55906810100005089, ' \ '18.21753991000014139 42.6201032570001388, 18.07601972700010151 42.65131256700003348, ' \ '18.0432235040000819 42.70205312700007028, 17.90162194100014403 42.75189850500014188, ' \ '17.8928328790000819 42.79083893400003547, 17.72095787900005348 42.8262393250000315, ' \ '17.7618921230000808 42.77871328300012976, 17.74870853000004445 42.77204010600017625, ' \ '17.21387780000011958 42.98261139500014849, 17.04615319100011561 42.9950625670000619, ' \ '17.00163821700004974 43.05149974200010377, 17.05567467500011958 43.02895742400001211)),' \ '((16.19467207100007045 43.07440827000000638, 16.254893425000148 43.06854889500006323, ' \ '16.08716881600014403 43.01146067900008063, 16.04883873800011429 43.06517161700004692, ' \ '16.19467207100007045 43.07440827000000638)),((16.56275475400011032 43.22898997600010773, ' \ '16.65951582100009887 43.21596914300012315, 16.72771243600001867 43.16461823100003414, ' \ '17.19336998800014271 43.12726471600016964, 16.67017662900013875 43.12547435099999404, ' \ '16.37159264400014536 43.19550202000006323, 16.49642988400006516 43.21808502800014651, ' \ '16.58326256600014403 43.18866608300005794, 16.52051842500006273 43.22898997600010773, ' \ '16.56275475400011032 43.22898997600010773)),((16.80681399800010922 43.34247467700005529, ' \ '16.89234459700011826 43.31220123900006058, 16.84620201900008851 43.27338288000005662, ' \ '16.62826582100012729 43.26373932500008834, 16.50074303500014139 43.28424713700003679, ' \ '16.42188561300008587 43.31757233300011478, 16.40577233200011165 43.33270905200011214, ' \ '16.45346113400009358 43.35317617400009738, 16.42628014400008851 43.39411041900011412, ' \ '16.44703209700008983 43.39484284100014122, 16.80681399800010922 43.34247467700005529)),' \ '((16.29818769600012729 43.40363190300011809, 16.30274498800008587 43.38727448100009099, ' \ '16.39144941500012465 43.34638092700005529, 16.348643425000148 43.33869049700003018, ' \ '16.20045006600014403 43.40704987200003018, 16.29818769600012729 43.40363190300011809)),' \ '((16.33415774800010922 43.50153229400014254, 16.3752547540000819 43.49017975500008504, ' \ '16.21143639400008851 43.49005768400009231, 16.26441491000014139 43.51288483300011478, ' \ '16.33415774800010922 43.50153229400014254)),((15.67888431100004709 43.64801666900014254, ' \ '15.74040774800010922 43.62750885600009099, 15.67204837300002396 43.63743724200010377, ' \ '15.60377037900013875 43.67470937700007028, 15.67888431100004709 43.64801666900014254)),' \ '((15.36736087300005238 43.79010651200015047, 15.39568118600007551 43.7724063170000619, ' \ '15.22779381600014403 43.87445709800014981, 15.24073326900014536 43.88076406500009341, ' \ '15.36736087300005238 43.79010651200015047)),((15.44271894600009887 43.89907461100013109, ' \ '15.35865319100014403 43.91937897300014981, 15.26124108200011165 44.01105377800003282, ' \ '15.38404381600008719 43.9701602230000077, 15.44271894600009887 43.89907461100013109)),' \ '((15.22575931100010393 44.06622955900014915, 15.25440514400008851 44.01788971600014122, ' \ '15.12183678500014139 44.09223053600005926, 15.06251061300008587 44.16193268400012073, ' \ '15.22575931100010393 44.06622955900014915)),((14.83545983200014007 44.15102773600013109, ' \ '14.85726972700010151 44.15204498900000374, 14.86915123800014271 44.14052969000006499, ' \ '14.83521569100008719 44.14166901200009363, 14.81983483200014007 44.15302155199999845, ' \ '14.82243899800005238 44.16868724200004692, 14.83545983200014007 44.15102773600013109)),' \ '((14.98511803500011297 44.09096914300012315, 15.21680748800008587 43.91278717700008372, ' \ '15.13331139400011693 43.92121002800003282, 15.19450931100004709 43.87262604400017096, ' \ '15.10661868600007551 43.92544179900015422, 14.84961998800014271 44.17560455900014915, ' \ '14.98511803500011297 44.09096914300012315)),((14.765961134000122 44.26504140800015819, ' \ '14.74854576900014536 44.26166413000014188, 14.73959394600012729 44.28017812700015554, ' \ '14.79167728000007287 44.27252838700003679, 14.765961134000122 44.26504140800015819)),' \ '((14.66138756600011561 44.30866120000014519, 14.6407983730000808 44.31183502800003282, ' \ '14.59506269600007045 44.34711334800006455, 14.643565300000148 44.32575104400011412, ' \ '14.66138756600011561 44.30866120000014519)),((14.81120853000004445 44.35004303600000242, ' \ '14.75619550900009358 44.36399974200004692, 14.76343834700008983 44.41535065300017493, ' \ '14.80323326900008851 44.40550364800004957, 14.81120853000004445 44.35004303600000242)),' \ '((14.27116946700002131 44.61253489800004957, 14.23259524800005238 44.62604401200012205, ' \ '14.2657983730000808 44.67951080900003547, 14.28044681100007551 44.67755768400009231, ' \ '14.27116946700002131 44.61253489800004957)),((14.84522545700008322 44.60053131700011875, ' \ '14.93824303500014139 44.59414297100001079, 15.07553144600007045 44.48407623900006058, ' \ '14.91114342500011958 44.54547760600014783, 15.04802493600004709 44.43943919500001982, ' \ '15.09669030000009116 44.41518789300000947, 15.04151451900014536 44.47662995000008834, ' \ '15.25440514400008851 44.34003327000000638, 15.165049675000148 44.36737702000006323, ' \ '15.22022545700008322 44.3127302100001117, 15.13086998800008587 44.33258698100003414, ' \ '15.17237389400014536 44.29913971600016964, 15.12875410200007309 44.31199778900018771, ' \ '15.08920332100009887 44.37421295800000109, 15.11719811300014271 44.38719310099999404, ' \ '15.04900149800010922 44.39468008000015686, 14.89747155000009116 44.49091217699999845, ' \ '14.91863040500010129 44.50454336100013109, 14.87696373800011429 44.55975983300005794, ' \ '14.73365319100008719 44.70319245000014519, 14.84522545700008322 44.60053131700011875)),' \ '((14.41000410200010151 44.60097890800001608, 14.52662194100011561 44.50372955900012073, ' \ '14.53435306100010393 44.48407623900006058, 14.42261803500008455 44.57387929900009738, ' \ '14.36304772200014668 44.57343170800000109, 14.38257897200014668 44.60325755399999537, ' \ '14.33578535200007309 44.71678294500010509, 14.39747155000009116 44.6856143250000315, ' \ '14.41000410200010151 44.60097890800001608)),((14.75326582100007045 44.84585195500012844, ' \ '14.74048912900011032 44.82050202000000638, 14.82243899800005238 44.77142975500005662, ' \ '14.84961998800014271 44.70319245000014519, 14.65788821700004974 44.79877350500014188, ' \ '14.7268172540000819 44.79877350500014188, 14.6858016290000819 44.8471540390000456, ' \ '14.75326582100007045 44.84585195500012844)),((14.47103925900006516 44.95392487200003018, ' \ '14.45191491000008455 44.79877350500014188, 14.47217858200011165 44.7079531920000619, ' \ '14.53435306100010393 44.63426341400010244, 14.51335696700007816 44.618841864000089, ' \ '14.42790774800005238 44.65656159100014122, 14.29420006600008719 44.9086367860001161, ' \ '14.30152428500011297 44.94342682500014519, 14.38738040500004445 44.90900299700003018, ' \ '14.39031009200004974 44.96039459800012139, 14.41138756600008719 44.95636627800014651, ' \ '14.27849368600004709 45.1133487000000315, 14.29957116000014139 45.16233958499999801, ' \ '14.35621178500014139 45.16925690300008966, 14.387705925000148 45.03904857000013351, ' \ '14.47103925900006516 44.95392487200003018)),((14.56332441500012465 45.24974192900008063, ' \ '14.62378991000011297 45.17548248900006058, 14.59742272200011826 45.16644928600005926, ' \ '14.66529381600011561 45.16181061400011743, 14.66529381600011561 45.08734772300006455, ' \ '14.74048912900011032 45.07306549700014386, 14.81495201900008851 44.97748444200009033, ' \ '14.70639082100009887 44.9467227230000077, 14.62891686300014271 44.97817617400004053, ' \ '14.62086022200008983 45.04559967700011214, 14.61695397200008983 45.02464427300007799, ' \ '14.51050866000014139 45.03217194200011875, 14.43873131600014403 45.07050202000006323, ' \ '14.4670516290000819 45.12409088700015047, 14.53012129000009622 45.13483307500014519, ' \ '14.53435306100010393 45.23753489800002114, 14.56332441500012465 45.24974192900008063)),' \ '((16.36947066200013978 46.54057118800012915, 16.63767134600004738 46.47447703100009164, ' \ '16.75508020000012266 46.38187286400001597, 16.83765913900006694 46.38187286400001597, ' \ '16.88923221800007468 46.29216257800014489, 17.05294315600005461 46.15346303300005104, ' \ '17.20859257000006437 46.11656606000003933, 17.27587528500004055 46.01202463800002818, ' \ '17.31680301900004793 45.99765859000002877, 17.29013798000011093 45.98463612900009423, ' \ '17.40620324700006449 45.94365671800015605, 17.59110152100009827 45.93621531200012953, ' \ '17.65652388500006964 45.84541982000014571, 17.80917606600013414 45.81441396100005647, ' \ '17.85806197100004056 45.77172922800004073, 18.21121870900006456 45.78537180600012846, ' \ '18.40438521300006869 45.74180857400001798, 18.57347049900010916 45.81668772400014689, ' \ '18.6556360270001278 45.90758656800015558, 18.7755253500000947 45.88283355700004051, ' \ '18.90130578600007993 45.93120269800006383, 18.87288374800004931 45.89523590100002082, ' \ '18.90699019400011593 45.86795074500018643, 18.85531376100007606 45.85735707600009903, ' \ '18.84497847500006174 45.8157058720000947, 18.96848514800012708 45.66873809800016204, ' \ '18.90357954900008508 45.57308502200005762, 18.94171675700005153 45.53892689999999277, ' \ '19.01809452300011571 45.56740061400002162, 19.10625451700005328 45.51164174500017623, ' \ '19.00961958800010621 45.49867095900005154, 19.00300500400010151 45.45536611000007099, ' \ '19.03742150900006891 45.42229319300010104, 18.97592655400006834 45.39495636000008005, ' \ '19.09199182100007874 45.34999786400005917, 19.12475467900009107 45.29811472600006539, ' \ '19.36308638500014467 45.24824696900010679, 19.40783817500010855 45.20313344400013023, ' \ '19.39068160000005037 45.16933705700016333, 19.22593713300008744 45.16194732700016345, ' \ '19.12186079900010327 45.195795390000157, 19.13767378700009658 45.14603098600004216, ' \ '19.04486291500009543 45.13724599300006446, 19.08227665200013234 45.08494944300004192, ' \ '19.0872375890000967 44.97710072800013847, 19.13167932100006396 44.95317454000003465, ' \ '19.06667036900009293 44.90568389900012392, 18.99142948400006503 44.9149339800001286, ' \ '19.01582076000008215 44.86563466400004074, 18.88962691200009658 44.86119049100013001, ' \ '18.78338016700013213 44.91374542300012251, 18.79175174900009893 45.00154368100008639, ' \ '18.73831831900008638 45.0159097290000858, 18.68405806500004473 45.08479441400000098, ' \ '18.64871138500012648 45.06267689999999959, 18.61667199700013953 45.09766184500010411, ' \ '18.54959598800010667 45.09476796500011631, 18.51703983500007666 45.05585561200003042, ' \ '18.23788374800011525 45.15745147700012296, 18.15365116400005263 45.0975584930001645, ' \ '18.00347945100011771 45.1493382780000303, 17.83573775200005684 45.0644338990000648, ' \ '17.68473921700012852 45.1639627080000281, 17.48185754400009273 45.11440500900012296, ' \ '17.49622359200009214 45.1416901650001563, 17.44775109900012922 45.13430043600014585, ' \ '17.44330692500011537 45.16205068000009248, 17.38243208800008688 45.1396231090000839, ' \ '17.26895064300006766 45.18954254200015441, 17.24548954300007608 45.15538442000017483, ' \ '17.18709517400012032 45.14856313100001728, 17.0363033440001459 45.23047027600007652, ' \ '17.00829471800011561 45.21615590500009318, 17.00829471800011561 45.24416453100009505, ' \ '16.94731652900014751 45.23568959600000028, 16.9243721930001243 45.28452382500016427, ' \ '16.81171757000004163 45.18122263700009, 16.52894413300009546 45.22225372400005483, ' \ '16.38921106000003647 45.11683380099999852, 16.31624393700010955 45.00123362300008978, ' \ '16.12152714000009723 45.09616322900008356, 16.02044803900011516 45.213933818000001, ' \ '15.79234826700013627 45.18980092400012438, 15.76361617000014803 44.97555043600003444, ' \ '15.7308533120001357 44.92723297200008403, 15.77343469200010873 44.84501576800015243, ' \ '15.71607385200013596 44.80320953400008932, 15.72847619600008784 44.76910308800002269, ' \ '15.80568078600006743 44.69665273000013883, 15.88877648900006534 44.72424794500012979, ' \ '15.96897831200004703 44.63924021400013942, 16.02830285600006732 44.62471913700009907, ' \ '16.04473596200011798 44.58937245700018082, 16.00608199000004106 44.54100331600012908, ' \ '16.11646285000011858 44.52146962500013672, 16.15966434700004584 44.41610138000002905, ' \ '16.13827030500004867 44.37760243800015303, 16.20286584400008678 44.35977406800010669, ' \ '16.18756962000011868 44.28241444999999032, 16.21578495300011014 44.20815541600011045, ' \ '16.32688928200008149 44.08237498000012522, 16.50103885900011846 43.99271637000008184, ' \ '16.67859908100004418 43.8406843060001421, 16.71260217300007866 43.77151540100005889, ' \ '17.03051558500007445 43.54847991900005866, 17.27050093600007585 43.46321380700000248, ' \ '17.28993127500007176 43.3034302780000786, 17.44206669100009321 43.15243174300015028, ' \ '17.6284119050001209 43.04657257100008394, 17.66272505700004558 42.96569895500012137, ' \ '17.63450972400008254 42.950402731000068, 17.51563561300008587 42.95888906500012183, ' \ '17.47087649800005238 43.01341380400010905, 17.50196373800014271 43.03099192900005221, ' \ '17.43360436300014271 43.01740143400009231, 17.46021569100011561 43.03099192900005221, ' \ '17.42611738400009358 43.06517161700004692, 17.4045516290000819 43.05149974200010377, ' \ '17.31625410200012993 43.12726471600016964, 17.11394290500004445 43.21320221600008438, ' \ '16.88062584700011826 43.40595123900006058, 16.62582441500009622 43.44904205900009231, ' \ '16.52466881600011561 43.51080963700009363, 16.39144941500012465 43.51080963700009363, ' \ '16.47339928500008455 43.5381533870001789, 16.43384850400013875 43.54975006700000506, ' \ '16.11768639400008851 43.52448151200003679, 16.17237389400014536 43.4896914730000077, ' \ '16.11312910200004467 43.47890859600009605, 15.95948326900011693 43.50397370000008834, ' \ '15.987315300000148 43.54490794500010509, 15.92530358200011165 43.55857982000004824, ' \ '15.91895592500009116 43.62872955900012073, 15.96631920700011165 43.64118073100003414, ' \ '15.90479576900014536 43.64801666900014254, 15.95297285200010151 43.65086497599999404, ' \ '15.95045006600008719 43.68854401200015047, 15.70630944100008719 43.76341380400005221, ' \ '15.6174422540000819 43.82550690300017493, 15.66309655000009116 43.81297435099999404, ' \ '15.67888431100004709 43.81928131700011875, 15.45508873800014271 43.92804596600014122, ' \ '15.14454186300011429 44.19546133000015686, 15.15219160200012993 44.23529694200014717, ' \ '15.11036217500011958 44.26434967700011214, 15.14063561300011429 44.28245677300013483, ' \ '15.17660566500009622 44.24994538000005662, 15.20777428500008455 44.27277252800014651, ' \ '15.19809004000012465 44.30166250200007028, 15.295258009000122 44.25067780199999845, ' \ '15.30274498800008587 44.29913971600016964, 15.26124108200011165 44.33258698100003414, ' \ '15.42448978000001603 44.26797109600006763, 15.52865644600009887 44.27179596600008438, ' \ '15.30795332100009887 44.35439687700007028, 15.00733483200014007 44.56972890800012976, ' \ '14.883799675000148 44.7236188820001388, 14.883799675000148 44.86147695500012844, 14.92164147200008983 ' \ '44.95880768400009231, 14.85279381600011561 45.09365469000000815, 14.65788821700004974 ' \ '45.19660065300017493, 14.57081139400008851 45.29364655200011214, 14.31153405000009116 ' \ '45.34398021000005485, 14.23259524800005238 45.14935944200000506, 14.17937259200007816 ' \ '45.13450755400005221, 14.19312584700008983 45.10561758000012844, 14.14389082100007045 ' \ '45.05939362200003018, 14.151377800000148 44.97748444200009033, 14.06885826900014536 ' \ '44.94953034100014122, 14.08383222700007309 44.9863955750000315, 14.04029381600014403 ' \ '45.03896719000015025, 14.0756942070000548 44.98371002800003282, 14.02051842500011958 ' \ '44.90110911700004692, 13.97266686300011429 44.90110911700004692, 13.99301191500009622 ' \ '44.88129303600014453, 13.97266686300011429 44.82664622599999404, 14.00001061300008587 ' \ '44.81305573100003414, 13.89014733200011165 44.83348216400010244, 13.91797936300014271 ' \ '44.77826569200009033, 13.90316816500009622 44.77240631700014717, 13.89698326900011693 ' \ '44.81305573100003414, 13.78711998800014271 44.87506745000008834, 13.84229576900008851 ' \ '44.88812897300006455, 13.79460696700010658 44.89496491100008768, 13.77409915500007287 ' \ '44.96381256700014717, 13.6232202480000808 45.07306549700014386, 13.61255944100014403 ' \ '45.11786530199999845, 13.72624759200004974 45.13450755400005221, 13.5959578790000819 ' \ '45.14541250200001343, 13.57545006600011561 45.26487864800007799, 13.60271243600001867 ' \ '45.28534577000012007, 13.57545006600011561 45.30646393400006389, 13.60954837300005238 ' \ '45.32013580900017757, 13.54127037900013875 45.34613678600005926, 13.50709069100014403 ' \ '45.51190827000000638, 13.62901778100007277 45.45898346000016943, 13.75929406800014476 ' \ '45.46316925100011019, 13.88900191200011136 45.42363678000005223, 13.98263960800005634 ' \ '45.47531321200001742, 13.97189091000012695 45.5142255660000643, 14.09291711400010172 ' \ '45.47391794800002174, 14.21869755100007637 45.49717234400004884, 14.37279667100006009 ' \ '45.47784535800009564, 14.4689148350000778 45.52559438100014688, 14.49857710800012001 ' \ '45.59618438800005435, 14.58094934100009255 45.66780792200010808, 14.66848921700008646 ' \ '45.53396596300005683, 14.79716353300005949 45.46518463200006011, 14.88160282300009385 ' \ '45.46978383400001178, 14.9226339110000481 45.51494903600017494, 15.13926151500010064 ' \ '45.43004465799999991, 15.32519331800011742 45.45283396399999276, 15.36136682100004691 ' \ '45.48203114900003641, 15.29666792800006192 45.52295888300012905, 15.2685559480001416 ' \ '45.60166208900012919, 15.37376916500011248 45.64021270800010655, 15.25501672300006817 ' \ '45.72346344000011698, 15.42906294700014769 45.77529490200011253, 15.45128381300008868 ' \ '45.81513743100013869, 15.67607629400006886 45.84169911700014666, 15.65943648300003588 ' \ '45.88882802400014782, 15.69798710100010908 46.0362092080000167, 15.58988000500005455 ' \ '46.11351715100001059, 15.62284956800010605 46.19170359400006021, 16.01920780400010358 ' \ '46.29882883700007312, 16.05961877400008575 46.33231516600015709, 16.0579651280001201 ' \ '46.37753204400003426, 16.2756262620000598 46.37316538500006402, 16.23490523300009158 ' \ '46.4933389280001137, 16.36947066200013978 46.54057118800012915))) ' geom = QgsGeometry.fromWkt(wkt) f = QgsFeature() f.setGeometry(geom) image = QImage(200, 200, QImage.Format_RGB32) painter = QPainter() ms = QgsMapSettings() crs = QgsCoordinateReferenceSystem.fromProj4('+proj=ortho +lat_0=36.5 +lon_0=-118.8 +x_0=0 +y_0=0 +a=6371000 +b=6371000 +units=m +no_defs') self.assertTrue(crs.isValid()) ms.setDestinationCrs(crs) ms.setExtent(QgsRectangle(1374999.8, 3912610.7, 4724462.5, 6505499.6)) ms.setOutputSize(image.size()) context = QgsRenderContext.fromMapSettings(ms) context.setPainter(painter) context.setScaleFactor(96 / 25.4) # 96 DPI ct = QgsCoordinateTransform(QgsCoordinateReferenceSystem('epsg:4326'), crs, QgsProject.instance()) self.assertTrue(ct.isValid()) context.setCoordinateTransform(ct) context.setExtent(ct.transformBoundingBox(ms.extent(), QgsCoordinateTransform.ReverseTransform)) fill_symbol = QgsFillSymbol.createSimple({'color': '#ffffff', 'outline_color': '#ffffff', 'outline_width': '10'}) painter.begin(image) try: image.fill(QColor(0, 0, 0)) fill_symbol.startRender(context) fill_symbol.renderFeature(f, context) fill_symbol.stopRender(context) finally: painter.end() assert self.imageCheck('Reprojection errors polygon', 'reprojection_errors_polygon', image) #also test linestring linestring = QgsGeometry(geom.constGet().boundary()) f.setGeometry(linestring) line_symbol = QgsLineSymbol.createSimple({'color': '#ffffff', 'outline_width': '10'}) image = QImage(200, 200, QImage.Format_RGB32) painter.begin(image) try: image.fill(QColor(0, 0, 0)) line_symbol.startRender(context) line_symbol.renderFeature(f, context) line_symbol.stopRender(context) finally: painter.end() assert self.imageCheck('Reprojection errors linestring', 'reprojection_errors_linestring', image)
import qgis import pymgrs from qgis.core import QgsCoordinateReferenceSystem, QgsCoordinateTransform iface = qgis.utils.iface iface.mainWindow().statusBar().show() sourceCrs = QgsCoordinateReferenceSystem( iface.mapCanvas().mapSettings().destinationCrs().postgisSrid()) destCrs = QgsCoordinateReferenceSystem(4326) xform = QgsCoordinateTransform(sourceCrs, destCrs) #you can also do reverse convertion def updateSourceCrs(): global sourceCrs global destCrs global xform sourceCrs = QgsCoordinateReferenceSystem( iface.mapCanvas().mapSettings().destinationCrs().postgisSrid()) destCrs = QgsCoordinateReferenceSystem(4326) xform = QgsCoordinateTransform( sourceCrs, destCrs) #you can also do reverse convertion # bind signal def showCoords(xy): point = xform.transform(xy) iface.mainWindow().statusBar().showMessage( pymgrs.LLtoMGRS(point.y(), point.x()))
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
def processAlgorithm(self, parameters, context, feedback): source = self.parameterAsSource( parameters, self.INPUT, context ) sampled_raster = self.parameterAsRasterLayer( parameters, self.RASTERCOPY, context ) columnPrefix = self.parameterAsString( parameters, self.COLUMN_PREFIX, context ) if source is None: raise QgsProcessingException(self.invalidSourceError(parameters, self.INPUT)) source_fields = source.fields() raster_fields = QgsFields() # append field to vector as columnPrefix_bandCount for b in range(sampled_raster.bandCount()): raster_fields.append(QgsField( columnPrefix + str('_{}'.format(b + 1)), QVariant.Double ) ) # combine all the vector fields out_fields = QgsProcessingUtils.combineFields(source_fields, raster_fields) (sink, dest_id) = self.parameterAsSink( parameters, self.OUTPUT, context, out_fields, source.wkbType(), source.sourceCrs() ) if sink is None: raise QgsProcessingException(self.invalidSinkError(parameters, self.OUTPUT)) total = 100.0 / source.featureCount() if source.featureCount() else 0 features = source.getFeatures() # create the coordinates transformation context ct = QgsCoordinateTransform(source.sourceCrs(), sampled_raster.crs(), context.transformContext()) for n, i in enumerate(source.getFeatures()): attrs = i.attributes() if i.geometry().isMultipart() and i.geometry().constGet().partCount() > 1: sink.addFeature(i, QgsFeatureSink.FastInsert) feedback.setProgress(int(n * total)) feedback.reportError(self.tr('Impossible to sample data of multipart feature {}.').format(i.id())) continue # get the feature geometry as point point = QgsPointXY() if i.geometry().isMultipart(): point = i.geometry().asMultiPoint()[0] else: point = i.geometry().asPoint() # reproject to raster crs try: point = ct.transform(point) except QgsCsException: for b in range(sampled_raster.bandCount()): attrs.append(None) i.setAttributes(attrs) sink.addFeature(i, QgsFeatureSink.FastInsert) feedback.setProgress(int(n * total)) feedback.reportError(self.tr('Could not reproject feature {} to raster CRS').format(i.id())) continue for b in range(sampled_raster.bandCount()): value, ok = sampled_raster.dataProvider().sample(point, b + 1) if ok: attrs.append(value) else: attrs.append(NULL) i.setAttributes(attrs) sink.addFeature(i, QgsFeatureSink.FastInsert) feedback.setProgress(int(n * total)) return {self.OUTPUT: dest_id}
def processLine(source, sink, feedback, discardVertices): layercrs = source.sourceCrs() if layercrs != epsg4326: transto4326 = QgsCoordinateTransform(layercrs, epsg4326, QgsProject.instance()) transfrom4326 = QgsCoordinateTransform(epsg4326, layercrs, QgsProject.instance()) total = 100.0 / source.featureCount() if source.featureCount() else 0 iterator = source.getFeatures() num_bad = 0 maxseglen = settings.maxSegLength * 1000.0 maxSegments = settings.maxSegments for cnt, feature in enumerate(iterator): if feedback.isCanceled(): break try: if feature.geometry().isMultipart(): seg = feature.geometry().asMultiPolyline() else: seg = [feature.geometry().asPolyline()] numseg = len(seg) if numseg < 1 or len(seg[0]) < 2: continue # Create a new Line Feature fline = QgsFeature() # If the input is not 4326 we need to convert it to that and then back to the output CRS if discardVertices: ptStart = QgsPointXY(seg[0][0][0], seg[0][0][1]) if layercrs != epsg4326: # Convert to 4326 ptStart = transto4326.transform(ptStart) pts = [ptStart] numpoints = len(seg[numseg - 1]) ptEnd = QgsPointXY(seg[numseg - 1][numpoints - 1][0], seg[numseg - 1][numpoints - 1][1]) if layercrs != epsg4326: # Convert to 4326 ptEnd = transto4326.transform(ptEnd) l = geod.InverseLine(ptStart.y(), ptStart.x(), ptEnd.y(), ptEnd.x()) if l.s13 > maxseglen: n = int(math.ceil(l.s13 / maxseglen)) if n > maxSegments: n = maxSegments seglen = l.s13 / n for i in range(1, n): s = seglen * i g = l.Position( s, Geodesic.LATITUDE | Geodesic.LONGITUDE | Geodesic.LONG_UNROLL) pts.append(QgsPointXY(g['lon2'], g['lat2'])) pts.append(ptEnd) if layercrs != epsg4326: # Convert each point back to the output CRS for x, pt in enumerate(pts): pts[x] = transfrom4326.transform(pt) fline.setGeometry(QgsGeometry.fromPolylineXY(pts)) else: if not feature.geometry().isMultipart(): line = seg[0] numpoints = len(line) ptStart = QgsPointXY(line[0][0], line[0][1]) if layercrs != epsg4326: # Convert to 4326 ptStart = transto4326.transform(ptStart) pts = [ptStart] for x in range(1, numpoints): ptEnd = QgsPointXY(line[x][0], line[x][1]) if layercrs != epsg4326: # Convert to 4326 ptEnd = transto4326.transform(ptEnd) l = geod.InverseLine(ptStart.y(), ptStart.x(), ptEnd.y(), ptEnd.x()) n = int(math.ceil(l.s13 / maxseglen)) if l.s13 > maxseglen: if n > maxSegments: n = maxSegments seglen = l.s13 / n for i in range(1, n): s = seglen * i g = l.Position( s, Geodesic.LATITUDE | Geodesic.LONGITUDE | Geodesic.LONG_UNROLL) pts.append(QgsPointXY(g['lon2'], g['lat2'])) pts.append(ptEnd) ptStart = ptEnd if layercrs != epsg4326: # Convert each point back to the output CRS for x, pt in enumerate(pts): pts[x] = transfrom4326.transform(pt) fline.setGeometry(QgsGeometry.fromPolylineXY(pts)) else: # MultiLineString outseg = [] for line in seg: numpoints = len(line) ptStart = QgsPointXY(line[0][0], line[0][1]) if layercrs != epsg4326: # Convert to 4326 ptStart = transto4326.transform(ptStart) pts = [ptStart] for x in range(1, numpoints): ptEnd = QgsPointXY(line[x][0], line[x][1]) if layercrs != epsg4326: # Convert to 4326 ptEnd = transto4326.transform(ptEnd) l = geod.InverseLine(ptStart.y(), ptStart.x(), ptEnd.y(), ptEnd.x()) n = int(math.ceil(l.s13 / maxseglen)) if l.s13 > maxseglen: if n > maxSegments: n = maxSegments seglen = l.s13 / n for i in range(1, n): s = seglen * i g = l.Position( s, Geodesic.LATITUDE | Geodesic.LONGITUDE | Geodesic.LONG_UNROLL) pts.append(QgsPointXY( g['lon2'], g['lat2'])) pts.append(ptEnd) ptStart = ptEnd if layercrs != epsg4326: # Convert each point back to the output CRS for x, pt in enumerate(pts): pts[x] = transfrom4326.transform(pt) outseg.append(pts) fline.setGeometry(QgsGeometry.fromMultiPolylineXY(outseg)) fline.setAttributes(feature.attributes()) sink.addFeature(fline) except: num_bad += 1 #traceback.print_exc() pass feedback.setProgress(int(cnt * total)) return num_bad
def test_insert_srsName(self): """Test srsName is respected when insering""" post_data = """ <Transaction xmlns="http://www.opengis.net/wfs" xsi:schemaLocation="http://www.qgis.org/gml http://localhost:8000/?SERVICE=WFS&REQUEST=DescribeFeatureType&VERSION=1.0.0&TYPENAME=as_symbols" service="WFS" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="{version}" xmlns:gml="http://www.opengis.net/gml"> <Insert xmlns="http://www.opengis.net/wfs"> <as_symbols xmlns="http://www.qgis.org/gml"> <name xmlns="http://www.qgis.org/gml">{name}</name> <geometry xmlns="http://www.qgis.org/gml"> <gml:Point srsName="{srsName}"> <gml:coordinates cs="," ts=" ">{coordinates}</gml:coordinates> </gml:Point> </geometry> </as_symbols> </Insert> </Transaction> """ project = self.testdata_path + \ "test_project_wms_grouped_layers.qgs" assert os.path.exists(project), "Project file not found: " + project query_string = '?SERVICE=WFS&MAP={}'.format( urllib.parse.quote(project)) request = post_data.format( name='4326-test1', version='1.1.0', srsName='EPSG:4326', coordinates='10.67,52.48' ) header, body = self._execute_request( query_string, requestMethod=QgsServerRequest.PostMethod, data=request.encode('utf-8')) # Verify vl = QgsVectorLayer(self.testdata_path + 'test_project_wms_grouped_layers.gpkg|layername=as_symbols', 'as_symbols') self.assertTrue(vl.isValid()) feature = next(vl.getFeatures(QgsFeatureRequest(QgsExpression('"name" = \'4326-test1\'')))) geom = feature.geometry() tr = QgsCoordinateTransform(QgsCoordinateReferenceSystem.fromEpsgId(4326), vl.crs(), QgsCoordinateTransformContext()) geom_4326 = QgsGeometry.fromWkt('point( 10.67 52.48)') geom_4326.transform(tr) self.assertEqual(geom.asWkt(0), geom_4326.asWkt(0)) # Now: insert a feature in layer's CRS request = post_data.format( name='25832-test1', version='1.1.0', srsName='EPSG:25832', coordinates='613412,5815738' ) header, body = self._execute_request( query_string, requestMethod=QgsServerRequest.PostMethod, data=request.encode('utf-8')) feature = next(vl.getFeatures(QgsFeatureRequest(QgsExpression('"name" = \'25832-test1\'')))) geom = feature.geometry() self.assertEqual(geom.asWkt(0), geom_4326.asWkt(0)) # Tests for inverted axis issue GH #36584 # Cleanup self.assertTrue(vl.startEditing()) vl.selectByExpression('"name" LIKE \'4326-test%\'') vl.deleteSelectedFeatures() self.assertTrue(vl.commitChanges()) self.i = 0 def _test(version, srsName, lat_lon=False): self.i += 1 name = '4326-test_%s' % self.i request = post_data.format( name=name, version=version, srsName=srsName, coordinates='52.48,10.67' if lat_lon else '10.67,52.48' ) header, body = self._execute_request( query_string, requestMethod=QgsServerRequest.PostMethod, data=request.encode('utf-8')) feature = next(vl.getFeatures(QgsFeatureRequest(QgsExpression('"name" = \'%s\'' % name)))) geom = feature.geometry() self.assertEqual(geom.asWkt(0), geom_4326.asWkt(0), "Transaction Failed: %s , %s, lat_lon=%s" % (version, srsName, lat_lon)) _test('1.1.0', 'urn:ogc:def:crs:EPSG::4326', lat_lon=True) _test('1.1.0', 'http://www.opengis.net/def/crs/EPSG/0/4326', lat_lon=True) _test('1.1.0', 'http://www.opengis.net/gml/srs/epsg.xml#4326', lat_lon=False) _test('1.1.0', 'EPSG:4326', lat_lon=False) _test('1.0.0', 'urn:ogc:def:crs:EPSG::4326', lat_lon=True) _test('1.0.0', 'http://www.opengis.net/def/crs/EPSG/0/4326', lat_lon=True) _test('1.0.0', 'http://www.opengis.net/gml/srs/epsg.xml#4326', lat_lon=False) _test('1.0.0', 'EPSG:4326', lat_lon=False) def _test_getFeature(version, srsName, lat_lon=False): # Now get the feature through WFS using BBOX filter bbox = QgsGeometry.fromWkt('point( 10.7006 52.4317)').boundingBox() bbox.grow(0.0001) bbox_text = "%s,%s,%s,%s" % ((bbox.yMinimum(), bbox.xMinimum(), bbox.yMaximum(), bbox.xMaximum()) if lat_lon else (bbox.xMinimum(), bbox.yMinimum(), bbox.xMaximum(), bbox.yMaximum())) req = query_string + '&REQUEST=GetFeature&VERSION={version}&TYPENAME=as_symbols&SRSNAME={srsName}&BBOX={bbox},{srsName}'.format(version=version, srsName=srsName, bbox=bbox_text) header, body = self._execute_request(req) self.assertTrue(b'gid>7' in body, "GetFeature Failed: %s , %s, lat_lon=%s" % (version, srsName, lat_lon)) _test_getFeature('1.1.0', 'urn:ogc:def:crs:EPSG::4326', lat_lon=True) _test_getFeature('1.1.0', 'EPSG:4326', lat_lon=False) _test_getFeature('1.0.0', 'urn:ogc:def:crs:EPSG::4326', lat_lon=True) _test_getFeature('1.0.0', 'EPSG:4326', lat_lon=False) # Cleanup self.assertTrue(vl.startEditing()) vl.selectByExpression('"name" LIKE \'4326-test%\'') vl.deleteSelectedFeatures() self.assertTrue(vl.commitChanges())
def processAlgorithm(self, parameters, context, feedback): expression = self.parameterAsString(parameters, self.EXPRESSION, context) layers = self.parameterAsLayerList(parameters, self.LAYERS, context) layersDict = {} if layers: layersDict = {lyr.source(): lyr for lyr in layers} crs = self.parameterAsCrs(parameters, self.CRS, context) if crs is None or not crs.isValid(): if not layers: raise QgsProcessingException( self.tr("No reference layer selected nor CRS provided")) else: crs = list(layersDict.values())[0].crs() bbox = self.parameterAsExtent(parameters, self.EXTENT, context) if bbox.isNull() and not layers: raise QgsProcessingException( self.tr("No reference layer selected nor extent box provided")) if not bbox.isNull(): bboxCrs = self.parameterAsExtentCrs(parameters, self.EXTENT, context) if bboxCrs != crs: transform = QgsCoordinateTransform(bboxCrs, crs, context.project()) bbox = transform.transformBoundingBox(bbox) if bbox.isNull() and layers: bbox = QgsProcessingUtils.combineLayerExtents(layers, crs) cellsize = self.parameterAsDouble(parameters, self.CELLSIZE, context) if cellsize == 0 and not layers: raise QgsProcessingException( self.tr( "No reference layer selected nor cellsize value provided")) def _cellsize(layer): ext = layer.extent() if layer.crs() != crs: transform = QgsCoordinateTransform(layer.crs(), crs, context.project()) ext = transform.transformBoundingBox(ext) return (ext.xMaximum() - ext.xMinimum()) / layer.width() if cellsize == 0: cellsize = min([_cellsize(lyr) for lyr in layersDict.values()]) # check for layers available in the model layersDictCopy = layersDict.copy( ) # need a shallow copy because next calls invalidate iterator for lyr in layersDictCopy.values(): expression = self.mappedNameToLayer(lyr, expression, layersDict, context) # check for layers available in the project for lyr in QgsProcessingUtils.compatibleRasterLayers( context.project()): expression = self.mappedNameToLayer(lyr, expression, layersDict, context) # create the list of layers to be passed as inputs to RasterCalculaltor # at this phase expression has been modified to match available layers # in the current scope entries = [] for name, lyr in layersDict.items(): for n in range(lyr.bandCount()): ref = '{:s}@{:d}'.format(name, n + 1) if ref in expression: entry = QgsRasterCalculatorEntry() entry.ref = ref entry.raster = lyr entry.bandNumber = n + 1 entries.append(entry) # Append any missing entry from the current project for entry in QgsRasterCalculatorEntry.rasterEntries(): if not [e for e in entries if e.ref == entry.ref]: entries.append(entry) output = self.parameterAsOutputLayer(parameters, self.OUTPUT, context) width = round((bbox.xMaximum() - bbox.xMinimum()) / cellsize) height = round((bbox.yMaximum() - bbox.yMinimum()) / cellsize) driverName = GdalUtils.getFormatShortNameFromFilename(output) calc = QgsRasterCalculator(expression, output, driverName, bbox, crs, width, height, entries) res = calc.processCalculation(feedback) if res == QgsRasterCalculator.ParserError: raise QgsProcessingException(self.tr("Error parsing formula")) return {self.OUTPUT: output}
def run(self, layers): """Experimental impact function. :param layers: List of layers expected to contain at least: H: Polygon layer of inundation areas E: Vector layer of roads :type layers: list :returns: A new line layer with inundated roads marked. :type: safe_layer """ target_field = self.parameters['target_field'] road_type_field = self.parameters['road_type_field'] threshold_min = self.parameters['min threshold [m]'] threshold_max = self.parameters['max threshold [m]'] if threshold_min > threshold_max: message = tr( 'The minimal threshold is greater then the maximal specified ' 'threshold. Please check the values.') raise GetDataError(message) # Extract data H = get_hazard_layer(layers) # Flood E = get_exposure_layer(layers) # Roads question = get_question(H.get_name(), E.get_name(), self) H = H.get_layer() E = E.get_layer() # reproject self.extent to the hazard projection hazard_crs = H.crs() hazard_authid = hazard_crs.authid() if hazard_authid == 'EPSG:4326': viewport_extent = self.extent else: geo_crs = QgsCoordinateReferenceSystem() geo_crs.createFromSrid(4326) viewport_extent = extent_to_geo_array(QgsRectangle(*self.extent), geo_crs, hazard_crs) # Align raster extent and viewport # assuming they are both in the same projection raster_extent = H.dataProvider().extent() clip_xmin = raster_extent.xMinimum() # clip_xmax = raster_extent.xMaximum() clip_ymin = raster_extent.yMinimum() # clip_ymax = raster_extent.yMaximum() if viewport_extent[0] > clip_xmin: clip_xmin = viewport_extent[0] if viewport_extent[1] > clip_ymin: clip_ymin = viewport_extent[1] # TODO: Why have these two clauses when they are not used? # Commenting out for now. # if viewport_extent[2] < clip_xmax: # clip_xmax = viewport_extent[2] # if viewport_extent[3] < clip_ymax: # clip_ymax = viewport_extent[3] height = ((viewport_extent[3] - viewport_extent[1]) / H.rasterUnitsPerPixelY()) height = int(height) width = ((viewport_extent[2] - viewport_extent[0]) / H.rasterUnitsPerPixelX()) width = int(width) raster_extent = H.dataProvider().extent() xmin = raster_extent.xMinimum() xmax = raster_extent.xMaximum() ymin = raster_extent.yMinimum() ymax = raster_extent.yMaximum() x_delta = (xmax - xmin) / H.width() x = xmin for i in range(H.width()): if abs(x - clip_xmin) < x_delta: # We have found the aligned raster boundary break x += x_delta _ = i y_delta = (ymax - ymin) / H.height() y = ymin for i in range(H.width()): if abs(y - clip_ymin) < y_delta: # We have found the aligned raster boundary break y += y_delta clip_extent = [x, y, x + width * x_delta, y + height * y_delta] # Clip and polygonize small_raster = clip_raster(H, width, height, QgsRectangle(*clip_extent)) (flooded_polygon_inside, flooded_polygon_outside) = polygonize_gdal(small_raster, threshold_min, threshold_max) # Filter geometry and data using the extent extent = QgsRectangle(*self.extent) request = QgsFeatureRequest() request.setFilterRect(extent) if flooded_polygon_inside is None: message = tr( 'There are no objects in the hazard layer with "value">%s.' 'Please check the value or use other extent.' % (threshold_min, )) raise GetDataError(message) # reproject the flood polygons to exposure projection exposure_crs = E.crs() exposure_authid = exposure_crs.authid() if hazard_authid != exposure_authid: flooded_polygon_inside = reproject_vector_layer( flooded_polygon_inside, E.crs()) flooded_polygon_outside = reproject_vector_layer( flooded_polygon_outside, E.crs()) # Clip exposure by the extent # extent_as_polygon = QgsGeometry().fromRect(extent) # no need to clip since It is using a bbox request # line_layer = clip_by_polygon( # E, # extent_as_polygon # ) # Find inundated roads, mark them line_layer = split_by_polygon_in_out(E, flooded_polygon_inside, flooded_polygon_outside, target_field, 1, request) target_field_index = line_layer.dataProvider().\ fieldNameIndex(target_field) # Generate simple impact report epsg = get_utm_epsg(self.extent[0], self.extent[1]) output_crs = QgsCoordinateReferenceSystem(epsg) transform = QgsCoordinateTransform(E.crs(), output_crs) road_len = flooded_len = 0 # Length of roads roads_by_type = dict() # Length of flooded roads by types roads_data = line_layer.getFeatures() road_type_field_index = line_layer.fieldNameIndex(road_type_field) for road in roads_data: attributes = road.attributes() road_type = attributes[road_type_field_index] if road_type.__class__.__name__ == 'QPyNullVariant': road_type = tr('Other') geom = road.geometry() geom.transform(transform) length = geom.length() road_len += length if road_type not in roads_by_type: roads_by_type[road_type] = {'flooded': 0, 'total': 0} roads_by_type[road_type]['total'] += length if attributes[target_field_index] == 1: flooded_len += length roads_by_type[road_type]['flooded'] += length table_body = [ question, TableRow([ tr('Road Type'), tr('Flooded in the threshold (m)'), tr('Total (m)') ], header=True), TableRow([tr('All'), int(flooded_len), int(road_len)]) ] table_body.append(TableRow(tr('Breakdown by road type'), header=True)) for t, v in roads_by_type.iteritems(): table_body.append(TableRow([t, int(v['flooded']), int(v['total'])])) impact_summary = Table(table_body).toNewlineFreeString() map_title = tr('Roads inundated') style_classes = [ dict(label=tr('Not Inundated'), value=0, colour='#1EFC7C', transparency=0, size=0.5), dict(label=tr('Inundated'), value=1, colour='#F31A1C', transparency=0, size=0.5) ] style_info = dict(target_field=target_field, style_classes=style_classes, style_type='categorizedSymbol') # Convert QgsVectorLayer to inasafe layer and return it line_layer = Vector(data=line_layer, name=tr('Flooded roads'), keywords={ 'impact_summary': impact_summary, 'map_title': map_title, 'target_field': target_field }, style_info=style_info) return line_layer
def print_map(self, mode='pdf'): """Open impact report dialog that used define report options. :param mode: Mode for report - defaults to PDF. :type mode: """ # Check if selected layer is valid impact_layer = self.iface.activeLayer() if impact_layer is None: # noinspection PyCallByClass,PyTypeChecker,PyArgumentList QtGui.QMessageBox.warning( self.parent, self.tr('InaSAFE'), self.tr('Please select a valid impact layer before trying to ' 'print.')) return # Open Impact Report Dialog print_dialog = ImpactReportDialog(self.iface) print_dialog.button_ok = QtGui.QPushButton(self.tr('OK')) print_dialog.button_box.addButton(print_dialog.button_ok, QtGui.QDialogButtonBox.ActionRole) # noinspection PyUnresolvedReferences print_dialog.button_ok.clicked.connect(print_dialog.accept) print_dialog.button_save_pdf.hide() print_dialog.button_open_composer.hide() if not print_dialog.exec_() == QtGui.QDialog.Accepted: # noinspection PyTypeChecker self.show_dynamic_message( self, m.Message(m.Heading(self.tr('Map Creator'), **WARNING_STYLE), m.Text(self.tr('Report generation cancelled!')))) return # Get the extent of the map for report use_full_extent = print_dialog.analysis_extent_radio.isChecked() if use_full_extent: map_crs = self.iface.mapCanvas().mapRenderer().destinationCrs() layer_crs = self.iface.activeLayer().crs() layer_extent = self.iface.activeLayer().extent() if map_crs != layer_crs: # noinspection PyCallingNonCallable transform = QgsCoordinateTransform(layer_crs, map_crs) layer_extent = transform.transformBoundingBox(layer_extent) area_extent = layer_extent else: area_extent = self.iface.mapCanvas().extent() # Get selected template path to use if print_dialog.default_template_radio.isChecked(): template_path = print_dialog.template_combo.itemData( print_dialog.template_combo.currentIndex()) else: template_path = print_dialog.template_path.text() if not os.path.exists(template_path): # noinspection PyCallByClass,PyTypeChecker,PyArgumentList QtGui.QMessageBox.warning( self.parent, self.tr('InaSAFE'), self.tr('Please select a valid template before printing. ' 'The template you choose does not exist.')) return # Instantiate and prepare Report # noinspection PyTypeChecker self.show_dynamic_message( self, m.Message( m.Heading(self.tr('Map Creator'), **PROGRESS_UPDATE_STYLE), m.Text(self.tr('Preparing map and report')))) impact_report = ImpactReport(self.iface, template_path, impact_layer) impact_report.extent = area_extent # Get other setting settings = QSettings() logo_path = settings.value('inasafe/organisation_logo_path', '', type=str) impact_report.organisation_logo = logo_path disclaimer_text = settings.value('inasafe/reportDisclaimer', '', type=str) impact_report.disclaimer = disclaimer_text north_arrow_path = settings.value('inasafe/north_arrow_path', '', type=str) impact_report.north_arrow = north_arrow_path template_warning_verbose = bool( settings.value('inasafe/template_warning_verbose', True, type=bool)) # Check if there's missing elements needed in the template component_ids = [ 'safe-logo', 'north-arrow', 'organisation-logo', 'impact-map', 'impact-legend' ] impact_report.component_ids = component_ids if template_warning_verbose and \ len(impact_report.missing_elements) != 0: title = self.tr('Template is missing some elements') question = self.tr( 'The composer template you are printing to is missing ' 'these elements: %s. Do you still want to continue') % ( ', '.join(impact_report.missing_elements)) # noinspection PyCallByClass,PyTypeChecker answer = QtGui.QMessageBox.question( self.parent, title, question, QtGui.QMessageBox.Yes | QtGui.QMessageBox.No) if answer == QtGui.QMessageBox.No: return create_pdf_flag = bool(mode == 'pdf') self.show_busy() if create_pdf_flag: self.print_map_to_pdf(impact_report) else: self.open_map_in_composer(impact_report) self.hide_busy()
def processAlgorithm(self, parameters, context, feedback): source = self.parameterAsSource(parameters, self.INPUT, context) if source is None: raise QgsProcessingException( self.invalidSourceError(parameters, self.INPUT)) join_source = self.parameterAsSource(parameters, self.JOIN, context) if join_source is None: raise QgsProcessingException( self.invalidSourceError(parameters, self.JOIN)) join_fields = self.parameterAsFields(parameters, self.JOIN_FIELDS, context) discard_nomatch = self.parameterAsBool(parameters, self.DISCARD_NONMATCHING, context) summaries = [ self.statistics[i][0] for i in sorted( self.parameterAsEnums(parameters, self.SUMMARIES, context)) ] if not summaries: # none selected, so use all summaries = [s[0] for s in self.statistics] source_fields = source.fields() fields_to_join = QgsFields() join_field_indexes = [] if not join_fields: # no fields selected, use all join_fields = [ join_source.fields().at(i).name() for i in range(len(join_source.fields())) ] def addFieldKeepType(original, stat): """ Adds a field to the output, keeping the same data type as the original """ field = QgsField(original) field.setName(field.name() + '_' + stat) fields_to_join.append(field) def addField(original, stat, type): """ Adds a field to the output, with a specified type """ field = QgsField(original) field.setName(field.name() + '_' + stat) field.setType(type) if type == QVariant.Double: field.setLength(20) field.setPrecision(6) fields_to_join.append(field) numeric_fields = (('count', QVariant.Int, 'count'), ('unique', QVariant.Int, 'variety'), ('min', QVariant.Double, 'min'), ('max', QVariant.Double, 'max'), ('range', QVariant.Double, 'range'), ('sum', QVariant.Double, 'sum'), ('mean', QVariant.Double, 'mean'), ('median', QVariant.Double, 'median'), ('stddev', QVariant.Double, 'stDev'), ('minority', QVariant.Double, 'minority'), ('majority', QVariant.Double, 'majority'), ('q1', QVariant.Double, 'firstQuartile'), ('q3', QVariant.Double, 'thirdQuartile'), ('iqr', QVariant.Double, 'interQuartileRange')) datetime_fields = (('count', QVariant.Int, 'count'), ('unique', QVariant.Int, 'countDistinct'), ('empty', QVariant.Int, 'countMissing'), ('filled', QVariant.Int), ('min', None), ('max', None)) string_fields = (('count', QVariant.Int, 'count'), ('unique', QVariant.Int, 'countDistinct'), ('empty', QVariant.Int, 'countMissing'), ('filled', QVariant.Int), ('min', None, 'min'), ('max', None, 'max'), ('min_length', QVariant.Int, 'minLength'), ('max_length', QVariant.Int, 'maxLength'), ('mean_length', QVariant.Double, 'meanLength')) field_types = [] for f in join_fields: idx = join_source.fields().lookupField(f) if idx >= 0: join_field_indexes.append(idx) join_field = join_source.fields().at(idx) if join_field.isNumeric(): field_types.append('numeric') field_list = numeric_fields elif join_field.type() in (QVariant.Date, QVariant.Time, QVariant.DateTime): field_types.append('datetime') field_list = datetime_fields else: field_types.append('string') field_list = string_fields for f in field_list: if f[0] in summaries: if f[1] is not None: addField(join_field, f[0], f[1]) else: addFieldKeepType(join_field, f[0]) out_fields = QgsProcessingUtils.combineFields(source_fields, fields_to_join) (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, out_fields, source.wkbType(), source.sourceCrs()) if sink is None: raise QgsProcessingException( self.invalidSinkError(parameters, self.OUTPUT)) # do the join predicates = [ self.predicates[i][0] for i in self.parameterAsEnums(parameters, self.PREDICATE, context) ] features = source.getFeatures() total = 100.0 / source.featureCount() if source.featureCount() else 0 # bounding box transform bbox_transform = QgsCoordinateTransform(source.sourceCrs(), join_source.sourceCrs(), context.project()) for current, f in enumerate(features): if feedback.isCanceled(): break if not f.hasGeometry(): if not discard_nomatch: # ensure consistent count of attributes - otherwise non matching # features will have incorrect attribute length # and provider may reject them attrs = f.attributes() if len(attrs) < len(out_fields): attrs += [NULL] * (len(out_fields) - len(attrs)) f.setAttributes(attrs) sink.addFeature(f, QgsFeatureSink.FastInsert) continue bbox = bbox_transform.transformBoundingBox( f.geometry().boundingBox()) engine = None values = [] request = QgsFeatureRequest().setFilterRect( bbox).setSubsetOfAttributes( join_field_indexes).setDestinationCrs( source.sourceCrs(), context.transformContext()) for test_feat in join_source.getFeatures(request): if feedback.isCanceled(): break join_attributes = [] for a in join_field_indexes: join_attributes.append(test_feat.attributes()[a]) if engine is None: engine = QgsGeometry.createGeometryEngine( f.geometry().constGet()) engine.prepareGeometry() for predicate in predicates: if getattr(engine, predicate)(test_feat.geometry().constGet()): values.append(join_attributes) break feedback.setProgress(int(current * total)) if len(values) == 0: if discard_nomatch: continue else: # ensure consistent count of attributes - otherwise non matching # features will have incorrect attribute length # and provider may reject them attrs = f.attributes() if len(attrs) < len(out_fields): attrs += [NULL] * (len(out_fields) - len(attrs)) f.setAttributes(attrs) sink.addFeature(f, QgsFeatureSink.FastInsert) else: attrs = f.attributes() for i in range(len(join_field_indexes)): attribute_values = [v[i] for v in values] field_type = field_types[i] if field_type == 'numeric': stat = QgsStatisticalSummary() for v in attribute_values: stat.addVariant(v) stat.finalize() for s in numeric_fields: if s[0] in summaries: attrs.append(getattr(stat, s[2])()) elif field_type == 'datetime': stat = QgsDateTimeStatisticalSummary() stat.calculate(attribute_values) for s in datetime_fields: if s[0] in summaries: if s[0] == 'filled': attrs.append(stat.count() - stat.countMissing()) elif s[0] == 'min': attrs.append( stat.statistic( QgsDateTimeStatisticalSummary.Min)) elif s[0] == 'max': attrs.append( stat.statistic( QgsDateTimeStatisticalSummary.Max)) else: attrs.append(getattr(stat, s[2])()) else: stat = QgsStringStatisticalSummary() for v in attribute_values: if v == NULL: stat.addString('') else: stat.addString(str(v)) stat.finalize() for s in string_fields: if s[0] in summaries: if s[0] == 'filled': attrs.append(stat.count() - stat.countMissing()) else: attrs.append(getattr(stat, s[2])()) f.setAttributes(attrs) sink.addFeature(f, QgsFeatureSink.FastInsert) return {self.OUTPUT: dest_id}