~ubuntu-branches/ubuntu/vivid/grass/vivid-proposed

« back to all changes in this revision

Viewing changes to scripts/r.in.wms/wms_cap_parsers.py

  • Committer: Package Import Robot
  • Author(s): Bas Couwenberg
  • Date: 2015-02-20 23:12:08 UTC
  • mfrom: (8.2.6 experimental)
  • Revision ID: package-import@ubuntu.com-20150220231208-1u6qvqm84v430b10
Tags: 7.0.0-1~exp1
* New upstream release.
* Update python-ctypes-ternary.patch to use if/else instead of and/or.
* Drop check4dev patch, rely on upstream check.
* Add build dependency on libpq-dev to grass-dev for libpq-fe.h.
* Drop patches applied upstream, refresh remaining patches.
* Update symlinks for images switched from jpg to png.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
"""!
 
2
@brief Parsers for WMS/WMTS/NASA OnEarth capabilities files.
 
3
 
 
4
List of classes:
 
5
 - wms_cap_parsers::BaseCapabilitiesTree
 
6
 - wms_cap_parsers::WMSXMLNsHandler
 
7
 - wms_cap_parsers::WMSCapabilitiesTree
 
8
 - wms_cap_parsers::WMTSXMLNsHandler
 
9
 - wms_cap_parsers::WMTSCapabilitiesTree
 
10
 - wms_cap_parsers::OnEarthCapabilitiesTree
 
11
 
 
12
(C) 2012 by the GRASS Development Team
 
13
 
 
14
This program is free software under the GNU General Public License
 
15
(>=v2). Read the file COPYING that comes with GRASS for details.
 
16
 
 
17
@author Stepan Turek <stepan.turek seznam.cz> (Mentor: Martin Landa)
 
18
"""
 
19
try:
 
20
    from xml.etree.ElementTree import ParseError
 
21
except ImportError: # < Python 2.7
 
22
    from xml.parsers.expat import ExpatError as ParseError
 
23
 
 
24
import xml.etree.ElementTree as etree
 
25
import grass.script as grass 
 
26
 
 
27
class BaseCapabilitiesTree(etree.ElementTree):
 
28
    def __init__(self, cap_file):
 
29
        """!Initialize xml.etree.ElementTree
 
30
        """
 
31
        try:
 
32
            etree.ElementTree.__init__(self, file = cap_file)
 
33
        except ParseError:
 
34
            raise ParseError(_("Unable to parse XML file"))
 
35
        except IOError as error:
 
36
            raise ParseError(_("Unable to open XML file '%s'.\n%s\n" % (cap_file, error)))
 
37
 
 
38
        if self.getroot() is None:
 
39
            raise ParseError(_("Root node was not found.")) 
 
40
        
 
41
class WMSXMLNsHandler:
 
42
    def __init__(self, caps):
 
43
        """!Handle XML namespaces according to WMS version of capabilities.
 
44
        """
 
45
        self.namespace = "{http://www.opengis.net/wms}"
 
46
        
 
47
        if caps.getroot().find("Service") is not None:
 
48
            self.use_ns = False
 
49
        elif caps.getroot().find(self.namespace + "Service") is not None:
 
50
            self.use_ns = True
 
51
        else:
 
52
            raise ParseError(_("Unable to parse capabilities file.\n\
 
53
                                Tag <%s> was not found.") % "Service")
 
54
    
 
55
    def Ns(self, tag_name):
 
56
        """!Add namespace to tag_name according to version
 
57
        """
 
58
        if self.use_ns:
 
59
            tag_name = self.namespace + tag_name
 
60
        return tag_name
 
61
 
 
62
class WMSCapabilitiesTree(BaseCapabilitiesTree):
 
63
    def __init__(self, cap_file, force_version = None):
 
64
        """!Parses WMS capabilities file.
 
65
            If the capabilities file cannot be parsed if it raises xml.etree.ElementTree.ParseError.
 
66
 
 
67
        The class manges inheritance in 'Layer' elements. Inherited elements 
 
68
        are added to 'Layer' element.
 
69
        The class also removes elements which are in invalid form and are needed 
 
70
        by wxGUI capabilities dialog.
 
71
 
 
72
        @param cap_file - capabilities file        
 
73
        @param force_version - force capabilities file version (1.1.1, 1.3.0)
 
74
        """
 
75
        BaseCapabilitiesTree.__init__(self, cap_file)
 
76
        self.xml_ns = WMSXMLNsHandler(self)
 
77
 
 
78
        grass.debug('Checking WMS capabilities tree.', 4)
 
79
        
 
80
        if not "version" in self.getroot().attrib:
 
81
            raise ParseError(_("Missing version attribute root node "
 
82
                                "in Capabilities XML file"))
 
83
        else:
 
84
            wms_version = self.getroot().attrib["version"]
 
85
        
 
86
        if wms_version == "1.3.0":
 
87
            self.proj_tag = "CRS"
 
88
        else:
 
89
            self.proj_tag = "SRS"
 
90
 
 
91
        if force_version is not None:
 
92
            if wms_version != force_version:
 
93
                raise ParseError(_("WMS server does not support '%s' version.") % wms_version)
 
94
 
 
95
        capability = self._find(self.getroot(), "Capability")
 
96
        root_layer = self._find(capability, "Layer")
 
97
 
 
98
        self._checkFormats(capability)
 
99
        self._checkLayerTree(root_layer)
 
100
 
 
101
        grass.debug('Check of WMS capabilities tree was finished.', 4)
 
102
 
 
103
    def _checkFormats(self, capability):
 
104
        """!Check if format element is defined.
 
105
        """        
 
106
        request = self._find(capability, "Request")
 
107
        get_map = self._find(request, "GetMap")
 
108
        formats = self._findall(get_map, "Format")
 
109
 
 
110
    def _checkLayerTree(self, parent_layer, first = True):
 
111
        """!Recursively check layer tree and manage inheritance in the tree
 
112
        """
 
113
        if first:
 
114
            self._initLayer(parent_layer, None)
 
115
        
 
116
        layers = parent_layer.findall((self.xml_ns.Ns("Layer")))
 
117
        
 
118
        for l in layers:
 
119
            self._initLayer(l, parent_layer)
 
120
            self._checkLayerTree(l, False)
 
121
        
 
122
    def _initLayer(self, layer, parent_layer):
 
123
        """Inherit elements from parent layer
 
124
 
 
125
        @param layer - <Layer> element which inherits
 
126
        @param parent_layer - <Layer> element which is inherited from 
 
127
        """
 
128
        if parent_layer is not None:
 
129
            replaced_elements = [ ["EX_GeographicBoundingBox", "replace"],
 
130
                                  ["Attribution", "replace"],
 
131
                                  ["MinScaleDenominator", "replace"],
 
132
                                  ["MaxScaleDenominator", "replace"],
 
133
                                  ["AuthorityURL", "add"]]
 
134
            
 
135
            for element in replaced_elements:
 
136
                elems = layer.findall(self.xml_ns.Ns(element[0]))
 
137
 
 
138
                if len(elems) != 0 or element[1] == "add":
 
139
                    for e in parent_layer.findall(self.xml_ns.Ns(element[0])):
 
140
                        layer.append(e)
 
141
            
 
142
            inh_arguments = ["queryable", "cascaded", "opaque",
 
143
                             "noSubsets", "fixedWidth", "fixedHeight"]
 
144
            
 
145
            for attr in parent_layer.attrib:
 
146
                if attr not in layer.attrib and attr in inh_arguments:
 
147
                    layer.attrib[attr] = parent_layer.attrib[attr]
 
148
            
 
149
            self._inhNotSame(self.proj_tag, "element_content", layer, parent_layer)
 
150
            self._inhNotSame("BoundingBox", "attribute", layer, parent_layer, self.proj_tag)
 
151
 
 
152
            # remove invalid Styles 
 
153
            styles = layer.findall(self.xml_ns.Ns('Style'))
 
154
            for s in styles:
 
155
                s_name = s.find(self.xml_ns.Ns('Name'))
 
156
                if s_name is None or not s_name.text:
 
157
                    grass.debug('Removed invalid <Style> element.', 4)
 
158
                    layer.remove(s)
 
159
 
 
160
            self._inhNotSame("Style", "child_element_content", layer, parent_layer, "Name")
 
161
            self._inhNotSame("Dimension", "attribute", layer, parent_layer, "name")
 
162
 
 
163
    def _inhNotSame(self, element_name, cmp_type, layer, parent_layer, add_arg = None):
 
164
        """Inherit elements which have unique values.
 
165
 
 
166
        @param element_name - name of inherited element
 
167
        @param cmp_type - 'element_content' - compared value is text of <Layer> element
 
168
        @param cmp_type - 'child_element_content' - compared value is text of a child of the <Layer> element
 
169
        @param cmp_type - 'attribute' - compared value is text of the <Layer> element attribute
 
170
        @param layer - <Layer> element which inherits
 
171
        @param parent_layer - <Layer> element which is inherited from 
 
172
        @param add_arg - name of child element or attribute
 
173
        """
 
174
        elem = layer.findall(self.xml_ns.Ns(element_name))
 
175
 
 
176
        parent_elems = []
 
177
        if parent_layer is not None:
 
178
            parent_elems = parent_layer.findall(self.xml_ns.Ns(element_name))
 
179
        
 
180
        for par_elem in parent_elems:
 
181
            parent_cmp_text = None
 
182
            if cmp_type == "attribute":
 
183
                if add_arg in par_elem.attrib:
 
184
                    parent_cmp_text = par_elem.attrib[add_arg];
 
185
 
 
186
            elif cmp_type == "element_content":
 
187
                parent_cmp_text = par_elem.text
 
188
                
 
189
            elif cmp_type == "child_element_content":
 
190
                parent_cmp = par_elem.find(self.xml_ns.Ns(add_arg))
 
191
                if parent_cmp is not None:
 
192
                    parent_cmp_text = parent_cmp.text
 
193
            
 
194
            if parent_cmp_text is None:
 
195
                continue
 
196
            
 
197
            is_there = False
 
198
            for elem in elem:
 
199
                cmp_text = None
 
200
                if cmp_type == "attribute":
 
201
                    if add_arg in elem.attrib:
 
202
                        cmp_text = elem.attrib[add_arg]
 
203
                
 
204
                elif cmp_type == "element_content":
 
205
                    cmp_text = elem.text
 
206
                
 
207
                elif cmp_type == "child_element_content":
 
208
                    cmp = elem.find(self.xml_ns.Ns(add_arg))
 
209
                    if cmp is not None:
 
210
                        cmp_text = cmp.text
 
211
                
 
212
                if cmp_text is None or \
 
213
                   cmp_text.lower() == parent_cmp_text.lower():
 
214
                    is_there = True
 
215
                    break
 
216
            
 
217
            if not is_there:
 
218
                layer.append(par_elem)
 
219
 
 
220
    def _find(self, etreeElement, tag):
 
221
        """!Find child element.
 
222
            If the element is not found it raises xml.etree.ElementTree.ParseError.  
 
223
        """
 
224
        res = etreeElement.find(self.xml_ns.Ns(tag))
 
225
 
 
226
        if res is None:
 
227
            raise ParseError(_("Unable to parse capabilities file. \n\
 
228
                                Tag <%s> was not found.") % tag)
 
229
 
 
230
        return res
 
231
 
 
232
    def _findall(self, etreeElement, tag):
 
233
        """!Find all children element.
 
234
            If no element is found it raises xml.etree.ElementTree.ParseError.  
 
235
        """
 
236
        res = etreeElement.findall(self.xml_ns.Ns(tag))
 
237
 
 
238
        if not res:
 
239
            raise ParseError(_("Unable to parse capabilities file. \n\
 
240
                                Tag <%s> was not found.") % tag)
 
241
 
 
242
        return res
 
243
 
 
244
    def getprojtag(self):
 
245
        """!Return projection tag according to version of capabilities (CRS/SRS).
 
246
        """
 
247
        return self.proj_tag
 
248
 
 
249
    def getxmlnshandler(self):
 
250
        """!Return WMSXMLNsHandler object.
 
251
        """
 
252
        return self.xml_ns
 
253
 
 
254
class WMTSXMLNsHandler:
 
255
    """!Handle XML namespaces which are used in WMTS capabilities file.
 
256
    """
 
257
    def NsWmts(self, tag):
 
258
        """!Add namespace.
 
259
        """
 
260
        return "{http://www.opengis.net/wmts/1.0}" + tag
 
261
 
 
262
    def NsOws(self, tag):
 
263
        """!Add namespace.
 
264
        """
 
265
        return "{http://www.opengis.net/ows/1.1}" + tag
 
266
 
 
267
class WMTSCapabilitiesTree(BaseCapabilitiesTree):
 
268
    def __init__(self, cap_file):
 
269
        """!Parses WMTS capabilities file.
 
270
            If the capabilities file cannot be parsed it raises xml.etree.ElementTree.ParseError.
 
271
 
 
272
        The class also removes elements which are in invalid form and are needed 
 
273
        by wxGUI capabilities dialog or for creation of GetTile request by GRASS WMS library.
 
274
 
 
275
        @param cap_file - capabilities file        
 
276
        """
 
277
        BaseCapabilitiesTree.__init__(self, cap_file)
 
278
        self.xml_ns = WMTSXMLNsHandler()
 
279
        
 
280
        grass.debug('Checking WMTS capabilities tree.', 4)
 
281
 
 
282
        contents = self._find(self.getroot(), 'Contents', self.xml_ns.NsWmts)
 
283
 
 
284
        tile_mat_sets = self._findall(contents, 'TileMatrixSet', self.xml_ns.NsWmts)
 
285
 
 
286
        for mat_set in tile_mat_sets:
 
287
            if not self._checkMatSet(mat_set):
 
288
                grass.debug('Removed invalid <TileMatrixSet> element.', 4)
 
289
                contents.remove(mat_set)
 
290
 
 
291
        # are there any <TileMatrixSet> elements after the check
 
292
        self._findall(contents, 'TileMatrixSet', self.xml_ns.NsWmts)
 
293
 
 
294
        layers = self._findall(contents, 'Layer', self.xml_ns.NsWmts)
 
295
        for l in layers:
 
296
            if not self._checkLayer(l):
 
297
                grass.debug('Removed invalid <Layer> element.', 4)
 
298
                contents.remove(l)
 
299
 
 
300
        # are there any <Layer> elements after the check
 
301
        self._findall(contents, 'Layer', self.xml_ns.NsWmts)
 
302
 
 
303
        grass.debug('Check of WMTS capabilities tree was finished.', 4)
 
304
 
 
305
    def _checkMatSet(self, mat_set):
 
306
        """!Check <TileMatrixSet>.
 
307
        """
 
308
        mat_set_id = mat_set.find(self.xml_ns.NsOws('Identifier'))
 
309
        if mat_set_id is None or not mat_set_id.text:
 
310
            return False
 
311
 
 
312
        mat_set_srs = mat_set.find(self.xml_ns.NsOws('SupportedCRS'))
 
313
        if mat_set_srs is None or \
 
314
           not mat_set_srs.text:
 
315
            return False
 
316
 
 
317
        tile_mats = mat_set.findall(self.xml_ns.NsWmts('TileMatrix'))
 
318
        if not tile_mats:
 
319
            return False
 
320
 
 
321
        for t_mat in tile_mats:
 
322
            if not self._checkMat(t_mat):
 
323
               grass.debug('Removed invalid <TileMatrix> element.', 4)
 
324
               mat_set.remove(t_mat)
 
325
 
 
326
        tile_mats = mat_set.findall(self.xml_ns.NsWmts('TileMatrix'))
 
327
        if not tile_mats:
 
328
            return False
 
329
 
 
330
        return True
 
331
 
 
332
    def _checkMat(self, t_mat):        
 
333
        """!Check <TileMatrix>.
 
334
        """
 
335
        def _checkElement(t_mat, e, func):
 
336
            element = t_mat.find(self.xml_ns.NsWmts(e))
 
337
            if element is None or not element.text: 
 
338
                return False
 
339
 
 
340
            try:
 
341
                e = func(element.text)
 
342
            except ValueError:
 
343
                return False
 
344
 
 
345
            if e < 0:
 
346
                return False
 
347
            return True
 
348
 
 
349
        for e, func in [['ScaleDenominator', float], 
 
350
                        ['TileWidth', int], 
 
351
                        ['TileHeight', int]]:
 
352
            if not _checkElement(t_mat, e, func):
 
353
                return False
 
354
                        
 
355
        tile_mat_id = t_mat.find(self.xml_ns.NsOws('Identifier'))
 
356
        if tile_mat_id is None or not tile_mat_id.text:
 
357
            return False
 
358
 
 
359
        tl_str = t_mat.find(self.xml_ns.NsWmts('TopLeftCorner'))
 
360
        if tl_str is None or not tl_str.text:
 
361
            return False
 
362
 
 
363
        tl = tl_str.text.split(' ')
 
364
        if len(tl) < 2:
 
365
            return False
 
366
 
 
367
        for t in tl:              
 
368
            try:
 
369
                t = float(t)
 
370
            except ValueError:
 
371
                return False
 
372
        return True
 
373
    
 
374
    def _checkLayer(self, layer):
 
375
        """!Check <Layer> element.
 
376
        """
 
377
        layer_id = layer.find(self.xml_ns.NsOws('Identifier'))
 
378
        if layer_id is None or not layer_id.text:
 
379
            return False
 
380
 
 
381
        mat_set_links = layer.findall(self.xml_ns.NsWmts('TileMatrixSetLink'))
 
382
        if not mat_set_links:
 
383
            return False
 
384
 
 
385
        styles = layer.findall(self.xml_ns.NsWmts('Style'))
 
386
        if not styles:
 
387
            return False
 
388
 
 
389
        for s in styles:
 
390
            s_name = s.find(self.xml_ns.NsOws('Identifier'))
 
391
            if s_name is None or not s_name.text:
 
392
                grass.debug('Removed invalid <Style> element.', 4)
 
393
                layer.remove(s_name)
 
394
 
 
395
        contents = self.getroot().find(self.xml_ns.NsWmts('Contents'))
 
396
        mat_sets = contents.findall(self.xml_ns.NsWmts('TileMatrixSet'))
 
397
 
 
398
        for link in  mat_set_links:
 
399
            # <TileMatrixSetLink> does not point to existing  <TileMatrixSet>
 
400
            if not self._checkMatSetLink(link, mat_sets):
 
401
                grass.debug('Removed invalid <TileMatrixSetLink> element.', 4)
 
402
                layer.remove(link)
 
403
 
 
404
        return True
 
405
 
 
406
    def _checkMatSetLink(self, link, mat_sets):
 
407
        """!Check <TileMatrixSetLink> element.
 
408
        """
 
409
        mat_set_link_id = link.find(self.xml_ns.NsWmts('TileMatrixSet')).text
 
410
        found = False
 
411
 
 
412
        for mat_set in mat_sets:
 
413
            mat_set_id = mat_set.find(self.xml_ns.NsOws('Identifier')).text
 
414
 
 
415
            if mat_set_id != mat_set_link_id:
 
416
                continue
 
417
 
 
418
            # the link points to existing <TileMatrixSet>
 
419
            found = True
 
420
 
 
421
            tile_mat_set_limits = link.find(self.xml_ns.NsWmts('TileMatrixSetLimits'))
 
422
            if tile_mat_set_limits is None:
 
423
                continue
 
424
 
 
425
            tile_mat_limits = tile_mat_set_limits.findall(self.xml_ns.NsWmts('TileMatrixLimits'))
 
426
            for limit in tile_mat_limits:
 
427
                if not self._checkMatSetLimit(limit):
 
428
                    grass.debug('Removed invalid <TileMatrixLimits> element.', 4)
 
429
                    tile_mat_limits.remove(limit)
 
430
 
 
431
            # are there any <TileMatrixLimits> elements after the check
 
432
            tile_mat_limits = tile_mat_set_limits.findall(self.xml_ns.NsWmts('TileMatrixLimits'))
 
433
            if not tile_mat_limits:
 
434
                grass.debug('Removed invalid <TileMatrixSetLimits> element.', 4)
 
435
                link.remove(tile_mat_set_limits)
 
436
          
 
437
        if not found:
 
438
            return False
 
439
 
 
440
        return True
 
441
 
 
442
    def _checkMatSetLimit(self, limit):
 
443
        """!Check <TileMatrixLimits> element.
 
444
        """
 
445
        limit_tile_mat = limit.find(self.xml_ns.NsWmts('TileMatrix'))
 
446
        if limit_tile_mat is None or not limit_tile_mat.text:
 
447
            return False
 
448
 
 
449
        for i in ['MinTileRow', 'MaxTileRow', 'MinTileCol', 'MaxTileCol']:
 
450
            i_tag = limit.find(self.xml_ns.NsWmts(i))
 
451
            if i_tag is None:
 
452
                return False
 
453
            try:
 
454
                int(i_tag.text)
 
455
            except ValueError:
 
456
                return False
 
457
        return True
 
458
 
 
459
    def _find(self, etreeElement, tag, ns = None):
 
460
        """!Find child element.
 
461
            If the element is not found it raises xml.etree.ElementTree.ParseError.  
 
462
        """
 
463
        if not ns:
 
464
            res = etreeElement.find(tag)
 
465
        else:
 
466
            res = etreeElement.find(ns(tag))
 
467
 
 
468
        if res is None:
 
469
            raise ParseError(_("Unable to parse capabilities file. \n\
 
470
                                Tag '%s' was not found.") % tag)
 
471
 
 
472
        return res
 
473
 
 
474
    def _findall(self, etreeElement, tag, ns = None):
 
475
        """!Find all children element.
 
476
            If no element is found it raises xml.etree.ElementTree.ParseError.  
 
477
        """
 
478
        if not ns:
 
479
            res = etreeElement.findall(tag)
 
480
        else:
 
481
            res = etreeElement.findall(ns(tag))
 
482
 
 
483
        if not res:
 
484
            raise ParseError(_("Unable to parse capabilities file. \n\
 
485
                                Tag '%s' was not found.") % tag)
 
486
 
 
487
        return res
 
488
 
 
489
    def getxmlnshandler(self):
 
490
        """!Return WMTSXMLNsHandler object.
 
491
        """
 
492
        return self.xml_ns
 
493
 
 
494
class OnEarthCapabilitiesTree(BaseCapabilitiesTree):
 
495
    def __init__(self, cap_file):
 
496
        """!Parse NASA OnEarth tile service file.
 
497
            If the file cannot be parsed it raises xml.etree.ElementTree.ParseError.
 
498
 
 
499
        The class also removes elements which are in invalid form and are needed 
 
500
        by wxGUI capabilities dialog or for creation of GetMap request by GRASS WMS library.
 
501
 
 
502
        @param cap_file - capabilities file        
 
503
        """
 
504
        BaseCapabilitiesTree.__init__(self, cap_file)
 
505
 
 
506
        grass.debug('Checking OnEarth capabilities tree.', 4)
 
507
 
 
508
        self._checkLayerTree(self.getroot())
 
509
 
 
510
        grass.debug('Check if OnEarth capabilities tree was finished.', 4)
 
511
 
 
512
    def _checkLayerTree(self, parent_layer, first = True):
 
513
        """!Recursively check layer tree.
 
514
        """
 
515
        if first:
 
516
            tiled_patterns = self._find(parent_layer, 'TiledPatterns')
 
517
            layers = tiled_patterns.findall('TiledGroup')
 
518
            layers += tiled_patterns.findall('TiledGroups')
 
519
            parent_layer = tiled_patterns
 
520
        else:
 
521
            layers = parent_layer.findall('TiledGroup')
 
522
            layers += parent_layer.findall('TiledGroups')
 
523
 
 
524
        for l in layers:
 
525
            if not self._checkLayer(l):
 
526
                grass.debug(('Removed invalid <%s> element.' % l.tag), 4)
 
527
                parent_layer.remove(l)
 
528
            if l.tag == 'TiledGroups':
 
529
               self._checkLayerTree(l, False)
 
530
 
 
531
    def _find(self, etreeElement, tag):
 
532
        """!Find child element.
 
533
            If the element is not found it raises xml.etree.ElementTree.ParseError.  
 
534
        """
 
535
        res = etreeElement.find(tag)
 
536
 
 
537
        if res is None:
 
538
            raise  ParseError(_("Unable to parse tile service file. \n\
 
539
                                 Tag <%s> was not found.") % tag)
 
540
 
 
541
        return res
 
542
 
 
543
    def _checkLayer(self, layer):
 
544
        """!Check <TiledGroup>/<TiledGroups> elements.
 
545
        """
 
546
        if layer.tag == 'TiledGroups':
 
547
            return True
 
548
            
 
549
        name = layer.find('Name')
 
550
        if name is None or not name.text:
 
551
            return False
 
552
 
 
553
        t_patts = layer.findall('TilePattern')
 
554
 
 
555
        for patt in t_patts:
 
556
            urls = self._getUrls(patt)
 
557
            for url in urls:
 
558
                if not self.gettilepatternurldata(url):
 
559
                    urls.remove(url)
 
560
 
 
561
            # check if there are any vaild urls
 
562
            if not urls:
 
563
                grass.debug('<TilePattern>  was removed. It has no valid url.', 4)
 
564
                layer.remove(patt)
 
565
            patt.text = '\n'.join(urls)
 
566
 
 
567
        t_patts = layer.findall('TilePattern')
 
568
        if not t_patts:
 
569
            return False
 
570
 
 
571
        return True
 
572
 
 
573
    def _getUrls(self, tile_pattern):
 
574
        """!Get all urls from tile pattern.
 
575
        """
 
576
        urls = []
 
577
        if  tile_pattern.text is not None:
 
578
            tile_patt_lines = tile_pattern.text.split('\n')
 
579
 
 
580
            for line in tile_patt_lines:
 
581
                if 'request=GetMap' in line:
 
582
                    urls.append(line.strip())
 
583
        return urls
 
584
 
 
585
    def gettilepatternurldata(self, url):
 
586
        """!Parse url string in Tile Pattern.
 
587
        """
 
588
        par_url = bbox = width = height = None 
 
589
 
 
590
        bbox_idxs = self.geturlparamidxs(url, "bbox=")
 
591
        if bbox_idxs is None:
 
592
            return None
 
593
 
 
594
        par_url = [url[:bbox_idxs[0] - 1], url[bbox_idxs[1]:]]
 
595
 
 
596
        bbox = url[bbox_idxs[0] + len('bbox=') : bbox_idxs[1]]
 
597
        bbox_list = bbox.split(',')
 
598
        if len(bbox_list) < 4:
 
599
            return None
 
600
 
 
601
        try:
 
602
            bbox = map(float, bbox.split(','))
 
603
        except ValueError:
 
604
            return None
 
605
 
 
606
        width_idxs = self.geturlparamidxs(url, "width=")
 
607
        if width_idxs is None:
 
608
            return None
 
609
 
 
610
        try:
 
611
            width = int(url[width_idxs[0] + len('width=') : width_idxs[1]])
 
612
        except  ValueError:
 
613
            return None
 
614
 
 
615
        height_idxs = self.geturlparamidxs(url, "height=")
 
616
        if height_idxs is None:
 
617
            return None
 
618
 
 
619
        try:
 
620
            height = int(url[height_idxs[0] + len('height=') : height_idxs[1]])
 
621
        except  ValueError:
 
622
            return None
 
623
 
 
624
        if height < 0 or width < 0:
 
625
            return None
 
626
 
 
627
        return par_url, bbox, width, height
 
628
 
 
629
    def geturlparamidxs(self, params_str, param_key):
 
630
        """!Find start and end index of parameter and it's value in url string
 
631
        """
 
632
        start_i = params_str.lower().find(param_key)
 
633
        if start_i < 0: 
 
634
            return None
 
635
        end_i = params_str.find("&", start_i)
 
636
        if end_i < 0:
 
637
            end_i = len(params_str)
 
638
 
 
639
        return (start_i, end_i)