Coverage for gws-app/gws/base/ows/server/layer_caps.py: 0%
88 statements
« prev ^ index » next coverage.py v7.8.0, created at 2025-04-17 01:37 +0200
« prev ^ index » next coverage.py v7.8.0, created at 2025-04-17 01:37 +0200
1"""Utilities to deal with LayerCaps objects."""
3from typing import Optional
5import gws
6import gws.gis.extent
7import gws.lib.uom
8import gws.lib.xmlx as xmlx
10from . import core
13def for_layer(layer: gws.Layer, user: gws.User, service: Optional[gws.OwsService] = None) -> core.LayerCaps:
14 """Create ``LayerCaps`` for a layer."""
16 lc = core.LayerCaps(leaves=[], children=[])
18 lc.layer = layer
19 lc.title = layer.title
20 lc.model = layer.root.app.modelMgr.find_model(layer.ows, layer, user=user, access=gws.Access.read)
22 geom_name = layer.ows.geometryName
23 if not geom_name and lc.model:
24 geom_name = lc.model.geometryName
25 if not geom_name:
26 geom_name = 'geometry'
28 lc.layerName = layer.ows.layerName
29 lc.featureName = layer.ows.featureName
30 lc.geometryName = geom_name
32 lc.xmlNamespace = layer.ows.xmlNamespace
34 if lc.xmlNamespace:
35 lc.layerNameQ = xmlx.namespace.qualify_name(lc.layerName, lc.xmlNamespace)
36 lc.featureNameQ = xmlx.namespace.qualify_name(lc.featureName, lc.xmlNamespace)
37 lc.geometryNameQ = xmlx.namespace.qualify_name(lc.geometryName, lc.xmlNamespace)
38 else:
39 lc.layerNameQ = lc.layerName
40 lc.featureNameQ = lc.featureName
41 lc.geometryNameQ = lc.geometryName
43 lc.hasLegend = layer.hasLegend
44 lc.isSearchable = layer.isSearchable
46 scales = [gws.lib.uom.res_to_scale(r) for r in layer.resolutions]
47 lc.minScale = int(min(scales))
48 lc.maxScale = int(max(scales))
50 lc.bounds = []
51 if service:
52 lc.bounds = [
53 gws.Bounds(
54 crs=b.crs,
55 extent=gws.gis.extent.transform_from_wgs(layer.wgsExtent, b.crs)
56 )
57 for b in service.supportedBounds
58 ]
60 return lc
63def layer_name_matches(lc: core.LayerCaps, name: str) -> bool:
64 """Check if the layer name in the caps matches the given name."""
66 if ':' in name:
67 return name == lc.layerNameQ
68 else:
69 return name == lc.layerName
72def feature_name_matches(lc: core.LayerCaps, name: str) -> bool:
73 """Check if the feature name in the caps matches the given name."""
75 if ':' in name:
76 return name == lc.featureNameQ
77 else:
78 return name == lc.featureName
81def xml_schema(lcs: list[core.LayerCaps], user: gws.User) -> gws.XmlElement:
82 """Create an ad-hoc XML Schema for a list of `LayerCaps`."""
84 ns = None
86 for lc in lcs:
87 if not lc.xmlNamespace:
88 gws.log.debug(f'xml_schema: skip {lc.layer.uid}: no xmlns')
89 continue
90 if not lc.model:
91 gws.log.debug(f'xml_schema: skip {lc.layer.uid}: no model')
92 continue
93 if not ns:
94 ns = lc.xmlNamespace
95 continue
96 if lc.xmlNamespace.xmlns != ns.xmlns:
97 gws.log.debug(f'xml_schema: skip {lc.layer.uid}: wrong xmlns')
98 continue
100 if not ns:
101 raise gws.NotFoundError('xml_schema: no xmlns found')
103 tag = [
104 'xsd:schema',
105 {
106 f'xmlns': ns.uri,
107 'targetNamespace': ns.uri,
108 'elementFormDefault': 'qualified',
109 }
110 ]
112 if ns.extendsGml:
113 gml = xmlx.namespace.get('gml3')
114 tag[1][f'xmlns:{gml.xmlns}'] = gml.uri
115 tag.append(['xsd:import', {'namespace': gml.uri, 'schemaLocation': gml.schemaLocation}])
117 for lc in lcs:
118 elements = []
119 for f in lc.model.fields:
120 if user.can_read(f):
121 if f.attributeType == gws.AttributeType.geometry:
122 typ = _GEOM_TO_XSD.get(gws.u.get(f, 'geometryType'))
123 else:
124 typ = _ATTR_TO_XSD.get(f.attributeType)
126 if typ:
127 elements.append(['xsd:element', {
128 'maxOccurs': '1',
129 'minOccurs': '0',
130 'nillable': 'false' if f.isRequired else 'true',
131 'name': f.name,
132 'type': typ,
133 }])
135 type_name = f'{lc.featureName}Type'
137 type_def = ['xsd:complexContent']
138 if ns.extendsGml:
139 type_def.append(['xsd:extension', {'base': 'gml:AbstractFeatureType'}, ['xsd:sequence', elements]])
140 else:
141 type_def.append(['xsd:sequence', elements])
143 atts = {
144 'name': lc.featureName,
145 'type': type_name,
146 }
147 if ns.extendsGml:
148 atts['substitutionGroup'] = 'gml:AbstractFeature'
150 tag.append(['xsd:complexType', {'name': type_name}, type_def])
151 tag.append(['xsd:element', atts])
153 return xmlx.tag(*tag)
156# map attributes types to XSD
157# https://www.w3.org/TR/xmlschema11-2/#built-in-primitive-datatypes
159_ATTR_TO_XSD = {
160 gws.AttributeType.bool: 'xsd:boolean',
161 gws.AttributeType.bytes: 'xsd:hexBinary',
162 gws.AttributeType.date: 'xsd:date',
163 gws.AttributeType.datetime: 'xsd:dateTime',
164 gws.AttributeType.feature: '',
165 gws.AttributeType.featurelist: '',
166 gws.AttributeType.file: '',
167 gws.AttributeType.float: 'xsd:float',
168 gws.AttributeType.floatlist: '',
169 gws.AttributeType.geometry: 'gml:GeometryPropertyType',
170 gws.AttributeType.int: 'xsd:decimal',
171 gws.AttributeType.intlist: '',
172 gws.AttributeType.str: 'xsd:string',
173 gws.AttributeType.strlist: '',
174 gws.AttributeType.time: 'xsd:time',
175}
177_GEOM_TO_XSD = {
178 gws.GeometryType.point: 'gml:PointPropertyType',
179 gws.GeometryType.linestring: 'gml.LineStringPropertyType',
180 gws.GeometryType.polygon: 'gml:PolygonPropertyType',
181 gws.GeometryType.multipoint: 'gml:MultiPointPropertyType',
182 gws.GeometryType.multilinestring: 'gml:MultiLineStringPropertyType',
183 gws.GeometryType.multipolygon: 'gml:MultiPolygonPropertyType',
184 gws.GeometryType.multicurve: 'gml:MultiCurvePropertyType',
185 gws.GeometryType.multisurface: 'gml:MultiSurfacePropertyType',
186 gws.GeometryType.linearring: 'gml:LinearRingPropertyType',
187 gws.GeometryType.tin: 'gml:TinPropertyType',
188 gws.GeometryType.surface: 'gml:SurfacePropertyType',
189}