Exemplo n.º 1
0
def save_as_gml(layer, path):
    transform_context = QgsProject.instance().transformContext()
    save_options = QgsVectorFileWriter.SaveVectorOptions()
    save_options.driverName = QgsVectorFileWriter.driverForExtension("gml")
    save_options.fileEncoding = "UTF-8"
    QgsVectorFileWriter.writeAsVectorFormatV2(
        layer,
        path,
        transform_context,
        save_options,
    )
    output = QgsVectorLayer(path, "output", "ogr")
    assert output.isValid()
    return output
Exemplo n.º 2
0
    def save_as_geojson(self, output_path: Path) -> Path:
        output_file = Path(output_path, f"{self.resource_name}.geojson")

        options = QgsVectorFileWriter.SaveVectorOptions()
        options.driverName = "GeoJSON"
        options.fileEncoding = "utf-8"

        src_crs = self.layer.crs()
        dst_crs = QgsCoordinateReferenceSystem("EPSG:4326")
        options.ct = QgsCoordinateTransform(src_crs, dst_crs,
                                            QgsProject.instance())

        if hasattr(QgsVectorFileWriter, "writeAsVectorFormatV3"):
            writer_, msg, _, _ = QgsVectorFileWriter.writeAsVectorFormatV3(
                self.layer,
                str(output_file),
                QgsProject.instance().transformContext(),
                options,
            )
        else:
            writer_, msg = QgsVectorFileWriter.writeAsVectorFormatV2(
                self.layer,
                str(output_file),
                QgsProject.instance().transformContext(),
                options,
            )
        if msg != "":
            raise DataPackageException(
                tr("Could not write layer {} to disk", output_file),
                bar_msg(tr("Check the log for more details")),
            )

        return output_file
Exemplo n.º 3
0
def save_vector_layer_as_gpkg(layer, target_dir, update_datasource=False):
    """Save layer as a single table GeoPackage in the target_dir. Update the original layer datasource if needed.
    If the original layer has already a fid column with non-unique values, it will be renamed to first free fid_x.
    """
    layer_name = remove_forbidden_chars("_".join(layer.name().split()))
    layer_filename = get_unique_filename(
        os.path.join(target_dir, f"{layer_name}.gpkg"))
    transform_context = QgsProject.instance().transformContext()
    writer_opts = QgsVectorFileWriter.SaveVectorOptions()
    writer_opts.fileEncoding = "UTF-8"
    writer_opts.layerName = layer_name
    writer_opts.driverName = "GPKG"
    if layer.fields().lookupField("fid") >= 0:
        converter = FieldConverter(layer)
        writer_opts.fieldValueConverter = converter
    res, err = QgsVectorFileWriter.writeAsVectorFormatV2(
        layer, layer_filename, transform_context, writer_opts)
    if res != QgsVectorFileWriter.NoError:
        return layer_filename, f"Couldn't properly save layer: {layer_filename}. \n{err}"
    if update_datasource:
        provider_opts = QgsDataProvider.ProviderOptions()
        provider_opts.fileEncoding = "UTF-8"
        provider_opts.layerName = layer_name
        provider_opts.driverName = "GPKG"
        datasource = f"{layer_filename}|layername={layer_name}"
        layer.setDataSource(datasource, layer_name, "ogr", provider_opts)
    return layer_filename, None
Exemplo n.º 4
0
    def response_gpx_mode(self, request):
        """
        Download GPX of data
        :param request: Http Django request object
        :return: http response with attached file
        """

        if not self.layer.download_gpx:
            return HttpResponseForbidden()

        # check for vector type
        if self.metadata_layer.qgis_layer.geometryType(
        ) == QgsWkbTypes.PolygonGeometry:
            return HttpResponseForbidden()

        tmp_dir = tempfile.TemporaryDirectory()

        filename = self.metadata_layer.qgis_layer.name()

        # 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.qgis_layer,
                                       qgs_request, self)

        save_options = QgsVectorFileWriter.SaveVectorOptions()
        save_options.driverName = 'GPX'
        save_options.fileEncoding = 'utf-8'
        save_options.datasourceOptions = [
            "GPX_USE_EXTENSIONS=1",
            "GPX_EXTENSIONS_NS_URL=http://osgeo.org/gdal",
            "GPX_EXTENSIONS_NS=ogr"
        ]

        filename = self.metadata_layer.qgis_layer.name() + '.gpx'

        # Make a selection based on the request
        self._selection_responde_download_mode(qgs_request, save_options)

        gpx_tmp_path = os.path.join(tmp_dir.name, filename)
        error_code, error_message = QgsVectorFileWriter.writeAsVectorFormatV2(
            self.metadata_layer.qgis_layer, gpx_tmp_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)

        response = HttpResponse(open(gpx_tmp_path, 'rb').read(),
                                content_type='application/octet-stream')
        tmp_dir.cleanup()
        response['Content-Disposition'] = f'attachment; filename={filename}'
        response.set_cookie('fileDownload', 'true')
        return response
Exemplo n.º 5
0
    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

        if hasattr(QgsVectorFileWriter, "writeAsVectorFormatV3"):
            # noinspection PyCallByClass
            writer_, msg, _, _ = QgsVectorFileWriter.writeAsVectorFormatV3(layer, str(output_file),
                                                                           QgsProject.instance().transformContext(),
                                                                           options)
        else:
            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
Exemplo n.º 6
0
    def testShapefilesWithNoAttributes(self):
        """Test issue GH #38834"""

        ml = QgsVectorLayer('Point?crs=epsg:4326', 'test', 'memory')
        self.assertTrue(ml.isValid())

        d = QTemporaryDir()
        options = QgsVectorFileWriter.SaveVectorOptions()
        options.driverName = 'ESRI Shapefile'
        options.layerName = 'writetest'
        err, _ = QgsVectorFileWriter.writeAsVectorFormatV2(
            ml, os.path.join(d.path(), 'writetest.shp'),
            QgsCoordinateTransformContext(), options)
        self.assertEqual(err, QgsVectorFileWriter.NoError)
        self.assertTrue(os.path.isfile(os.path.join(d.path(),
                                                    'writetest.shp')))

        vl = QgsVectorLayer(os.path.join(d.path(), 'writetest.shp'))
        self.assertTrue(
            bool(vl.dataProvider().capabilities()
                 & QgsVectorDataProvider.AddFeatures))

        # Let's try if we can really add features
        feature = QgsFeature(vl.fields())
        geom = QgsGeometry.fromWkt('POINT(9 45)')
        feature.setGeometry(geom)
        self.assertTrue(vl.startEditing())
        self.assertTrue(vl.addFeatures([feature]))
        self.assertTrue(vl.commitChanges())
        del (vl)

        vl = QgsVectorLayer(os.path.join(d.path(), 'writetest.shp'))
        self.assertEqual(vl.featureCount(), 1)
Exemplo n.º 7
0
    def export_layer_to_shape(self, context, sql_layer, dpt, table, dirname, feedback):
        # Construction filename
        prefixe = "N_3V_"
        suffixe = "_" + dpt
        tablename = table.upper()
        if table == 'element':
            tablename = 'R_' + tablename + '_PORTION'
        if 'poi' in table and 'portion' not in table:
            tablename = tablename[4:]
        if "val" in table:
            prefixe = "3V_"
            suffixe = ""
            if "avancement" in table:
                tablename = "AVANCEMENT_VAL"
            if "statut" in table:
                tablename = "STATUT_VAL"
            if "portion" in table or "repere" in table:
                tablename = "TYPE" + tablename
        geomtype = {
            QgsWkbTypes.LineGeometry: '_L',
            QgsWkbTypes.PointGeometry: '_P',
            QgsWkbTypes.NullGeometry: ''
        }
        geomcode = geomtype[sql_layer.geometryType()]
        filename = prefixe + tablename + geomcode + suffixe

        transform_context = context.project().transformContext()
        options = QgsVectorFileWriter.SaveVectorOptions()
        options.driverName = "ESRI ShapeFile"
        options.fileEncoding = "utf-8"

        # construction du répertoire
        if not os.path.exists(dirname):
            os.mkdir(dirname)
        ext = '.shp'
        if geomcode == '':
            ext = '.dbf'
        file_path = os.path.join(dirname, filename + ext)

        # Enregistrement du fichier shape
        error = QgsVectorFileWriter.writeAsVectorFormatV2(
            layer=sql_layer,
            fileName=file_path,
            transformContext=transform_context,
            options=options
        )

        # Vérification du retour de l'enregistrement et des erreurs potentiels
        if error[0] == QgsVectorFileWriter.ErrCreateLayer:
            raise QgsProcessingException(
                "Erreur lors de l'écriture du fichier :\n"
                "Vérifiez que le chemin de destination est valide")
        if error[0] != QgsVectorFileWriter.NoError:
            raise QgsProcessingException("Erreur lors de l'écriture du fichier :" + error[1])
        return [file_path, filename]
Exemplo n.º 8
0
        def _write_to_ogr(destination_path, new_layer_name, driverName=None):
            """Writes features to new or existing OGR layer"""

            tmp_dir = QTemporaryDir()
            tmp_path = os.path.join(tmp_dir.path(), 'isochrone.json')
            with open(tmp_path, 'w+') as f:
                f.write(geojson)

            tmp_layer = QgsVectorLayer(tmp_path, 'tmp_isochrone', 'ogr')

            if not tmp_layer.isValid():
                raise Exception(
                    _('Cannot create temporary layer for isochrone result.'))

            # Note: shp attribute names are max 10 chars long
            save_options = QgsVectorFileWriter.SaveVectorOptions()
            if driverName is not None:
                save_options.driverName = driverName
            save_options.layerName = new_layer_name
            save_options.fileEncoding = 'utf-8'

            # This is nonsense to me: if the file does not exist the actionOnExistingFile
            # should be ignored instead of raising an error, probable QGIS bug
            if os.path.exists(destination_path):
                # Check if the layer already exists
                layer_exists = QgsVectorFileWriter.targetLayerExists(
                    destination_path, new_layer_name)

                if layer_exists:
                    raise Exception(
                        _('Cannot save isochrone result to destination layer: layer already exists (use "qgis_layer_id" instead)!'
                          ))

                save_options.actionOnExistingFile = QgsVectorFileWriter.CreateOrOverwriteLayer

            error_code, error_message = QgsVectorFileWriter.writeAsVectorFormatV2(
                tmp_layer, destination_path,
                project.qgis_project.transformContext(), save_options)

            if error_code != QgsVectorFileWriter.NoError:
                raise Exception(
                    _('Cannot save isochrone result to destination layer: ') +
                    error_message)

            layer_uri = destination_path

            if driverName != 'ESRI Shapefile':
                layer_uri += '|layername=' + new_layer_name

            provider = 'ogr'
            return layer_uri, provider
Exemplo n.º 9
0
 def writeAsVectorFormat(self, name, driver_name, target_crs=None):
     transform_context = QgsProject.instance().transformContext()
     save_options = QgsVectorFileWriter.SaveVectorOptions()
     save_options.driverName = driver_name
     save_options.fileEncoding = "UTF-8"
     save_options.onlySelectedFeatures = self.selectedFeatureCount() != 0
     if target_crs is not None or target_crs != self.crs():
         save_options.ct = QgsCoordinateTransform(
             self.crs(),
             QgsCoordinateReferenceSystem(target_crs),
             transform_context,
         )
     return QgsVectorFileWriter.writeAsVectorFormatV2(
         self, name, transform_context, save_options)
Exemplo n.º 10
0
    def response_csv_mode(self, request):
        """
        Download csv of data
        :param request: Http Django request object
        :return: http response with attached file
        """

        if not self.layer.download_csv:
            return HttpResponseForbidden()

        # 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 = 'csv'
        save_options.fileEncoding = 'utf-8'

        tmp_dir = tempfile.TemporaryDirectory()

        filename = self._build_download_filename(request) + '.csv'

        # Make a selection based on the request
        self._selection_responde_download_mode(qgs_request, save_options)

        xls_tmp_path = os.path.join(tmp_dir.name, filename)
        error_code, error_message = QgsVectorFileWriter.writeAsVectorFormatV2(
            self.metadata_layer.qgis_layer,
            xls_tmp_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)

        response = HttpResponse(
            open(xls_tmp_path, 'rb').read(), content_type='text/csv')
        tmp_dir.cleanup()
        response['Content-Disposition'] = f'attachment; filename={filename}'
        response.set_cookie('fileDownload', 'true')
        return response
Exemplo n.º 11
0
    def export_as_xlsx(context, file_path, input_layer) -> None:
        """ Export the layer to XLSX to the given path. """
        options = QgsVectorFileWriter.SaveVectorOptions()
        options.driverName = QgsVectorFileWriter.driverForExtension('xlsx')
        options.fileEncoding = 'UTF-8'
        options.layerName = input_layer.name()
        options.layerOptions = ['OGR_XLSX_FIELD_TYPES=AUTO']

        write_result, error_message = QgsVectorFileWriter.writeAsVectorFormatV2(
            input_layer,
            file_path,
            context.project().transformContext(),
            options,
        )
        if write_result != QgsVectorFileWriter.NoError:
            raise QgsProcessingException(error_message)
Exemplo n.º 12
0
def write_layer_to_gpkg2(layer, gpkgfile, layername):

    options = QgsVectorFileWriter.SaveVectorOptions()
    from pathlib import Path
    if Path(gpkgfile).exists():
        options.actionOnExistingFile = QgsVectorFileWriter.CreateOrOverwriteLayer
    else:
        options.actionOnExistingFile = QgsVectorFileWriter.CreateOrOverwriteFile

    # to get rid of spaces in the layer name
    options.layerName = layername
    context = QgsProject.instance().transformContext()

    if hasattr(QgsVectorFileWriter, "writeAsVectorFormatV3"):
        return QgsVectorFileWriter.writeAsVectorFormatV3(layer, gpkgfile, context, options)
    else:
        return QgsVectorFileWriter.writeAsVectorFormatV2(layer, gpkgfile, context, options)
Exemplo n.º 13
0
def save_layer_as(orig_layer, dest_path, save_format, crs=None):
    if crs is None:
        crs = orig_layer.crs()
    old_lc_numeric = locale.getlocale(locale.LC_NUMERIC)
    locale.setlocale(locale.LC_NUMERIC, 'C')
    if Qgis.QGIS_VERSION_INT < 31003:
        writer_error = QgsVectorFileWriter.writeAsVectorFormat(
            orig_layer, dest_path, 'utf-8', crs, save_format)
    else:
        options = QgsVectorFileWriter.SaveVectorOptions()
        options.fileEncoding = 'utf8'
        options.driverName = save_format
        ctc = QgsCoordinateTransformContext()
        if crs is not None:
            ctc.addCoordinateOperation(orig_layer.crs(), crs, 'proj')
        writer_error = QgsVectorFileWriter.writeAsVectorFormatV2(
            orig_layer, dest_path, ctc, options)
    locale.setlocale(locale.LC_NUMERIC, old_lc_numeric)
    return writer_error
Exemplo n.º 14
0
    def save_as_geojson(self, output_path: Path) -> Path:
        output_file = Path(output_path, f"{self.resource_name}.geojson")

        options = QgsVectorFileWriter.SaveVectorOptions()
        options.driverName = "GeoJSON"
        options.fileEncoding = "utf-8"

        src_crs = self.layer.crs()
        dst_crs = QgsCoordinateReferenceSystem("EPSG:4326")
        options.ct = QgsCoordinateTransform(src_crs, dst_crs,
                                            QgsProject.instance())

        writer_, msg = QgsVectorFileWriter.writeAsVectorFormatV2(
            self.layer,
            str(output_file),
            QgsProject.instance().transformContext(),
            options,
        )

        return output_file
Exemplo n.º 15
0
    def send_output_file(self, handler):
        format_dict = WFSFormats[self.format]

        # read the GML
        gml_path = join(self.temp_dir, '{}.gml'.format(self.filename))
        output_layer = QgsVectorLayer(gml_path, 'qgis_server_wfs_features',
                                      'ogr')

        if not output_layer.isValid():
            handler.appendBody(b'')
            self.logger.critical(
                'Output layer {} is not valid.'.format(gml_path))
            return False

        # Temporary file where to write the output
        temporary = QTemporaryFile(
            join(
                self.temp_dir,
                'to-{}-XXXXXX.{}'.format(self.format,
                                         format_dict['filenameExt'])))
        temporary.open()
        output_file = temporary.fileName()
        temporary.remove()  # Fix issue #18

        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
            # noinspection PyUnresolvedReferences
            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'')
            exc_type, _, exc_tb = exc_info()
            self.logger.critical(str(e))
            self.logger.critical(exc_type)
            self.logger.critical('\n'.join(traceback.format_tb(exc_tb)))
            return False

        if format_dict['zip']:
            # compress files
            import zipfile
            try:
                import zlib  # NOQA
                compression = zipfile.ZIP_DEFLATED
            except ImportError:
                compression = zipfile.ZIP_STORED

            # create the zip file
            base_filename = splitext(output_file)[0]
            zip_file_path = join(self.temp_dir, '{}.zip'.format(base_filename))
            with zipfile.ZipFile(zip_file_path, 'w') as zf:

                # Add the main file
                arc_filename = '{}.{}'.format(self.typename,
                                              format_dict['filenameExt'])
                zf.write(output_file,
                         compress_type=compression,
                         arcname=arc_filename)

                for extension in format_dict['extToZip']:
                    file_path = join(self.temp_dir,
                                     '{}.{}'.format(base_filename, extension))
                    if exists(file_path):
                        arc_filename = '{}.{}'.format(self.typename, extension)
                        zf.write(file_path,
                                 compress_type=compression,
                                 arcname=arc_filename)

                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
Exemplo n.º 16
0
    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 = HarmonyQGISDialog()

        # get stored settings
        settings = QgsSettings()

        # Fetch the currently loaded layers
        layers = QgsProject.instance().layerTreeRoot().children()
        layerNames = [layer.name() for layer in layers]

        # Clear the contents of the comboBox from previous runs
        self.dlg.comboBox.clear()
        # Populate the comboBox with names of all the loaded layers
        self.dlg.comboBox.addItems(layerNames)

        # use the previous layer as the default if it is in the existing layers
        # layerName = settings.value("harmony_qgis/layer")
        # if layerName and layerName in layerNames:
        #     self.dlg.comboBox.setCurrentIndex(layerNames.index(layerName))
        layer = self.iface.activeLayer()
        if layer:
            self.dlg.comboBox.setCurrentIndex(layerNames.index(layer.name()))

        # fill the harmnoy url input with the saved setting if available
        harmonyUrl = settings.value("harmony_qgis/harmony_url")
        if harmonyUrl:
            self.dlg.harmonyUrlLineEdit.setText(harmonyUrl)

        collectionId = settings.value("harmony_qgis/collection_id")
        if collectionId:
            self.dlg.collectionField.setText(collectionId)

        version = settings.value("harmony_qgis/version") or "1.0.0"
        self.dlg.versionField.setText(version)

        variable = settings.value("harmony_qgis/variable")
        if variable:
            self.dlg.variableField.setText(variable)

        # clear the table
        self.dlg.tableWidget.setRowCount(0)

        # set the table header
        self.dlg.tableWidget.setHorizontalHeaderLabels('Parameter;Value'.split(';'))

        # add a parameter/value when the 'Add' button is clicked
        self.dlg.addButton.clicked.connect(self.addSearchParameter)

        # show the dialog
        self.dlg.show()
        # Run the dialog event loop
        result = self.dlg.exec_()
        # See if OK was pressed
        if result:

            collectionId = str(self.dlg.collectionField.text())
            version = str(self.dlg.versionField.text())
            variable = str(self.dlg.variableField.text())

            layerName = str(self.dlg.comboBox.currentText())
            # TODO handle the case where there is more than one layer by this name
            layer = QgsProject.instance().mapLayersByName(layerName)[0]
            opts = QgsVectorFileWriter.SaveVectorOptions()
            opts.driverName = 'GeoJson'
            tempFile = '/tmp/qgis.json'
            QgsVectorFileWriter.writeAsVectorFormatV2(layer, tempFile, QgsCoordinateTransformContext(), opts)

            harmonyUrl = self.dlg.harmonyUrlLineEdit.text()
            path = collectionId + "/" + "ogc-api-coverages/" + version + "/collections/" + variable + "/coverage/rangeset"
            url = harmonyUrl + "/" + path
            print(url)

            tempFileHandle = open(tempFile, 'r')
            contents = tempFileHandle.read()
            tempFileHandle.close()
            geoJson = rewind(contents)
            tempFileHandle = open(tempFile, 'w')
            tempFileHandle.write(geoJson)
            tempFileHandle.close()
            tempFileHandle = open(tempFile, 'rb')

            multipart_form_data = {
                'shapefile': (layerName + '.geojson', tempFileHandle, 'application/geo+json')
            }

            rowCount = self.dlg.tableWidget.rowCount()
            for row in range(rowCount):
                parameter = self.dlg.tableWidget.item(row, 0).text()
                value = self.dlg.tableWidget.item(row, 1).text()
                multipart_form_data[parameter] = (None, value)

            resp = requests.post(url, files=multipart_form_data, stream=True)
            tempFileHandle.close()
            # print(resp)
            # print(resp.text)
            with open('/tmp/harmony_output_image.tif', 'wb') as fd:
                for chunk in resp.iter_content(chunk_size=128):
                    fd.write(chunk)

            os.remove(tempFile)

            self.iface.addRasterLayer('/tmp/harmony_output_image.tif', layerName + '-' + variable)
            # QgsRasterLayer('/tmp/harmony_output_image.tif', layerName)

            # save settings
            if collectionId != "":
                settings.setValue("harmony_qgis/collection_id", collectionId)
            if version != "":
                settings.setValue("harmony_qgis/version", version)
            if variable != "":
                settings.setValue("harmony_qgis/variable", variable)
            if harmonyUrl != "":
                settings.setValue("harmony_qgis/harmony_url", harmonyUrl)
            settings.setValue("harmony_qgis/layer", layerName)
Exemplo n.º 17
0
    def create_geopackage(project_type: ProjectType, file_path, crs,
                          transform_context) -> None:
        """ Create the geopackage for the given path. """
        encoding = 'UTF-8'
        driver_name = QgsVectorFileWriter.driverForExtension('gpkg')
        for table in project_type.layers:

            layer_path = str(tables[table])
            if layer_path != 'None':
                layer_path += "?crs={}".format(crs.authid())

            vector_layer = QgsVectorLayer(layer_path, table, "memory")
            data_provider = vector_layer.dataProvider()

            fields = QgsFields()

            path = resources_path('data_models', '{}.csv'.format(table))
            csv = load_csv(table, path)

            for csv_feature in csv.getFeatures():
                field = QgsField(name=csv_feature['name'],
                                 type=int(csv_feature['type']))
                field.setComment(csv_feature['comment'])
                field.setAlias(csv_feature['alias'])
                fields.append(field)

            del csv

            # add fields
            data_provider.addAttributes(fields)
            vector_layer.updateFields()

            # set create file layer options
            options = QgsVectorFileWriter.SaveVectorOptions()
            options.driverName = driver_name
            options.fileEncoding = encoding

            options.actionOnExistingFile = QgsVectorFileWriter.CreateOrOverwriteFile
            if os.path.exists(file_path):
                options.actionOnExistingFile = QgsVectorFileWriter.CreateOrOverwriteLayer

            options.layerName = vector_layer.name()
            options.layerOptions = ['FID=id']

            # write file
            if Qgis.QGIS_VERSION_INT >= 31900:
                write_result, error_message, _, _ = QgsVectorFileWriter.writeAsVectorFormatV3(
                    vector_layer, file_path, transform_context, options)
            else:
                # 3.10 <= QGIS <3.18
                write_result, error_message = QgsVectorFileWriter.writeAsVectorFormatV2(
                    vector_layer, file_path, transform_context, options)

            # result
            if write_result != QgsVectorFileWriter.NoError:
                raise QgsProcessingException(
                    '* ERROR: {}'.format(error_message))

            del fields
            del data_provider
            del vector_layer
        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', 'test', 'memory')
            self.assertTrue(ml.isValid())

            d = QTemporaryDir()
            options = QgsVectorFileWriter.SaveVectorOptions()
            options.driverName = 'GPKG'
            options.layerName = 'layer_a'
            err, _ = QgsVectorFileWriter.writeAsVectorFormatV2(ml, os.path.join(d.path(), 'transaction_test.gpkg'), QgsCoordinateTransformContext(), options)

            self.assertEqual(err, QgsVectorFileWriter.NoError)
            self.assertTrue(os.path.isfile(os.path.join(d.path(), 'transaction_test.gpkg')))

            options.layerName = 'layer_b'
            options.actionOnExistingFile = QgsVectorFileWriter.CreateOrOverwriteLayer
            err, _ = QgsVectorFileWriter.writeAsVectorFormatV2(ml, os.path.join(d.path(), 'transaction_test.gpkg'), QgsCoordinateTransformContext(), options)

            layer_a = QgsVectorLayer(os.path.join(d.path(), 'transaction_test.gpkg|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(), {})
            layer_a.changeAttributeValue(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)

            layer_a.undoStack().undo()
            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 geometry
            f = next(layer_a.getFeatures())
            self.assertTrue(layer_a.changeGeometry(f.id(), QgsGeometry.fromWkt('point(9 43)')))

            _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 anothr 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)')

            self.assertTrue(layer_a.startEditing())
            layer_a.changeAttributeValue(f.id(), 1, 321)
            buffer = layer_a.editBuffer()
            self.assertEqual(buffer.changedAttributeValues(), {1: {1: 321}})
            layer_a.undoStack().undo()
            self.assertEqual(buffer.changedAttributeValues(), {})

            # Change geometry
            self.assertTrue(layer_a.changeGeometry(f.id(), QgsGeometry.fromWkt('point(9 43)')))
            f = next(layer_a.getFeatures())
            self.assertEqual(f.geometry().asWkt().upper(), 'POINT (9 43)')
            self.assertEqual(buffer.changedGeometries()[1].asWkt().upper(), 'POINT (9 43)')
            layer_a.undoStack().undo()
            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(), [2])
            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)

            layer_a.undoStack().redo()
            self.assertEqual(buffer.deletedAttributeIds(), [2])
            self.assertEqual(layer_a.fields().lookupField(field.name()), -1)

            self.assertTrue(layer_a.rollBack())

            ###########################################
            # Rename attribute

            self.assertTrue(layer_a.startEditing())

            attr_idx = layer_a.fields().lookupField(field.name())
            self.assertNotEqual(attr_idx, -1)

            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)
Exemplo n.º 19
0
    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
Exemplo n.º 20
0
    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
        }
Exemplo n.º 21
0
    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
Exemplo n.º 22
0
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
Exemplo n.º 23
0
    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
Exemplo n.º 24
0
    def convert_to_gpkg(self, target_path):
        """
        Convert a layer to geopackage in the target path and adjust its datasource. If
        a layer is already a geopackage, the dataset will merely be copied to the target
        path.

        :param layer: The layer to copy
        :param target_path: A path to a folder into which the data will be copied
        :param keep_existent: if True and target file already exists, keep it as it is
        """

        if not self.layer.type(
        ) == QgsMapLayer.VectorLayer or not self.layer.isValid():
            return

        file_path = self.filename
        suffix = ""
        uri_parts = self.layer.source().split("|", 1)
        if len(uri_parts) > 1:
            suffix = uri_parts[1]

        dest_file = ""
        new_source = ""
        # check if the source is a geopackage, and merely copy if it's the case
        if (os.path.isfile(file_path)
                and self.layer.dataProvider().storageType() == "GPKG"):
            source_path, file_name = os.path.split(file_path)
            dest_file = os.path.join(target_path, file_name)
            if not os.path.isfile(dest_file):
                shutil.copy(os.path.join(source_path, file_name), dest_file)

            if self.provider_metadata is not None:
                metadata = self.metadata
                metadata["path"] = dest_file
                new_source = self.provider_metadata.encodeUri(metadata)

            if new_source == "":
                new_source = os.path.join(target_path, file_name)
                if suffix != "":
                    new_source = "{}|{}".format(new_source, suffix)

        layer_subset_string = self.layer.subsetString()
        if new_source == "":
            pattern = re.compile("[\W_]+")  # NOQA
            cleaned_name = pattern.sub("", self.layer.name())
            dest_file = os.path.join(target_path,
                                     "{}.gpkg".format(cleaned_name))
            suffix = 0
            while os.path.isfile(dest_file):
                suffix += 1
                dest_file = os.path.join(
                    target_path, "{}_{}.gpkg".format(cleaned_name, suffix))

            # clone vector layer and strip it of filter, joins, and virtual fields
            source_layer = self.layer.clone()
            source_layer.setSubsetString("")
            source_layer_joins = source_layer.vectorJoins()
            for join in source_layer_joins:
                source_layer.removeJoin(join.joinLayerId())
            fields = source_layer.fields()
            virtual_field_count = 0
            for i in range(0, len(fields)):
                if fields.fieldOrigin(i) == QgsFields.OriginExpression:
                    source_layer.removeExpressionField(i - virtual_field_count)
                    virtual_field_count += 1

            options = QgsVectorFileWriter.SaveVectorOptions()
            options.fileEncoding = "UTF-8"
            options.driverName = "GPKG"
            (error,
             returned_dest_file) = QgsVectorFileWriter.writeAsVectorFormatV2(
                 source_layer, dest_file, QgsCoordinateTransformContext(),
                 options)
            if error != QgsVectorFileWriter.NoError:
                return
            if returned_dest_file:
                new_source = returned_dest_file
            else:
                new_source = dest_file

        self._change_data_source(new_source, "ogr")
        if layer_subset_string:
            self.layer.setSubsetString(layer_subset_string)

        return dest_file
Exemplo n.º 25
0
def exportLayer(layer,
                fields=None,
                toShapefile=False,
                path=None,
                force=False,
                log=None):
    filename = layer.source().split("|")[0]
    destFilename = layer.name()
    fields = fields or []
    if layer.type() == layer.VectorLayer:
        if toShapefile and (
                force or layer.fields().count() != len(fields) or
            (os.path.splitext(filename.lower())[1] != EXT_SHAPEFILE)):
            # Export with Shapefile extension
            ext = EXT_SHAPEFILE
        elif force or os.path.splitext(filename.lower())[1] != EXT_GEOPACKAGE or \
                layer.fields().count() != len(fields) or not isSingleTableGpkg(filename):
            # Export with GeoPackage extension
            ext = EXT_GEOPACKAGE
        else:
            # No need to export
            if log is not None:
                log.logInfo(
                    QCoreApplication.translate(
                        "GeocatBridge",
                        f"No need to export layer {destFilename} stored at {filename}"
                    ))
            return filename

        # 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 tempFilenameInTempFolder(destFilename + ext)
        if Qgis.QGIS_VERSION_INT < 31003:
            # Use writeAsVectorFormat for QGIS versions < 3.10.3 for backwards compatibility
            QgsVectorFileWriter.writeAsVectorFormat(
                layer,
                output,
                fileEncoding="UTF-8",
                attributes=attrs,
                driverName="ESRI Shapefile" if ext == EXT_SHAPEFILE else "")
        else:
            # Use writeAsVectorFormatV2 for QGIS versions >= 3.10.3 to avoid DeprecationWarnings
            transform_ctx = QgsProject.instance().transformContext()
            options = QgsVectorFileWriter.SaveVectorOptions()
            options.fileEncoding = "UTF-8"
            options.attributes = attrs
            options.driverName = "ESRI Shapefile" if ext == EXT_SHAPEFILE else ""
            QgsVectorFileWriter.writeAsVectorFormatV2(layer, output,
                                                      transform_ctx, options)
        if log is not None:
            log.logInfo(
                QCoreApplication.translate(
                    "GeocatBridge",
                    f"Layer {destFilename} exported to {output}"))
        return output
    else:
        # Export raster
        if force or not filename.lower().endswith("tif"):
            output = path or tempFilenameInTempFolder(destFilename + ".tif")
            writer = QgsRasterFileWriter(output)
            writer.setOutputFormat("GTiff")
            writer.writeRaster(layer.pipe(), layer.width(), layer.height(),
                               layer.extent(), layer.crs())
            del writer
            if log is not None:
                log.logInfo(
                    QCoreApplication.translate(
                        "GeocatBridge",
                        f"Layer {destFilename} exported to {output}"))
            return output
        else:
            if log is not None:
                log.logInfo(
                    QCoreApplication.translate(
                        "GeocatBridge",
                        f"No need to export layer {destFilename} stored at {filename}"
                    ))
            return filename
Exemplo n.º 26
0
def write_layer_to_gpkg(layer, gpkgfile,  layername):
    options = QgsVectorFileWriter.SaveVectorOptions()
    options.actionOnExistingFile = QgsVectorFileWriter.CreateOrOverwriteLayer
    options.layerName = layername
    context = QgsProject.instance().transformContext()
    QgsVectorFileWriter.writeAsVectorFormatV2(layer, gpkgfile, context, options)
Exemplo n.º 27
0
    def convert_vector_layer(
            layer,  # pylint: disable=too-many-locals,too-many-branches,too-many-statements
            project,
            data_folder,
            feedback,
            conversion_results: ConversionResults,
            change_source_on_error: bool = False,
            verbose_log=False):
        """
        Converts a vector layer to a standard format
        """
        if layer.customProperty('original_uri'):
            uri = layer.customProperty('original_uri')
            if verbose_log:
                feedback.pushDebugInfo(
                    f'Original layer URI from custom properties is {uri}')
        else:
            uri = layer.source()
            if verbose_log:
                feedback.pushDebugInfo(
                    'Original layer URI not found in custom properties')

        source = QgsProviderRegistry.instance().decodeUri(
            layer.providerType(), uri)
        if verbose_log:
            feedback.pushInfo('')

        # older versions of QGIS didn't correctly strip out the subset from the layerName:
        if 'subset' not in source:
            if '|subset=' in source['layerName']:
                if verbose_log:
                    feedback.pushDebugInfo(
                        'Stripping out subset string from layerName: {}'.
                        format(source['layerName']))
                layer_name = source['layerName']
                parts = layer_name.split('|subset=')
                if len(parts) == 2:
                    source['layerName'] = parts[0]
                    if verbose_log:
                        feedback.pushDebugInfo('Cleaned layer name: {}'.format(
                            source['layerName']))
                elif verbose_log:
                    feedback.reportError('Failed to strip subset string!')
            elif '|subset=' in source['path']:
                path = source['path']
                if verbose_log:
                    feedback.pushDebugInfo(
                        'Stripping out subset string from path: {}'.format(
                            source['path']))
                parts = path.split('|subset=')
                if len(parts) == 2:
                    source['path'] = parts[0]
                    if verbose_log:
                        feedback.pushDebugInfo('Cleaned path: {}'.format(
                            source['path']))
                elif verbose_log:
                    feedback.reportError('Failed to strip subset string!')

        # convert to Geopackage
        source_uri = QgsProviderRegistry.instance().encodeUri(
            layer.providerType(), {
                'path': source['path'],
                'layerName': source['layerName']
            })

        # Sometimes the case varies in ArcMap documents, so when comparing to previously converted layers
        # we use a case-insensitive path/layername which is normalized
        result_key = QgsProviderRegistry.instance().encodeUri(
            layer.providerType(), {
                'path': pathlib.Path(
                    source['path']).resolve().as_posix().lower(),
                'layerName': source['layerName'].lower()
            })

        if verbose_log:
            feedback.pushDebugInfo('Converting layer: {} ( {} )'.format(
                source['path'], source['layerName']))
            feedback.pushDebugInfo(f'Cached result key: {result_key}')

        provider_options = QgsDataProvider.ProviderOptions()
        provider_options.transformContext = project.transformContext()
        subset = layer.subsetString()

        # have we maybe already converted this layer??
        if result_key in conversion_results.layer_map:

            previous_results = conversion_results.layer_map[result_key]
            if previous_results.get('error'):
                if verbose_log:
                    feedback.pushDebugInfo(
                        'Already tried to convert this layer, but failed last time, skipping...'
                    )
                    feedback.pushDebugInfo('Restoring stored URI')

                layer.setDataSource(uri, layer.name(), 'ogr', provider_options)
            else:
                if verbose_log:
                    feedback.pushDebugInfo(
                        'Already converted this layer, reusing previous converted path: {} layername: {}'
                        .format(previous_results['destPath'],
                                previous_results['destLayer']))

                layer.setDataSource(
                    QgsProviderRegistry.instance().encodeUri(
                        'ogr', {
                            'path': previous_results['destPath'],
                            'layerName': previous_results['destLayer']
                        }), layer.name(), 'ogr', provider_options)
                if verbose_log:
                    feedback.pushDebugInfo('new source {}'.format(
                        layer.dataProvider().dataSourceUri()))
            if subset:
                if verbose_log:
                    feedback.pushDebugInfo(
                        'Resetting subset string: {}'.format(subset))
                layer.setSubsetString(subset)

            return previous_results

        source_layer = QgsVectorLayer(source_uri, '', layer.providerType())

        path = pathlib.Path(source['path'])

        dest_file_name = ((pathlib.Path(data_folder) /
                           path.stem).with_suffix('.gpkg')).as_posix()
        if dest_file_name not in conversion_results.created_databases:
            # about to use a new file -- let's double-check that it doesn't already exist. We don't want
            # to put layers into a database which we didn't make for this project
            counter = 1
            while pathlib.Path(dest_file_name).exists():
                counter += 1
                dest_file_name = ((pathlib.Path(data_folder) /
                                   (path.stem + '_' + str(counter))
                                   ).with_suffix('.gpkg')).as_posix()
                if dest_file_name in conversion_results.created_databases:
                    break

            if verbose_log:
                feedback.pushDebugInfo(
                    'Creating new destination file {}'.format(dest_file_name))
        elif verbose_log:
            feedback.pushDebugInfo(
                'Reusing existing destination file {}'.format(dest_file_name))

        # now this filename is ok for other layers to be stored in for this conversion
        conversion_results.created_databases.add(dest_file_name)

        layer_name_candidate = source['layerName']
        counter = 1
        while QgsVectorFileWriter.targetLayerExists(dest_file_name,
                                                    layer_name_candidate):
            counter += 1
            layer_name_candidate = '{}_{}'.format(source['layerName'], counter)

        if verbose_log:
            feedback.pushDebugInfo(
                'Target layer name is {}'.format(layer_name_candidate))

        if not source_layer.isValid():
            if verbose_log:
                feedback.reportError('Source layer is not valid')
            if path.exists():
                if verbose_log:
                    feedback.pushDebugInfo('File path DOES exist')

                    test_layer = QgsVectorLayer(path.as_posix())
                    sub_layers = test_layer.dataProvider().subLayers()
                    feedback.pushDebugInfo(
                        f'Readable layers from "{path.as_posix()}" are:')
                    for sub_layer in sub_layers:
                        _, name, count, geom_type, _, _ = sub_layer.split(
                            QgsDataProvider.sublayerSeparator())
                        feedback.pushDebugInfo(
                            f'- "{name}" ({count} features, geometry type {geom_type})'
                        )

            if path.exists() and path.suffix.lower() == '.mdb':
                try:
                    source['layerName'].encode('ascii')
                except UnicodeDecodeError:
                    error = f'''MDB layers with unicode names are not supported by QGIS -- cannot convert "{source['layerName']}"'''
                    if verbose_log:
                        feedback.reportError(error)
                        feedback.pushDebugInfo('Restoring stored URI')

                    layer.setDataSource(uri, layer.name(), 'ogr',
                                        provider_options)
                    if subset:
                        if verbose_log:
                            feedback.pushDebugInfo(
                                'Resetting subset string: {}'.format(subset))
                        layer.setSubsetString(subset)

                    conversion_results.layer_map[result_key] = {'error': error}
                    return conversion_results.layer_map[result_key]

                # maybe a non-spatial table, which can't be read with GDAL < 3.2
                source_layer = None

                if verbose_log:
                    feedback.pushDebugInfo('Layer type is {}'.format(
                        QgsWkbTypes.displayString(layer.wkbType())))

                if layer.wkbType() == QgsWkbTypes.NoGeometry:
                    if verbose_log:
                        feedback.pushDebugInfo(
                            'Attempting fallback for non-spatial tables')
                    try:
                        source_layer = ConversionUtils.convert_mdb_table_to_memory_layer(
                            str(path), source['layerName'])
                        if verbose_log:
                            feedback.pushDebugInfo('Fallback succeeded!')
                    except Exception as e:  # nopep8, pylint: disable=broad-except
                        if verbose_log:
                            feedback.reportError('Fallback failed: {}'.format(
                                str(e)))
                        source_layer = None
                elif verbose_log:
                    feedback.reportError(
                        'Nothing else to try, conversion failed')

                if not source_layer:
                    # here we fake things. We don't leave the original path to the mdb layer intact in the converted
                    # project, as this can cause massive issues with QGIS as it attempts to re-read this path constantly
                    # rather we "pretend" that the conversion was ok and set the broken layer's path to what the gpkg
                    # converted version WOULD have been! It'll still be broken in the converted project (obviously),
                    # but QGIS will no longer try endless to read the MDB and get all hung up on this...
                    conversion_results.layer_map[result_key] = {
                        'sourcePath':
                        source['path'],
                        'sourceLayer':
                        source['layerName'],
                        'destPath':
                        dest_file_name,
                        'destLayer':
                        layer_name_candidate,
                        'error':
                        'Could not open {} ({}) for conversion'.format(
                            source_uri, source['layerName'])
                    }

                    if change_source_on_error:
                        if verbose_log:
                            feedback.pushDebugInfo('Restoring stored URI')

                        layer.setDataSource(uri, layer.name(), 'ogr',
                                            provider_options)
                        if subset:
                            if verbose_log:
                                feedback.pushDebugInfo(
                                    'Resetting subset string: {}'.format(
                                        subset))
                            layer.setSubsetString(subset)
                        if verbose_log:
                            feedback.pushDebugInfo('new source {}'.format(
                                layer.dataProvider().dataSourceUri()))

                    return conversion_results.layer_map[result_key]
            else:
                if not path.exists():
                    error = 'The referenced file {} does NOT exist!'.format(
                        str(path))
                else:
                    error = 'The referenced file exists, but could not open {} ({}) for conversion'.format(
                        source_uri, source['layerName'])

                if verbose_log:
                    feedback.reportError(error)
                    feedback.pushDebugInfo('Restoring stored URI')

                layer.setDataSource(uri, layer.name(), 'ogr', provider_options)
                if subset:
                    if verbose_log:
                        feedback.pushDebugInfo(
                            'Resetting subset string: {}'.format(subset))
                    layer.setSubsetString(subset)

                conversion_results.layer_map[result_key] = {'error': error}
                return conversion_results.layer_map[result_key]

        if verbose_log:
            feedback.pushDebugInfo('Source is valid, converting')

        options = QgsVectorFileWriter.SaveVectorOptions()

        options.layerName = layer_name_candidate
        options.actionOnExistingFile = QgsVectorFileWriter.CreateOrOverwriteLayer if pathlib.Path(
            dest_file_name).exists(
            ) else QgsVectorFileWriter.CreateOrOverwriteFile
        options.feedback = feedback

        error, error_message = QgsVectorFileWriter.writeAsVectorFormatV2(
            source_layer, dest_file_name, project.transformContext(), options)
        if error != QgsVectorFileWriter.NoError:
            if verbose_log:
                feedback.reportError('Failed: {}'.format(error_message))
                feedback.pushDebugInfo('Restoring stored URI')

            layer.setDataSource(uri, layer.name(), 'ogr', provider_options)
            if subset:
                if verbose_log:
                    feedback.pushDebugInfo(
                        'Resetting subset string: {}'.format(subset))
                layer.setSubsetString(subset)

            conversion_results.layer_map[result_key] = {'error': error_message}
            return conversion_results.layer_map[result_key]

        if verbose_log:
            feedback.pushDebugInfo('Success!')

        provider_options = QgsDataProvider.ProviderOptions()
        provider_options.transformContext = project.transformContext()
        subset = layer.subsetString()
        layer.setDataSource(
            QgsProviderRegistry.instance().encodeUri(
                'ogr', {
                    'path': dest_file_name,
                    'layerName': options.layerName
                }), layer.name(), 'ogr', provider_options)
        if subset:
            if verbose_log:
                feedback.pushDebugInfo(
                    'Resetting subset string: {}'.format(subset))
            layer.setSubsetString(subset)

        if verbose_log:
            feedback.pushDebugInfo('new source {}'.format(
                layer.dataProvider().dataSourceUri()))

        conversion_results.layer_map[result_key] = {
            'sourcePath': source['path'],
            'sourceLayer': source['layerName'],
            'destPath': dest_file_name,
            'destLayer': options.layerName
        }

        return conversion_results.layer_map[result_key]