diff --git a/xlsformconverter/XLSFormConverter.py b/xlsformconverter/XLSFormConverter.py index 3dad9f3..e810a0e 100644 --- a/xlsformconverter/XLSFormConverter.py +++ b/xlsformconverter/XLSFormConverter.py @@ -84,6 +84,8 @@ class XLSFormConverter(QObject): custom_title = None preferred_language = None basemap = None + crs = QgsCoordinateReferenceSystem("EPSG:3857") + extent = QgsRectangle() geometries = None groups_as_tabs = False @@ -452,7 +454,9 @@ def create_layer(self, name=None): self.output_file, layer_fields, layer_geometry, - QgsCoordinateReferenceSystem(4326), + QgsCoordinateReferenceSystem("EPSG:4326") + if self.crs.authid() == "EPSG:3857" + else self.crs, QgsProject.instance().transformContext(), writer_options, ) @@ -1003,7 +1007,8 @@ def process_project_write(self, document): ms = QgsMapSettings() ms.setDestinationCrs(self.output_project.crs()) ms.setOutputSize(QSize(500, 500)) - ms.setExtent(self.output_extent) + if self.output_extent: + ms.setExtent(self.output_extent) ms.writeXml(mapcanvasNode, document) def set_custom_title(self, title): @@ -1015,6 +1020,15 @@ def set_preferred_language(self, language): def set_basemap(self, basemap): self.basemap = basemap + def set_crs(self, crs): + if crs.isValid(): + self.crs = crs + else: + self.crs = QgsCoordinateReferenceSystem("EPSG:3857") + + def set_extent(self, extent): + self.extent = extent + def set_geometries(self, geometries): self.geometries = geometries @@ -1167,7 +1181,7 @@ def convert( os.path.join(output_directory, settings_filename + ".gpkg") ) self.output_project = QgsProject() - self.output_project.setCrs(QgsCoordinateReferenceSystem("EPSG:3587")) + self.output_project.setCrs(self.crs) if self.basemap in self.BASEMAPS: base_layer = QgsRasterLayer( @@ -1175,7 +1189,6 @@ def convert( self.basemap, "wms", ) - self.output_project.setCrs(base_layer.crs()) self.output_project.addMapLayer(base_layer) # Choices handling @@ -1641,7 +1654,9 @@ def convert( ) ) - if survey_extent: + if not self.extent.isEmpty(): + survey_extent = self.extent + elif survey_extent: transform = QgsCoordinateTransform( current_layer[0].crs(), self.output_project.crs(), @@ -1649,20 +1664,62 @@ def convert( ) try: survey_extent = transform.transformBoundingBox(survey_extent) - # Insure the initial project extent is not too zoomed in - if survey_extent.width() < 200: - padding = (200 - survey_extent.width()) / 2 - survey_extent.setXMinimum(survey_extent.xMinimum() - padding) - survey_extent.setXMaximum(survey_extent.xMaximum() + padding) - if survey_extent.height() < 200: - padding = (200 - survey_extent.height()) / 2 - survey_extent.setYMinimum(survey_extent.yMinimum() - padding) - survey_extent.setYMaximum(survey_extent.yMaximum() + padding) - self.output_extent = survey_extent + if ( + self.output_project.crs().mapUnits() != Qgis.DistanceUnit.Unknown + and self.output_project.crs().mapUnits() + != Qgis.DistanceUnit.Degrees + ): + # Insure the initial project extent is not too zoomed in + if survey_extent.width() < 200: + padding = (200 - survey_extent.width()) / 2 + survey_extent.setXMinimum(survey_extent.xMinimum() - padding) + survey_extent.setXMaximum(survey_extent.xMaximum() + padding) + if survey_extent.height() < 200: + padding = (200 - survey_extent.height()) / 2 + survey_extent.setYMinimum(survey_extent.yMinimum() - padding) + survey_extent.setYMaximum(survey_extent.yMaximum() + padding) + + survey_extent.scale(1.05) + self.output_extent = survey_extent except QgsCsException: - self.output_extent = QgsRectangle(297905, 3866631, 2336801, 7381331) - else: - self.output_extent = QgsRectangle(297905, 3866631, 2336801, 7381331) + survey_extent = None + + if not survey_extent: + if self.output_project.crs().authid() == "EPSG:3857": + survey_extent = QgsRectangle(-9.88, 33.41, 40.97, 61.11) + else: + survey_extent = self.output_project.crs().bounds() + if survey_extent.width() < survey_extent.height(): + survey_extent.setYMinimum( + survey_extent.yMinimum() + + survey_extent.height() / 2 + - survey_extent.width() / 2 + ) + survey_extent.setYMaximum( + survey_extent.yMinimum() + + survey_extent.height() / 2 + + survey_extent.width() / 2 + ) + else: + survey_extent.setXMinimum( + survey_extent.xMinimum() + + survey_extent.width() / 2 + - survey_extent.height() / 2 + ) + survey_extent.setXMaximum( + survey_extent.xMinimum() + + survey_extent.width() / 2 + + survey_extent.height() / 2 + ) + + transform = QgsCoordinateTransform( + QgsCoordinateReferenceSystem("EPSG:4326"), + self.output_project.crs(), + self.output_project.transformContext(), + ) + survey_extent = transform.transformBoundingBox(survey_extent) + + self.output_extent = survey_extent if self.output_project.crs().authid() == "EPSG:3857": # Display coordinates in WGS84 to provide a more useful experience for the average person diff --git a/xlsformconverter/XLSFormConverterAlgorithms.py b/xlsformconverter/XLSFormConverterAlgorithms.py index 04fd7f3..0016bdf 100644 --- a/xlsformconverter/XLSFormConverterAlgorithms.py +++ b/xlsformconverter/XLSFormConverterAlgorithms.py @@ -4,8 +4,10 @@ QgsProcessing, QgsProcessingAlgorithm, QgsProcessingParameterBoolean, + QgsProcessingParameterCrs, QgsProcessingParameterDefinition, QgsProcessingParameterEnum, + QgsProcessingParameterExtent, QgsProcessingParameterFeatureSource, QgsProcessingParameterFile, QgsProcessingParameterFolderDestination, @@ -35,6 +37,8 @@ class XLSFormConverterAlgorithm(QgsProcessingAlgorithm): BASEMAP = "BASEMAP" GROUPS_AS_TABS = "GROUPS_AS_TABS" UPLOAD_TO_QFIELDCLOUD = "UPLOAD_TO_QFIELDCLOUD" + CRS = "CRS" + EXTENT = "EXTENT" GEOMETRIES = "GEOMETRIES" OUTPUT = "OUTPUT" @@ -107,6 +111,14 @@ def initAlgorithm(self, config=None): ) ) + self.addParameter( + QgsProcessingParameterBoolean( + self.GROUPS_AS_TABS, + self.tr("Use form tabs for root groups"), + defaultValue=False, + ) + ) + self.addParameter( QgsProcessingParameterBoolean( self.UPLOAD_TO_QFIELDCLOUD, @@ -115,10 +127,18 @@ def initAlgorithm(self, config=None): ) ) - param = QgsProcessingParameterBoolean( - self.GROUPS_AS_TABS, - self.tr("Serve root groups as feature form tabs"), - defaultValue=False, + param = QgsProcessingParameterCrs( + self.CRS, + self.tr("Project CRS"), + optional=True, + ) + param.setFlags(param.flags() | QgsProcessingParameterDefinition.Flag.Advanced) + self.addParameter(param) + + param = QgsProcessingParameterExtent( + self.EXTENT, + self.tr("Project extent"), + optional=True, ) param.setFlags(param.flags() | QgsProcessingParameterDefinition.Flag.Advanced) self.addParameter(param) @@ -145,6 +165,8 @@ def processAlgorithm(self, parameters, context, feedback): xlsform_file = self.parameterAsString(parameters, self.INPUT, context) title = self.parameterAsString(parameters, self.TITLE, context) language = self.parameterAsString(parameters, self.LANGUAGE, context) + crs = self.parameterAsCrs(parameters, self.CRS, context) + extent = self.parameterAsExtent(parameters, self.EXTENT, context, crs) geometries = self.parameterAsSource(parameters, self.GEOMETRIES, context) basemap = "OpenStreetMap" @@ -174,6 +196,14 @@ def processAlgorithm(self, parameters, context, feedback): converter.set_basemap(basemap) converter.set_geometries(geometries) converter.set_groups_as_tabs(groups_as_tabs) + converter.set_crs(crs) + converter.set_extent(extent) + if not crs.isValid() and not extent.isEmpty(): + feedback.pushWarning( + self.tr( + "Project extent parameter ignored, a required project CRS parameter is missing." + ) + ) project_file = converter.convert(output_directory)