def testOverwriteGPKG(self): """Test that overwriting the same origin GPKG file works only if the layername is different""" # Prepare test data ml = QgsVectorLayer('Point?field=firstfield:int&field=secondfield:int', 'test', 'memory') provider = ml.dataProvider() ft = QgsFeature() ft.setAttributes([4, -10]) provider.addFeatures([ft]) filehandle, filename = tempfile.mkstemp('.gpkg') options = QgsVectorFileWriter.SaveVectorOptions() options.driverName = 'GPKG' options.layerName = 'test' write_result, error_message = QgsVectorFileWriter.writeAsVectorFormat( ml, filename, options) self.assertEqual(write_result, QgsVectorFileWriter.NoError, error_message) # Real test vl = QgsVectorLayer("%s|layername=test" % filename, 'src_test', 'ogr') self.assertTrue(vl.isValid()) self.assertEqual(vl.featureCount(), 1) # This must fail write_result, error_message = QgsVectorFileWriter.writeAsVectorFormat( vl, filename, options) self.assertEqual(write_result, QgsVectorFileWriter.ErrCreateDataSource) self.assertEqual(error_message, 'Cannot overwrite a OGR layer in place') options.layerName = 'test2' write_result, error_message = QgsVectorFileWriter.writeAsVectorFormat( vl, filename, options) self.assertEqual(write_result, QgsVectorFileWriter.NoError, error_message)
def save_as_dbf(self, raster_source) -> bool: """Save/export a copy of the RAT to path""" # Add .VAT: .DBF is added by the exporter if not raster_source.upper().endswith('.VAT'): raster_source = raster_source + '.vat' options = QgsVectorFileWriter.SaveVectorOptions() options.driverName = 'ESRI Shapefile' options.layerOptions = ['SHPT=NULL'] writer = QgsVectorFileWriter.create(raster_source, self.qgis_fields(), QgsWkbTypes.Unknown, QgsCoordinateReferenceSystem(), QgsCoordinateTransformContext(), options) self.path = raster_source + '.dbf' rat_log('RAT saved as DBF for layer %s' % raster_source) return writer.addFeatures(self.qgis_features())
def testWriterWithExtent(self): """Check writing using extent filter.""" source_file = os.path.join(TEST_DATA_DIR, 'points.shp') source_layer = QgsVectorLayer(source_file, 'Points', 'ogr') self.assertTrue(source_layer.isValid()) options = QgsVectorFileWriter.SaveVectorOptions() options.driverName = 'ESRI Shapefile' options.filterExtent = QgsRectangle(-111, 26, -96, 38) dest_file_name = os.path.join(str(QDir.tempPath()), 'extent_no_transform.shp') write_result, error_message = QgsVectorFileWriter.writeAsVectorFormat( source_layer, dest_file_name, options) self.assertEqual(write_result, QgsVectorFileWriter.NoError, error_message) # Open result and check created_layer = QgsVectorLayer('{}|layerid=0'.format(dest_file_name), 'test', 'ogr') features = [f for f in created_layer.getFeatures()] self.assertEqual(len(features), 5) for f in features: self.assertTrue(f.geometry().intersects(options.filterExtent))
def testWriterWithExtentAndReprojection(self): """Check writing using extent filter with reprojection.""" source_file = os.path.join(TEST_DATA_DIR, 'points.shp') source_layer = QgsVectorLayer(source_file, 'Points', 'ogr') self.assertTrue(source_layer.isValid()) options = QgsVectorFileWriter.SaveVectorOptions() options.driverName = 'ESRI Shapefile' options.filterExtent = QgsRectangle(-12511460, 3045157, -10646621, 4683497) options.ct = QgsCoordinateTransform(source_layer.crs(), QgsCoordinateReferenceSystem.fromEpsgId(3785), QgsProject.instance()) dest_file_name = os.path.join(str(QDir.tempPath()), 'extent_transform.shp') write_result, error_message = QgsVectorFileWriter.writeAsVectorFormat( source_layer, dest_file_name, options) self.assertEqual(write_result, QgsVectorFileWriter.NoError, error_message) # Open result and check created_layer = QgsVectorLayer('{}|layerid=0'.format(dest_file_name), 'test', 'ogr') features = [f for f in created_layer.getFeatures()] self.assertEqual(len(features), 5) for f in features: self.assertTrue(f.geometry().intersects(options.filterExtent))
def memoryToShp(layer, scheme, layerName): ln = layerName.replace('/', '-').replace('\\', '-').replace( '>', '-').replace('<', '-').replace(' ', '')[:MAX_FILE_NAME_SIZE - 26] layerFile = u'{0}_{1}'.format( ln, time.strftime('%d_%m_%Y_%H_%M_%S', time.localtime())) (prjPath, prjExt) = os.path.splitext(QgsProject.instance().fileName()) layerFileName = prjPath + u'_{0}.gpkg'.format(scheme) options = QgsVectorFileWriter.SaveVectorOptions() if os.path.exists(layerFileName): options.actionOnExistingFile = QgsVectorFileWriter.CreateOrOverwriteLayer options.layerName = layerFile _writer = QgsVectorFileWriter.writeAsVectorFormat(layer, layerFileName, options) if _writer and _writer[0] > 0: QgsMessageLog.logMessage( u"Layer write:{} error: {}".format(layer.name(), _writer), "QgisPDS.error", Qgis.Critical) layerName = createLayerName(layerName) layerFileName = layerFileName + '|layername=' + layerFile return QgsVectorLayer(layerFileName, layerName, 'ogr')
def testCreateDGN(self): ml = QgsVectorLayer('Point?crs=epsg:4326', 'test', 'memory') provider = ml.dataProvider() feat = QgsFeature() feat.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(10, 10))) provider.addFeatures([feat]) filename = os.path.join(str(QDir.tempPath()), 'testCreateDGN.dgn') crs = QgsCoordinateReferenceSystem() crs.createFromId(4326, QgsCoordinateReferenceSystem.EpsgCrsId) rc, errmsg = QgsVectorFileWriter.writeAsVectorFormat(ml, filename, 'utf-8', crs, 'DGN') # open the resulting file vl = QgsVectorLayer(filename, '', 'ogr') self.assertTrue(vl.isValid()) self.assertEqual(vl.featureCount(), 1) del vl # append options = QgsVectorFileWriter.SaveVectorOptions() options.driverName = 'DGN' options.layerName = 'test' options.actionOnExistingFile = QgsVectorFileWriter.AppendToLayerNoNewFields write_result, error_message = QgsVectorFileWriter.writeAsVectorFormat( ml, filename, options) self.assertEqual(write_result, QgsVectorFileWriter.NoError, error_message) # open the resulting file vl = QgsVectorLayer(filename, '', 'ogr') self.assertTrue(vl.isValid()) self.assertEqual(vl.featureCount(), 2) del vl os.unlink(filename)
def _save_layer_to_file(layer: QgsVectorLayer, output_path: Path) -> Path: """ Save layer to file""" output_file = output_path / f'{layer.name().replace(" ", "")}.csv' LOGGER.debug(f'Saving layer to a file {output_file.name}') converter = CsvFieldValueConverter(layer) options = QgsVectorFileWriter.SaveVectorOptions() options.driverName = "csv" options.fileEncoding = "utf-8" options.layerOptions = ["SEPARATOR=COMMA"] options.fieldValueConverter = converter # noinspection PyCallByClass writer_, msg = QgsVectorFileWriter.writeAsVectorFormatV2( layer, str(output_file), QgsProject.instance().transformContext(), options) if msg: raise ProcessInterruptedException( tr('Process ended'), bar_msg=bar_msg( tr('Exception occurred during data extraction: {}', msg))) return output_file
def create_geopackage_table(geometry_type, table_name, geopackage_path, full_path, field_tuple_list): """ Creates tables in existing or new geopackages geometry_type (string): NoGeometry, Polygon, Linestring, Point, etc... table_name (string): Name for the new table geopackage_path (string): full path to the geopackage i.e., dir/package.gpkg full_path (string): full path including the layer i.e., dir/package.gpkg|layername=layer field_tuple_list (list): a list of tuples as field name and QVariant field types i.e., [('my_field', QVarient.Double)] """ memory_layer = QgsVectorLayer(geometry_type, "memory_layer", "memory") fields = [] for field_tuple in field_tuple_list: field = QgsField(field_tuple[0], field_tuple[1]) fields.append(field) memory_layer.dataProvider().addAttributes(fields) memory_layer.updateFields() options = QgsVectorFileWriter.SaveVectorOptions() options.layerName = table_name options.driverName = 'GPKG' if os.path.exists(geopackage_path): options.actionOnExistingFile = QgsVectorFileWriter.CreateOrOverwriteLayer QgsVectorFileWriter.writeAsVectorFormat(memory_layer, geopackage_path, options)
def testSetBufferedGroupsAfterAutomaticGroups(self): ml = QgsVectorLayer( 'Point?crs=epsg:4326&field=int:integer&field=int2:integer', 'test', 'memory') # Load 2 layer from a geopackage d = QTemporaryDir() options = QgsVectorFileWriter.SaveVectorOptions() options.driverName = 'GPKG' options.layerName = 'layer_a' err, msg, newFileName, newLayer = QgsVectorFileWriter.writeAsVectorFormatV3( ml, os.path.join(d.path(), 'test_EditBufferGroup.gpkg'), QgsCoordinateTransformContext(), options) options.layerName = 'layer_b' options.actionOnExistingFile = QgsVectorFileWriter.CreateOrOverwriteLayer err, msg, newFileName, newLayer = QgsVectorFileWriter.writeAsVectorFormatV3( ml, os.path.join(d.path(), 'test_EditBufferGroup.gpkg'), QgsCoordinateTransformContext(), options) layer_a = QgsVectorLayer(newFileName + '|layername=layer_a') self.assertTrue(layer_a.isValid()) layer_b = QgsVectorLayer(newFileName + '|layername=layer_b') self.assertTrue(layer_b.isValid()) project = QgsProject() project.addMapLayers([layer_a, layer_b, ml]) project.setTransactionMode(Qgis.TransactionMode.AutomaticGroups) project.setTransactionMode(Qgis.TransactionMode.BufferedGroups) project.startEditing() success, rollbackErrors = project.rollBack(True) self.assertTrue(success)
def response_shp_mode(self, request): """ Download Shapefile of data :param request: Http Django request object :return: http response with attached file """ if not self.layer.download: return HttpResponseForbidden() tmp_dir = tempfile.TemporaryDirectory() filename = self._build_download_filename(request) # Apply filter backends, store original subset string qgs_request = QgsFeatureRequest() original_subset_string = self.metadata_layer.qgis_layer.subsetString() if hasattr(self, 'filter_backends'): for backend in self.filter_backends: backend().apply_filter(request, self.metadata_layer, qgs_request, self) save_options = QgsVectorFileWriter.SaveVectorOptions() save_options.driverName = 'ESRI Shapefile' save_options.fileEncoding = 'utf-8' # Make a selection based on the request self._selection_responde_download_mode(qgs_request, save_options) file_path = os.path.join(tmp_dir.name, filename) error_code, error_message = QgsVectorFileWriter.writeAsVectorFormatV2( self.metadata_layer.qgis_layer, file_path, self.metadata_layer.qgis_layer.transformContext(), save_options ) # Restore the original subset string and select no features self.metadata_layer.qgis_layer.selectByIds([]) self.metadata_layer.qgis_layer.setSubsetString(original_subset_string) if error_code != QgsVectorFileWriter.NoError: tmp_dir.cleanup() return HttpResponse(status=500, reason=error_message) # Check for extra fields to add self._add_extrafields(request, file_path, filename) filenames = ["{}{}".format(filename, ftype) for ftype in self.shp_extentions] zip_filename = "{}.zip".format(filename) # Open BytesIO to grab in-memory ZIP contents s = io.BytesIO() # The zip compressor zf = zipfile.ZipFile(s, "w") for fpath in filenames: # Add file, at correct path ftoadd = os.path.join(tmp_dir.name, fpath) if os.path.exists(ftoadd): zf.write(ftoadd, fpath) # Must close zip for all contents to be written zf.close() tmp_dir.cleanup() # Grab ZIP file from in-memory, make response with correct MIME-type response = HttpResponse( s.getvalue(), content_type="application/x-zip-compressed") response['Content-Disposition'] = 'attachment; filename=%s' % zip_filename response.set_cookie('fileDownload', 'true') return response
def to_layer(features, crs, encoding, geom_type, layer_type, path): first_feat = features[0] fields = first_feat.fields() layer = None if layer_type == 'memory': layer = QgsVectorLayer(geom_type + '?crs=' + crs.authid(), path, "memory") pr = layer.dataProvider() pr.addAttributes(fields.toList()) layer.updateFields() layer.startEditing() pr.addFeatures(features) layer.commitChanges() elif layer_type == 'shapefile': wkbTypes = { 'Point': QgsWkbTypes.Point, 'Linestring': QgsWkbTypes.LineString, 'Polygon': QgsWkbTypes.Polygon } options = QgsVectorFileWriter.SaveVectorOptions() options.driverName = "ESRI Shapefile" options.fileEncoding = encoding file_writer = QgsVectorFileWriter.create( path, fields, wkbTypes[geom_type], crs, QgsCoordinateTransformContext(), options) if file_writer.hasError() != QgsVectorFileWriter.NoError: print("Error when creating shapefile: ", file_writer.errorMessage()) del file_writer layer = QgsVectorLayer(path, ntpath.basename(path)[:-4], "ogr") pr = layer.dataProvider() layer.startEditing() pr.addFeatures(features) layer.commitChanges() elif layer_type == 'postgis': # this is needed to load the table later # uri = connstring + """ type=""" + geom_types[geom_type] + """ table=\"""" + schema_name + """\".\"""" + table_name + """\" (geom) """ connstring, schema_name, table_name = path uri = connstring + """ type=""" + geom_type + """ table=\"""" + schema_name + """\".\"""" + table_name + """\" (geom) """ crs_id = crs.postgisSrid() try: con = psycopg2.connect(connstring) cur = con.cursor() create_query = cur.mogrify( """DROP TABLE IF EXISTS "%s"."%s"; CREATE TABLE "%s"."%s"( geom geometry(%s, %s))""", (AsIs(schema_name), AsIs(table_name), AsIs(schema_name), AsIs(table_name), geom_type, AsIs(crs_id))) cur.execute(create_query) con.commit() post_q_flds = { 2: 'bigint', 6: 'numeric', 1: 'bool', 'else': 'text', 4: 'numeric' } for f in fields: f_type = f.type() if f_type not in [2, 6, 1]: f_type = 'else' attr_query = cur.mogrify( """ALTER TABLE "%s"."%s" ADD COLUMN "%s" %s""", (AsIs(schema_name), AsIs(table_name), AsIs( f.name()), AsIs(post_q_flds[f_type]))) cur.execute(attr_query) con.commit() field_names = ",".join(['"' + f.name() + '"' for f in fields]) for feature in features: attrs = [i if i else None for i in feature.attributes()] insert_query = cur.mogrify( """INSERT INTO "%s"."%s" (%s, geom) VALUES %s, ST_GeomFromText(%s,%s))""", (AsIs(schema_name), AsIs(table_name), AsIs(field_names), tuple(attrs), feature.geometry().asWkt(), AsIs(crs_id))) idx = insert_query.find(b', ST_GeomFromText') - 1 insert_query = insert_query[:idx] + insert_query[(idx + 1):] cur.execute(insert_query) con.commit() pkey_query = cur.mogrify( """ALTER TABLE "%s"."%s" DROP COLUMN IF EXISTS seg_id; ALTER TABLE "%s"."%s" ADD COLUMN seg_id serial PRIMARY KEY NOT NULL;""", (AsIs(schema_name), AsIs(table_name), AsIs(schema_name), AsIs(table_name))) cur.execute(pkey_query) con.commit() con.close() layer = QgsVectorLayer(uri, table_name, 'postgres') except psycopg2.DatabaseError as e: # fix_print_with_import print(e) return layer
def read_mf_recharge_dates(self): QSWATMOD_path_dict = self.dirs_and_paths() stdate, eddate, stdate_warmup, eddate_warmup = self.define_sim_period() wd = QSWATMOD_path_dict['SMfolder'] startDate = stdate.strftime("%m-%d-%Y") # Create swatmf_results tree inside root = QgsProject.instance().layerTreeRoot() if root.findGroup("swatmf_results"): swatmf_results = root.findGroup("swatmf_results") else: swatmf_results = root.insertGroup(0, "swatmf_results") input1 = QgsProject.instance().mapLayersByName("mf_grid (MODFLOW)")[0] provider = input1.dataProvider() if self.dlg.checkBox_recharge.isChecked() and self.dlg.radioButton_mf_results_d.isChecked(): filename = "swatmf_out_MF_recharge" # Open "swatmf_out_MF_recharge" file y = ("MODFLOW", "--Calculated", "daily") # Remove unnecssary lines with open(os.path.join(wd, filename), "r") as f: data = [x.strip() for x in f if x.strip() and not x.strip().startswith(y)] # Remove blank lines date = [x.strip().split() for x in data if x.strip().startswith("Day:")] # Collect only lines with dates onlyDate = [x[1] for x in date] # Only date # data1 = [x.split() for x in data] # make each line a list sdate = datetime.datetime.strptime(startDate, "%m-%d-%Y") # Change startDate format dateList = [(sdate + datetime.timedelta(days = int(i)-1)).strftime("%m-%d-%Y") for i in onlyDate] self.dlg.comboBox_mf_results_sdate.clear() self.dlg.comboBox_mf_results_sdate.addItems(dateList) self.dlg.comboBox_mf_results_edate.clear() self.dlg.comboBox_mf_results_edate.addItems(dateList) self.dlg.comboBox_mf_results_edate.setCurrentIndex(len(dateList)-1) # Copy mf_grid shapefile to swatmf_results tree name_ext = "mf_rch_daily.gpkg" output_dir = QSWATMOD_path_dict['SMshps'] # Check if there is an exsting mf_recharge shapefile if not any(lyr.name() == ("mf_rch_daily") for lyr in list(QgsProject.instance().mapLayers().values())): mf_rch_shapfile = os.path.join(output_dir, name_ext) # overwrite option options = QgsVectorFileWriter.SaveVectorOptions() options.actionOnExistingFile = QgsVectorFileWriter.CreateOrOverwriteFile QgsVectorFileWriter.writeAsVectorFormat(input1, mf_rch_shapfile, options) layer = QgsVectorLayer(mf_rch_shapfile, '{0}'.format("mf_rch_daily"), 'ogr') # Put in the group root = QgsProject.instance().layerTreeRoot() swatmf_results = root.findGroup("swatmf_results") QgsProject.instance().addMapLayer(layer, False) swatmf_results.insertChildNode(0, QgsLayerTreeLayer(layer)) msgBox = QMessageBox() msgBox.setWindowIcon(QtGui.QIcon(':/QSWATMOD2/pics/sm_icon.png')) msgBox.setWindowTitle("Created!") msgBox.setText("'mf_rch_daily.gpkg' file is created in 'swatmf_results' group!") msgBox.exec_() elif self.dlg.checkBox_recharge.isChecked() and self.dlg.radioButton_mf_results_m.isChecked(): filename = "swatmf_out_MF_recharge_monthly" # Open "swatmf_out_MF_recharge" file y = ("Monthly") # Remove unnecssary lines with open(os.path.join(wd, filename), "r") as f: data = [x.strip() for x in f if x.strip() and not x.strip().startswith(y)] # Remove blank lines date = [x.strip().split() for x in data if x.strip().startswith("month:")] # Collect only lines with dates onlyDate = [x[1] for x in date] # Only date # data1 = [x.split() for x in data] # make each line a list dateList = pd.date_range(startDate, periods=len(onlyDate), freq='M').strftime("%b-%Y").tolist() self.dlg.comboBox_mf_results_sdate.clear() self.dlg.comboBox_mf_results_sdate.addItems(dateList) self.dlg.comboBox_mf_results_edate.clear() self.dlg.comboBox_mf_results_edate.addItems(dateList) self.dlg.comboBox_mf_results_edate.setCurrentIndex(len(dateList)-1) # Copy mf_grid shapefile to swatmf_results tree name_ext = "mf_rch_monthly.gpkg" output_dir = QSWATMOD_path_dict['SMshps'] # Check if there is an exsting mf_recharge shapefile if not any(lyr.name() == ("mf_rch_monthly") for lyr in list(QgsProject.instance().mapLayers().values())): mf_rch_shapfile = os.path.join(output_dir, name_ext) options = QgsVectorFileWriter.SaveVectorOptions() options.actionOnExistingFile = QgsVectorFileWriter.CreateOrOverwriteFile QgsVectorFileWriter.writeAsVectorFormat(input1, mf_rch_shapfile, options) layer = QgsVectorLayer(mf_rch_shapfile, '{0}'.format("mf_rch_monthly"), 'ogr') # Put in the group root = QgsProject.instance().layerTreeRoot() swatmf_results = root.findGroup("swatmf_results") QgsProject.instance().addMapLayer(layer, False) swatmf_results.insertChildNode(0, QgsLayerTreeLayer(layer)) msgBox = QMessageBox() msgBox.setWindowIcon(QtGui.QIcon(':/QSWATMOD2/pics/sm_icon.png')) msgBox.setWindowTitle("Created!") msgBox.setText("'mf_rch_monthly.gpkg' file is created in 'swatmf_results' group!") msgBox.exec_() elif self.dlg.checkBox_recharge.isChecked() and self.dlg.radioButton_mf_results_y.isChecked(): filename = "swatmf_out_MF_recharge_yearly" # Open "swatmf_out_MF_recharge" file y = ("Yearly") # Remove unnecssary lines with open(os.path.join(wd, filename), "r") as f: data = [x.strip() for x in f if x.strip() and not x.strip().startswith(y)] # Remove blank lines date = [x.strip().split() for x in data if x.strip().startswith("year:")] # Collect only lines with dates onlyDate = [x[1] for x in date] # Only date # data1 = [x.split() for x in data] # make each line a list dateList = pd.date_range(startDate, periods = len(onlyDate), freq = 'A').strftime("%Y").tolist() self.dlg.comboBox_mf_results_sdate.clear() self.dlg.comboBox_mf_results_sdate.addItems(dateList) self.dlg.comboBox_mf_results_edate.clear() self.dlg.comboBox_mf_results_edate.addItems(dateList) self.dlg.comboBox_mf_results_edate.setCurrentIndex(len(dateList)-1) # Copy mf_grid shapefile to swatmf_results tree name_ext = "mf_rch_yearly.gpkg" output_dir = QSWATMOD_path_dict['SMshps'] # Check if there is an exsting mf_recharge shapefile if not any(lyr.name() == ("mf_rch_yearly") for lyr in list(QgsProject.instance().mapLayers().values())): mf_rch_shapfile = os.path.join(output_dir, name_ext) options = QgsVectorFileWriter.SaveVectorOptions() options.actionOnExistingFile = QgsVectorFileWriter.CreateOrOverwriteFile QgsVectorFileWriter.writeAsVectorFormat(input1, mf_rch_shapfile, options) layer = QgsVectorLayer(mf_rch_shapfile, '{0}'.format("mf_rch_yearly"), 'ogr') # Put in the group root = QgsProject.instance().layerTreeRoot() swatmf_results = root.findGroup("swatmf_results") QgsProject.instance().addMapLayer(layer, False) swatmf_results.insertChildNode(0, QgsLayerTreeLayer(layer)) msgBox = QMessageBox() msgBox.setWindowIcon(QtGui.QIcon(':/QSWATMOD2/pics/sm_icon.png')) msgBox.setWindowTitle("Created!") msgBox.setText("'mf_rch_yearly.gpkg' file is created in 'swatmf_results' group!") msgBox.exec_() else: self.dlg.comboBox_mf_results_sdate.clear()
def on_btnRun_clicked(self): if self.inputfile == '': QMessageBox.critical(self,'Map Creator', 'Please specify input coordinate file.') return if self.outputfile == '': QMessageBox.critical(self,'Map Creator', 'Please specify output shapefile.') return self.setCursor(Qt.WaitCursor) #Open coordinate input file f = open(self.inputfile, 'r') lines = f.readlines() f.close() header = lines[0].split(',')[0] totfeat = len(lines) - 1 lines.pop(0) lines.reverse() #Create vector layer basename = os.path.basename(self.outputfile) vlayer = QgsVectorLayer("Polygon", basename, "memory") vprovider = vlayer.dataProvider() fld = QgsField(header,QVariant.String) flds = QgsFields() flds.append(fld) vprovider.addAttributes([fld]) vlayer.startEditing() hull = [] for cnt, line in enumerate(lines): line = line.rstrip().split(',') numcoords = int((len(line) - 1) / 2) hull[:] = [] geom = QgsGeometry() feat = QgsFeature() feat.setFields(flds) for i in range(numcoords): hull.append(QgsPointXY(float(line[i*2+1]),float(line[i*2+2]))) geom = geom.fromMultiPointXY(hull) geom = geom.convexHull() feat.setGeometry(geom) feat.setAttribute(header,str(line[0])) result = vlayer.addFeature(feat) if not result: self.setCursor(Qt.ArrowCursor) QMessageBox.critical(self,'Map Creator', 'Processing error.') return self.ui.ProgressBar.setValue(float(cnt+1)/float(totfeat) * 100.0) QApplication.processEvents() vlayer.commitChanges() vlayer.updateExtents() #Write the output shapefile if os.path.exists(self.outputfile): QgsVectorFileWriter.deleteShapeFile(self.outputfile) voptions = QgsVectorFileWriter.SaveVectorOptions() voptions.driverName = 'ESRI Shapefile' voptions.fileEncoding = 'utf-8' result = QgsVectorFileWriter.writeAsVectorFormat(vlayer, self.outputfile, voptions) if result[0] != 0: QMessageBox.critical(self,'Map Creator','Error creating shapefile.') else: #Ask to add shapfile to map name = QFileInfo(self.outputfile).completeBaseName() result = QMessageBox.question(self,'Map Creator', 'Add shapefile to map?', QMessageBox.Yes, QMessageBox.No) if result == QMessageBox.Yes: self.iface.addVectorLayer(self.outputfile, name, 'ogr') self.setCursor(Qt.ArrowCursor)
def raster_to_polygon(raster_path, out_gpkg, out_layer_name, raster_value, surface_name='valley bottom'): # raster_layer = QgsRasterLayer(raster_path, 'in_raster') out_polygon_path = os.path.join(out_gpkg, out_layer_name) # --------- PROCESSING ------------- # -- DEM -- tempdir = tempfile.TemporaryDirectory() temp_raster = os.path.join(tempdir.name, "less_than.tif") gp_calc = processing.run('gdal:rastercalculator', {'INPUT_A': raster_path, 'BAND_A': 1, 'FORMULA': f'(A <= {raster_value})', 'OUTPUT': temp_raster}) # 'raster'}) # raster_less_than = gp_calc['OUTPUT'] raster_less_than = QgsRasterLayer(gp_calc['OUTPUT']) # -- DEM to VECTOR -- gp_raw = processing.run("gdal:polygonize", {'INPUT': raster_less_than, 'BAND': 1, 'FIELD': 'DN', 'EIGHT_CONNECTEDNESS': False, 'EXTRA': '', 'OUTPUT': 'TEMPORARY_OUTPUT'}) raw_vector = QgsVectorLayer( gp_raw['OUTPUT'], "raw_vectors", "ogr") # TODO remove when done # QgsProject.instance().addMapLayer(raw_vector) # -- CALCULATE AREA -- # create a provider pv = raw_vector.dataProvider() # add the attribute and update pv.addAttributes([QgsField('raw_area_m', QVariant.Int), QgsField( 'max_elev_m', QVariant.Double), QgsField('surface_name', QVariant.String)]) raw_vector.updateFields() # Create a context and scope context = QgsExpressionContext() context.appendScopes( QgsExpressionContextUtils.globalProjectLayerScopes(raw_vector)) # Loop through and add the areas delete_features = [] with edit(raw_vector): # loop them for feature in raw_vector.getFeatures(): if feature['DN'] != 1: delete_features.append(feature.id()) else: context.setFeature(feature) feature['raw_area_m'] = QgsExpression('$area').evaluate(context) feature['max_elev_m'] = raster_value feature['surface_name'] = surface_name raw_vector.updateFeature(feature) raw_vector.dataProvider().deleteFeatures(delete_features) # -- BUFFER POLYGONS -- gp_buffered = processing.run("native:buffer", {'INPUT': raw_vector, 'DISTANCE': 0.000001, 'SEGMENTS': 5, 'END_CAP_STYLE': 0, 'JOIN_STYLE': 0, 'MITER_LIMIT': 2, 'DISSOLVE': False, 'OUTPUT': 'TEMPORARY_OUTPUT'}) buffered_vector = gp_buffered['OUTPUT'] # TODO remove when final # QgsProject.instance().addMapLayer(buffered_vector) # -- Simplify Polygons -- gp_simple = processing.run("native:simplifygeometries", {'INPUT': buffered_vector, 'METHOD': 0, 'TOLERANCE': simplify_tolerance, 'OUTPUT': 'TEMPORARY_OUTPUT'}) simple_vector = gp_simple['OUTPUT'] # QgsVectorLayer( # gp_simplify['OUTPUT'], "simplified_polygons", 'ogr') # -- Smooth the polygons -- gp_smooth = processing.run("native:smoothgeometry", {'INPUT': simple_vector, 'ITERATIONS': 1, 'OFFSET': smoothing_offset, 'MAX_ANGLE': 180, 'OUTPUT': 'TEMPORARY_OUTPUT'}) smooth_vector = gp_smooth['OUTPUT'] # QgsVectorLayer( # , "smoothed_polygons", 'ogr') gp_multi = processing.run("native:multiparttosingleparts", {'INPUT': smooth_vector, 'OUTPUT': 'TEMPORARY_OUTPUT'}) multi_vector = gp_multi['OUTPUT'] # Fix any crossed geometry as final vector gp_fix = processing.run("native:fixgeometries", {'INPUT': multi_vector, 'OUTPUT': 'TEMPORARY_OUTPUT'}) final_vector = gp_fix['OUTPUT'] # Create a context and scope # Understand WTF this is?? context = QgsExpressionContext() context.appendScopes( QgsExpressionContextUtils.globalProjectLayerScopes(final_vector)) # add an area attribute # create a provider pv = final_vector.dataProvider() # add the attribute and update pv.addAttributes([QgsField('area_m', QVariant.Int)]) final_vector.updateFields() # Loop through and add the areas with edit(final_vector): # loop them for feature in final_vector.getFeatures(): context.setFeature(feature) feature['area_m'] = QgsExpression('$area').evaluate(context) final_vector.updateFeature(feature) # -- Delete Unneeded Fields -- pv = final_vector.dataProvider() pv.deleteAttributes([1, 2]) final_vector.updateFields() # -- Delete small polygons -- # data provider capabilities caps = final_vector.dataProvider().capabilities() # features and empty list of features to delete features = final_vector.getFeatures() delete_features = [] # if the layer can have deleted features if caps & QgsVectorDataProvider.DeleteFeatures: for feature in features: if feature['area_m'] <= polygon_min_size: delete_features.append(feature.id()) final_vector.dataProvider().deleteFeatures(delete_features) # TODO fix final data export options = QgsVectorFileWriter.SaveVectorOptions() options.layerName = out_layer_name options.driverName = 'GPKG' if os.path.exists(out_gpkg): options.actionOnExistingFile = QgsVectorFileWriter.CreateOrOverwriteLayer QgsVectorFileWriter.writeAsVectorFormat( final_vector, out_gpkg, options) # open the output layer QgsVectorLayer(out_polygon_path, out_layer_name, 'ogr')
def sendOutputFile(self, handler): format_dict = WFSFormats[self.format] # read the GML gml_path = join(self.tempdir, '{}.gml'.format(self.filename)) output_layer = QgsVectorLayer(gml_path, 'qgis_server_wfs_features', 'ogr') # Temporary file where to write the output temporary = QTemporaryFile( join(QDir.tempPath(), 'request-WFS-XXXXXX.{}'.format(format_dict['filenameExt']))) temporary.open() output_file = temporary.fileName() temporary.close() if output_layer.isValid(): try: # create save options options = QgsVectorFileWriter.SaveVectorOptions() # driver name options.driverName = format_dict['ogrProvider'] # file encoding options.fileEncoding = 'utf-8' # coordinate transformation if format_dict['forceCRS']: options.ct = QgsCoordinateTransform( output_layer.crs(), QgsCoordinateReferenceSystem(format_dict['forceCRS']), QgsProject.instance()) # datasource options if format_dict['ogrDatasourceOptions']: options.datasourceOptions = format_dict[ 'ogrDatasourceOptions'] # write file if Qgis.QGIS_VERSION_INT >= 31003: write_result, error_message = QgsVectorFileWriter.writeAsVectorFormatV2( output_layer, output_file, QgsProject.instance().transformContext(), options) else: write_result, error_message = QgsVectorFileWriter.writeAsVectorFormat( output_layer, output_file, options) if write_result != QgsVectorFileWriter.NoError: handler.appendBody(b'') self.logger.critical(error_message) return False except Exception as e: handler.appendBody(b'') self.logger.critical(str(e)) return False if format_dict['zip']: # compress files import zipfile # noinspection PyBroadException try: import zlib # NOQA compression = zipfile.ZIP_DEFLATED except Exception: compression = zipfile.ZIP_STORED # create the zip file zip_file_path = join(self.tempdir, '%s.zip' % self.filename) with zipfile.ZipFile(zip_file_path, 'w') as zf: # add all files zf.write(join( self.tempdir, '%s.%s' % (self.filename, format_dict['filenameExt'])), compress_type=compression, arcname='%s.%s' % (self.typename, format_dict['filenameExt'])) for e in format_dict['extToZip']: file_path = join(self.tempdir, '%s.%s' % (self.filename, e)) if exists(file_path): zf.write(file_path, compress_type=compression, arcname='%s.%s' % (self.typename, e)) zf.close() f = QFile(zip_file_path) if f.open(QFile.ReadOnly): ba = f.readAll() handler.appendBody(ba) return True else: # return the file created without zip f = QFile(output_file) if f.open(QFile.ReadOnly): ba = f.readAll() handler.appendBody(ba) return True handler.appendBody(b'') self.logger.critical('Error no output file') return False
def exportLayer(layer, fields=None, to_shapefile=False, path=None, force=False, logger=None): logger = logger or feedback filepath, _, ext = lyr_utils.getLayerSourceInfo(layer) lyr_name, safe_name = lyr_utils.getLayerTitleAndName(layer) fields = fields or [] if layer.type() == layer.VectorLayer: if to_shapefile and (force or layer.fields().count() != len(fields) or ext != EXT_SHAPEFILE): # Export with Shapefile extension ext = EXT_SHAPEFILE elif force or ext != EXT_GEOPACKAGE or layer.fields().count() != len(fields) \ or not isSingleTableGpkg(filepath): # Export with GeoPackage extension ext = EXT_GEOPACKAGE else: # No need to export logger.logInfo( f"No need to export layer {lyr_name} stored at {filepath}") return filepath # Perform GeoPackage or Shapefile export attrs = [ i for i, f in enumerate(layer.fields()) if len(fields) == 0 or f.name() in fields ] output = path or tempFileInSubFolder(safe_name + ext) encoding = "UTF-8" driver = "ESRI Shapefile" if ext == EXT_SHAPEFILE else "GPKG" options = None if hasattr(QgsVectorFileWriter, 'SaveVectorOptions'): # QGIS v3.x has the SaveVectorOptions object options = QgsVectorFileWriter.SaveVectorOptions() options.fileEncoding = encoding options.attributes = attrs options.driverName = driver # Make sure that we are using the latest (non-deprecated) write method if hasattr(QgsVectorFileWriter, 'writeAsVectorFormatV3'): # Use writeAsVectorFormatV3 for QGIS versions >= 3.20 to avoid DeprecationWarnings result = QgsVectorFileWriter.writeAsVectorFormatV3( layer, output, QgsCoordinateTransformContext(), options) # noqa elif hasattr(QgsVectorFileWriter, 'writeAsVectorFormatV2'): # Use writeAsVectorFormatV2 for QGIS versions >= 3.10.3 to avoid DeprecationWarnings result = QgsVectorFileWriter.writeAsVectorFormatV2( layer, output, QgsCoordinateTransformContext(), options) # noqa else: # Use writeAsVectorFormat for QGIS versions < 3.10.3 for backwards compatibility result = QgsVectorFileWriter.writeAsVectorFormat( layer, output, fileEncoding=encoding, attributes=attrs, driverName=driver) # noqa # Check if first item in result tuple is an error code if result[0] == QgsVectorFileWriter.NoError: logger.logInfo(f"Layer {lyr_name} exported to {output}") else: # Dump the result tuple as-is when there are errors (the tuple size depends on the QGIS version) logger.logError( f"Layer {lyr_name} failed to export.\n\tResult object: {str(result)}" ) return output else: # Export raster if force or not filepath.lower().endswith("tif"): output = path or tempFileInSubFolder(safe_name + ".tif") writer = QgsRasterFileWriter(output) writer.setOutputFormat("GTiff") writer.writeRaster(layer.pipe(), layer.width(), layer.height(), layer.extent(), layer.crs()) del writer logger.logInfo(f"Layer {lyr_name} exported to {output}") return output else: logger.logInfo( f"No need to export layer {lyr_name} stored at {filepath}") return filepath
def sendOutputFile(self, handler): formatDict = WFSFormats[self.format] # read the GML outputLayer = QgsVectorLayer( os.path.join(self.tempdir, '%s.gml' % self.filename), 'qgis_server_wfs_features', 'ogr') if outputLayer.isValid(): try: # create save options options = QgsVectorFileWriter.SaveVectorOptions() # driver name options.driverName = formatDict['ogrProvider'] # file encoding options.fileEncoding = 'utf-8' # coordinate transformation if formatDict['forceCRS']: options.ct = QgsCoordinateTransform( outputLayer.crs(), QgsCoordinateReferenceSystem(formatDict['forceCRS']), QgsProject.instance()) # datasource options if formatDict['ogrDatasourceOptions']: options.datasourceOptions = formatDict[ 'ogrDatasourceOptions'] # write file write_result, error_message = QgsVectorFileWriter.writeAsVectorFormat( outputLayer, os.path.join( self.tempdir, '%s.%s' % (self.filename, formatDict['filenameExt'])), options) if write_result != QgsVectorFileWriter.NoError: handler.appendBody(b'') QgsMessageLog.logMessage(error_message, "wfsOutputExtension", Qgis.Critical) return False except Exception as e: handler.appendBody(b'') QgsMessageLog.logMessage(str(e), "wfsOutputExtension", Qgis.Critical) return False if formatDict['zip']: # compress files import zipfile try: import zlib compression = zipfile.ZIP_DEFLATED except: compression = zipfile.ZIP_STORED # create the zip file with zipfile.ZipFile( os.path.join(self.tempdir, '%s.zip' % self.filename), 'w') as zf: # add all files zf.write(os.path.join( self.tempdir, '%s.%s' % (self.filename, formatDict['filenameExt'])), compress_type=compression, arcname='%s.%s' % (self.typename, formatDict['filenameExt'])) for e in formatDict['extToZip']: if os.path.exists( os.path.join(self.tempdir, '%s.%s' % (self.filename, e))): zf.write(os.path.join(self.tempdir, '%s.%s' % (self.filename, e)), compress_type=compression, arcname='%s.%s' % (self.typename, e)) zf.close() f = QFile(os.path.join(self.tempdir, '%s.zip' % self.filename)) if (f.open(QFile.ReadOnly)): ba = f.readAll() handler.appendBody(ba) return True else: # return the file created without zip f = QFile( os.path.join( self.tempdir, '%s.%s' % (self.filename, formatDict['filenameExt']))) if (f.open(QFile.ReadOnly)): ba = f.readAll() handler.appendBody(ba) return True handler.appendBody(b'') QgsMessageLog.logMessage('Error no output file', "wfsOutputExtension", Qgis.Critical) return False
def run(self): for dlIndex in range(0, self.total_download_count): url = self.all_urls[dlIndex][0] url_parts = url.split('/') file_name = url_parts[-1].split('?')[0] data_dir_name = self.all_urls[dlIndex][1] data_dir_name = data_dir_name.replace(":", "_suhde_") dir_path = os.path.join(self.data_download_dir, data_dir_name) dir_path = os.path.join(dir_path, file_name.split('.')[0]) data_type = self.all_urls[dlIndex][3] percentage = dlIndex / float(self.total_download_count) * 100.0 self.setProgress(percentage) if not os.path.exists(dir_path): QgsMessageLog.logMessage("Skipping directory: " + dir_path, 'NLSgpkgloader', 1) continue for listed_file_name in os.listdir(dir_path): if data_type == "gml" and listed_file_name.endswith(".xml"): driver = ogr.GetDriverByName('GML') data_source = driver.Open( os.path.join(dir_path, listed_file_name), 0) layer_count = data_source.GetLayerCount() mtk_layer_count = 0 # Used for breaking from the for loop when all MTK layers chosen by the user have been added for i in range(layer_count): if self.isCanceled(): return False layer = data_source.GetLayerByIndex(i) layer_name = layer.GetName() if layer_name in self.products: new_layer = QgsVectorLayer( os.path.join(dir_path, listed_file_name) + "|layerid=" + str(i), layer_name, "ogr") if new_layer.isValid(): options = QgsVectorFileWriter.SaveVectorOptions( ) options.layerName = layer_name options.driverName = "GPKG" options.fileEncoding = "UTF-8" if os.path.isfile(self.gpkg_path): if QgsVectorLayer(self.gpkg_path + "|layername=" + layer_name).isValid(): options.actionOnExistingFile = QgsVectorFileWriter.AppendToLayerNoNewFields else: options.actionOnExistingFile = QgsVectorFileWriter.CreateOrOverwriteLayer else: options.actionOnExistingFile = QgsVectorFileWriter.CreateOrOverwriteFile e = QgsVectorFileWriter.writeAsVectorFormat( new_layer, self.gpkg_path, options) if e[0]: QgsMessageLog.logMessage( "Failed to write layer " + layer_name + " to geopackage", 'NLSgpkgloader', 2) break mtk_layer_count += 1 else: # TODO: handle invalid layer error #QgsMessageLog.logMessage("Invalid layer: " + listed_file_name + ":" layer_name, 'NLSgpkgloader', 2) pass if mtk_layer_count == len(self.products): break else: QgsMessageLog.logMessage( "cannot add the data type " + data_type + ", listed_file_name: " + listed_file_name, 'NLSgpkgloader', 0) return True
def _test(autoTransaction): """Test buffer methods within and without transactions - create a feature - save - retrieve the feature - change geom and attrs - test changes are seen in the buffer """ def _check_feature(wkt): f = next(layer_a.getFeatures()) self.assertEqual(f.geometry().asWkt().upper(), wkt) f = list(buffer.addedFeatures().values())[0] self.assertEqual(f.geometry().asWkt().upper(), wkt) ml = QgsVectorLayer( 'Point?crs=epsg:4326&field=int:integer&field=int2:integer', 'test', 'memory') self.assertTrue(ml.isValid()) d = QTemporaryDir() options = QgsVectorFileWriter.SaveVectorOptions() options.driverName = 'GPKG' options.layerName = 'layer_a' err, msg, newFileName, newLayer = QgsVectorFileWriter.writeAsVectorFormatV3( ml, os.path.join(d.path(), 'transaction_test.gpkg'), QgsCoordinateTransformContext(), options) self.assertEqual(err, QgsVectorFileWriter.NoError) self.assertTrue(os.path.isfile(newFileName)) layer_a = QgsVectorLayer(newFileName + '|layername=layer_a') self.assertTrue(layer_a.isValid()) project = QgsProject() project.setAutoTransaction(autoTransaction) project.addMapLayers([layer_a]) ########################################### # Tests with a new feature self.assertTrue(layer_a.startEditing()) buffer = layer_a.editBuffer() f = QgsFeature(layer_a.fields()) f.setAttribute('int', 123) f.setGeometry(QgsGeometry.fromWkt('point(7 45)')) self.assertTrue(layer_a.addFeatures([f])) _check_feature('POINT (7 45)') # Need to fetch the feature because its ID is NULL (-9223372036854775808) f = next(layer_a.getFeatures()) self.assertEqual(len(buffer.addedFeatures()), 1) layer_a.undoStack().undo() self.assertEqual(len(buffer.addedFeatures()), 0) layer_a.undoStack().redo() self.assertEqual(len(buffer.addedFeatures()), 1) f = list(buffer.addedFeatures().values())[0] self.assertEqual(f.attribute('int'), 123) # Now change attribute self.assertEqual(buffer.changedAttributeValues(), {}) spy_attribute_changed = QSignalSpy(layer_a.attributeValueChanged) layer_a.changeAttributeValue(f.id(), 1, 321) self.assertEqual(len(spy_attribute_changed), 1) self.assertEqual(spy_attribute_changed[0], [f.id(), 1, 321]) self.assertEqual(len(buffer.addedFeatures()), 1) # This is surprising: because it was a new feature it has been changed directly self.assertEqual(buffer.changedAttributeValues(), {}) f = list(buffer.addedFeatures().values())[0] self.assertEqual(f.attribute('int'), 321) spy_attribute_changed = QSignalSpy(layer_a.attributeValueChanged) layer_a.undoStack().undo() self.assertEqual(len(spy_attribute_changed), 1) self.assertEqual(spy_attribute_changed[0], [f.id(), 1, 123]) self.assertEqual(buffer.changedAttributeValues(), {}) f = list(buffer.addedFeatures().values())[0] self.assertEqual(f.attribute('int'), 123) f = next(layer_a.getFeatures()) self.assertEqual(f.attribute('int'), 123) # Change multiple attributes spy_attribute_changed = QSignalSpy(layer_a.attributeValueChanged) layer_a.changeAttributeValues(f.id(), {1: 321, 2: 456}) self.assertEqual(len(spy_attribute_changed), 2) self.assertEqual(spy_attribute_changed[0], [f.id(), 1, 321]) self.assertEqual(spy_attribute_changed[1], [f.id(), 2, 456]) buffer = layer_a.editBuffer() # This is surprising: because it was a new feature it has been changed directly self.assertEqual(buffer.changedAttributeValues(), {}) spy_attribute_changed = QSignalSpy(layer_a.attributeValueChanged) layer_a.undoStack().undo() # This is because QgsVectorLayerUndoCommandChangeAttribute plural if not autoTransaction: layer_a.undoStack().undo() f = next(layer_a.getFeatures()) self.assertEqual(f.attribute('int'), 123) self.assertEqual(f.attribute('int2'), None) self.assertEqual(len(spy_attribute_changed), 2) self.assertEqual( spy_attribute_changed[1 if autoTransaction else 0], [f.id(), 2, None]) self.assertEqual( spy_attribute_changed[0 if autoTransaction else 1], [f.id(), 1, 123]) # Change geometry f = next(layer_a.getFeatures()) spy_geometry_changed = QSignalSpy(layer_a.geometryChanged) self.assertTrue( layer_a.changeGeometry(f.id(), QgsGeometry.fromWkt('point(9 43)'))) self.assertTrue(len(spy_geometry_changed), 1) self.assertEqual(spy_geometry_changed[0][0], f.id()) self.assertEqual(spy_geometry_changed[0][1].asWkt(), QgsGeometry.fromWkt('point(9 43)').asWkt()) _check_feature('POINT (9 43)') self.assertEqual(buffer.changedGeometries(), {}) layer_a.undoStack().undo() _check_feature('POINT (7 45)') self.assertEqual(buffer.changedGeometries(), {}) self.assertTrue( layer_a.changeGeometry(f.id(), QgsGeometry.fromWkt('point(9 43)'))) _check_feature('POINT (9 43)') self.assertTrue( layer_a.changeGeometry(f.id(), QgsGeometry.fromWkt('point(10 44)'))) _check_feature('POINT (10 44)') # This is another surprise: geometry edits get collapsed into a single # one because they have the same hardcoded id layer_a.undoStack().undo() _check_feature('POINT (7 45)') self.assertTrue(layer_a.commitChanges()) ########################################### # Tests with the existing feature # Get the feature f = next(layer_a.getFeatures()) self.assertTrue(f.isValid()) self.assertEqual(f.attribute('int'), 123) self.assertEqual(f.geometry().asWkt().upper(), 'POINT (7 45)') # Change single attribute self.assertTrue(layer_a.startEditing()) spy_attribute_changed = QSignalSpy(layer_a.attributeValueChanged) layer_a.changeAttributeValue(f.id(), 1, 321) self.assertEqual(len(spy_attribute_changed), 1) self.assertEqual(spy_attribute_changed[0], [f.id(), 1, 321]) buffer = layer_a.editBuffer() self.assertEqual(buffer.changedAttributeValues(), {1: {1: 321}}) f = next(layer_a.getFeatures()) self.assertEqual(f.attribute(1), 321) spy_attribute_changed = QSignalSpy(layer_a.attributeValueChanged) layer_a.undoStack().undo() f = next(layer_a.getFeatures()) self.assertEqual(f.attribute(1), 123) self.assertEqual(len(spy_attribute_changed), 1) self.assertEqual(spy_attribute_changed[0], [f.id(), 1, 123]) self.assertEqual(buffer.changedAttributeValues(), {}) # Change attributes spy_attribute_changed = QSignalSpy(layer_a.attributeValueChanged) layer_a.changeAttributeValues(f.id(), {1: 111, 2: 654}) self.assertEqual(len(spy_attribute_changed), 2) self.assertEqual(spy_attribute_changed[0], [1, 1, 111]) self.assertEqual(spy_attribute_changed[1], [1, 2, 654]) f = next(layer_a.getFeatures()) self.assertEqual(f.attributes(), [1, 111, 654]) self.assertEqual(buffer.changedAttributeValues(), {1: { 1: 111, 2: 654 }}) spy_attribute_changed = QSignalSpy(layer_a.attributeValueChanged) layer_a.undoStack().undo() # This is because QgsVectorLayerUndoCommandChangeAttribute plural if not autoTransaction: layer_a.undoStack().undo() self.assertEqual(len(spy_attribute_changed), 2) self.assertEqual( spy_attribute_changed[0 if autoTransaction else 1], [1, 1, 123]) self.assertEqual( spy_attribute_changed[1 if autoTransaction else 0], [1, 2, None]) f = next(layer_a.getFeatures()) self.assertEqual(f.attributes(), [1, 123, None]) self.assertEqual(buffer.changedAttributeValues(), {}) # Change geometry spy_geometry_changed = QSignalSpy(layer_a.geometryChanged) self.assertTrue( layer_a.changeGeometry(f.id(), QgsGeometry.fromWkt('point(9 43)'))) self.assertEqual(spy_geometry_changed[0][0], 1) self.assertEqual(spy_geometry_changed[0][1].asWkt(), QgsGeometry.fromWkt('point(9 43)').asWkt()) f = next(layer_a.getFeatures()) self.assertEqual(f.geometry().asWkt().upper(), 'POINT (9 43)') self.assertEqual(buffer.changedGeometries()[1].asWkt().upper(), 'POINT (9 43)') spy_geometry_changed = QSignalSpy(layer_a.geometryChanged) layer_a.undoStack().undo() self.assertEqual(spy_geometry_changed[0][0], 1) self.assertEqual(spy_geometry_changed[0][1].asWkt(), QgsGeometry.fromWkt('point(7 45)').asWkt()) self.assertEqual(buffer.changedGeometries(), {}) f = next(layer_a.getFeatures()) self.assertEqual(f.geometry().asWkt().upper(), 'POINT (7 45)') self.assertEqual(buffer.changedGeometries(), {}) # Delete an existing feature self.assertTrue(layer_a.deleteFeature(f.id())) with self.assertRaises(StopIteration): next(layer_a.getFeatures()) self.assertEqual(buffer.deletedFeatureIds(), [f.id()]) layer_a.undoStack().undo() self.assertTrue(layer_a.getFeature(f.id()).isValid()) self.assertEqual(buffer.deletedFeatureIds(), []) ########################################### # Test delete # Delete a new feature f = QgsFeature(layer_a.fields()) f.setAttribute('int', 555) f.setGeometry(QgsGeometry.fromWkt('point(8 46)')) self.assertTrue(layer_a.addFeatures([f])) f = [ f for f in layer_a.getFeatures() if f.attribute('int') == 555 ][0] self.assertTrue(f.id() in buffer.addedFeatures()) self.assertTrue(layer_a.deleteFeature(f.id())) self.assertFalse(f.id() in buffer.addedFeatures()) self.assertFalse(f.id() in buffer.deletedFeatureIds()) layer_a.undoStack().undo() self.assertTrue(f.id() in buffer.addedFeatures()) ########################################### # Add attribute field = QgsField('attr1', QVariant.String) self.assertTrue(layer_a.addAttribute(field)) self.assertNotEqual(layer_a.fields().lookupField(field.name()), -1) self.assertEqual(buffer.addedAttributes(), [field]) layer_a.undoStack().undo() self.assertEqual(layer_a.fields().lookupField(field.name()), -1) self.assertEqual(buffer.addedAttributes(), []) layer_a.undoStack().redo() self.assertNotEqual(layer_a.fields().lookupField(field.name()), -1) self.assertEqual(buffer.addedAttributes(), [field]) self.assertTrue(layer_a.commitChanges()) ########################################### # Remove attribute self.assertTrue(layer_a.startEditing()) buffer = layer_a.editBuffer() attr_idx = layer_a.fields().lookupField(field.name()) self.assertNotEqual(attr_idx, -1) self.assertTrue(layer_a.deleteAttribute(attr_idx)) self.assertEqual(buffer.deletedAttributeIds(), [attr_idx]) self.assertEqual(layer_a.fields().lookupField(field.name()), -1) layer_a.undoStack().undo() self.assertEqual(buffer.deletedAttributeIds(), []) self.assertEqual(layer_a.fields().lookupField(field.name()), attr_idx) # This is totally broken at least on OGR/GPKG: the rollback # does not restore the original fields if False: layer_a.undoStack().redo() self.assertEqual(buffer.deletedAttributeIds(), [attr_idx]) self.assertEqual(layer_a.fields().lookupField(field.name()), -1) # Rollback! self.assertTrue(layer_a.rollBack()) self.assertIn('attr1', layer_a.dataProvider().fields().names()) self.assertIn('attr1', layer_a.fields().names()) self.assertEqual(layer_a.fields().names(), layer_a.dataProvider().fields().names()) attr_idx = layer_a.fields().lookupField(field.name()) self.assertNotEqual(attr_idx, -1) self.assertTrue(layer_a.startEditing()) attr_idx = layer_a.fields().lookupField(field.name()) self.assertNotEqual(attr_idx, -1) ########################################### # Rename attribute attr_idx = layer_a.fields().lookupField(field.name()) self.assertEqual(layer_a.fields().lookupField('new_name'), -1) self.assertTrue(layer_a.renameAttribute(attr_idx, 'new_name')) self.assertEqual(layer_a.fields().lookupField('new_name'), attr_idx) layer_a.undoStack().undo() self.assertEqual(layer_a.fields().lookupField(field.name()), attr_idx) self.assertEqual(layer_a.fields().lookupField('new_name'), -1) layer_a.undoStack().redo() self.assertEqual(layer_a.fields().lookupField('new_name'), attr_idx) self.assertEqual(layer_a.fields().lookupField(field.name()), -1) ############################################# # Try hard to make this fail for transactions if autoTransaction: self.assertTrue(layer_a.commitChanges()) self.assertTrue(layer_a.startEditing()) f = next(layer_a.getFeatures()) # Do for i in range(10): spy_attribute_changed = QSignalSpy( layer_a.attributeValueChanged) layer_a.changeAttributeValue(f.id(), 2, i) self.assertEqual(len(spy_attribute_changed), 1) self.assertEqual(spy_attribute_changed[0], [f.id(), 2, i]) buffer = layer_a.editBuffer() self.assertEqual(buffer.changedAttributeValues(), {f.id(): { 2: i }}) f = next(layer_a.getFeatures()) self.assertEqual(f.attribute(2), i) # Undo/redo for i in range(9): # Undo spy_attribute_changed = QSignalSpy( layer_a.attributeValueChanged) layer_a.undoStack().undo() f = next(layer_a.getFeatures()) self.assertEqual(f.attribute(2), 8 - i) self.assertEqual(len(spy_attribute_changed), 1) self.assertEqual(spy_attribute_changed[0], [f.id(), 2, 8 - i]) buffer = layer_a.editBuffer() self.assertEqual(buffer.changedAttributeValues(), {f.id(): { 2: 8 - i }}) # Redo spy_attribute_changed = QSignalSpy( layer_a.attributeValueChanged) layer_a.undoStack().redo() f = next(layer_a.getFeatures()) self.assertEqual(f.attribute(2), 9 - i) self.assertEqual(len(spy_attribute_changed), 1) self.assertEqual(spy_attribute_changed[0], [f.id(), 2, 9 - i]) # Undo again spy_attribute_changed = QSignalSpy( layer_a.attributeValueChanged) layer_a.undoStack().undo() f = next(layer_a.getFeatures()) self.assertEqual(f.attribute(2), 8 - i) self.assertEqual(len(spy_attribute_changed), 1) self.assertEqual(spy_attribute_changed[0], [f.id(), 2, 8 - i]) buffer = layer_a.editBuffer() self.assertEqual(buffer.changedAttributeValues(), {f.id(): { 2: 8 - i }}) # Last check f = next(layer_a.getFeatures()) self.assertEqual(f.attribute(2), 8 - i) self.assertEqual(buffer.changedAttributeValues(), {f.id(): { 2: 0 }}) layer_a.undoStack().undo() buffer = layer_a.editBuffer() self.assertEqual(buffer.changedAttributeValues(), {}) f = next(layer_a.getFeatures()) self.assertEqual(f.attribute(2), None)
def run(self): """Run method that performs all the real work""" # Create the dialog with elements (after translation) and keep reference # Only create GUI ONCE in callback, so that it will only load when the plugin is started if self.first_start == True: self.first_start = False self.dlg = TransferLayerFileGdbToGeoPackageDialog() self.dlg.pushButton.clicked.connect(self.select_output_file) # show the dialog self.dlg.show() # Run the dialog event loop result = self.dlg.exec_() # See if OK was pressed if result: # Faire une lsite de ce que contient la geobase list = fiona.listlayers(self.dlg.lineEdit.text()) tmp = self.dlg.lineEdit.text() filename = tmp.replace(".gdb", "") gdb = filename + '.gdb' # progressMessageBar = self.iface.messageBar().createMessage("Doing something boring...") # progress = self.QProgressBar() for name in list: try: name2 = (name.replace('_', '')) ce = '{0}'.format(gdb) + '|' + 'layername=' + '{0}'.format( name) # faire un layer avec la string layer = QgsVectorLayer(ce, '{0}'.format(name), 'ogr') if path.isfile(filename + '.gpkg'): options = QgsVectorFileWriter.SaveVectorOptions() # permet de copier plusieurs classe d'entité et table options.actionOnExistingFile = QgsVectorFileWriter.CreateOrOverwriteLayer options.driverName = 'GPKG' options.layerName = '{0}'.format(name2) QgsVectorFileWriter.writeAsVectorFormat( layer, filename, options) else: options = QgsVectorFileWriter.SaveVectorOptions() options.driverName = 'GPKG' # options.layerName = '{0}'.format(name2) QgsVectorFileWriter.writeAsVectorFormat( layer, filename, options) except Exception as e: continue self.iface.messageBar().pushMessage("Réussi ! ", "Le GeoPackage est situé : " + filename + '.gpkg', level=Qgis.Success, duration=10) pass
def processAlgorithm(self, parameters, context, feedback): """ Here is where the processing itself takes place. """ source = self.parameterAsSource(parameters, self.INPUT, context) path = self.parameterAsFile(parameters, self.OUTPUT, context) field_def = { 'idx': QVariant.Int, 'name': QVariant.String, 'type': QVariant.Int, 'typeName': QVariant.String, 'length': QVariant.Int, 'precision': QVariant.Int, 'comment': QVariant.String, 'alias': QVariant.String } # create virtual layer vl = QgsVectorLayer("None", "fields", "memory") pr = vl.dataProvider() # define fields fields = QgsFields() for n, t in field_def.items(): fields.append(QgsField(name=n, type=t)) # add fields pr.addAttributes(fields) vl.updateFields( ) # tell the vector layer to fetch changes from the provider # add feature based on field description field_index = 0 for f in providerFields(source.fields()): field_index += 1 feat = QgsFeature() feat.setAttributes([ field_index, f.name(), f.type(), f.typeName(), f.length(), f.precision(), f.comment(), f.alias() ]) pr.addFeatures([feat]) # set create file layer options options = QgsVectorFileWriter.SaveVectorOptions() options.driverName = QgsVectorFileWriter.driverForExtension('csv') options.fileEncoding = 'UTF-8' options.actionOnExistingFile = QgsVectorFileWriter.CreateOrOverwriteFile options.layerOptions = ['CREATE_CSVT=YES'] # write file write_result, error_message = QgsVectorFileWriter.writeAsVectorFormat( vl, path, options) # result if write_result != QgsVectorFileWriter.NoError: raise QgsProcessingException( self.tr('* ERROR: {0}').format(error_message)) del fields del pr del vl # create layer dest_layer = QgsVectorLayer(path, self.OUTPUT_LAYER, 'ogr') if not dest_layer.isValid(): raise QgsProcessingException( self.tr('* ERROR: Can\'t load layer {1} in {0}').format( path, self.OUTPUT_LAYER)) # Add layer to context context.temporaryLayerStore().addMapLayer(dest_layer) context.addLayerToLoadOnCompletion( dest_layer.id(), QgsProcessingContext.LayerDetails(self.OUTPUT_LAYER, context.project(), self.OUTPUT_LAYER)) return {self.OUTPUT: path, self.OUTPUT_LAYER: dest_layer.id()}
def create_zensus_rings(self): ''' intersect study area with zensus raster ''' self.log('Extrahiere Siedlungszellen aus Zensusdaten...') epsg = self.project.settings.EPSG zensus_file = os.path.join(self.project.basedata.base_path, self.project.settings.ZENSUS_100_FILE) bbox = get_bbox(self.gemeinden.table) clipped_raster, raster_epsg = clip_raster(zensus_file, bbox) raster_layer = QgsRasterLayer(clipped_raster) parameters = { 'INPUT_RASTER': raster_layer, 'RASTER_BAND': 1, 'FIELD_NAME': 'ew', 'OUTPUT': 'memory:' } point_layer = processing.run('native:pixelstopoints', parameters)['OUTPUT'] point_layer.setSubsetString('ew>0') parameters = { 'INPUT': point_layer, 'TARGET_CRS': f'EPSG:{epsg}', 'OUTPUT': 'memory:' } point_proj = processing.run('native:reprojectlayer', parameters)['OUTPUT'] # clip with max distance from project area buffer_geom = self.project_frame.geom.buffer(self.rings[-1], 100) buffer = QgsFeature() buffer.setGeometry(buffer_geom) overlay = QgsVectorLayer(f'Polygon?crs=EPSG:{epsg}', 'buffer', 'memory') overlay.dataProvider().addFeature(buffer) parameters = { 'INPUT': point_proj, 'OVERLAY': overlay, 'OUTPUT': 'memory:' } clipped_layer = processing.run('native:clip', parameters)['OUTPUT'] # intersect with rings to get distance bin ring_layer = QgsVectorLayer(f'Polygon?crs=EPSG:{epsg}', 'rings', 'memory') pr = ring_layer.dataProvider() pr.addAttributes([QgsField('ring', QVariant.Int)]) ring_layer.updateFields() prev_outer_circle = None for distance in self.rings: ring = QgsFeature() outer_circle = self.project_frame.geom.buffer(distance, 100) if prev_outer_circle is not None: geom = outer_circle.difference(prev_outer_circle) else: geom = outer_circle prev_outer_circle = outer_circle ring.setGeometry(geom) ring.setAttributes([distance]) pr.addFeature(ring) self.log('Verschneide Siedlungszellen mit Entfernungsringen ' 'und Gemeinden...') parameters = { 'INPUT': clipped_layer, 'OVERLAY': ring_layer, 'OUTPUT': 'memory:' } clipped_w_distance = processing.run('native:intersection', parameters)['OUTPUT'] # intersect with "gemeinden" to add AGS to cells gem_overlay = create_layer(self.gemeinden, 'Polygon', fields=['AGS'], name='overlay', epsg=epsg) parameters = { 'INPUT': clipped_w_distance, 'OVERLAY': gem_overlay, 'OUTPUT': 'memory:' } clipped_w_ags = processing.run('native:intersection', parameters)['OUTPUT'] options = QgsVectorFileWriter.SaveVectorOptions() options.actionOnExistingFile = \ QgsVectorFileWriter.CreateOrOverwriteLayer options.layerName = 'zensus_rings' QgsVectorFileWriter.writeAsVectorFormat(clipped_w_ags, self.gemeinden.workspace.path, options) return clipped_w_ags
def SplitMesh(outputdb, meshtable, output_table, SplitFlag, keycolum, divcolumn): mlayer = meshtable if type(meshtable) is str: # 入力メッシュレイヤ dmesh = outputdb + "|layername=" + meshtable mlayer = QgsVectorLayer(dmesh, "mesh", "ogr") if mlayer.isValid(): print("dmesh Layer load OK") else: print("dmesh Layer load Fail") print("dmesh=" + meshtable) sys.exit() out_tb = outputdb + "|layername=" + output_table crsstr = mlayer.crs().authid() # 作業結果出力レイヤ vectL = 'Polygon?crs=' + crsstr vl1 = QgsVectorLayer(vectL, "temporary_mesh", "memory") if not vl1: print("Virtual Layer failed to load!") sys.exit() else: print(out_tb) #vl1.setCrs( mlayer.crs() ) pr1 = vl1.dataProvider() # フィールド定義 pr1.addAttributes([ QgsField(keycolum, QVariant.String), QgsField(divcolumn, QVariant.Int) ]) vl1.updateFields() # vl1.beginEditCommand("Add Polygons") features = mlayer.getFeatures() for feature in features: code = feature[keycolum] divide_f = feature[divcolumn] #print( 'code =' + code+ ' divide=' + str(divide_f) ) geom = feature.geometry() geomSingleType = QgsWkbTypes.isSingleType(geom.wkbType()) if divide_f == 0: if geom.type() == QgsWkbTypes.PolygonGeometry: if geomSingleType: x = geom.asPolygon() #print("Polygon: ", x, "Area: ", geom.area()) for xp in x: #print("xp:",xp ) #for xxp in xp: # print("xxp:",xxp) # 座標の場所を判定して位置関係を正規化したほうがいいかも # p0_1 = GetCyuuten(xp[0], xp[1]) p1_2 = GetCyuuten(xp[1], xp[2]) p2_3 = GetCyuuten(xp[2], xp[3]) p3_4 = GetCyuuten(xp[3], xp[4]) pC_C = GetCyuuten(p0_1, p2_3) # 新しいキーコード ncode1 = code + '-01' Polygon1 = QgsGeometry.fromPolygonXY([[ QgsPointXY(xp[0].x(), xp[0].y()), QgsPointXY(p0_1.x(), p0_1.y()), QgsPointXY(pC_C.x(), pC_C.y()), QgsPointXY(p3_4.x(), p3_4.y()) ]]) # add a feature fet = QgsFeature(pr1.fields()) fet.setGeometry(Polygon1) fet[keycolum] = ncode1 fet[divcolumn] = divide_f # 新しい feature を作って別レイヤに格納する retc = pr1.addFeatures([fet]) #print("add new") #print( retc ) # 新しいキーコード ncode2 = code + '-02' Polygon2 = QgsGeometry.fromPolygonXY([[ QgsPointXY(p0_1.x(), p0_1.y()), QgsPointXY(xp[1].x(), xp[1].y()), QgsPointXY(p1_2.x(), p1_2.y()), QgsPointXY(pC_C.x(), pC_C.y()) ]]) fet2 = QgsFeature(pr1.fields()) fet2.setGeometry(Polygon2) fet2[keycolum] = ncode2 fet2[divcolumn] = divide_f # 新しい feature を作って別レイヤに格納する retc = pr1.addFeatures([fet2]) # 新しいキーコード ncode3 = code + '-03' Polygon3 = QgsGeometry.fromPolygonXY([[ QgsPointXY(pC_C.x(), pC_C.y()), QgsPointXY(p1_2.x(), p1_2.y()), QgsPointXY(xp[2].x(), xp[2].y()), QgsPointXY(p2_3.x(), p2_3.y()) ]]) fet3 = QgsFeature(pr1.fields()) fet3.setGeometry(Polygon3) fet3[keycolum] = ncode3 fet3[divcolumn] = divide_f # 新しい feature を作って別レイヤに格納する retc = pr1.addFeatures([fet3]) # 新しいキーコード ncode4 = code + '-04' Polygon4 = QgsGeometry.fromPolygonXY([[ QgsPointXY(p3_4.x(), p3_4.y()), QgsPointXY(pC_C.x(), pC_C.y()), QgsPointXY(p2_3.x(), p2_3.y()), QgsPointXY(xp[3].x(), xp[3].y()) ]]) fet4 = QgsFeature(pr1.fields()) fet4.setGeometry(Polygon4) fet4[keycolum] = ncode4 fet4[divcolumn] = divide_f # 新しい feature を作って別レイヤに格納する retc = pr1.addFeatures([fet4]) #print(Polygon1) # print(Polygon2) #Poly #feat.setGeometry( QgsGeometry.fromPolygonXY([QgsPointXY(546016, 4760165), p2, p3])) #qPolygon1 = QgsGeometry.fromPolygonXY([ xp[0],p0_1, pC_C,xp[0]]) #print( qPolygon1 ) # 一点目 2点目の中点を求める 2 else: x = geom.asMultiPolygon() #print("MultiPolygon: ", x, "Area: ", geom.area()) else: print("geometry is not polygon!") else: # 分割不要ポリゴンはそのまま書き込む retc = pr1.addFeatures([feature]) vl1.updateExtents() vl1.endEditCommand() vl1.commitChanges() features2 = vl1.getFeatures() print("-------------------------- vl1 features ------------------------") #for feature2 in features2: # print( feature2 ) options = QgsVectorFileWriter.SaveVectorOptions() options.driverName = 'GPKG' options.actionOnExistingFile = QgsVectorFileWriter.CreateOrOverwriteLayer options.layerName = output_table #print( "outputdb ==>"+outputdb ) #print( "output ==>"+output_table ) # 結果レイヤ書き込み write_result, error = QgsVectorFileWriter.writeAsVectorFormat( vl1, outputdb, options) #if error == QgsVectorFileWriter.NoError: # print("success again!") #print( error ) #print( write_result ) return (output_table)
def __init__(self, iface: QgisInterface, version: str, plugin_dir: str, isBatch: bool, isHUC: bool=False, logFile: Optional[str]=None) -> None: """Initialise class variables.""" settings = QSettings() if settings.contains('/QSWATPlus/SWATPlusDir'): SWATPlusDir = settings.value('/QSWATPlus/SWATPlusDir') if not os.path.isdir(SWATPlusDir): SWATPlusDir = Parameters._SWATPLUSDEFAULTDIR else: SWATPlusDir = Parameters._SWATPLUSDEFAULTDIR if not os.path.isdir(SWATPlusDir): QSWATUtils.error('''Cannot find SWATPlus directory, expected to be {0}. Please use the Parameters form to set its location.'''.format(SWATPlusDir), isBatch) else: settings.setValue('/QSWATPlus/SWATPlusDir', SWATPlusDir) ## SWATPlus directory self.SWATPlusDir = SWATPlusDir ## Directory containing QSWAT plugin self.plugin_dir = plugin_dir ## Databases directory: part of plugin # containing template project and reference databases, plus soil database for STATSGO and SSURGO self.dbPath = QSWATUtils.join(self.SWATPlusDir, Parameters._DBDIR) ## Path of template project database self.dbProjTemplate = QSWATUtils.join(self.dbPath, Parameters._DBPROJ) ## Path of template reference database self.dbRefTemplate = QSWATUtils.join(self.dbPath, Parameters._DBREF) ## Directory of TauDEM executables self.TauDEMDir = TauDEMUtils.findTauDEMDir(settings, not isBatch)[0] ## Path of mpiexec self.mpiexecPath = TauDEMUtils.findMPIExecPath(settings) proj: QgsProject = QgsProject.instance() title = proj.title() ## QGIS interface self.iface = iface ## project projection (set from DEM) self.crsProject: Optional[QgsCoordinateReferenceSystem] = None ## Stream burn-in depth self.burninDepth: int = proj.readNumEntry(title, 'params/burninDepth', Parameters._BURNINDEPTH)[0] ## Channel width multiplier self.channelWidthMultiplier: float = proj.readDoubleEntry(title, 'params/channelWidthMultiplier', Parameters._CHANNELWIDTHMULTIPLIER)[0] ## Channel width exponent self.channelWidthExponent: float = proj.readDoubleEntry(title, 'params/channelWidthExponent', Parameters._CHANNELWIDTHEXPONENT)[0] ## Channel depth multiplier self.channelDepthMultiplier: float = proj.readDoubleEntry(title, 'params/channelDepthMultiplier', Parameters._CHANNELDEPTHMULTIPLIER)[0] ## Channel depth exponent self.channelDepthExponent: float = proj.readDoubleEntry(title, 'params/channelDepthExponent', Parameters._CHANNELDEPTHEXPONENT)[0] ## reach slope multiplier self.reachSlopeMultiplier: float = proj.readDoubleEntry(title, 'params/reachSlopeMultiplier', Parameters._MULTIPLIER)[0] ## tributary slope multiplier self.tributarySlopeMultiplier: float = proj.readDoubleEntry(title, 'params/tributarySlopeMultiplier', Parameters._MULTIPLIER)[0] ## mean slope multiplier self.meanSlopeMultiplier: float = proj.readDoubleEntry(title, 'params/meanSlopeMultiplier', Parameters._MULTIPLIER)[0] ## main length multiplier self.mainLengthMultiplier: float = proj.readDoubleEntry(title, 'params/mainLengthMultiplier', Parameters._MULTIPLIER)[0] ## tributary length multiplier self.tributaryLengthMultiplier: float = proj.readDoubleEntry(title, 'params/tributaryLengthMultiplier', Parameters._MULTIPLIER)[0] ## upslope HRU drain percent self.upslopeHRUDrain: int = proj.readNumEntry(title, 'params/upslopeHRUDrain', Parameters._UPSLOPEHRUDRAIN)[0] ## Index of slope group in Layers panel self.slopeGroupIndex = -1 ## Index of landuse group in Layers panel self.landuseGroupIndex = -1 ## Index of soil group in Layers panel self.soilGroupIndex = -1 ## Index of watershed group in Layers panel self.watershedGroupIndex = -1 ## Index of results group in Layers panel self.resultsGroupIndex = -1 ## Index of animation group in Layers panel self.animationGroupIndex = -1 ## Flag showing if using existing watershed self.existingWshed = False ## Flag showing if using grid model self.useGridModel = False ## flag to show if using landscape units self.useLandscapes = False ## flag to show if dividing into left/right/headwater landscape units self.useLeftRight = False ## Path of DEM raster self.demFile = '' ## Path of filled DEM raster self.felFile = '' ## Path of stream burn-in shapefile self.burnFile = '' ## Path of DEM after burning-in self.burnedDemFile = '' ## Path of D8 flow direction raster self.pFile = '' ## Path of D8 flow accumulation raster self.ad8File = '' ## Path of subbasins raster self.basinFile = '' ## path of channel basins raster self.channelBasinFile = '' ## path of channel basins file with lakes masked out self.chBasinNoLakeFile = '' ## Path of channel raster self.srcChannelFile = '' ## Path of valleyDepthsFile # value at each point in this raster is the drop in metres # from the point to where its D8 flow path meets a channel # Channel elevations are measured at points adjacent to the channel # to avoid problems caused by burning-in self.valleyDepthsFile = '' ## Path of outlets shapefile self.outletFile = '' ## path of snapped outlets file self.snapFile = '' ## Path of outlets shapefile for extra reservoirs and point sources self.extraOutletFile = '' ## Path of stream shapefile self.streamFile = '' ## Path of stream shapefile calculated by delineation # since streamFile is set to streams from grid when using a grid model self.delinStreamFile = '' ## Path of channel shapefile self.channelFile = '' ## Path of subbasins shapefile or grid file when using grids self.subbasinsFile = '' ## Path of subbasins shapefile before lakes removed. Not used with grid models. self.subsNoLakesFile = '' ## Path of watershed shapefile: shows channel basins. Not used with grid models. self.wshedFile = '' ## Path of file like D8 contributing area but with heightened values at subbasin outlets self.hd8File = '' ## Path of distance to stream outlets raster self.distStFile = '' ## Path of distance to channel raster self.distChFile = '' ## Path of slope raster self.slopeFile = '' ## path of lakes shapefile self.lakeFile = '' ## Path of slope bands raster self.slopeBandsFile = '' ## Path of landuse raster self.landuseFile = '' ## Path of soil raster self.soilFile = '' ## path of floodplain raster self.floodFile = '' ## path of raster generated from playa lakes self.playaFile = '' ## Nodata value for DEM self.elevationNoData = 0.0 ## DEM horizontal block size self.xBlockSize = 0 ## DEM vertical block size self.yBlockSize = 0 ## Nodata value for basins raster self.basinNoData = 0 ## Nodata value for distance to outlets raster self.distStNoData = 0.0 ## Nodata value for distance to channel raster self.distChNoData = 0.0 ## Nodata value for slope raster self.slopeNoData = 0 ## Nodata value for landuse raster self.cropNoData = 0 ## Nodata value for soil raster self.soilNoData = 0 ## Nodata value for floodplain raster self.floodNoData = -1 ## Area of DEM cell in square metres self.cellArea = 0.0 ## channel threshold in square metres self.channelThresholdArea = 10000000.0 # 1000 hectares default ## gridSize as count of DEM cells per side (grid model only) self.gridSize = 0 ## list of landuses exempt from HRU removal self.exemptLanduses: List[str] = [] ## table of landuses being split self.splitLanduses: Dict[str, Dict[str, float]] = dict() ## Elevation bands threshold in metres self.elevBandsThreshold = 0 ## Number of elevation bands self.numElevBands = 0 ## Topology object self.topo: QSWATTopology = QSWATTopology(isBatch, isHUC) projFile: str = proj.fileName() projPath: str = QFileInfo(projFile).canonicalFilePath() pdir, base = os.path.split(projPath) ## Project name self.projName = os.path.splitext(base)[0] ## Project directory self.projDir = pdir ## QSWAT+ version self.version = version ## DEM directory self.demDir = '' ## Landuse directory self.landuseDir = '' ## Soil directory self.soilDir = '' ## Landscape directory self.landscapeDir = '' ## Floodplain directory self.floodDir = '' ## text directory self.textDir = '' ## Rasters directory self.rastersDir = '' ## Shapes directory self.shapesDir = '' ## Scenarios directory self.scenariosDir = '' ## Results directory self.resultsDir = '' ## Plots directory self.plotsDir = '' ## png directory for storing png images used to create animation videos self.pngDir = '' ## animation directory for storing animation files self.animationDir = '' self.createSubDirectories() ## path of full lsus shapefile self.fullLSUsFile = QSWATUtils.join(self.shapesDir, Parameters._LSUS1 + '.shp') ## path of actual lsus shapefile (after channel mergers self.actLSUsFile = QSWATUtils.join(self.shapesDir, Parameters._LSUS2 + '.shp') ## Path of FullHRUs shapefile self.fullHRUsFile = QSWATUtils.join(self.shapesDir, Parameters._HRUS1 + '.shp') ## Path of ActHRUs shapefile self.actHRUsFile = QSWATUtils.join(self.shapesDir, Parameters._HRUS2 + '.shp') ## Flag to show if running in batch mode self.isBatch = isBatch ## flag for HUC projects self.isHUC = isHUC ## log file for message output for HUC projects self.logFile = logFile ## Path of project database self.db: DBUtils = DBUtils(self.projDir, self.projName, self.dbProjTemplate, self.dbRefTemplate, self.isHUC, self.logFile, self.isBatch) ## multiplier to turn DEM distances to metres self.horizontalFactor = 1 ## multiplier to turn elevations to metres self.verticalFactor = 1 ## vertical units self.verticalUnits = Parameters._METRES # positions of sub windows ## Position of delineation form self.delineatePos = QPoint(0, 100) ## Position of HRUs form self.hrusPos = QPoint(0, 100) ## Position of parameters form self.parametersPos = QPoint(50, 100) ## Position of landscape form self.landscapePos = QPoint(50, 80) ## Position of select subbasins form self.selectSubsPos = QPoint(50, 100) ## Position of select reservoirs form self.selectResPos = QPoint(50, 100) ## Position of about form self.aboutPos = QPoint(50, 100) ## Position of elevation bands form self.elevationBandsPos = QPoint(50, 100) ## Position of split landuses form self.splitPos = QPoint(50, 100) ## Position of select landuses form self.selectLuPos = QPoint(50, 100) ## Position of exempt landuses form self.exemptPos = QPoint(50, 100) ## Position of outlets form self.outletsPos = QPoint(50, 100) ## Position of select outlets file form self.selectOutletFilePos = QPoint(50, 100) ## Position of select outlets form self.selectOutletPos = QPoint(50, 100) ## Position of visualise form self.visualisePos = QPoint(0, 100) ## rasters open that need to be closed if memory exception occurs self.openRasters: Set[Raster] = set() ## options for creating shapefiles self.vectorFileWriterOptions = QgsVectorFileWriter.SaveVectorOptions() self.vectorFileWriterOptions.ActionOnExistingFile = QgsVectorFileWriter.CreateOrOverwriteFile self.vectorFileWriterOptions.driverName = "ESRI Shapefile" self.vectorFileWriterOptions.fileEncoding = "UTF-8" ## will set to choice made when converting from ArcSWAT, if that was how the project file was created # 0: Full # 1: Existing # 2: No GIS # NB These values are defined in convertFromArc.py self.fromArcChoice = -1
def testStartEditingCommitRollBack(self): ml = QgsVectorLayer( 'Point?crs=epsg:4326&field=int:integer&field=int2:integer', 'test', 'memory') self.assertTrue(ml.isValid()) # Layer A geopackage A d = QTemporaryDir() options = QgsVectorFileWriter.SaveVectorOptions() options.driverName = 'GPKG' options.layerName = 'layer_a' err, msg, newFileName, newLayer = QgsVectorFileWriter.writeAsVectorFormatV3( ml, os.path.join(d.path(), 'test_EditBufferGroup_A.gpkg'), QgsCoordinateTransformContext(), options) self.assertEqual(err, QgsVectorFileWriter.NoError) self.assertTrue(os.path.isfile(newFileName)) layer_a = QgsVectorLayer(newFileName + '|layername=layer_a') self.assertTrue(layer_a.isValid()) # Layer B geopackage B options.layerName = 'layer_b' err, msg, newFileName, newLayer = QgsVectorFileWriter.writeAsVectorFormatV3( ml, os.path.join(d.path(), 'test_EditBufferGroup_B.gpkg'), QgsCoordinateTransformContext(), options) self.assertEqual(err, QgsVectorFileWriter.NoError) self.assertTrue(os.path.isfile(newFileName)) layer_b = QgsVectorLayer(newFileName + '|layername=layer_b') self.assertTrue(layer_b.isValid()) # Layer C memory layer_c = QgsVectorLayer( 'Point?crs=epsg:4326&field=int:integer&field=int2:integer', 'test', 'memory') self.assertTrue(layer_c.isValid()) project = QgsProject() project.addMapLayers([layer_a, layer_b, layer_c]) project.setTransactionMode(Qgis.TransactionMode.BufferedGroups) editBufferGroup = project.editBufferGroup() # Check layers in group self.assertIn(layer_a, editBufferGroup.layers()) self.assertIn(layer_b, editBufferGroup.layers()) self.assertIn(layer_c, editBufferGroup.layers()) self.assertFalse(editBufferGroup.isEditing()) self.assertTrue(editBufferGroup.startEditing()) self.assertTrue(editBufferGroup.isEditing()) self.assertTrue(layer_a.editBuffer()) self.assertTrue(layer_b.editBuffer()) self.assertTrue(layer_c.editBuffer()) self.assertEqual(len(editBufferGroup.modifiedLayers()), 0) commitErrors = [] self.assertTrue(editBufferGroup.commitChanges(commitErrors, False)) self.assertTrue(editBufferGroup.isEditing()) self.assertTrue(editBufferGroup.commitChanges(commitErrors, True)) self.assertFalse(editBufferGroup.isEditing()) self.assertTrue(editBufferGroup.startEditing()) self.assertTrue(editBufferGroup.isEditing()) f = QgsFeature(layer_a.fields()) f.setAttribute('int', 123) f.setGeometry(QgsGeometry.fromWkt('point(7 45)')) self.assertTrue(layer_a.addFeatures([f])) self.assertEqual(len(editBufferGroup.modifiedLayers()), 1) self.assertIn(layer_a, editBufferGroup.modifiedLayers()) # Check feature in layer edit buffer but not in provider till commit self.assertEqual(layer_a.featureCount(), 1) self.assertEqual(layer_a.dataProvider().featureCount(), 0) rollbackErrors = [] self.assertTrue(editBufferGroup.rollBack(rollbackErrors, False)) self.assertTrue(editBufferGroup.isEditing()) self.assertEqual(layer_a.featureCount(), 0) self.assertTrue(layer_a.addFeatures([f])) self.assertEqual(layer_a.featureCount(), 1) self.assertEqual(layer_a.dataProvider().featureCount(), 0) self.assertTrue(editBufferGroup.commitChanges(commitErrors, True)) self.assertFalse(editBufferGroup.isEditing()) self.assertEqual(layer_a.featureCount(), 1) self.assertEqual(layer_a.dataProvider().featureCount(), 1)
def run(self): # pylint: disable=missing-docstring,too-many-locals electorate_geometries, electorate_attributes = self.calculate_new_electorates() # we also need a dictionary of meshblock number to all electorate types meshblock_electorates = {} electorate_features = [] for electorate_feature_id, attributes in electorate_attributes.items(): electorate_code = attributes[self.ELECTORATE_CODE] geometry = electorate_geometries[electorate_feature_id] meshblocks = attributes[self.MESHBLOCKS] electorate_type = attributes[self.ELECTORATE_TYPE] name = attributes[self.ELECTORATE_NAME] for m in meshblocks: meshblock_number = m[self.meshblock_number_idx] if meshblock_number not in meshblock_electorates: meshblock_electorates[meshblock_number] = {} meshblock_electorates[meshblock_number][electorate_type] = electorate_code electorate_feature = QgsFeature() electorate_feature.setGeometry(geometry) electorate_feature.setAttributes([electorate_type, electorate_code, name]) electorate_features.append(electorate_feature) electorate_layer = QgsVectorLayer( "Polygon?crs=EPSG:2193&field=type:string(2)&field=code:string&field=name:string", "source", "memory") if not electorate_layer.dataProvider().addFeatures(electorate_features): return False options = QgsVectorFileWriter.SaveVectorOptions() options.driverName = 'GPKG' options.layerName = 'electorates' options.actionOnExistingFile = QgsVectorFileWriter.CreateOrOverwriteFile error, self.message = QgsVectorFileWriter.writeAsVectorFormat(electorate_layer, self.dest_file, options) if error: return False layer = QgsVectorLayer( "NoGeometry?field=meshblock_number:int&field=gn_code:string&field=gs_code:string&field=m_code:string", "source", "memory") meshblock_features = [] for meshblock_number, electorates in meshblock_electorates.items(): f = QgsFeature() gn = electorates[self.GN] if self.GN in electorates else NULL gs = electorates[self.GS] if self.GS in electorates else NULL m = electorates[self.M] if self.M in electorates else NULL f.setAttributes([meshblock_number, gn, gs, m]) meshblock_features.append(f) layer.dataProvider().addFeatures(meshblock_features) options = QgsVectorFileWriter.SaveVectorOptions() options.driverName = 'GPKG' options.layerName = 'meshblocks' options.actionOnExistingFile = QgsVectorFileWriter.CreateOrOverwriteLayer error, self.message = QgsVectorFileWriter.writeAsVectorFormat(layer, self.dest_file, options) if error: return False options = QgsVectorFileWriter.SaveVectorOptions() options.driverName = 'GPKG' options.layerName = 'user_log' options.actionOnExistingFile = QgsVectorFileWriter.CreateOrOverwriteLayer error, self.message = QgsVectorFileWriter.writeAsVectorFormat(self.user_log_layer, self.dest_file, options) if error: return False return True
def run(self): """Run method that performs all the real work""" # Create the dialog with elements (after translation) and keep reference # Only create GUI ONCE in callback, so that it will only load when the plugin is started if self.first_start == True: self.first_start = False self.dlg = SurvexImportDialog() self.dlg.selectedFile.clear() self.dlg.fileSelector.clicked.connect(self.select_3d_file) self.dlg.selectedGPKG.clear() self.dlg.GPKGSelector.clicked.connect(self.select_gpkg) self.dlg.CRSFromProject.setChecked(False) self.dlg.CRSFromFile.clicked.connect(self.crs_from_file) self.dlg.CRSFromFile.setChecked(False) self.dlg.CRSFromProject.clicked.connect(self.crs_from_project) self.dlg.ImportAll.clicked.connect(self.toggle_import_all) self.dlg.Legs.clicked.connect(self.all_checked) self.dlg.Stations.clicked.connect(self.all_checked) self.dlg.Polygons.clicked.connect(self.all_checked) self.dlg.Walls.clicked.connect(self.all_checked) self.dlg.XSections.clicked.connect(self.all_checked) self.dlg.Traverses.clicked.connect(self.all_checked) self.dlg.LegsSurface.clicked.connect(self.all_checked) self.dlg.LegsSplay.clicked.connect(self.all_checked) self.dlg.LegsDuplicate.clicked.connect(self.all_checked) self.dlg.StationsSurface.clicked.connect(self.all_checked) self.dlg.show() # show the dialog result = self.dlg.exec_() # Run the dialog event loop if result: # The user pressed OK, and this is what happened next! survex_3d = self.dlg.selectedFile.text() gpkg_file = self.dlg.selectedGPKG.text() include_legs = self.dlg.Legs.isChecked() include_stations = self.dlg.Stations.isChecked() include_polygons = self.dlg.Polygons.isChecked() include_walls = self.dlg.Walls.isChecked() include_xsections = self.dlg.XSections.isChecked() include_traverses = self.dlg.Traverses.isChecked() exclude_surface_legs = not self.dlg.LegsSurface.isChecked() exclude_splay_legs = not self.dlg.LegsSplay.isChecked() exclude_duplicate_legs = not self.dlg.LegsDuplicate.isChecked() exclude_surface_stations = not self.dlg.StationsSurface.isChecked() use_clino_wgt = self.dlg.UseClinoWeights.isChecked() include_up_down = self.dlg.IncludeUpDown.isChecked() discard_features = not self.dlg.KeepFeatures.isChecked() if not os.path.exists(survex_3d): raise Exception("File '%s' doesn't exist" % survex_3d) if discard_features: self.leg_list = [] self.station_list = [] self.station_xyz = {} self.xsect_list = [] # Read .3d file as binary, parse, and save data structures with open(survex_3d, 'rb') as fp: line = fp.readline().rstrip() # File ID check if not line.startswith(b'Survex 3D Image File'): raise IOError('Not a survex .3d file: ' + survex_3d) line = fp.readline().rstrip() # File format version if not line.startswith(b'v'): raise IOError('Unrecognised survex .3d version in ' + survex_3d) version = int(line[1:]) if version < 8: raise IOError('Survex .3d version >= 8 required in ' + survex_3d) line = fp.readline().rstrip( ) # Metadata (title and coordinate system) fields = line.split(b'\x00') previous_title = '' if discard_features else self.title if previous_title: self.title = previous_title + ' + ' + fields[0].decode( 'ascii') else: self.title = fields[0].decode('ascii') self.set_crs( fields[1].decode('ascii') if len(fields) > 1 else None) line = fp.readline().rstrip( ) # Timestamp, unused in present application if not line.startswith(b'@'): raise IOError('Unrecognised timestamp in ' + survex_3d) # timestamp = int(line[1:]) flag = ord(fp.read(1)) # file-wide flag if flag & 0x80: # abort if extended elevation raise IOError("Can't deal with extended elevation in " + survex_3d) # All file-wide header data read in, now read byte-wise # according to .3d spec. Note that all elements must # be processed, in order, otherwise we get out of sync. # We first define some baseline dates date0 = QDate(1900, 1, 1) date1 = QDate(1900, 1, 1) date2 = QDate(1900, 1, 1) label, style = '', 0xff # initialise label and style legs = [] # will be used to capture leg data between MOVEs xsect = [] # will be used to capture XSECT data nlehv = None # .. remains None if there isn't any error data... while True: # start of byte-gobbling while loop char = fp.read(1) if not char: # End of file (reached prematurely?) raise IOError('Premature end of file in ' + survex_3d) byte = ord(char) if byte <= 0x05: # STYLE if byte == 0x00 and style == 0x00: # this signals end of data if legs: # there may be a pending list of legs to save self.leg_list.append((legs, nlehv)) break # escape from byte-gobbling while loop else: style = byte elif byte <= 0x0e: # Reserved continue elif byte == 0x0f: # MOVE xyz = self.read_xyz(fp) if legs: self.leg_list.append((legs, nlehv)) legs = [] elif byte == 0x10: # DATE (none) date1 = date2 = date0 elif byte == 0x11: # DATE (single date) days = unpack('<H', fp.read(2))[0] date1 = date2 = date0.addDays(days) elif byte == 0x12: # DATE (date range, short format) days, extra = unpack('<HB', fp.read(3)) date1 = date0.addDays(days) date2 = date0.addDays(days + extra + 1) elif byte == 0x13: # DATE (date range, long format) days1, days2 = unpack('<HH', fp.read(4)) date1 = date0.addDays(days1) date2 = date0.addDays(days2) elif byte <= 0x1e: # Reserved continue elif byte == 0x1f: # Error info nlehv = unpack('<iiiii', fp.read(20)) elif byte <= 0x2f: # Reserved continue elif byte <= 0x33: # XSECT label = self.read_label(fp, label) if byte & 0x02: lrud = unpack('<iiii', fp.read(16)) else: lrud = unpack('<hhhh', fp.read(8)) xsect.append((label, lrud)) if byte & 0x01: # XSECT_END self.xsect_list.append(xsect) xsect = [] elif byte <= 0x3f: # Reserved continue elif byte <= 0x7f: # LINE flag = byte & 0x3f if not (flag & 0x20): label = self.read_label(fp, label) xyz_prev = xyz xyz = self.read_xyz(fp) while (True): # code pattern to implement logic if exclude_surface_legs and flag & 0x01: break if exclude_duplicate_legs and flag & 0x02: break if exclude_splay_legs and flag & 0x04: break legs.append(((xyz_prev, xyz), label, style, date1, date2, flag)) break elif byte <= 0xff: # LABEL (or NODE) flag = byte & 0x7f label = self.read_label(fp, label) xyz = self.read_xyz(fp) while (True): # code pattern to implement logic if exclude_surface_stations and flag & 0x01 and not flag & 0x02: break self.station_list.append((xyz, label, flag)) break self.station_xyz[label] = xyz # End of byte-gobbling while loop # file closes automatically, with open(survex_3d, 'rb') as fp: layers = [] # used to keep a list of the created layers if include_stations and self.station_list: # station layer station_layer = self.add_layer('stations', 'PointZ') attrs = [ QgsField(self.station_attr[k], QVariant.Int) for k in self.station_flags ] attrs.insert(0, QgsField('ELEVATION', QVariant.Double)) attrs.insert(0, QgsField('NAME', QVariant.String)) station_layer.dataProvider().addAttributes(attrs) station_layer.updateFields() features = [] for (xyz, label, flag) in self.station_list: xyz = [0.01 * v for v in xyz] attrs = [1 if flag & k else 0 for k in self.station_flags] attrs.insert(0, round(xyz[2], 2)) # elevation attrs.insert(0, label) feat = QgsFeature() geom = QgsGeometry(QgsPoint(*xyz)) feat.setGeometry(geom) feat.setAttributes(attrs) features.append(feat) station_layer.dataProvider().addFeatures(features) layers.append(station_layer) if include_legs and self.leg_list: # leg layer leg_layer = self.add_layer('legs', 'LineStringZ') attrs = [ QgsField(self.leg_attr[k], QVariant.Int) for k in self.leg_flags ] if nlehv: [ attrs.insert(0, QgsField(s, QVariant.Double)) for s in self.error_fields ] attrs.insert(0, QgsField('NLEGS', QVariant.Int)) attrs.insert(0, QgsField('DATE2', QVariant.Date)) attrs.insert(0, QgsField('DATE1', QVariant.Date)) attrs.insert(0, QgsField('STYLE', QVariant.String)) attrs.insert(0, QgsField('ELEVATION', QVariant.Double)) attrs.insert(0, QgsField('NAME', QVariant.String)) leg_layer.dataProvider().addAttributes(attrs) leg_layer.updateFields() features = [] for legs, nlehv in self.leg_list: for (xyz_pair, label, style, from_date, to_date, flag) in legs: elev = 0.5 * sum([0.01 * xyz[2] for xyz in xyz_pair]) points = [] for xyz in xyz_pair: xyz = [0.01 * v for v in xyz] points.append(QgsPoint(*xyz)) attrs = [1 if flag & k else 0 for k in self.leg_flags] if nlehv: [ attrs.insert(0, 0.01 * v) for v in reversed(nlehv[1:5]) ] attrs.insert(0, nlehv[0]) attrs.insert(0, to_date) attrs.insert(0, from_date) attrs.insert(0, self.style_type[style]) attrs.insert(0, round(elev, 2)) attrs.insert(0, label) linestring = QgsLineString() linestring.setPoints(points) feat = QgsFeature() geom = QgsGeometry(linestring) feat.setGeometry(geom) feat.setAttributes(attrs) features.append(feat) leg_layer.dataProvider().addFeatures(features) layers.append(leg_layer) # Now do wall features if asked if (include_traverses or include_xsections or include_walls or include_polygons) and self.xsect_list: trav_features = [] wall_features = [] xsect_features = [] quad_features = [] for xsect in self.xsect_list: if len(xsect) < 2: # if there's only one station .. continue # .. give up as we don't know which way to face centerline = [ ] # will contain the station position and LRUD data for label, lrud in xsect: xyz = self.station_xyz[ label] # look up coordinates from label lrud_or_zero = tuple([max(0, v) for v in lrud ]) # deal with missing data centerline.append( xyz + lrud_or_zero) # and collect as 7-uple direction = [ ] # will contain the corresponding direction vectors # The calculations below use integers for xyz and lrud, and # conversion to metres is left to the end. Then dh2 is an # integer and the test for a plumb is safely dh2 = 0. # The directions are unit vectors optionally weighted by # cos(inclination) = dh/dl where dh^2 = dx^2 + dy^2 (note, no dz^2), # and dl^2 = dh^2 + dz^2. The normalisation is correspondingly # either 1/dh, or 1/dh * dh/dl = 1/dl. for i, xyzlrud in enumerate(centerline): x, y, z = xyzlrud[0:3] if i > 0: dx, dy, dz = x - xp, y - yp, z - zp dh2 = dx * dx + dy * dy # integer horizontal displacement (mm^2) norm = sqrt(dh2 + dz * dz) if use_clino_wgt else sqrt(dh2) dx, dy = (dx / norm, dy / norm) if dh2 > 0 and norm > 0 else (0, 0) direction.append((dx, dy)) xp, yp, zp = x, y, z left_wall = [] right_wall = [] up_down = [] # We build the walls by walking through the list # of stations and directions, with simple defaults # for the start and end stations for i, (x, y, z, l, r, u, d) in enumerate(centerline): d1x, d1y = direction[i - 1] if i > 0 else (0, 0) d2x, d2y = direction[i] if i + 1 < len( centerline) else (0, 0) dx, dy = d1x + d2x, d1y + d2y # mean (sum of) direction vectors norm = sqrt(dx * dx + dy * dy) # normalise to unit vector ex, ey = (dx / norm, dy / norm) if norm > 0 else (0, 0) # Convert to metres when saving the points left_wall.append((0.01 * (x - l * ey), 0.01 * (y + l * ex), 0.01 * z)) right_wall.append((0.01 * (x + r * ey), 0.01 * (y - r * ex), 0.01 * z)) up_down.append((0.01 * u, 0.01 * d)) # Mean elevation of centerline, used for elevation attribute elev = 0.01 * sum([xyzlrud[2] for xyzlrud in centerline ]) / len(centerline) attrs = [round(elev, 2)] # Now create the feature sets - first the centerline traverse points = [] for xyzlrud in centerline: xyz = [0.01 * v for v in xyzlrud[0:3] ] # These were mm, convert to metres points.append(QgsPoint(*xyz)) linestring = QgsLineString() linestring.setPoints(points) feat = QgsFeature() geom = QgsGeometry(linestring) feat.setGeometry(geom) feat.setAttributes(attrs) trav_features.append(feat) # The walls as line strings for wall in (left_wall, right_wall): points = [QgsPoint(*xyz) for xyz in wall] linestring = QgsLineString() linestring.setPoints(points) feat = QgsFeature() geom = QgsGeometry(linestring) feat.setGeometry(geom) feat.setAttributes(attrs) wall_features.append(feat) # Slightly more elaborate, pair up points on left # and right walls, and build a cross section as a # 2-point line string, and a quadrilateral polygon # with a closed 5-point line string for the # exterior ring. Note that QGIS polygons are # supposed to have their points ordered clockwise. for i, xyz_pair in enumerate(zip(left_wall, right_wall)): elev = 0.01 * centerline[i][ 2] # elevation of station in centerline attrs = [round(elev, 2)] points = [QgsPoint(*xyz) for xyz in xyz_pair] linestring = QgsLineString() linestring.setPoints(points) feat = QgsFeature() geom = QgsGeometry(linestring) feat.setGeometry(geom) feat.setAttributes(attrs) xsect_features.append(feat) if i > 0: elev = 0.5 * (prev_xyz_pair[0][2] + xyz_pair[0][2] ) # average elevation attrs = [round(elev, 2)] if include_up_down: # average up / down attrs += [ 0.5 * (v1 + v2) for (v1, v2) in zip(up_down[i - 1], up_down[i]) ] points = [ ] # will contain the exterior 5-point ring, as follows... for xyz in tuple( reversed(prev_xyz_pair)) + xyz_pair + ( prev_xyz_pair[1], ): points.append(QgsPoint(*xyz)) linestring = QgsLineString() linestring.setPoints(points) polygon = QgsPolygon() polygon.setExteriorRing(linestring) feat = QgsFeature() geom = QgsGeometry(polygon) feat.setGeometry(geom) feat.setAttributes(attrs) quad_features.append(feat) prev_xyz_pair = xyz_pair # End of processing xsect_list - now add features to requested layers attrs = [QgsField('ELEVATION', QVariant.Double)] # common to all if include_traverses and trav_features: # traverse layer travs_layer = self.add_layer('traverses', 'LineStringZ') travs_layer.dataProvider().addAttributes(attrs) travs_layer.updateFields() travs_layer.dataProvider().addFeatures(trav_features) layers.append(travs_layer) if include_xsections and xsect_features: # xsection layer xsects_layer = self.add_layer('xsections', 'LineStringZ') xsects_layer.dataProvider().addAttributes(attrs) xsects_layer.updateFields() xsects_layer.dataProvider().addFeatures(xsect_features) layers.append(xsects_layer) if include_walls and wall_features: # wall layer walls_layer = self.add_layer('walls', 'LineStringZ') walls_layer.dataProvider().addAttributes(attrs) walls_layer.updateFields() walls_layer.dataProvider().addFeatures(wall_features) layers.append(walls_layer) if include_up_down: # add fields if requested for polygons attrs += [ QgsField(s, QVariant.Double) for s in ('MEAN_UP', 'MEAN_DOWN') ] if include_polygons and quad_features: # polygon layer quads_layer = self.add_layer('polygons', 'PolygonZ') quads_layer.dataProvider().addAttributes(attrs) quads_layer.updateFields() quads_layer.dataProvider().addFeatures(quad_features) layers.append(quads_layer) # All layers have been created, now update extents and add to QGIS registry if layers: [layer.updateExtents() for layer in layers] QgsProject.instance().addMapLayers(layers) # Write to GeoPackage if requested if gpkg_file: opts = [ QgsVectorFileWriter.CreateOrOverwriteFile, QgsVectorFileWriter.CreateOrOverwriteLayer ] for i, layer in enumerate(layers): options = QgsVectorFileWriter.SaveVectorOptions() options.actionOnExistingFile = opts[int( i > 0)] # create file or layer layer_name = layer.name() match = search( ' - ([a-z]*)', layer_name) # ie, extract 'legs', 'stations', etc options.layerName = str( match.group(1)) if match else layer_name writer = QgsVectorFileWriter.writeAsVectorFormat( layer, gpkg_file, options) if writer: msg = "'{}' -> {} in {}".format( layer_name, options.layerName, gpkg_file) QgsMessageLog.logMessage(msg, tag='Import .3d', level=Qgis.Info) options, writer = None, None
def _init_ext_layer(self, geom_str, idx, crs): """given non map of feat, init a qgis layer :map_feat: {geom_string: list_of_feat} """ ext = self.ext driver_name = ext.upper() # might not needed for layer_name = self._layer_name(geom_str, idx) # sqlite max connection 64 # if xyz space -> more than 64 vlayer, # then create new fname # fname = make_unique_full_path(ext=ext) fname = make_fixed_full_path(self._layer_fname(), ext=ext) if geom_str: geomz = geom_str if geom_str.endswith("Z") else "{}Z".format( geom_str) else: geomz = "NoGeometry" vlayer = QgsVectorLayer("{geom}?crs={crs}&index=yes".format(geom=geomz, crs=crs), layer_name, "memory") # this should be done in main thread # QgsVectorFileWriter.writeAsVectorFormat(vlayer, fname, "UTF-8", vlayer.sourceCrs(), # driver_name) db_layer_name = self._db_layer_name(geom_str, idx) options = QgsVectorFileWriter.SaveVectorOptions() options.fileEncoding = "UTF-8" options.driverName = driver_name options.ct = QgsCoordinateTransform(vlayer.sourceCrs(), vlayer.sourceCrs(), QgsProject.instance()) options.layerName = db_layer_name options.actionOnExistingFile = QgsVectorFileWriter.CreateOrOverwriteLayer # update mode if hasattr(QgsVectorFileWriter, "writeAsVectorFormatV2"): err = QgsVectorFileWriter.writeAsVectorFormatV2( vlayer, fname, vlayer.transformContext(), options) else: err = QgsVectorFileWriter.writeAsVectorFormat( vlayer, fname, options) if err[0] == QgsVectorFileWriter.ErrCreateDataSource: options.actionOnExistingFile = QgsVectorFileWriter.CreateOrOverwriteFile if hasattr(QgsVectorFileWriter, "writeAsVectorFormatV2"): err = QgsVectorFileWriter.writeAsVectorFormatV2( vlayer, fname, vlayer.transformContext(), options) else: err = QgsVectorFileWriter.writeAsVectorFormat( vlayer, fname, options) if err[0] != QgsVectorFileWriter.NoError: raise Exception("%s: %s" % err) self._update_constraint_trigger(fname, db_layer_name) uri = "%s|layername=%s" % (fname, db_layer_name) vlayer = QgsVectorLayer(uri, layer_name, "ogr") self._save_meta_vlayer(vlayer) return vlayer
def testOverwriteLayer(self): """Tests writing a layer with a field value converter.""" ml = QgsVectorLayer('Point?field=firstfield:int', 'test', 'memory') provider = ml.dataProvider() ft = QgsFeature() ft.setAttributes([1]) provider.addFeatures([ft]) options = QgsVectorFileWriter.SaveVectorOptions() options.driverName = 'GPKG' options.layerName = 'test' filename = '/vsimem/out.gpkg' write_result, error_message = QgsVectorFileWriter.writeAsVectorFormat( ml, filename, options) self.assertEqual(write_result, QgsVectorFileWriter.NoError, error_message) ds = ogr.Open(filename, update=1) lyr = ds.GetLayerByName('test') self.assertIsNotNone(lyr) f = lyr.GetNextFeature() self.assertEqual(f['firstfield'], 1) ds.CreateLayer('another_layer') del f del lyr del ds caps = QgsVectorFileWriter.editionCapabilities(filename) self.assertTrue((caps & QgsVectorFileWriter.CanAddNewLayer)) self.assertTrue((caps & QgsVectorFileWriter.CanAppendToExistingLayer)) self.assertTrue( (caps & QgsVectorFileWriter.CanAddNewFieldsToExistingLayer)) self.assertTrue((caps & QgsVectorFileWriter.CanDeleteLayer)) self.assertTrue(QgsVectorFileWriter.targetLayerExists( filename, 'test')) self.assertFalse( QgsVectorFileWriter.areThereNewFieldsToCreate( filename, 'test', ml, [0])) # Test CreateOrOverwriteLayer ml = QgsVectorLayer('Point?field=firstfield:int', 'test', 'memory') provider = ml.dataProvider() ft = QgsFeature() ft.setAttributes([2]) provider.addFeatures([ft]) options = QgsVectorFileWriter.SaveVectorOptions() options.driverName = 'GPKG' options.layerName = 'test' options.actionOnExistingFile = QgsVectorFileWriter.CreateOrOverwriteLayer filename = '/vsimem/out.gpkg' write_result, error_message = QgsVectorFileWriter.writeAsVectorFormat( ml, filename, options) self.assertEqual(write_result, QgsVectorFileWriter.NoError, error_message) ds = ogr.Open(filename) lyr = ds.GetLayerByName('test') self.assertIsNotNone(lyr) f = lyr.GetNextFeature() self.assertEqual(f['firstfield'], 2) # another_layer should still exist self.assertIsNotNone(ds.GetLayerByName('another_layer')) del f del lyr del ds # Test CreateOrOverwriteFile ml = QgsVectorLayer('Point?field=firstfield:int', 'test', 'memory') provider = ml.dataProvider() ft = QgsFeature() ft.setAttributes([3]) provider.addFeatures([ft]) options = QgsVectorFileWriter.SaveVectorOptions() options.driverName = 'GPKG' options.layerName = 'test' filename = '/vsimem/out.gpkg' write_result, error_message = QgsVectorFileWriter.writeAsVectorFormat( ml, filename, options) self.assertEqual(write_result, QgsVectorFileWriter.NoError, error_message) ds = ogr.Open(filename) lyr = ds.GetLayerByName('test') self.assertIsNotNone(lyr) f = lyr.GetNextFeature() self.assertEqual(f['firstfield'], 3) # another_layer should no longer exist self.assertIsNone(ds.GetLayerByName('another_layer')) del f del lyr del ds # Test AppendToLayerNoNewFields ml = QgsVectorLayer('Point?field=firstfield:int&field=secondfield:int', 'test', 'memory') provider = ml.dataProvider() ft = QgsFeature() ft.setAttributes([4, -10]) provider.addFeatures([ft]) self.assertTrue( QgsVectorFileWriter.areThereNewFieldsToCreate( filename, 'test', ml, [0, 1])) options = QgsVectorFileWriter.SaveVectorOptions() options.driverName = 'GPKG' options.layerName = 'test' options.actionOnExistingFile = QgsVectorFileWriter.AppendToLayerNoNewFields filename = '/vsimem/out.gpkg' write_result, error_message = QgsVectorFileWriter.writeAsVectorFormat( ml, filename, options) self.assertEqual(write_result, QgsVectorFileWriter.NoError, error_message) ds = ogr.Open(filename) lyr = ds.GetLayerByName('test') self.assertEqual(lyr.GetLayerDefn().GetFieldCount(), 1) self.assertIsNotNone(lyr) f = lyr.GetNextFeature() self.assertEqual(f['firstfield'], 3) f = lyr.GetNextFeature() self.assertEqual(f['firstfield'], 4) del f del lyr del ds # Test AppendToLayerAddFields ml = QgsVectorLayer('Point?field=firstfield:int&field=secondfield:int', 'test', 'memory') provider = ml.dataProvider() ft = QgsFeature() ft.setAttributes([5, -1]) provider.addFeatures([ft]) self.assertTrue( QgsVectorFileWriter.areThereNewFieldsToCreate( filename, 'test', ml, [0, 1])) options = QgsVectorFileWriter.SaveVectorOptions() options.driverName = 'GPKG' options.layerName = 'test' options.actionOnExistingFile = QgsVectorFileWriter.AppendToLayerAddFields filename = '/vsimem/out.gpkg' write_result, error_message = QgsVectorFileWriter.writeAsVectorFormat( ml, filename, options) self.assertEqual(write_result, QgsVectorFileWriter.NoError, error_message) ds = ogr.Open(filename) lyr = ds.GetLayerByName('test') self.assertEqual(lyr.GetLayerDefn().GetFieldCount(), 2) self.assertIsNotNone(lyr) f = lyr.GetNextFeature() self.assertEqual(f['firstfield'], 3) if hasattr(f, "IsFieldSetAndNotNull"): # GDAL >= 2.2 self.assertFalse(f.IsFieldSetAndNotNull('secondfield')) else: self.assertFalse(f.IsFieldSet('secondfield')) f = lyr.GetNextFeature() self.assertEqual(f['firstfield'], 4) if hasattr(f, "IsFieldSetAndNotNull"): self.assertFalse(f.IsFieldSetAndNotNull('secondfield')) else: self.assertFalse(f.IsFieldSet('secondfield')) f = lyr.GetNextFeature() self.assertEqual(f['firstfield'], 5) self.assertEqual(f['secondfield'], -1) del f del lyr del ds gdal.Unlink(filename)
def processAlgorithm(self, parameters, context, feedback): """ Here is where the processing itself takes place. """ source_pts = self.parameterAsSource(parameters, self.INPUT_POINTS, context) input_field = self.parameterAsString(parameters, self.INPUT_FIELD, context) source_dem = self.parameterAsRasterLayer(parameters, self.INPUT_DEM, context) out_directory = self.parameterAsString(parameters, self.OUTPUT_DIR, context) out_type_nr = self.parameterAsInt(parameters, self.OUTPUT_TYPE, context) out_type = QgsVectorFileWriter.supportedFormatExtensions( )[:2][out_type_nr] to_gpkg = out_type == 'gpkg' load_results = self.parameterAsBool(parameters, self.LOAD_RESULTS, context) if source_pts is None: raise QgsProcessingException( self.invalidSourceError(parameters, self.INPUT_POINTS)) if source_dem is None: raise QgsProcessingException( self.invalidSourceError(parameters, self.INPUT_DEM)) feedback.pushInfo("Input data loaded! Creating catchments...") feedback.setProgress(1) unique_field = input_field if input_field else "" if unique_field: field_idx = source_pts.fields().lookupField(unique_field) unique_values = source_pts.uniqueValues(field_idx) else: unique_values = [f.id() for f in source_pts.getFeatures()] feedback.pushInfo(f"Creating directory: {out_directory}") mkdir(out_directory) bname = f"catchment{'s' if to_gpkg else ''}" output_basename = os.path.join(out_directory, bname) # Compute the number of steps to display within the progress bar and # get features from source total_nr = len(unique_values) total = 100. / total_nr if source_pts.featureCount() else 1 output_layers = [] for i, unique_value in enumerate(unique_values): # Stop the algorithm if cancel button has been clicked if feedback.isCanceled(): break table = f"catchment_{unique_value}" if to_gpkg else "" file_mod = "" if to_gpkg else f"_{unique_value}" filename = f"{output_basename}{file_mod}" destination = f"{filename}.{out_type}" output_uri = destination + (f"|layername={table}" if to_gpkg else "") feedback.pushInfo( self.tr('Creating layer: {}').format(destination)) if unique_field: req_filter = f"{QgsExpression.quotedColumnRef(unique_field)}={QgsExpression.quotedValue(unique_value)}" req = QgsFeatureRequest().setFilterExpression(req_filter) else: req = QgsFeatureRequest(unique_value) # feature id for source_pt in source_pts.getFeatures(req): if feedback.isCanceled(): break # Get x and y coordinate from point feature geom = source_pt.geometry() p = geom.asPoint() x = p.x() y = p.y() feedback.pushInfo( 'Creating upslope area for point ({:.2f}, {:.2f}) - {} of {}' .format(x, y, i + 1, total_nr)) # Calculate catchment raster from point feature catchraster = processing.run( "saga:upslopearea", { 'TARGET': None, 'TARGET_PT_X': x, 'TARGET_PT_Y': y, 'ELEVATION': source_dem, 'SINKROUTE': None, 'METHOD': 0, 'CONVERGE': 1.1, 'AREA': 'TEMPORARY_OUTPUT' }, context=context, feedback=feedback, ) # Polygonize raster catchment catchpoly = processing.run( "gdal:polygonize", { 'INPUT': catchraster["AREA"], 'BAND': 1, 'FIELD': 'DN', 'EIGHT_CONNECTEDNESS': False, 'OUTPUT': 'TEMPORARY_OUTPUT' }, context=context, feedback=feedback, ) # Select features having DN = 100 catchpoly_lyr = QgsProcessingUtils.mapLayerFromString( catchpoly["OUTPUT"], context=context) catchpoly_lyr.selectByExpression('"DN"=100') options = QgsVectorFileWriter.SaveVectorOptions() options.driverName = "GPKG" if to_gpkg else "ESRI Shapefile" options.layerName = table options.actionOnExistingFile = QgsVectorFileWriter.CreateOrOverwriteLayer options.onlySelectedFeatures = True trans_context = QgsCoordinateTransformContext() write_result, error_message = QgsVectorFileWriter.writeAsVectorFormatV2( catchpoly_lyr, destination, trans_context, options) if write_result != 0: feedback.pushInfo(f"Initial write failed: {error_message}") # retry with option for creating the dataset options.actionOnExistingFile = QgsVectorFileWriter.CreateOrOverwriteFile write_result, error_message = QgsVectorFileWriter.writeAsVectorFormatV2( catchpoly_lyr, destination, trans_context, options) feedback.pushInfo( f"Final write attempt: {write_result} == 0 -> SUCCESS or {error_message}" ) output_layer = QgsProcessingUtils.mapLayerFromString( output_uri, context=context) output_layers.append(output_uri) if load_results: context.temporaryLayerStore().addMapLayer(output_layer) context.addLayerToLoadOnCompletion( output_layer.id(), QgsProcessingContext.LayerDetails( table if to_gpkg else f"catchment {unique_value}", context.project(), self.OUTPUT_LAYERS)) feedback.setProgress(int((i + 1) * total)) return { self.OUTPUT_DIR: out_directory, self.OUTPUT_LAYERS: output_layers }