/[thuban]/branches/WIP-pyshapelib-bramz/Extensions/wms/parser.py
ViewVC logotype

Annotation of /branches/WIP-pyshapelib-bramz/Extensions/wms/parser.py

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2164 - (hide annotations)
Tue Apr 13 13:15:22 2004 UTC (20 years, 10 months ago) by joey
Original Path: trunk/thuban/Extensions/wms/parser.py
File MIME type: text/x-python
File size: 16207 byte(s)
Use a variable for denoting the sample filename

1 joey 2133 # Copyright (c) 2004 by Intevation GmbH
2     # Authors:
3     # Martin Schulze <[email protected]>
4     #
5     # This program is free software; you can redistribute it and/or modify
6     # it under the terms of the GNU General Public License as published by
7     # the Free Software Foundation; either version 2 of the License, or
8     # (at your option) any later version.
9     #
10     # This program is distributed in the hope that it will be useful,
11     # but WITHOUT ANY WARRANTY; without even the implied warranty of
12     # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13     # GNU General Public License for more details.
14     #
15     # You should have received a copy of the GNU General Public License
16     # along with this program; if not, write to the Free Software
17     # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18    
19     """
20     Inspect WMS Capabilities for later processing.
21    
22     Information should only be retrieved with the proper get*() methods.
23    
24     class WMSCapabilitiesParser:
25     __init__()
26    
27     grok(string)
28    
29     getTitle()
30     getAbstract()
31     getFees()
32     getAccessConstraints()
33     getFormats()
34     getLayers()
35     getSRS()
36    
37     getLayerTitle(layer)
38     getLayerSRS(layer)
39     getLayerLatLonBBox(layer)
40     getLayerBBox(layer, srs)
41    
42     isQueryable(layer)
43    
44     get_srs_discrepancies()
45     """
46    
47     __version__ = "$Revision$"
48     # $Source$
49     # $Id$
50    
51     import xml.dom.minidom
52    
53     from domutils import getElementsByName, getElementByName
54    
55     class WMSCapabilitiesParser:
56     """
57     Thuban class to parse capabilities supplied as large string.
58    
59     This class provides methods to parse and retrieve particular
60     information from the WMS capabilities XML. Information should
61     only be extracted by the respective get*() methods.
62     """
63    
64     layers = None
65     title = None
66     abstract = None
67     fees = None
68     access = None
69     formats = None
70     srs_discrepancies = None
71    
72    
73     def __init__(self):
74     """
75     Initialises an instance in this class.
76     """
77    
78     # Note that we must not initialise internal variables of the
79     # class in a mutable way or it will be shared among all
80     # instances. None is immutable, [] is not.
81     layers = []
82    
83    
84     def grok(self, data):
85     """
86     Parses the XML response to a WMS GetCapabilities request.
87    
88     Internal datastructure of the class will be filled.
89     Information should only be retrieved with the respective
90     get*() methods.
91     """
92 joey 2153 xml_dom = xml.dom.minidom.parseString(data)
93     root = xml_dom.documentElement
94 joey 2133
95     # Extract the title
96     foo = getElementByName(getElementByName(root, 'Service'), 'Title')
97     if foo:
98     self.title = foo.childNodes[0].data
99    
100     # Extract the abstract
101     foo = getElementByName(getElementByName(root, 'Service'), 'Abstract')
102     if foo and len(foo.childNodes[0].data):
103     self.abstract = foo.childNodes[0].data
104    
105     # Extract fees information
106     foo = getElementByName(getElementByName(root, 'Service'), 'Fees')
107     if foo and len(foo.childNodes[0].data) \
108 joey 2155 and foo.childNodes[0].data.lower() != 'none':
109 joey 2133 self.fees = foo.childNodes[0].data
110    
111     # Extract access information
112     foo = getElementByName(getElementByName(root, 'Service'),
113     'AccessConstraints')
114     if foo and len(foo.childNodes[0].data) \
115 joey 2155 and foo.childNodes[0].data.lower() != 'none':
116 joey 2133 self.access = foo.childNodes[0].data
117    
118     # Extract output format information
119     foo = getElementsByName(
120     getElementByName(getElementByName(getElementByName(
121     root, 'Capability'), 'Request'), 'GetMap'), 'Format')
122     self.formats = map((lambda i: i.childNodes[0].data), foo)
123    
124     # Extract layer names
125     self.layers = []
126     self.peekLayers(getElementByName(getElementByName(
127     root, 'Capability'), 'Layer'), -1)
128    
129 joey 2153 xml_dom.unlink()
130 joey 2133
131 joey 2153
132 joey 2133 def peekLayers(self, top, parent):
133     """
134     Inspect the provided DOM fragment referenced as top.
135    
136     This method will inspect all included layers and traverse the
137     tree recursively in order to fill the internal datastructure.
138    
139     Note that SRS other than EPSG:* are not yet supported,
140     especially there is no support for AUTO and NONE.
141     """
142    
143     index = len (self.layers)
144     self.layers.append({})
145     self.layers[index]['parent'] = parent
146    
147     for foo in top.attributes.keys():
148     if foo == 'queryable':
149     self.layers[index]['queryable'] \
150     = int(top.attributes.get(foo).nodeValue)
151    
152     foo = getElementByName(top, 'Title')
153     if foo and len(foo.childNodes[0].data):
154     self.layers[index]['title'] = foo.childNodes[0].data
155    
156     foo = getElementByName(top, 'Name')
157     if foo and len(foo.childNodes[0].data):
158     self.layers[index]['name'] = foo.childNodes[0].data
159    
160     # These values are only used for an integrity check
161     for foo in getElementsByName(top, 'SRS'):
162     for srs in foo.childNodes[0].data.split(" "):
163     if srs[0:5] == 'EPSG:':
164     srs = srs[5:]
165     try:
166     self.layers[index]['srs'].append(srs)
167     except KeyError:
168     self.layers[index]['srs'] = [srs]
169    
170     foo = getElementByName(top, 'LatLonBoundingBox')
171     if foo is not None:
172     self.layers[index]['llbbox'] = {}
173     for corner in ['minx', 'miny', 'maxx', 'maxy']:
174     self.layers[index]['llbbox'][corner] \
175     = foo.attributes.get(corner).nodeValue
176    
177     boxes = getElementsByName(top, 'BoundingBox')
178     if boxes != []:
179     self.layers[index]['bbox'] = {}
180     for foo in boxes:
181     srs = foo.attributes.get('SRS').nodeValue
182     if srs[0:5] == 'EPSG:':
183     srs = srs[5:]
184     self.layers[index]['bbox'][srs] = {}
185     for corner in ['minx', 'miny', 'maxx', 'maxy']:
186     self.layers[index]['bbox'][srs][corner] \
187     = foo.attributes.get(corner).nodeValue
188    
189     # Traverse subsidiary layers
190     sublayer = getElementsByName(top, 'Layer')
191     for l in sublayer:
192     self.peekLayers(l, index)
193    
194    
195     def getTitle(self):
196     """
197     Returns the main title of the WMS object.
198    
199     If no title is provided in the capabilities, an empty string
200     is returned.
201     """
202     if self.title is None:
203     return ''
204     else:
205     return self.title
206    
207    
208     def getAbstract(self):
209     """
210     Returns the abstract of the WMS object.
211    
212     If no abstract is provided in the capabilities, an empty
213     string is returned.
214     """
215     if self.abstract is None:
216     return ''
217     else:
218     return self.abstract
219    
220    
221     def getFees(self):
222     """
223     Returns the fees information of the WMS object.
224    
225     If no information is provided in the capabilities or if it is
226     set to 'none', an empty string is returned.
227     """
228     if self.fees is None:
229     return ''
230     else:
231     return self.fees
232    
233    
234     def getAccessConstraints(self):
235     """
236     Returns information about access constraints for the WMS object.
237    
238     If no information is provided in the capabilities or if it is
239     set to 'none', an empty string is returned.
240     """
241     if self.access is None:
242     return ''
243     else:
244     return self.access
245    
246    
247     def getFormats(self):
248     """
249     Returns a list of supported output formats.
250    
251     These are used in the GetMap request. This method will
252     default to 'image/jpeg' if no format is recognised in XML
253     Capabilities, assuming that JPEG will always be supported on
254     the server side.
255     """
256     if self.formats is None:
257     return ['image/jpeg']
258     else:
259     return self.formats
260    
261     def getLayers(self):
262     """
263     Returns a list of layer names.
264    
265     Only named layers will be returned, since a layer may have a
266     title but doesn't have to have a name associated to it as
267     well. If no layers were found, an empty list is returned.
268     """
269     result = []
270     for layer in self.layers:
271     if 'name' in layer:
272     result.append(layer['name'])
273    
274     return result
275    
276    
277     def getSRS(self):
278     """
279     Returns the root list of spatial reference systems (SRS).
280    
281     This list is taken from the root layer. Those SRS are common
282     to all subsidiary layers. If no SRS are common to all layers,
283     an empty list is returned. If no layers were detected, an
284     empty list is returned as well.
285     """
286     if len(self.layers) == 0:
287     return []
288    
289     # index 0 denotes the root layer by design
290     if 'srs' in self.layers[0].keys():
291     return self.layers[0]['srs']
292    
293    
294     def getLayerTitle(self, name):
295     """
296     Returns the title of the named layer.
297    
298     If no such title or no such layer exists, an empty string is
299     returned.
300     """
301     for layer in self.layers:
302     if 'name' in layer:
303     if layer['name'] == name:
304     if 'title' in layer:
305     return layer['title']
306    
307     return ''
308    
309    
310     def getLayerSRS(self, name):
311     """
312     Returns a list of spacial reference systems (SRS).
313    
314     The SRS are specified by the European Petroleum Survey Group
315     (EPSG). There should be at least one SRS per layer, though.
316     The prefix 'EPSG:' will be stripped. If none exists, an empty
317     list is returned.
318    
319     The specification [OGC 01-068r3] says about inheritance of
320     SRS:
321    
322     - Every layer is available in one or more SRS (or in an
323     undefined SRS)
324    
325     - Geographic information whose SRS is undefined
326     (e.g. digitised historical maps) shall use 'NONE'
327     (case-sensitive). This implementation does not support
328     this.
329    
330     - Every layer shall have at least one SRS element that is
331     either stated explicitly or inherited from a parent layer.
332    
333     - The root layer element shall include a sequence of zero or
334     more SRS elements listing all SRS which are common for to
335     all subsidiary layers.
336    
337     - Layers may optionally add to the global SRS list, or to the
338     list inherited from a parent layer.
339    
340 joey 2144 - A server which has the ability to transform data to
341     different SRSes may choose not to provide an explicit
342     BoundingBox for every possible SRS available for each Layer.
343     Thus the list of <SRS> elements are authoritative.
344    
345 joey 2133 This implementation returns the list of SRS for the given
346     layer, calculated by looking at BoundingBoxes defined in the
347     named layer and all layers higher in the hierarchy up to the
348     root layer which weren't overwritten.
349     """
350     for i in range(len(self.layers)):
351     if 'name' in self.layers[i]:
352     if self.layers[i]['name'] == name:
353     pivot = i
354     break
355     else:
356     return []
357    
358     result = []
359     while pivot != -1:
360 joey 2144 if 'srs' in self.layers[pivot]:
361     for srs in self.layers[pivot]['srs']:
362 joey 2133 if srs not in result:
363     result.append(srs)
364     pivot = self.layers[pivot]['parent']
365    
366     return result
367    
368    
369     def getLayerLatLonBBox(self, name):
370     """
371     Returns a dictionary of the LatLonBoundingBox.
372    
373     ... for the named layer if it exists. The SRS is always
374     EPSG:4326 per convention. There should be at least one,
375     though, inherited from the root layer at least. If none
376     exists, the value None is returned.
377     """
378     for layer in self.layers:
379     if 'name' in layer:
380     if layer['name'] == name:
381     if 'llbbox' in layer:
382     return layer['llbbox']
383     else:
384     # No LatLonBoundingBox found in current layer,
385     # so traverse the hierarchy upwards and check
386     # again until there is one.
387     pivot = layer
388     while pivot['parent'] != -1:
389     pivot = self.layers[pivot['parent']]
390     if 'llbbox' in pivot:
391     return pivot['llbbox']
392    
393     return None
394    
395    
396     def getLayerBBox(self, name, srs):
397     """
398     Returns a dictionary of the BoundingBox.
399    
400     If no such BoundingBox exists, None is returned.
401    
402     The specification [OGC 01-068r3] says about BoundingBoxes:
403    
404     - Layers may have zero or more BoundingBox elements what are
405     either stated explicitly or inherited from a parent layer.
406    
407     - A layer may have multiple BoundingBox elements, but each one
408     shall state a different SRS.
409    
410     - A layer inherits any BoundingBoxes defined by its
411     parents.
412    
413     - A BoundingBox inherited from the parent layer for a
414     particular SRS is replaced by any declaration for the same
415     SRS in the current layer.
416    
417     - A BoundingBox in the child layer for a new SRS which is not
418     already declared by the parent, is added to the list of
419     BoundingBoxes for the child layer.
420    
421     - A single layer shall not contain more than one BoundingBox
422     element for the same SRS.
423     """
424     for layer in self.layers:
425     if 'name' in layer:
426     if layer['name'] == name:
427     if 'bbox' in layer:
428     if srs in layer['bbox']:
429     return layer['bbox'][srs]
430    
431     # No BoundingBox for the given SRS found in
432     # current layer, so traverse the hierarchy upwards
433     # and check again until there is one.
434     pivot = layer
435     while pivot['parent'] != -1:
436     pivot = self.layers[pivot['parent']]
437     if 'bbox' in pivot:
438     if srs in pivot['bbox']:
439     return pivot['bbox'][srs]
440    
441     return None
442    
443    
444     def isQueryable(self, name):
445     """
446     Returns the value of the queryable attribute of a layer.
447    
448     This attribute denotes whether this layer can be queried
449     through the GetFeatureInfo request. The default value is 0.
450    
451     The specification [OGC 01-068r3] this attribute can be
452     inherited.
453     """
454    
455     for layer in self.layers:
456     if 'name' in layer:
457     if layer['name'] == name:
458     try:
459     return layer['queryable']
460     except KeyError:
461     # No attribute in this layer, so traverse the
462     # hierarchy upwards
463     pivot = layer
464     while pivot['parent'] != -1:
465     pivot = self.layers[pivot['parent']]
466     if 'queryable' in pivot:
467     return pivot['queryable']
468     return 0
469    
470    
471     def get_srs_discrepancies(self):
472     """
473     Returns a list of layer identifications where the denoted SRS
474     values differ from the calculated ones through BoundingBox
475     elements.
476     """
477     return self.srs_discrepancies
478    
479    
480     if __name__ == "__main__":
481     print "This module cannot be executed standalone."
482    
483     import os
484    
485 joey 2164 sample = "test/sample.xml"
486 joey 2133 try:
487 joey 2164 f = open(sample, "r")
488 joey 2133 except IOError:
489     try:
490 joey 2164 f = open(os.path.dirname(__file__) + "/" + sample, "r")
491 joey 2133 except IOError:
492 joey 2164 print "Cannot open %s for reading" % sample
493 joey 2133
494     if f is not None:
495     sample = f.read();
496     f.close()
497    
498     ina = WMSCapabilitiesParser()
499     ina.grok(sample)

Properties

Name Value
svn:eol-style native
svn:keywords Author Date Id Revision

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26