def testProject(self):
     """
     Test project's transform context
     """
     project = QgsProject()
     context_changed_spy = QSignalSpy(project.transformContextChanged)
     context = project.transformContext()
     context.addSourceDestinationDatumTransform(QgsCoordinateReferenceSystem('EPSG:3111'),
                                                QgsCoordinateReferenceSystem('EPSG:4283'), 1, 2)
     project.setTransformContext(context)
     self.assertEqual(len(context_changed_spy), 1)
     self.assertEqual(project.transformContext().sourceDestinationDatumTransforms(), {('EPSG:3111', 'EPSG:4283'): QgsDatumTransform.TransformPair(1, 2)})
Ejemplo n.º 2
0
    def testTransformContextInheritsFromProject(self):
        """Test that when a layer is added to a project it inherits its context"""

        rl = QgsRasterLayer(self.rpath, 'raster')
        self.assertFalse(rl.transformContext().hasTransform(QgsCoordinateReferenceSystem(4326), QgsCoordinateReferenceSystem(3857)))

        p = QgsProject()
        self.assertFalse(p.transformContext().hasTransform(QgsCoordinateReferenceSystem(4326), QgsCoordinateReferenceSystem(3857)))
        p.setTransformContext(self.ctx)
        self.assertTrue(p.transformContext().hasTransform(QgsCoordinateReferenceSystem(4326), QgsCoordinateReferenceSystem(3857)))

        p.addMapLayers([rl])
        self.assertTrue(rl.transformContext().hasTransform(QgsCoordinateReferenceSystem(4326), QgsCoordinateReferenceSystem(3857)))
 def testProjectProj6(self):
     """
     Test project's transform context
     """
     project = QgsProject()
     context_changed_spy = QSignalSpy(project.transformContextChanged)
     context = project.transformContext()
     context.addCoordinateOperation(QgsCoordinateReferenceSystem('EPSG:3111'),
                                    QgsCoordinateReferenceSystem('EPSG:4283'), 'proj')
     project.setTransformContext(context)
     self.assertEqual(len(context_changed_spy), 1)
     self.assertEqual(project.transformContext().coordinateOperations(),
                      {('EPSG:3111', 'EPSG:4283'): 'proj'})
 def testProject(self):
     """
     Test project's transform context
     """
     project = QgsProject()
     context_changed_spy = QSignalSpy(project.transformContextChanged)
     context = project.transformContext()
     context.addSourceDestinationDatumTransform(
         QgsCoordinateReferenceSystem('EPSG:3111'),
         QgsCoordinateReferenceSystem('EPSG:4283'), 1, 2)
     project.setTransformContext(context)
     self.assertEqual(len(context_changed_spy), 1)
     self.assertEqual(
         project.transformContext().sourceDestinationDatumTransforms(), {
             ('EPSG:3111', 'EPSG:4283'): QgsDatumTransform.TransformPair(
                 1, 2)
         })
    def testProjectContextProj6(self):
        """
        Test creating transform using convenience constructor which takes project reference
        """
        p = QgsProject()
        context = p.transformContext()
        context.addCoordinateOperation(QgsCoordinateReferenceSystem('EPSG:28356'),
                                       QgsCoordinateReferenceSystem('EPSG:3111'), 'proj')
        p.setTransformContext(context)

        transform = QgsCoordinateTransform(QgsCoordinateReferenceSystem('EPSG:28356'), QgsCoordinateReferenceSystem('EPSG:3111'), p)
        self.assertEqual(transform.coordinateOperation(), 'proj')
Ejemplo n.º 6
0
    def testTransformContextInheritsFromProject(self):
        """Test that when a layer is added to a project it inherits its context"""

        rl = QgsRasterLayer(self.rpath, 'raster')
        self.assertFalse(rl.transformContext().hasTransform(
            QgsCoordinateReferenceSystem(4326),
            QgsCoordinateReferenceSystem(3857)))

        p = QgsProject()
        self.assertFalse(p.transformContext().hasTransform(
            QgsCoordinateReferenceSystem(4326),
            QgsCoordinateReferenceSystem(3857)))
        p.setTransformContext(self.ctx)
        self.assertTrue(p.transformContext().hasTransform(
            QgsCoordinateReferenceSystem(4326),
            QgsCoordinateReferenceSystem(3857)))

        p.addMapLayers([rl])
        self.assertTrue(rl.transformContext().hasTransform(
            QgsCoordinateReferenceSystem(4326),
            QgsCoordinateReferenceSystem(3857)))
Ejemplo n.º 7
0
    def testProjectContextProj6(self):
        """
        Test creating transform using convenience constructor which takes project reference
        """
        p = QgsProject()
        context = p.transformContext()
        context.addCoordinateOperation(QgsCoordinateReferenceSystem('EPSG:28356'),
                                       QgsCoordinateReferenceSystem('EPSG:3111'), 'proj')
        p.setTransformContext(context)

        transform = QgsCoordinateTransform(QgsCoordinateReferenceSystem('EPSG:28356'), QgsCoordinateReferenceSystem('EPSG:3111'), p)
        self.assertEqual(transform.coordinateOperation(), 'proj')
Ejemplo n.º 8
0
 def testProjectProj6(self):
     """
     Test project's transform context
     """
     project = QgsProject()
     context_changed_spy = QSignalSpy(project.transformContextChanged)
     context = project.transformContext()
     context.addCoordinateOperation(
         QgsCoordinateReferenceSystem('EPSG:3111'),
         QgsCoordinateReferenceSystem('EPSG:4283'), 'proj', True)
     project.setTransformContext(context)
     self.assertEqual(len(context_changed_spy), 1)
     self.assertEqual(project.transformContext().coordinateOperations(),
                      {('EPSG:3111', 'EPSG:4283'): 'proj'})
     context2 = project.transformContext()
     context2.addCoordinateOperation(
         QgsCoordinateReferenceSystem('EPSG:3111'),
         QgsCoordinateReferenceSystem('EPSG:4283'), 'proj', False)
     project.setTransformContext(context2)
     self.assertEqual(len(context_changed_spy), 2)
     self.assertEqual(project.transformContext(), context2)
     self.assertNotEqual(project.transformContext(), context)
    def testProjectContext(self):
        """
        Test creating transform using convenience constructor which takes project reference
        """
        p = QgsProject()
        context = p.transformContext()
        context.addSourceDestinationDatumTransform(QgsCoordinateReferenceSystem('EPSG:28356'),
                                                   QgsCoordinateReferenceSystem('EPSG:3111'), 1, 2)
        p.setTransformContext(context)

        transform = QgsCoordinateTransform(QgsCoordinateReferenceSystem('EPSG:28356'), QgsCoordinateReferenceSystem('EPSG:3111'), p)
        self.assertEqual(transform.sourceDatumTransformId(), 1)
        self.assertEqual(transform.destinationDatumTransformId(), 2)
Ejemplo n.º 10
0
    def testTransformContextIsSyncedFromProject(self):
        """Test that when a layer is synced when project context changes"""

        rl = QgsRasterLayer(self.rpath, 'raster')
        self.assertFalse(rl.transformContext().hasTransform(QgsCoordinateReferenceSystem(4326), QgsCoordinateReferenceSystem(3857)))

        p = QgsProject()
        self.assertFalse(p.transformContext().hasTransform(QgsCoordinateReferenceSystem(4326), QgsCoordinateReferenceSystem(3857)))
        p.setTransformContext(self.ctx)
        self.assertTrue(p.transformContext().hasTransform(QgsCoordinateReferenceSystem(4326), QgsCoordinateReferenceSystem(3857)))

        p.addMapLayers([rl])
        self.assertTrue(rl.transformContext().hasTransform(QgsCoordinateReferenceSystem(4326), QgsCoordinateReferenceSystem(3857)))

        # Now change the project context
        tc2 = QgsCoordinateTransformContext()
        p.setTransformContext(tc2)
        self.assertFalse(p.transformContext().hasTransform(QgsCoordinateReferenceSystem(4326), QgsCoordinateReferenceSystem(3857)))
        self.assertFalse(rl.transformContext().hasTransform(QgsCoordinateReferenceSystem(4326), QgsCoordinateReferenceSystem(3857)))
        p.setTransformContext(self.ctx)
        self.assertTrue(p.transformContext().hasTransform(QgsCoordinateReferenceSystem(4326), QgsCoordinateReferenceSystem(3857)))
        self.assertTrue(rl.transformContext().hasTransform(QgsCoordinateReferenceSystem(4326), QgsCoordinateReferenceSystem(3857)))
Ejemplo n.º 11
0
    def testTransformContextIsSyncedFromProject(self):
        """Test that when a layer is synced when project context changes"""

        rl = QgsRasterLayer(self.rpath, 'raster')
        self.assertFalse(rl.transformContext().hasTransform(QgsCoordinateReferenceSystem(4326), QgsCoordinateReferenceSystem(3857)))

        p = QgsProject()
        self.assertFalse(p.transformContext().hasTransform(QgsCoordinateReferenceSystem(4326), QgsCoordinateReferenceSystem(3857)))
        p.setTransformContext(self.ctx)
        self.assertTrue(p.transformContext().hasTransform(QgsCoordinateReferenceSystem(4326), QgsCoordinateReferenceSystem(3857)))

        p.addMapLayers([rl])
        self.assertTrue(rl.transformContext().hasTransform(QgsCoordinateReferenceSystem(4326), QgsCoordinateReferenceSystem(3857)))

        # Now change the project context
        tc2 = QgsCoordinateTransformContext()
        p.setTransformContext(tc2)
        self.assertFalse(p.transformContext().hasTransform(QgsCoordinateReferenceSystem(4326), QgsCoordinateReferenceSystem(3857)))
        self.assertFalse(rl.transformContext().hasTransform(QgsCoordinateReferenceSystem(4326), QgsCoordinateReferenceSystem(3857)))
        p.setTransformContext(self.ctx)
        self.assertTrue(p.transformContext().hasTransform(QgsCoordinateReferenceSystem(4326), QgsCoordinateReferenceSystem(3857)))
        self.assertTrue(rl.transformContext().hasTransform(QgsCoordinateReferenceSystem(4326), QgsCoordinateReferenceSystem(3857)))
Ejemplo n.º 12
0
    def testProjectContext(self):
        """
        Test creating transform using convenience constructor which takes project reference
        """
        p = QgsProject()
        context = p.transformContext()
        context.addSourceDestinationDatumTransform(QgsCoordinateReferenceSystem('EPSG:28356'),
                                                   QgsCoordinateReferenceSystem('EPSG:3111'), 1, 2)
        p.setTransformContext(context)

        transform = QgsCoordinateTransform(QgsCoordinateReferenceSystem('EPSG:28356'), QgsCoordinateReferenceSystem('EPSG:3111'), p)
        self.assertEqual(transform.sourceDatumTransformId(), 1)
        self.assertEqual(transform.destinationDatumTransformId(), 2)
Ejemplo n.º 13
0
def project_info(project_path):
    """Extracts project information and returns it as a dictionary"""

    info = {}
    p = QgsProject()
    canvas = QgsMapCanvas()

    def _readCanvasSettings(xmlDocument):
        canvas.readProject(xmlDocument)

    p.readProject.connect(_readCanvasSettings, Qt.DirectConnection)

    if p.read(project_path):

        # initial extent
        extent = canvas.extent()
        if p.crs().authid() != 4326:
            ct = QgsCoordinateTransform(
                p.crs(), QgsCoordinateReferenceSystem.fromEpsgId(4326),
                p.transformContext())
            extent = ct.transform(extent)

        info['initial_extent'] = [
            extent.xMinimum(),
            extent.yMinimum(),
            extent.xMaximum(),
            extent.yMaximum(),
        ]

        ####################################################
        # Main section

        info['title'] = p.metadata().title()
        if not info['title']:
            info['title'] = QgsServerProjectUtils.owsServiceTitle(p)
        if not info['title']:
            info['title'] = p.title()
        if not info['title']:
            info['title'] = p.baseName()

        info['description'] = p.metadata().abstract()
        if not info['description']:
            info['description'] = QgsServerProjectUtils.owsServiceAbstract(p)

        # Extent, CRS and published WMS layers typenames
        wmsOutputCrsList = QgsServerProjectUtils.wmsOutputCrsList(p)
        info[
            'crs'] = 'EPSG:4326' if 'EPSG:4326' in wmsOutputCrsList else wmsOutputCrsList[
                0]
        extent, info['wms_layers'] = project_wms(p, info['crs'])
        info['extent'] = [
            extent.xMinimum(),
            extent.yMinimum(),
            extent.xMaximum(),
            extent.yMaximum()
        ]
        geographic_extent = extent
        if info['crs'] != 'EPSG:4326':
            extent_crs = QgsCoordinateReferenceSystem.fromEpsgId(
                int(info['crs'].split(':')[1]))
            ct = QgsCoordinateTransform(
                extent_crs, QgsCoordinateReferenceSystem.fromEpsgId(4326),
                p.transformContext())
            geographic_extent = ct.transform(geographic_extent)
        info['geographic_extent'] = [
            geographic_extent.xMinimum(),
            geographic_extent.yMinimum(),
            geographic_extent.xMaximum(),
            geographic_extent.yMaximum()
        ]

        ####################################################
        # Metadata section

        m = p.metadata()
        metadata = {}
        for prop in (
                'title',
                'identifier',
                'parentIdentifier',
                'abstract',
                'author',
                'language',
                'categories',
                'history',
                'type',
        ):
            metadata[prop] = getattr(m, prop)()

        # links array
        metadata['links'] = _read_links(m)
        # contacts array
        metadata['contacts'] = _read_contacts(m)
        metadata['creationDateTime'] = m.creationDateTime().toString(
            Qt.ISODate)
        info['metadata'] = metadata

        ####################################################
        # WMS Service Capabilities section

        capabilities = {}
        for c in ('owsServiceAbstract', 'owsServiceAccessConstraints',
                  'owsServiceCapabilities', 'owsServiceContactMail',
                  'owsServiceContactOrganization', 'owsServiceContactPerson',
                  'owsServiceContactPhone', 'owsServiceContactPosition',
                  'owsServiceFees', 'owsServiceKeywords',
                  'owsServiceOnlineResource', 'owsServiceTitle', 'wcsLayerIds',
                  'wcsServiceUrl', 'wfsLayerIds', 'wfsServiceUrl',
                  'wfstDeleteLayerIds', 'wfstInsertLayerIds',
                  'wfstUpdateLayerIds', 'wmsDefaultMapUnitsPerMm', 'wmsExtent',
                  'wmsFeatureInfoAddWktGeometry',
                  'wmsFeatureInfoDocumentElement',
                  'wmsFeatureInfoDocumentElementNs',
                  'wmsFeatureInfoLayerAliasMap', 'wmsFeatureInfoPrecision',
                  'wmsFeatureInfoSchema',
                  'wmsFeatureInfoSegmentizeWktGeometry', 'wmsImageQuality',
                  'wmsInfoFormatSia2045', 'wmsInspireActivate',
                  'wmsInspireLanguage', 'wmsInspireMetadataDate',
                  'wmsInspireMetadataUrl', 'wmsInspireMetadataUrlType',
                  'wmsInspireTemporalReference', 'wmsMaxAtlasFeatures',
                  'wmsMaxHeight', 'wmsMaxWidth', 'wmsOutputCrsList',
                  'wmsRestrictedComposers', 'wmsRestrictedLayers',
                  'wmsRootName', 'wmsServiceUrl', 'wmsTileBuffer',
                  'wmsUseLayerIds', 'wmtsServiceUrl'):
            capabilities[c] = getattr(QgsServerProjectUtils, c)(p)

        info['capabilities'] = capabilities

        ####################################################
        # WMS Layers section

        info['wms_root_name'] = capabilities['wmsRootName'] if capabilities[
            'wmsRootName'] else p.title()
        restricted_wms = capabilities['wmsRestrictedLayers']
        wms_layers = {}
        use_ids = capabilities['wmsUseLayerIds']
        # Map layer title to layer name (or id if use_ids)
        wms_layers_map = {}
        # Maps a typename to a layer id
        wms_layers_typename_id_map = {}
        wms_layers_searchable = []
        wms_layers_queryable = []

        for l in p.mapLayers().values():
            if l.name() not in restricted_wms:
                wms_layers[l.id()] = layer_info(l)
                name = l.title() if l.title() else l.name()
                short_name = l.shortName() if l.shortName() else l.name()
                wms_layers_typename_id_map[short_name] = l.id()
                wms_layers_map[name] = l.id() if use_ids else short_name
                if bool(l.flags() & QgsMapLayer.Searchable):
                    wms_layers_searchable.append(l.id())
                if bool(l.flags() & QgsMapLayer.Identifiable):
                    wms_layers_queryable.append(l.id())

        info['wms_layers'] = wms_layers
        info['wms_layers_map'] = wms_layers_map
        info['wms_layers_searchable'] = wms_layers_searchable
        info['wms_layers_queryable'] = wms_layers_queryable
        info['wms_layers_typename_id_map'] = wms_layers_typename_id_map

        ####################################################
        # TOC tree (WMS published only)
        info['toc'] = get_toc(p, info)

    return info
Ejemplo n.º 14
0
    def virtualFields(params: Dict[str, str], response: QgsServerResponse, project: QgsProject) -> None:
        """ Get virtual fields for features
        In parameters:
            LAYER=wms-layer-name
            VIRTUALS={"key1": "first expression", "key2": "second expression"}
            // optionals
            FILTER=An expression to filter layer
            FIELDS=list of requested field separated by comma
            WITH_GEOMETRY=False
        """
        layer_name = params.get('LAYER', '')
        if not layer_name:
            raise ExpressionServiceError(
                "Bad request error",
                "Invalid 'VirtualFields' REQUEST: LAYER parameter is mandatory",
                400)

        # get layer
        layer = find_vector_layer(layer_name, project)
        # layer not found
        if not layer:
            raise ExpressionServiceError(
                "Bad request error",
                "Invalid LAYER parameter for 'VirtualFields': {} provided".format(layer_name),
                400)

        # get virtuals
        virtuals = params.get('VIRTUALS', '')
        if not virtuals:
            raise ExpressionServiceError(
                "Bad request error",
                "Invalid 'VirtualFields' REQUEST: VIRTUALS parameter is mandatory",
                400)

        # try to load virtuals dict
        try:
            vir_json = json.loads(virtuals)
        except Exception:
            QgsMessageLog.logMessage(
                "JSON loads virtuals '{}' exception:\n{}".format(virtuals, traceback.format_exc()),
                "lizmap", Qgis.Critical)
            raise ExpressionServiceError(
                "Bad request error",
                "Invalid 'VirtualFields' REQUEST: VIRTUALS '{}' are not well formed".format(virtuals),
                400)

        if not isinstance(vir_json, dict):
            raise ExpressionServiceError(
                "Bad request error",
                "Invalid 'VirtualFields' REQUEST: VIRTUALS '{}' are not well formed".format(virtuals),
                400)

        # create expression context
        exp_context = QgsExpressionContext()
        exp_context.appendScope(QgsExpressionContextUtils.globalScope())
        exp_context.appendScope(QgsExpressionContextUtils.projectScope(project))
        exp_context.appendScope(QgsExpressionContextUtils.layerScope(layer))

        # create distance area context
        da = QgsDistanceArea()
        da.setSourceCrs(layer.crs(), project.transformContext())
        da.setEllipsoid(project.ellipsoid())

        # parse virtuals
        exp_map = {}
        exp_parser_errors = []
        for k, e in vir_json.items():
            exp = QgsExpression(e)
            exp.setGeomCalculator(da)
            exp.setDistanceUnits(project.distanceUnits())
            exp.setAreaUnits(project.areaUnits())

            if exp.hasParserError():
                exp_parser_errors.append('Error "{}": {}'.format(e, exp.parserErrorString()))
                continue

            if not exp.isValid():
                exp_parser_errors.append('Expression not valid "{}"'.format(e))
                continue

            exp.prepare(exp_context)
            exp_map[k] = exp

        # expression parser errors found
        if exp_parser_errors:
            raise ExpressionServiceError(
                "Bad request error",
                "Invalid VIRTUALS for 'VirtualFields':\n{}".format('\n'.join(exp_parser_errors)),
                400)

        req = QgsFeatureRequest()

        # get filter
        req_filter = params.get('FILTER', '')
        if req_filter:
            req_exp = QgsExpression(req_filter)
            req_exp.setGeomCalculator(da)
            req_exp.setDistanceUnits(project.distanceUnits())
            req_exp.setAreaUnits(project.areaUnits())

            if req_exp.hasParserError():
                raise ExpressionServiceError(
                    "Bad request error",
                    "Invalid FILTER for 'VirtualFields' Error \"{}\": {}".format(
                        req_filter, req_exp.parserErrorString()),
                    400)

            if not req_exp.isValid():
                raise ExpressionServiceError(
                    "Bad request error",
                    "Invalid FILTER for 'VirtualFields' Expression not valid \"{}\"".format(req_filter),
                    400)

            req_exp.prepare(exp_context)
            req = QgsFeatureRequest(req_exp, exp_context)

        # With geometry
        with_geom = params.get('WITH_GEOMETRY', '').lower() in ['true', '1', 't']
        if not with_geom:
            req.setFlags(QgsFeatureRequest.NoGeometry)

        # Fields
        pk_attributes = layer.primaryKeyAttributes()
        attribute_list = [i for i in pk_attributes]
        fields = layer.fields()
        r_fields = [f.strip() for f in params.get('FIELDS', '').split(',') if f]
        for f in r_fields:
            attribute_list.append(fields.indexOf(f))

        # response
        response.setStatusCode(200)
        response.setHeader("Content-Type", "application/json")
        response.write('{ "type": "FeatureCollection","features":[')
        response.flush()

        json_exporter = QgsJsonExporter(layer)
        if attribute_list:
            json_exporter.setAttributes(attribute_list)

        separator = ''
        for feat in layer.getFeatures(req):
            fid = layer_name + '.' + get_server_fid(feat, pk_attributes)

            extra = {}

            # Update context
            exp_context.setFeature(feat)
            exp_context.setFields(feat.fields())

            # Evaluate expressions for virtual fields
            errors = {}
            for k, exp in exp_map.items():
                value = exp.evaluate(exp_context)
                if exp.hasEvalError():
                    extra[k] = None
                    errors[k] = exp.evalErrorString()
                else:
                    extra[k] = json.loads(QgsJsonUtils.encodeValue(value))
                    errors[k] = exp.expression()

            response.write(separator + json_exporter.exportFeature(feat, extra, fid))
            response.flush()
            separator = ',\n'
        response.write(']}')
        return
Ejemplo n.º 15
0
    def get_feature_with_form_scope(params: Dict[str, str], response: QgsServerResponse, project: QgsProject) -> None:
        """ Get filtered features with a form scope
        In parameters:
            LAYER=wms-layer-name
            FILTER=An expression to filter layer
            FORM_FEATURE={"type": "Feature", "geometry": {}, "properties": {}}
            // optionals
            FIELDS=list of requested field separated by comma
            WITH_GEOMETRY=False
        """
        layer_name = params.get('LAYER', '')
        if not layer_name:
            raise ExpressionServiceError(
                "Bad request error",
                "Invalid 'GetFeatureWithFormScope' REQUEST: LAYER parameter is mandatory",
                400)

        # get layer
        layer = find_vector_layer(layer_name, project)
        # layer not found
        if not layer:
            raise ExpressionServiceError(
                "Bad request error",
                "Invalid LAYER parameter for 'VirtualField': {} provided".format(layer_name),
                400)

        # get filter
        exp_filter = params.get('FILTER', '')
        if not exp_filter:
            raise ExpressionServiceError(
                "Bad request error",
                "Invalid 'GetFeatureWithFormScope' REQUEST: FILTER parameter is mandatory",
                400)

        # get form feature
        form_feature = params.get('FORM_FEATURE', '')
        if not form_feature:
            raise ExpressionServiceError(
                "Bad request error",
                "Invalid 'GetFeatureWithFormScope' REQUEST: FORM_FEATURE parameter is mandatory",
                400)

        # Check features
        try:
            geojson = json.loads(form_feature)
        except Exception:
            QgsMessageLog.logMessage(
                "JSON loads form feature '{}' exception:\n{}".format(form_feature, traceback.format_exc()),
                "lizmap", Qgis.Critical)
            raise ExpressionServiceError(
                "Bad request error",
                "Invalid 'GetFeatureWithFormScope' REQUEST: FORM_FEATURE '{}' are not well formed".format(form_feature),
                400)

        if not geojson or not isinstance(geojson, dict):
            raise ExpressionServiceError(
                "Bad request error",
                "Invalid 'GetFeatureWithFormScope' REQUEST: FORM_FEATURE '{}' are not well formed".format(form_feature),
                400)

        if ('type' not in geojson) or geojson['type'] != 'Feature':
            raise ExpressionServiceError(
                "Bad request error",
                ("Invalid 'GetFeatureWithFormScope' REQUEST: FORM_FEATURE '{}' are not well formed: type not defined "
                 "or not Feature.").format(form_feature),
                400)

        # try to load form feature
        # read fields
        form_feature_fields = QgsJsonUtils.stringToFields(
            form_feature,
            QTextCodec.codecForName("UTF-8"))
        # read features
        form_feature_list = QgsJsonUtils.stringToFeatureList(
            form_feature,
            form_feature_fields,
            QTextCodec.codecForName("UTF-8"))

        # features not well formed
        if not form_feature_list:
            raise ExpressionServiceError(
                "Bad request error",
                ("Invalid FORM_FEATURE for 'GetFeatureWithFormScope': not GeoJSON feature provided\n"
                 "{}").format(form_feature),
                400)

        if len(form_feature_list) != 1:
            raise ExpressionServiceError(
                "Bad request error",
                ("Invalid FORM_FEATURE for 'GetFeatureWithFormScope': not GeoJSON feature provided\n"
                 "{}").format(form_feature),
                400)

        # Get the form feature
        form_feat = form_feature_list[0]

        # create expression context
        exp_context = QgsExpressionContext()
        exp_context.appendScope(QgsExpressionContextUtils.globalScope())
        exp_context.appendScope(QgsExpressionContextUtils.projectScope(project))
        exp_context.appendScope(QgsExpressionContextUtils.layerScope(layer))
        exp_context.appendScope(QgsExpressionContextUtils.formScope(form_feat))

        # create distance area context
        da = QgsDistanceArea()
        da.setSourceCrs(layer.crs(), project.transformContext())
        da.setEllipsoid(project.ellipsoid())

        # Get filter expression
        exp_f = QgsExpression(exp_filter)
        exp_f.setGeomCalculator(da)
        exp_f.setDistanceUnits(project.distanceUnits())
        exp_f.setAreaUnits(project.areaUnits())

        if exp_f.hasParserError():
            raise ExpressionServiceError(
                "Bad request error",
                "Invalid FILTER for 'GetFeatureWithFormScope': Error \"{}\": {}".format(
                    exp_filter, exp_f.parserErrorString()),
                400)

        if not exp_f.isValid():
            raise ExpressionServiceError(
                "Bad request error",
                "Invalid FILTER for 'GetFeatureWithFormScope': Expression not valid \"{}\"".format(exp_filter),
                400)

        exp_f.prepare(exp_context)

        req = QgsFeatureRequest(exp_f, exp_context)

        # With geometry
        with_geom = params.get('WITH_GEOMETRY', '').lower() in ['true', '1', 't']
        if not with_geom:
            req.setFlags(QgsFeatureRequest.NoGeometry)

        # Fields
        pk_attributes = layer.primaryKeyAttributes()
        attribute_list = [i for i in pk_attributes]
        fields = layer.fields()
        r_fields = [f.strip() for f in params.get('FIELDS', '').split(',') if f]
        for f in r_fields:
            attribute_list.append(fields.indexOf(f))

        # response
        response.setStatusCode(200)
        response.setHeader("Content-Type", "application/json")
        response.write('{ "type": "FeatureCollection","features":[')
        response.flush()

        json_exporter = QgsJsonExporter(layer)
        if attribute_list:
            json_exporter.setAttributes(attribute_list)

        separator = ''
        for feat in layer.getFeatures(req):
            fid = layer_name + '.' + get_server_fid(feat, pk_attributes)
            response.write(separator + json_exporter.exportFeature(feat, {}, fid))
            response.flush()
            separator = ',\n'
        response.write(']}')
        return
Ejemplo n.º 16
0
    def replace_expression_text(params: Dict[str, str], response: QgsServerResponse, project: QgsProject) -> None:
        """ Replace expression texts against layer or features
        In parameters:
            LAYER=wms-layer-name
            STRING=A string with expression between [% and %]
            or
            STRINGS=["first string with expression", "second string with expression"]
            or
            STRINGS={"key1": "first string with expression", "key2": "second string with expression"}
            // optionals
            FEATURE={"type": "Feature", "geometry": {}, "properties": {}}
            or
            FEATURES=[{"type": "Feature", "geometry": {}, "properties": {}}, {"type": "Feature", "geometry": {},
            "properties": {}}]
            FORM_SCOPE=boolean to add formScope based on provided features
        """
        layername = params.get('LAYER', '')
        if not layername:
            raise ExpressionServiceError(
                "Bad request error",
                "Invalid 'ReplaceExpressionText' REQUEST: LAYER parameter is mandatory",
                400)

        # get layer
        layer = find_vector_layer(layername, project)
        # layer not found
        if not layer:
            raise ExpressionServiceError(
                "Bad request error",
                "Invalid LAYER parameter for 'ReplaceExpressionText': {} provided".format(layername),
                400)

        # get strings
        strings = params.get('STRINGS', '')
        if not strings:
            the_string = params.get('STRING', '')
            if not the_string:
                raise ExpressionServiceError(
                    "Bad request error",
                    "Invalid 'ReplaceExpressionText' REQUEST: STRING or STRINGS parameter is mandatory",
                    400)
            strings = '["{}"]'.format(the_string)

        # try to load expressions list or dict
        try:
            str_json = json.loads(strings)
        except Exception:
            QgsMessageLog.logMessage(
                "JSON loads strings '{}' exception:\n{}".format(strings, traceback.format_exc()),
                "lizmap", Qgis.Critical)
            raise ExpressionServiceError(
                "Bad request error",
                "Invalid 'ReplaceExpressionText' REQUEST: STRINGS '{}' are not well formed".format(strings),
                400)

        # get features
        features = params.get('FEATURES', '')
        if not features:
            feature = params.get('FEATURE', '')
            if feature:
                features = '[' + feature + ']'

        # create expression context
        exp_context = QgsExpressionContext()
        exp_context.appendScope(QgsExpressionContextUtils.globalScope())
        exp_context.appendScope(QgsExpressionContextUtils.projectScope(project))
        exp_context.appendScope(QgsExpressionContextUtils.layerScope(layer))

        # create distance area context
        da = QgsDistanceArea()
        da.setSourceCrs(layer.crs(), project.transformContext())
        da.setEllipsoid(project.ellipsoid())

        # organized strings
        str_map = {}
        str_items = []
        if isinstance(str_json, list):
            str_items = enumerate(str_json)
        elif isinstance(str_json, dict):
            str_items = str_json.items()
        for k, s in str_items:
            str_map[k] = s

        # create the body
        body = {
            'status': 'success',
            'results': [],
            'errors': [],
            'features': 0
        }

        # without features just replace expression string with layer context
        if not features:
            result = {}
            for k, s in str_map.items():
                value = QgsExpression.replaceExpressionText(s, exp_context, da)
                result[k] = json.loads(QgsJsonUtils.encodeValue(value))
            body['results'].append(result)
            write_json_response(body, response)
            return

        # Check features
        try:
            geojson = json.loads(features)
        except Exception:
            QgsMessageLog.logMessage(
                "JSON loads features '{}' exception:\n{}".format(features, traceback.format_exc()),
                "lizmap", Qgis.Critical)
            raise ExpressionServiceError(
                "Bad request error",
                "Invalid 'Evaluate' REQUEST: FEATURES '{}' are not well formed".format(features),
                400)

        if not geojson or not isinstance(geojson, list) or len(geojson) == 0:
            raise ExpressionServiceError(
                "Bad request error",
                "Invalid 'Evaluate' REQUEST: FEATURES '{}' are not well formed".format(features),
                400)

        if ('type' not in geojson[0]) or geojson[0]['type'] != 'Feature':
            raise ExpressionServiceError(
                "Bad request error",
                ("Invalid 'Evaluate' REQUEST: FEATURES '{}' are not well formed: type not defined or not "
                 "Feature.").format(features),
                400)

        # try to load features
        # read fields
        feature_fields = QgsJsonUtils.stringToFields(
            '{ "type": "FeatureCollection","features":' + features + '}',
            QTextCodec.codecForName("UTF-8"))
        # read features
        feature_list = QgsJsonUtils.stringToFeatureList(
            '{ "type": "FeatureCollection","features":' + features + '}',
            feature_fields,
            QTextCodec.codecForName("UTF-8"))

        # features not well formed
        if not feature_list:
            raise ExpressionServiceError(
                "Bad request error",
                ("Invalid FEATURES for 'ReplaceExpressionText': not GeoJSON features array "
                 "provided\n{}").format(features),
                400)

        # Extend layer fields with this provided in GeoJSON Features
        feat_fields = QgsFields(layer.fields())
        feat_fields.extend(feature_fields)

        # form scope
        add_form_scope = params.get('FORM_SCOPE', '').lower() in ['true', '1', 't']

        # loop through provided features to replace expression strings
        for f in feature_list:
            # clone the features with all attributes
            # those defined in layer + fields from GeoJSON Features
            feat = QgsFeature(feat_fields)
            feat.setGeometry(f.geometry())
            for field in f.fields():
                field_name = field.name()
                if feat_fields.indexOf(field_name) != -1:
                    feat.setAttribute(field_name, f[field_name])

            # Add form scope to expression context
            if add_form_scope:
                exp_context.appendScope(QgsExpressionContextUtils.formScope(feat))

            exp_context.setFeature(feat)
            exp_context.setFields(feat.fields())

            # replace expression strings with the new feature
            result = {}
            for k, s in str_map.items():
                value = QgsExpression.replaceExpressionText(s, exp_context, da)
                result[k] = json.loads(QgsJsonUtils.encodeValue(value))
            body['results'].append(result)

        write_json_response(body, response)
        return
Ejemplo n.º 17
0
    def evaluate(params: Dict[str, str], response: QgsServerResponse, project: QgsProject) -> None:
        """ Evaluate expressions against layer or features
        In parameters:
            LAYER=wms-layer-name
            EXPRESSION=An expression to evaluate
            or
            EXPRESSIONS=["first expression", "second expression"]
            or
            EXPRESSIONS={"key1": "first expression", "key2": "second expression"}
            // optionals
            FEATURE={"type": "Feature", "geometry": {}, "properties": {}}
            or
            FEATURES=[{"type": "Feature", "geometry": {}, "properties": {}}, {"type": "Feature", "geometry": {},
            "properties": {}}]
            FORM_SCOPE=boolean to add formScope based on provided features
        """

        layername = params.get('LAYER', '')
        if not layername:
            raise ExpressionServiceError(
                "Bad request error",
                "Invalid 'Evaluate' REQUEST: LAYER parameter is mandatory",
                400)

        # get layer
        layer = find_vector_layer(layername, project)
        # layer not found
        if not layer:
            raise ExpressionServiceError(
                "Bad request error",
                "Invalid LAYER parameter for 'Evaluate': {} provided".format(layername),
                400)

        # get expressions
        expressions = params.get('EXPRESSIONS', '')
        if not expressions:
            expression = params.get('EXPRESSION', '')
            if not expression:
                raise ExpressionServiceError(
                    "Bad request error",
                    "Invalid 'Evaluate' REQUEST: EXPRESSION or EXPRESSIONS parameter is mandatory",
                    400)
            expressions = '["{}"]'.format(expression)

        # try to load expressions list or dict
        try:
            exp_json = json.loads(expressions)
        except Exception:
            QgsMessageLog.logMessage(
                "JSON loads expressions '{}' exception:\n{}".format(expressions, traceback.format_exc()),
                "lizmap", Qgis.Critical)
            raise ExpressionServiceError(
                "Bad request error",
                "Invalid 'Evaluate' REQUEST: EXPRESSIONS '{}' are not well formed".format(expressions),
                400)

        # create expression context
        exp_context = QgsExpressionContext()
        exp_context.appendScope(QgsExpressionContextUtils.globalScope())
        exp_context.appendScope(QgsExpressionContextUtils.projectScope(project))
        exp_context.appendScope(QgsExpressionContextUtils.layerScope(layer))

        # create distance area context
        da = QgsDistanceArea()
        da.setSourceCrs(layer.crs(), project.transformContext())
        da.setEllipsoid(project.ellipsoid())

        # parse expressions
        exp_map = {}
        exp_parser_errors = []
        exp_items = []
        if isinstance(exp_json, list):
            exp_items = enumerate(exp_json)
        elif isinstance(exp_json, dict):
            exp_items = exp_json.items()
        for k, e in exp_items:
            exp = QgsExpression(e)
            exp.setGeomCalculator(da)
            exp.setDistanceUnits(project.distanceUnits())
            exp.setAreaUnits(project.areaUnits())

            if exp.hasParserError():
                exp_parser_errors.append('Error "{}": {}'.format(e, exp.parserErrorString()))
                continue

            if not exp.isValid():
                exp_parser_errors.append('Expression not valid "{}"'.format(e))
                continue

            exp.prepare(exp_context)
            exp_map[k] = exp

        # expression parser errors found
        if exp_parser_errors:
            raise ExpressionServiceError(
                "Bad request error",
                "Invalid EXPRESSIONS for 'Evaluate':\n{}".format('\n'.join(exp_parser_errors)),
                400)

        # get features
        features = params.get('FEATURES', '')
        if not features:
            feature = params.get('FEATURE', '')
            if feature:
                features = '[' + feature + ']'

        # create the body
        body = {
            'status': 'success',
            'results': [],
            'errors': [],
            'features': 0
        }

        # without features just evaluate expression with layer context
        if not features:
            result = {}
            error = {}
            for k, exp in exp_map.items():
                value = exp.evaluate(exp_context)
                if exp.hasEvalError():
                    result[k] = None
                    error[k] = exp.evalErrorString()
                else:
                    result[k] = json.loads(QgsJsonUtils.encodeValue(value))
            body['results'].append(result)
            body['errors'].append(error)
            write_json_response(body, response)
            return

        # Check features
        try:
            geojson = json.loads(features)
        except Exception:
            QgsMessageLog.logMessage(
                "JSON loads features '{}' exception:\n{}".format(features, traceback.format_exc()),
                "lizmap", Qgis.Critical)
            raise ExpressionServiceError(
                "Bad request error",
                "Invalid 'Evaluate' REQUEST: FEATURES '{}' are not well formed".format(features),
                400)

        if not geojson or not isinstance(geojson, list) or len(geojson) == 0:
            raise ExpressionServiceError(
                "Bad request error",
                "Invalid 'Evaluate' REQUEST: FEATURES '{}' are not well formed".format(features),
                400)

        if 'type' not in geojson[0] or geojson[0]['type'] != 'Feature':
            raise ExpressionServiceError(
                "Bad request error",
                ("Invalid 'Evaluate' REQUEST: FEATURES '{}' are not well formed: type not defined or not "
                 "Feature.").format(features),
                400)

        # try to load features
        # read fields
        feature_fields = QgsJsonUtils.stringToFields(
            '{ "type": "FeatureCollection","features":' + features + '}',
            QTextCodec.codecForName("UTF-8"))
        # read features
        feature_list = QgsJsonUtils.stringToFeatureList(
            '{ "type": "FeatureCollection","features":' + features + '}',
            feature_fields,
            QTextCodec.codecForName("UTF-8"))

        # features not well formed
        if not feature_list:
            raise ExpressionServiceError(
                "Bad request error",
                "Invalid FEATURES for 'Evaluate': not GeoJSON features array provided\n{}".format(features),
                400)

        # Extend layer fields with this provided in GeoJSON Features
        feat_fields = QgsFields(layer.fields())
        feat_fields.extend(feature_fields)

        # form scope
        add_form_scope = params.get('FORM_SCOPE', '').lower() in ['true', '1', 't']

        # loop through provided features to evaluate expressions
        for f in feature_list:
            # clone the features with all attributes
            # those defined in layer + fields from GeoJSON Features
            feat = QgsFeature(feat_fields)
            feat.setGeometry(f.geometry())
            for field in f.fields():
                fname = field.name()
                if feat_fields.indexOf(fname) != -1:
                    feat.setAttribute(fname, f[fname])

            # Add form scope to expression context
            if add_form_scope:
                exp_context.appendScope(QgsExpressionContextUtils.formScope(feat))

            exp_context.setFeature(feat)
            exp_context.setFields(feat.fields())

            # Evaluate expressions with the new feature
            result = {}
            error = {}
            for k, exp in exp_map.items():
                if add_form_scope:
                    # need to prepare the expression because the context has been updated with a new scope
                    exp.prepare(exp_context)
                value = exp.evaluate(exp_context)
                if exp.hasEvalError():
                    result[k] = None
                    error[k] = exp.evalErrorString()
                else:
                    result[k] = json.loads(QgsJsonUtils.encodeValue(value))
                    error[k] = exp.expression()
            body['results'].append(result)
            body['errors'].append(error)

        write_json_response(body, response)
        return