Coverage for gws-app/gws/base/ows/server/templatelib.py: 0%

76 statements  

« prev     ^ index     » next       coverage.py v7.8.0, created at 2025-04-17 01:37 +0200

1"""Helper functions for OWS service templates.""" 

2 

3from typing import Optional, Callable 

4 

5import gws 

6import gws.gis.gml 

7import gws.lib.mime 

8import gws.lib.uom 

9import gws.lib.xmlx as xmlx 

10 

11from . import core, request 

12 

13 

14# OGC 06-121r9 Table 34 

15# Ordered sequence of two double values in decimal degrees, with longitude before latitude 

16def ows_wgs84_bounding_box(lc: core.LayerCaps): 

17 return ( 

18 'ows:WGS84BoundingBox', 

19 ('ows:LowerCorner', coord_dms(lc.layer.wgsExtent[0]), ' ', coord_dms(lc.layer.wgsExtent[1])), 

20 ('ows:UpperCorner', coord_dms(lc.layer.wgsExtent[2]), ' ', coord_dms(lc.layer.wgsExtent[3])) 

21 ) 

22 

23 

24# OGC 06-121r3 sec 7.4.4 

25def ows_service_identification(ta: request.TemplateArgs): 

26 md = ta.service.metadata 

27 

28 return ( 

29 'ows:ServiceIdentification', 

30 ('ows:Title', md.title), 

31 ('ows:Abstract', md.abstract), 

32 ows_keywords(md), 

33 ('ows:ServiceType', ta.service.protocol), 

34 ('ows:ServiceTypeVersion', ta.version), 

35 ('ows:Fees', md.fees) if md.fees else None, 

36 ('ows:AccessConstraints', md.accessConstraints[0].title) if md.accessConstraints else None, 

37 ) 

38 

39 

40# OGC 06-121r3 sec 7.4.5 

41def ows_service_provider(ta: request.TemplateArgs): 

42 md = ta.service.metadata 

43 

44 return ( 

45 'ows:ServiceProvider', 

46 ('ows:ProviderName', md.contactProviderName), 

47 ('ows:ProviderSite', {'xlink:href': md.contactProviderSite}), 

48 ( 

49 'ows:ServiceContact', 

50 ('ows:IndividualName', md.contactPerson), 

51 ('ows:PositionName', md.contactPosition), 

52 ( 

53 'ows:ContactInfo', 

54 ( 

55 'ows:Phone', 

56 ('ows:Voice', md.contactPhone), 

57 ('ows:Facsimile', md.contactFax) 

58 ), 

59 ( 

60 'ows:Address', 

61 ('ows:DeliveryPoint', md.contactAddress), 

62 ('ows:City', md.contactCity), 

63 ('ows:AdministrativeArea', md.contactArea), 

64 ('ows:PostalCode', md.contactZip), 

65 ('ows:Country', md.contactCountry), 

66 ('ows:ElectronicMailAddress', md.contactEmail), 

67 ), 

68 ('ows:OnlineResource', {'xlink:href': md.contactUrl}), 

69 ), 

70 ('ows:Role', md.contactRole) 

71 ) 

72 ) 

73 

74 

75# OGC 06-121r3 table 15,16,17 

76# OGC 06-121r3 11.2: 

77# A URL prefix is defined as a string including... mandatory question mark 

78 

79def ows_service_url(ta: request.TemplateArgs, get=True, post=False): 

80 if get: 

81 yield 'ows:DCP/ows:HTTP/ows:Get', {'xlink:type': 'simple', 'xlink:href': ta.serviceUrl + '?'} 

82 if post: 

83 yield 'ows:DCP/ows:HTTP/ows:Post', {'xlink:type': 'simple', 'xlink:href': ta.serviceUrl} 

84 

85 

86def ows_value(value): 

87 return 'ows:Value', value 

88 

89 

90def online_resource(url): 

91 return 'OnlineResource', {'xlink:type': 'simple', 'xlink:href': url} 

92 

93 

94# OGC 01-068r3, 6.2.2 

95# The URL prefix shall end in either a '?' (in the absence of additional server-specific parameters) or a '&'. 

96# OGC 06-042, 6.3.3 

97# A URL prefix is defined... as a string including... mandatory question mark 

98 

99def dcp_service_url(ta: request.TemplateArgs): 

100 return 'DCPType/HTTP/Get', online_resource(ta.serviceUrl + '?') 

101 

102 

103def legend_url(ta: request.TemplateArgs, lc: core.LayerCaps): 

104 _, _, name = xmlx.namespace.split_name(lc.layerNameQ) 

105 return ( 

106 'LegendURL', 

107 ('Format', 'image/png'), 

108 online_resource(f'{ta.serviceUrl}?request=GetLegendGraphic&layer={name}')) 

109 

110 

111def ows_keywords(md: gws.Metadata): 

112 return _keywords(md, 'ows:Keywords', 'ows:Keyword') 

113 

114 

115def keywords(md: gws.Metadata): 

116 return _keywords(md, 'KeywordList', 'Keyword') 

117 

118 

119def _keywords(md: gws.Metadata, container_name, tag_name): 

120 kws = [] 

121 

122 if md.keywords: 

123 for kw in md.keywords: 

124 kws.append((tag_name, kw)) 

125 if md.inspireTheme: 

126 kws.append((tag_name, md.inspireThemeNameEn, {'vocabulary': 'GEMET - INSPIRE themes'})) 

127 if md.isoTopicCategories: 

128 for cat in md.isoTopicCategories: 

129 kws.append((tag_name, cat, {'vocabulary': 'ISO 19115:2003'})) 

130 if md.inspireMandatoryKeyword: 

131 kws.append((tag_name, md.inspireMandatoryKeyword, {'vocabulary': 'ISO'})) 

132 

133 if kws: 

134 return container_name, kws 

135 

136 

137def lon_lat_envelope(lc: core.LayerCaps): 

138 return ( 

139 'lonLatEnvelope', 

140 {'srsName': 'urn:ogc:def:crs:OGC:1.3:CRS84'}, 

141 ('gml:pos', coord_dms(lc.layer.wgsExtent[0]), ' ', coord_dms(lc.layer.wgsExtent[1])), 

142 ('gml:pos', coord_dms(lc.layer.wgsExtent[2]), ' ', coord_dms(lc.layer.wgsExtent[3])), 

143 ) 

144 

145 

146# Nested format (WFS 1, WMS): 

147# OGC 06-042 7.2.4.6.11 

148# The "type" attribute indicates the standard... The enclosed <Format> element... etc 

149 

150def meta_links_nested(ta: request.TemplateArgs, md: gws.Metadata): 

151 if md.metaLinks: 

152 for ml in md.metaLinks: 

153 yield meta_url_nested(ta, ml, 'MetadataURL') 

154 

155 

156def meta_url_nested(ta: request.TemplateArgs, ml: gws.MetadataLink, name: str): 

157 if ml: 

158 yield name, {'type': ml.type}, ('Format', ml.format), online_resource(ta.url_for(ml.url)) 

159 

160 

161# Simple format (WFS 2) 

162# OGC 09-025r1 Table 11 

163# The xlink:href element shall be used to reference any metadata. 

164# The optional about attribute may be used to reference the aspect of the element which includes 

165# this wfs:MetadataURL element that this metadata provides more information about. 

166# (whatever that means) 

167 

168def meta_links_simple(ta: request.TemplateArgs, md: gws.Metadata): 

169 if md.metaLinks: 

170 for ml in md.metaLinks: 

171 yield meta_url_simple(ta, ml, 'MetadataURL') 

172 

173 

174def meta_url_simple(ta: request.TemplateArgs, ml: gws.MetadataLink, name: str): 

175 if ml: 

176 yield name, {'xlink:href': ta.url_for(ml.url), 'about': ml.about} 

177 

178 

179# http://inspire.ec.europa.eu/schemas/common/1.0/network.xsd 

180# Scenario 2: Mandatory (where appropriate) metadata elements not mapped to standard capabilities, 

181# plus mandatory language parameters, 

182# plus OPTIONAL MetadataUrl pointing to an INSPIRE Compliant ISO metadata document 

183 

184def inspire_extended_capabilities(ta: request.TemplateArgs): 

185 md = ta.service.metadata 

186 return [ 

187 ( 

188 'inspire_common:ResourceLocator', 

189 ('inspire_common:URL', ta.serviceUrl), 

190 ('inspire_common:MediaType', 'application/xml'), 

191 ), 

192 ('inspire_common:ResourceType', md.inspireResourceType), 

193 ('inspire_common:TemporalReference/inspire_common:DateOfPublication', md.dateCreated), 

194 

195 ( 

196 'inspire_common:Conformity', 

197 ( 

198 'inspire_common:Specification', {'xsi:type': 'inspire_common:citationInspireInteroperabilityRegulation_eng'}, 

199 ('inspire_common:Title', 'COMMISSION REGULATION (EU) No 1089/2010 of 23 November 2010 implementing Directive 2007/2/EC of the European Parliament and of the Council as regards interoperability of spatial data sets and services'), 

200 ('inspire_common:DateOfPublication', '2010-12-08'), 

201 ('inspire_common:URI', 'OJ:L:2010:323:0011:0102:EN:PDF'), 

202 ( 

203 'inspire_common:ResourceLocator', 

204 ('inspire_common:URL', 'http://eur-lex.europa.eu/LexUriServ/LexUriServ.do?uri=OJ:L:2010:323:0011:0102:EN:PDF'), 

205 ('inspire_common:MediaType', 'application/pdf'), 

206 ), 

207 ), 

208 ('inspire_common:Degree', md.inspireDegreeOfConformity), 

209 ), 

210 ( 

211 'inspire_common:MetadataPointOfContact', 

212 ('inspire_common:OrganisationName', md.contactOrganization), 

213 ('inspire_common:EmailAddress', md.contactEmail), 

214 ), 

215 

216 ('inspire_common:MetadataDate', md.dateCreated), 

217 ('inspire_common:SpatialDataServiceType', md.inspireSpatialDataServiceType), 

218 ('inspire_common:MandatoryKeyword/inspire_common:KeywordValue', md.inspireMandatoryKeyword), 

219 

220 ( 

221 'inspire_common:Keyword', 

222 ( 

223 'inspire_common:OriginatingControlledVocabulary', 

224 ('inspire_common:Title', 'INSPIRE themes'), 

225 ('inspire_common:DateOfPublication', '2008-06-01'), 

226 ), 

227 ('inspire_common:KeywordValue', md.inspireThemeNameEn), 

228 ), 

229 

230 ( 

231 'inspire_common:SupportedLanguages', 

232 ('inspire_common:DefaultLanguage/inspire_common:Language', md.language3), 

233 ('inspire_common:SupportedLanguage/inspire_common:Language', md.language3), 

234 ), 

235 

236 ('inspire_common:ResponseLanguage/inspire_common:Language', md.language3) 

237 ] 

238 

239 

240def coord_dms(n): 

241 return round(n, gws.lib.uom.DEFAULT_PRECISION[gws.Uom.deg]) 

242 

243 

244def coord_m(n): 

245 return round(n, gws.lib.uom.DEFAULT_PRECISION[gws.Uom.m]) 

246 

247 

248def to_xml_response(ta: request.TemplateArgs, tag, extra_namespaces: Optional[list[gws.XmlNamespace]] = None) -> gws.ContentResponse: 

249 if ta.sr.isSoap: 

250 tag = 'soap:Envelope', ('soap:Header', ''), ('soap:Body', tag) 

251 

252 el = xmlx.tag(*tag) 

253 xml = el.to_string( 

254 extra_namespaces=extra_namespaces, 

255 with_xml_declaration=True, 

256 with_namespace_declarations=True, 

257 with_schema_locations=True 

258 ) 

259 

260 return gws.ContentResponse(mime=gws.lib.mime.XML, content=xml)