[GRASS-SVN] r54451 - grass-addons/grass6/raster/r.in.wms2

svn_grass at osgeo.org svn_grass at osgeo.org
Fri Dec 28 09:14:23 PST 2012


Author: martinl
Date: 2012-12-28 09:14:22 -0800 (Fri, 28 Dec 2012)
New Revision: 54451

Added:
   grass-addons/grass6/raster/r.in.wms2/description.html
   grass-addons/grass6/raster/r.in.wms2/wms_cap_parsers.py
Removed:
   grass-addons/grass6/raster/r.in.wms2/r.in.wms2.html
Modified:
   grass-addons/grass6/raster/r.in.wms2/Makefile
   grass-addons/grass6/raster/r.in.wms2/r.in.wms2.py
   grass-addons/grass6/raster/r.in.wms2/wms_base.py
   grass-addons/grass6/raster/r.in.wms2/wms_drv.py
   grass-addons/grass6/raster/r.in.wms2/wms_gdal_drv.py
Log:
r.in.wms2: backport from GRASS 7
           (author: Stepan Turek)


Modified: grass-addons/grass6/raster/r.in.wms2/Makefile
===================================================================
--- grass-addons/grass6/raster/r.in.wms2/Makefile	2012-12-28 17:09:00 UTC (rev 54450)
+++ grass-addons/grass6/raster/r.in.wms2/Makefile	2012-12-28 17:14:22 UTC (rev 54451)
@@ -2,9 +2,10 @@
 
 PGM = r.in.wms2.py
 
-MODULES = wms_base wms_drv wms_gdal_drv
-PYFILES := $(patsubst %,$(ETC)/%.py,$(MODULES))
-PYCFILES := $(patsubst %,$(ETC)/%.pyc,$(MODULES))
+MODULES = wms_base wms_drv wms_gdal_drv wms_cap_parsers
+ETCDIR = $(ETC)/r.in.wms2
+PYFILES := $(patsubst %,$(ETCDIR)/%.py,$(MODULES))
+PYCFILES := $(patsubst %,$(ETCDIR)/%.pyc,$(MODULES))
 
 include $(MODULE_TOPDIR)/include/Make/Script.make
 include $(MODULE_TOPDIR)/include/Make/Python.make
@@ -14,5 +15,5 @@
 $(ETCDIR):
 	$(MKDIR) $@
 
-$(ETC)/%: % | $(ETC)
+$(ETCDIR)/%: % | $(ETCDIR)
 	$(INSTALL_DATA) $< $@

Copied: grass-addons/grass6/raster/r.in.wms2/description.html (from rev 54450, grass-addons/grass6/raster/r.in.wms2/r.in.wms2.html)
===================================================================
--- grass-addons/grass6/raster/r.in.wms2/description.html	                        (rev 0)
+++ grass-addons/grass6/raster/r.in.wms2/description.html	2012-12-28 17:14:22 UTC (rev 54451)
@@ -0,0 +1,98 @@
+<h2>DESCRIPTION</h2>
+
+<em>r.in.wms2</em> handles all of downloading and importing raster
+data from an <a href="http://www.opengeospatial.org/standards/wms">OGC
+WMS</a>, <a href="http://www.opengeospatial.org/standards/wmts">OGC
+WMTS</a>  and <a href="http://onearth.jpl.nasa.gov/tiled.html">NASA OnEarth 
+Tiled WMS</a> web mapping servers. It needs only be told the desired data to
+collect (bounds and resolution) via a region, the server to get the
+data from, and the layer or layers to get. It downloads the data in
+tiles, reprojects it, imports it, and patches it back together.
+
+<h2>NOTES</h2>
+
+
+<p>To understand the data you are getting it is necessary to look at the
+capabilities of the WMS server. This should be available via a capabilities
+request. This is an
+<a href="http://wms.jpl.nasa.gov/wms.cgi?request=GetCapabilities">example
+capabilities request to NASA's OnEarth server</a>.
+
+<h3>NASA OnEarth Tiled WMS</h3>
+
+Into parameter <b>layers</b> insert name of <b>TiledGroup</b> from Tile Service file.
+<br>
+<br>
+
+Time variable is possible to specify in <b>urlparams</b> parameter.
+<br>
+e. g: urlparams='time=2012-1-1'
+
+
+<h2>EXAMPLES</h2>
+
+<h3>General Get Capabilities Request</h3>
+
+<div class="code"><pre>
+r.in.wms2 -c url=http://wms.cuzk.cz/wms.asp
+</pre></div>
+
+<h3>Download raster data from WMS server (GetMap request)</h3>
+
+
+<h4>World extend data:</h4>
+
+<div class="code"><pre>
+r.in.wms2 url=http://iceds.ge.ucl.ac.uk/cgi-bin/icedswms layers=bluemarble,landsat_1_01 styles=default,default output=landsat srs=4326 format=png 
+</pre></div>
+* Server supports only WMS 1.1.1 <br>
+
+<div class="code"><pre>
+r.in.wms2 url=http://132.156.97.59/cgi-bin/worldmin_en-ca_ows layers=GSC:WORLD_PrecambrianDomains output=pokus srs=4326 format=jpeg 
+</pre></div>
+* Server supports only WMS 1.1.1 
+
+<div class="code"><pre>
+r.in.wms2 url=http://gpp3-wxs.ign.fr/yourAPIkey/geoportail/wmts layers=ORTHOIMAGERY.ORTHOPHOTOS output=orthophoto srs=3857 format=jpeg driver=WMTS_GRASS style=normal password=* username=*
+</pre></div>
+* Username, password and API key can be get from <a href="http://api.ign.fr/">IGN API</a> website
+
+<div class="code"><pre>
+r.in.wms2 output=global_mosaic url=http://onearth.jpl.nasa.gov/wms.cgi layers='Global Mosaic, visual' driver=OnEarth_GRASS 
+</pre></div>
+
+<h4>Data in extend of Czech Republic:</h4>
+
+<div class="code"><pre>
+r.in.wms2 output=kn url=http://wms.cuzk.cz/wms.asp layers=prehledka_kraju-linie srs=4326 format=png
+</pre></div>
+
+<div class="code"><pre>
+r.in.wms2 url=http://geoportal.cuzk.cz/WMTS_ORTOFOTO/WMTService.aspx layers=orto output=ortofoto srs=3857 format=jpeg driver=WMTS_GRASS style=default
+</pre></div>
+
+
+<H2>REQUIRED PROGRAMS</H2>
+
+<EM>r.in.wms2</EM> requires the following programs to work:
+
+<ul>
+<li><a href="http://www.gdal.org">gdalwarp</a>
+</ul>
+
+<h2>SEE ALSO</h2>
+
+<em>
+  <a href="r.in.gdal.html">r.in.gdal</a>,
+  <a href="r.patch.html">r.patch</a>,
+  <a href="r.colors.html">r.colors</a>,
+  <a href="r.composite.html">r.composite</a>,
+  <a href="v.in.wfs.html">v.in.wfs</a>
+</em>
+
+<h2>AUTHORS</h2>
+
+Stepan Turek, Czech Technical University in Prague, Czech Republic (bachelor's final project 2012, mentor: Martin Landa) 
+
+<p>
+<i>Last changed: $Date$</i>

Deleted: grass-addons/grass6/raster/r.in.wms2/r.in.wms2.html
===================================================================
--- grass-addons/grass6/raster/r.in.wms2/r.in.wms2.html	2012-12-28 17:09:00 UTC (rev 54450)
+++ grass-addons/grass6/raster/r.in.wms2/r.in.wms2.html	2012-12-28 17:14:22 UTC (rev 54451)
@@ -1,75 +0,0 @@
-<h2>DESCRIPTION</h2>
-
-<em>r.in.wms2</em> handles all of downloading and importing raster
-data from an <a href="http://www.opengeospatial.org/standards/wms">OGC
-WMS</a> web mapping server. It need only be told the desired data to
-collect (bounds and resolution) via a region, the server to get the
-data from, and the layer or layers to get. It downloads the data in
-tiles, reprojects it, imports it, and patches it back together.
-
-<h2>NOTES</h2>
-
-<!--
-By default data is downloaded to <tt>$GISDBASE/wms_download</tt>. This can be changed
-by setting the <b>folder</b> option when using <em>r.in.wms</em>.
--->
-
-<p>To understand the data you are getting it is necessary to look at the
-capabilities of the WMS server. This should be available via a capabilities
-request. This is an
-<a href="http://wms.jpl.nasa.gov/wms.cgi?request=GetCapabilities">example
-capabilities request to NASA's OnEarth server</a>.
-
-<h2>EXAMPLES</h2>
-
-<h3>General Get Capabilities Request</h3>
-
-<div class="code"><pre>
-r.in.wms2 -c mapserver=http://wms.cuzk.cz/wms.asp
-</pre></div>
-
-<h3>Download raster data from WMS server (GetMap request)</h3>
-
-World extend data:
-
-<div class="code"><pre>
-r.in.wms2 mapserver=http://iceds.ge.ucl.ac.uk/cgi-bin/icedswms layers=bluemarble,landsat_1_01 styles=default,default output=landsat srs=4326 format=png 
-</pre></div>
-* Server supports only WMS 1.1.1 <br>
-
-<div class="code"><pre>
-r.in.wms2 mapserver=http://132.156.97.59/cgi-bin/worldmin_en-ca_ows layers=GSC:WORLD_PrecambrianDomains output=pokus srs=4326 format=jpeg 
-</pre></div>
-* Server supports only WMS 1.1.1 
-<br>
-<br>
-
-Data in extend of Czech Republic:
-
-<div class="code"><pre>
-r.in.wms2 output=kn mapserver=http://wms.cuzk.cz/wms.asp layers=prehledka_kraju-linie srs=4326 format=png
-</pre></div>
-
-
-<h2>TODO</h2>
-
-<ul>
-  <li>Implement Tiled WMS</li>
-</ul>
-
-<h2>SEE ALSO</h2>
-
-<em>
-  <a href="r.in.gdal.html">r.in.gdal</a>,
-  <a href="r.patch.html">r.patch</a>,
-  <a href="r.colors.html">r.colors</a>,
-  <a href="r.composite.html">r.composite</a>,
-  <a href="v.in.wfs.html">v.in.wfs</a>
-</em>
-
-<h2>AUTHORS</h2>
-
-Stepan Turek, Czech Technical University in Prague, Czech Republic (bachelor's final project 2012, mentor: Martin Landa) 
-
-<p>
-<i>Last changed: $Date$</i>

Modified: grass-addons/grass6/raster/r.in.wms2/r.in.wms2.py
===================================================================
--- grass-addons/grass6/raster/r.in.wms2/r.in.wms2.py	2012-12-28 17:09:00 UTC (rev 54450)
+++ grass-addons/grass6/raster/r.in.wms2/r.in.wms2.py	2012-12-28 17:14:22 UTC (rev 54451)
@@ -20,7 +20,7 @@
 #%end
 
 #%option
-#% key: mapserver
+#% key: url
 #% type: string
 #% description:URL of WMS server 
 #% required: yes
@@ -31,24 +31,18 @@
 #% type: string
 #% description: Layers to request from map server
 #% multiple: yes
-#% required: no
-#% guisection: Required
+#% required: yes
 #%end
 
-#%option 
-#% key: output
-#% type: string
-#% gisprompt: new,cell,raster
+#%option G_OPT_R_OUTPUT
 #% description: Name for output raster map
-#% required: no
-#% guisection: Required
 #%end
 
 #%option
 #% key: srs
 #% type: integer
 #% description: EPSG number of source projection for request 
-#% answer:4326
+#% answer:4326 
 #% guisection: Request properties
 #%end
 
@@ -105,11 +99,25 @@
 #%option
 #% key: urlparams
 #% type:string
-#% description: Addition query parameters for server (only with 'd' flag)
+#% description: Additional query parameters for server
 #% guisection: Request properties
 #%end
 
 #%option
+#% key: username
+#% type:string
+#% description: Username for server connection
+#% guisection: Request properties
+#%end
+
+#%option
+#% key: password
+#% type:string
+#% description: Password for server connection
+#% guisection: Request properties
+#%end
+
+#%option
 #% key: styles
 #% type: string
 #% description: Styles to request from map server
@@ -120,7 +128,7 @@
 #%option
 #% key: bgcolor
 #% type: string
-#% description: Color of map background (only with 'd' flag)
+#% description: Color of map background
 #% guisection: Map style
 #%end
 
@@ -134,25 +142,63 @@
 #% key: c
 #% description: Get capabilities
 #% guisection: Request properties
+#% suppress_required: yes
 #%end
 
-#%flag
-#% key: d
-#% description: Do not use GDAL WMS driver
+#%option
+#% key: driver
+#% type:string
+#% description: Driver for communication with server
+#% options:WMS_GDAL, WMS_GRASS, WMTS_GRASS, OnEarth_GRASS
+#% answer:WMS_GRASS
 #%end
 
+#%option G_OPT_F_INPUT
+#% key: capfile
+#% required: no
+#% gisprompt: old,file,bin_input
+#% description: Capabilities file 
+#%end
+
+#%option G_OPT_F_OUTPUT
+#% key: capfile_output
+#% required: no
+#% gisprompt: old,file,bin_input
+#% description: File where capabilities will be saved (only with 'c' flag).
+#%end
+
 import os
 import sys
 sys.path.insert(1, os.path.join(os.path.dirname(sys.path[0]), 'etc', 'r.in.wms2'))
 
 import grass.script as grass
 
+def GetRegionParams(opt_region):
+
+    # set region 
+    if opt_region:                 
+        if not grass.find_file(name = opt_region, element = 'windows', mapset = '.' )['name']:
+            grass.fatal(_("Region <%s> not found") % opt_region)
+        
+    if opt_region:
+        s = grass.read_command('g.region',
+                                quiet = True,
+                                flags = 'ug',
+                                region = opt_region)
+        region_params = grass.parse_key_val(s, val_type = float)
+    else:
+        region_params = grass.region()
+
+    return region_params
+
 def main():
-    if flags['d']:
-        grass.debug("Using own driver")
+
+
+    if 'GRASS' in options['driver']:
+        grass.debug("Using GRASS driver")
         from wms_drv import WMSDrv
         wms = WMSDrv()
-    else:
+    elif 'GDAL' in options['driver']:
         grass.debug("Using GDAL WMS driver")
         from wms_gdal_drv import WMSGdalDrv
         wms = WMSGdalDrv()
@@ -160,15 +206,15 @@
     if flags['c']:
         wms.GetCapabilities(options)
     else:
-        if not options['layers']:
-            grass.fatal(_("Required parameter <%s> not set") % 'layers')
-        if not options['output']:
-            grass.fatal(_("Required parameter <%s> not set") % 'output')
-        
-        wms.GetMap(options, flags)  
-    
+        from wms_base import GRASSImporter
+        options['region'] = GetRegionParams(options['region'])
+        importer = GRASSImporter(options['output'])
+        fetched_map = wms.GetMap(options, flags)
+        importer.ImportMapIntoGRASS(fetched_map)
+
     return 0
 
+
 if __name__ == "__main__":
     options, flags = grass.parser()
     sys.exit(main())

Modified: grass-addons/grass6/raster/r.in.wms2/wms_base.py
===================================================================
--- grass-addons/grass6/raster/r.in.wms2/wms_base.py	2012-12-28 17:09:00 UTC (rev 54450)
+++ grass-addons/grass6/raster/r.in.wms2/wms_base.py	2012-12-28 17:14:22 UTC (rev 54451)
@@ -1,8 +1,25 @@
+"""!
+ at brief Preparation of parameters for drivers, which download it, and managing downloaded data. 
+
+List of classes:
+ - wms_base::WMSBase
+ - wms_base::GRASSImporter
+ - wms_base::WMSDriversInfo
+
+(C) 2012 by the GRASS Development Team
+
+This program is free software under the GNU General Public License
+(>=v2). Read the file COPYING that comes with GRASS for details.
+
+ at author Stepan Turek <stepan.turek seznam.cz> (Mentor: Martin Landa)
+"""
+
 import os
 from   math import ceil
 
-import xml.etree.ElementTree as etree
-from urllib2 import urlopen, HTTPError, URLError
+import base64
+import urllib2
+from httplib import HTTPException
 
 import grass.script as grass
 
@@ -10,52 +27,21 @@
     def __init__(self):
         # these variables are information for destructor
         self.temp_files_to_cleanup = []
-        self.cleanup_mask   = False
-        self.cleanup_layers = False
         
-        self.bbox     = None
+        self.params = {}
+        self.tile_size = {'bbox' : None}
+
         self.temp_map = None
-        
+        self.temp_warpmap = None
+
     def __del__(self):
-        # removes temporary mask, used for import transparent or warped temp_map
-        if self.cleanup_mask:
-            # clear temporary mask, which was set by module      
-            if grass.run_command('r.mask',
-                                 quiet = True,
-                                 flags = 'r') != 0:  
-                grass.fatal(_('%s failed') % 'r.mask')
             
-            # restore original mask, if exists 
-            if grass.find_file(self.o_output + self.original_mask_suffix, element = 'cell', mapset = '.' )['name']:
-                if grass.run_command('g.copy',
-                                     quiet = True,
-                                     rast =  self.o_output + self.original_mask_suffix + ',MASK') != 0:
-                    grass.fatal(_('%s failed') % 'g.copy')
-        
         # tries to remove temporary files, all files should be
-        # removoved before, implemented just in case of unexpected
+        # removed before, implemented just in case of unexpected
         # stop of module
         for temp_file in self.temp_files_to_cleanup:
             grass.try_remove(temp_file)
         
-        # remove temporary created rasters
-        if self.cleanup_layers: 
-            maps = []
-            for suffix in ('.red', '.green', '.blue', '.alpha', self.original_mask_suffix):
-                rast = self.o_output + suffix
-                if grass.find_file(rast, element = 'cell', mapset = '.')['file']:
-                    maps.append(rast)
-            
-            if maps:
-                grass.run_command('g.remove',
-                                  quiet = True,
-                                  flags = 'f',
-                                  rast  = ','.join(maps))
-        
-        # deletes enviromental variable which overrides region 
-        if 'GRASS_REGION' in os.environ.keys():
-            os.environ.pop('GRASS_REGION')
-        
     def _debug(self, fn, msg):
         grass.debug("%s.%s: %s" %
                     (self.__class__.__name__, fn, msg))
@@ -63,137 +49,187 @@
     def _initializeParameters(self, options, flags):
         self._debug("_initialize_parameters", "started")
         
-        # inicialization of module parameters (options, flags)
-        self.flags = flags 
+        # initialization of module parameters (options, flags)
+        self.params['driver'] = options['driver']
+        drv_info = WMSDriversInfo()
+
+        driver_props = drv_info.GetDrvProperties(options['driver'])
+        self._checkIgnoeredParams(options, flags, driver_props)
+
+        self.params['capfile'] = options['capfile'].strip()
+
+        for key in ['url', 'layers', 'styles', 'method']:
+            self.params[key] = options[key].strip()
+
+        self.params['wms_version'] = options['wms_version']  
+        if self.params['wms_version'] == "1.3.0":
+            self.params['proj_name'] = "CRS"
+        else:
+            self.params['proj_name'] = "SRS"
+
+        self.flags = flags
+
         if self.flags['o']:
-            self.transparent = 'FALSE'
+            self.params['transparent'] = 'FALSE'
         else:
-            self.transparent = 'TRUE'   
-        
-        self.o_mapserver_url = options['mapserver'].strip() + "?" 
-        self.o_layers = options['layers'].strip()
-        self.o_styles = options['styles'].strip()
-        self.o_output = options['output']
-        self.o_method = options['method']
-        
-        self.o_bgcolor = options['bgcolor'].strip()
-        if self.o_bgcolor != "" and not flags["d"]:
-            grass.warning(_("Parameter bgcolor ignored, use -d flag"))
-        
-        self.o_urlparams = options['urlparams'].strip()
-        if self.o_urlparams != "" and not flags["d"]:
-            grass.warning(_("Parameter urlparams ignored, use -d flag"))
-        
-        self.o_wms_version = options['wms_version']        
-        if self.o_wms_version == "1.3.0":
-            self.projection_name = "CRS"
-        else:
-            self.projection_name = "SRS" 
-        
-        self.o_format = options['format']
-        if self.o_format == "geotiff":
-            self.mime_format = "image/geotiff"
-        elif self.o_format == "tiff":
-            self.mime_format = "image/tiff"
-        elif self.o_format == "png":
-            self.mime_format = "image/png"
-        elif self.o_format == "jpeg":
-            self.mime_format = "image/jpeg"
-            if flags['o']:
+            self.params['transparent'] = 'TRUE'   
+
+        for key in ['password', 'username', 'urlparams']:
+            self.params[key] = options[key] 
+
+        if (self.params ['password'] and self.params ['username'] == '') or \
+           (self.params ['password'] == '' and self.params ['username']):
+                grass.fatal(_("Please insert both %s and %s parameters or none of them." % ('password', 'username')))
+
+        self.params['bgcolor'] = options['bgcolor'].strip()
+
+        if options['format'] == "jpeg" and \
+           not 'format' in driver_props['ignored_params']:
+            if not flags['o'] and \
+              'WMS' in self.params['driver']:
                 grass.warning(_("JPEG format does not support transparency"))
-        elif self.o_format == "gif":
-            self.mime_format = "image/gif"
-        else:
-            self.mime_format = self.o_format
+
+        self.params['format'] = drv_info.GetFormat(options['format'])
+        if not self.params['format']:
+            self.params['format'] = self.params['format']
         
-        self.o_srs = int(options['srs'])
-        if self.o_srs <= 0:
-            grass.fatal(_("Invalid EPSG code %d") % self.o_srs)
+        #TODO: get srs from Tile Service file in OnEarth_GRASS driver 
+        self.params['srs'] = int(options['srs'])
+        if self.params['srs'] <= 0 and  not 'srs' in driver_props['ignored_params']:
+            grass.fatal(_("Invalid EPSG code %d") % self.params['srs'])
         
         # read projection info
         self.proj_location = grass.read_command('g.proj', 
                                                 flags ='jf').rstrip('\n')
-        
-        self.proj_srs = grass.read_command('g.proj', 
-                                           flags = 'jf', 
-                                           epsg = str(self.o_srs) ).rstrip('\n')
-        
+
+        if self.params['srs'] in [3857, 900913]:
+            # HACK: epsg 3857 def: http://spatialreference.org/ref/sr-org/7483/
+            # g.proj can return: ...+a=6378137 +rf=298.257223563... (WGS84 elipsoid def instead of sphere), it can make 20km shift in Y, when raster is transformed
+            # needed to be tested on more servers
+            self.proj_srs = '+proj=merc +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +no_defs +a=6378137 +b=6378137 +nadgrids=@null +to_meter=1 +wktext'
+        else:
+            self.proj_srs = grass.read_command('g.proj', 
+                                               flags = 'jf', 
+                                               epsg = str(self.params['srs']) ).rstrip('\n')
+
         if not self.proj_srs or not self.proj_location:
             grass.fatal(_("Unable to get projection info"))
-        
-        # set region 
-        self.o_region = options['region']
-	if self.o_region:                 
-            if not grass.find_file(name = self.o_region, element = 'windows', mapset = '.' )['name']:
-                grass.fatal(_("Region <%s> not found") % self.o_region)
-        
-        if self.o_region:
-            s = grass.read_command('g.region',
-                                   quiet = True,
-                                   flags = 'ug',
-                                   region = self.o_region)
-            self.region = grass.parse_key_val(s, val_type = float)
-        else:
-            self.region = grass.region()
-        
+
+        self.region = options['region']
+
         min_tile_size = 100
-        self.o_maxcols = int(options['maxcols'])
-        if self.o_maxcols <= min_tile_size:
+        maxcols = int(options['maxcols'])
+        if maxcols <= min_tile_size:
             grass.fatal(_("Maxcols must be greater than 100"))
         
-        self.o_maxrows = int(options['maxrows'])
-        if self.o_maxrows <= min_tile_size:
+        maxrows = int(options['maxrows'])
+        if maxrows <= min_tile_size:
             grass.fatal(_("Maxrows must be greater than 100"))
         
         # setting optimal tile size according to maxcols and maxrows constraint and region cols and rows      
-        self.tile_cols = int(self.region['cols'] / ceil(self.region['cols'] / float(self.o_maxcols)))
-        self.tile_rows = int(self.region['rows'] / ceil(self.region['rows'] / float(self.o_maxrows)))
+        self.tile_size['cols'] = int(self.region['cols'] / ceil(self.region['cols'] / float(maxcols)))
+        self.tile_size['rows'] = int(self.region['rows'] / ceil(self.region['rows'] / float(maxrows)))
         
-        # suffix for existing mask (during overriding will be saved
-        # into raster named:self.o_output + this suffix)
-        self.original_mask_suffix = "_temp_MASK"
-        
-        # check names of temporary rasters, which module may create 
-        maps = []
-        for suffix in ('.red', '.green', '.blue', '.alpha', self.original_mask_suffix ):
-            rast = self.o_output + suffix
-            if grass.find_file(rast, element = 'cell', mapset = '.')['file']:
-                maps.append(rast)
-        
-        if len(maps) != 0:
-            grass.fatal(_("Please change output name, or change names of these rasters: %s, "
-                          "module needs to create this temporary maps during runing") % ",".join(maps))
-        
         # default format for GDAL library
         self.gdal_drv_format = "GTiff"
         
         self._debug("_initialize_parameters", "finished")
 
+    def _checkIgnoeredParams(self, options, flags, driver_props):
+        """!Write warnings for set parameters and flags, which chosen driver does not use."""
+
+        not_relevant_params = []
+        for i_param in driver_props['ignored_params']:
+
+            if options.has_key(i_param) and \
+               options[i_param] and \
+               i_param not in ['srs', 'wms_version', 'format']: # params with default value
+                not_relevant_params.append('<' + i_param  + '>')
+
+        if len(not_relevant_params) > 0:
+            grass.warning(_("These parameter are ignored: %s\n\
+                             %s driver does not support the parameters." %\
+                            (','.join(not_relevant_params), options['driver'])))
+
+        not_relevant_flags = []
+        for i_flag in driver_props['ignored_flags']:
+
+            if flags[i_flag]:
+                not_relevant_flags.append('<' + i_flag  + '>')
+
+        if len(not_relevant_flags) > 0:
+            grass.warning(_("These flags are ignored: %s\n\
+                             %s driver does not support the flags." %\
+                            (','.join(not_relevant_flags), options['driver'])))
+
     def GetMap(self, options, flags):
-        """!Download data from WMS server and import data
-        (using GDAL library) into GRASS as a raster map."""
+        """!Download data from WMS server."""
 
         self._initializeParameters(options, flags)  
 
         self.bbox     = self._computeBbox()
         
-        self.temp_map = self._download()  
+        self.temp_map = self._download()
+
+        if not self.temp_map:
+            return
+
+        self._reprojectMap()
+
+        return self.temp_warpmap
+    
+    def _fetchCapabilities(self, options): 
+        """!Download capabilities from WMS server
+        """
+        grass.debug('Fetching capabilities file.')
+        cap_url = options['url']
+
+        if 'WMTS' in options['driver']:
+            cap_url += "?SERVICE=WMTS&REQUEST=GetCapabilities&VERSION=1.0.0"
+        elif 'OnEarth' in options['driver']:
+            cap_url += "?REQUEST=GetTileService"
+        else:
+            cap_url += "?SERVICE=WMS&REQUEST=GetCapabilities&VERSION=" + options['wms_version'] 
+            
+        try:
+            cap = self._fetchDataFromServer(cap_url, options['username'], options['password'])
+        except (IOError, HTTPException), e:
+            if urllib2.HTTPError == type(e) and e.code == 401:
+                grass.fatal(_("Authorization failed to '%s' when fetching capabilities.") % options['url'])
+            else:
+                grass.fatal(_("Unable to fetch capabilities from: '%s'") % options['url'])
         
-        self._createOutputMap() 
-    
+        return cap
+
+    def _fetchDataFromServer(self, url, username = None, password = None):
+        """!Fetch data from server
+        """      
+        request = urllib2.Request(url)
+        if username and password:
+                    base64string = base64.encodestring('%s:%s' % (username, password)).replace('\n', '')
+                    request.add_header("Authorization", "Basic %s" % base64string)
+        return urllib2.urlopen(request)
+
     def GetCapabilities(self, options): 
         """!Get capabilities from WMS server
         """
-        # download capabilities file
-        cap_url = options['mapserver'] + "?service=WMS&request=GetCapabilities&version=" + options['wms_version'] 
-        try:
-            cap = urlopen(cap_url)
-        except IOError:
-            grass.fatal(_("Unable to get capabilities from '%s'") % options['mapserver'])
+        cap  = self._fetchCapabilities(options)
+        capfile_output = options['capfile_output'].strip()
+
+        # save to file
+        if capfile_output:
+            try:
+                temp = open(capfile_output, "w")
+                temp.write(cap.read())
+                temp.close()
+                return
+            except IOError as error: 
+                grass.fatal(_("Unabble to open file '%s'.\n%s\n" % (cap_file, error)))
         
+        # print to output
         cap_lines = cap.readlines()
         for line in cap_lines: 
-            print line 
+            print line
         
     def _computeBbox(self):
         """!Get region extent for WMS query (bbox)
@@ -228,7 +264,7 @@
                  grass.fatal(_("Unable to write data into tempfile"))
             finally:           
                 temp_region_opened.close()            
-            
+
             points = grass.read_command('m.proj', flags = 'd',
                                         proj_output = self.proj_srs,
                                         proj_input = self.proj_location,
@@ -239,10 +275,13 @@
             
             points = points.splitlines()
             if len(points) != 4:
-                grass.fatal(_("Region defintion: 4 points required"))
+                grass.fatal(_("Region definition: 4 points required"))
             
             for point in points:
-                point = map(float, point.split("|"))
+                try:
+                    point = map(float, point.split("|"))
+                except ValueError:
+                    grass.fatal(_('Reprojection of region using m.proj failed.'))
                 if not bbox['maxy']:
                     bbox['maxy'] = point[1]
                     bbox['miny'] = point[1]
@@ -263,63 +302,146 @@
         self._debug("_computeBbox", "finished -> %s" % bbox)
 
         # Ordering of coordinates axis of geographic coordinate
-        # systems in WMS 1.3.0 is fliped. If self.flip_coords is 
+        # systems in WMS 1.3.0 is flipped. If  self.tile_size['flip_coords'] is 
         # True, coords in bbox need to be flipped in WMS query.
 
-        self.flip_coords = False  
-        hasLongLat = self.proj_srs.find("+proj=longlat")   
-        hasLatLong = self.proj_srs.find("+proj=latlong")   
-
-        if (hasLongLat != -1 or hasLatLong != -1) and self.o_wms_version == "1.3.0":
-            self.flip_coords = True
-
         return bbox
 
-    def _createOutputMap(self): 
-        """!Import downloaded data into GRASS, reproject data if needed
-        using gdalwarp
+    def _reprojectMap(self): 
+        """!Reproject data  using gdalwarp if needed
         """
         # reprojection of raster
         if self.proj_srs != self.proj_location: # TODO: do it better
             grass.message(_("Reprojecting raster..."))
-            temp_warpmap = self._tempfile()
+            self.temp_warpmap = grass.tempfile()
             
             if int(os.getenv('GRASS_VERBOSE', '2')) <= 2:
                 nuldev = file(os.devnull, 'w+')
             else:
                 nuldev = None
             
+            #"+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +no_defs"
             # RGB rasters - alpha layer is added for cropping edges of projected raster
-            if self.temp_map_bands_num == 3:
-                ps = grass.Popen(['gdalwarp',
-                                  '-s_srs', '%s' % self.proj_srs,
-                                  '-t_srs', '%s' % self.proj_location,
-                                  '-r', self.o_method, '-dstalpha',
-                                  self.temp_map, temp_warpmap], stdout = nuldev)
-            # RGBA rasters
-            else:
-                ps = grass.Popen(['gdalwarp',
-                                  '-s_srs', '%s' % self.proj_srs,
-                                  '-t_srs', '%s' % self.proj_location,
-                                  '-r', self.o_method,
-                                  self.temp_map, temp_warpmap], stdout = nuldev)
-            ps.wait()
+            try:
+                if self.temp_map_bands_num == 3:
+                    ps = grass.Popen(['gdalwarp',
+                                      '-s_srs', '%s' % self.proj_srs,
+                                      '-t_srs', '%s' % self.proj_location,
+                                      '-r', self.params['method'], '-dstalpha',
+                                      self.temp_map, self.temp_warpmap], stdout = nuldev)
+                # RGBA rasters
+                else:
+                    ps = grass.Popen(['gdalwarp',
+                                      '-s_srs', '%s' % self.proj_srs,
+                                      '-t_srs', '%s' % self.proj_location,
+                                      '-r', self.params['method'],
+                                      self.temp_map, self.temp_warpmap], stdout = nuldev)
+                ps.wait()
+            except OSError, e:
+                grass.fatal('%s \nThis can be caused by missing %s utility. ' % (e, 'gdalwarp'))
             
             if nuldev:
                 nuldev.close()
             
             if ps.returncode != 0:
                 grass.fatal(_('%s failed') % 'gdalwarp')
+            grass.try_remove(self.temp_map)
         # raster projection is same as projection of location
         else:
-            temp_warpmap = self.temp_map
+            self.temp_warpmap = self.temp_map
+            self.temp_files_to_cleanup.remove(self.temp_map)
+
+        return self.temp_warpmap
         
+    def _tempfile(self):
+        """!Create temp_file and append list self.temp_files_to_cleanup 
+            with path of file 
+     
+        @return string path to temp_file
+        """
+        temp_file = grass.tempfile()
+        if temp_file is None:
+            grass.fatal(_("Unable to create temporary files"))
+        
+        # list of created tempfiles for destructor
+        self.temp_files_to_cleanup.append(temp_file)
+        
+        return temp_file
+
+class GRASSImporter:
+    def __init__(self, opt_output):
+
+        self.cleanup_mask   = False
+        self.cleanup_layers = False
+
+        # output map name
+        self.opt_output = opt_output
+
+        # suffix for existing mask (during overriding will be saved
+        # into raster named:self.opt_output + this suffix)
+        self.original_mask_suffix = "_temp_MASK"
+
+        # check names of temporary rasters, which module may create 
+        maps = []
+        for suffix in ('.red', '.green', '.blue', '.alpha', self.original_mask_suffix ):
+            rast = self.opt_output + suffix
+            if grass.find_file(rast, element = 'cell', mapset = '.')['file']:
+                maps.append(rast)
+        
+        if len(maps) != 0:
+            grass.fatal(_("Please change output name, or change names of these rasters: %s, "
+                          "module needs to create this temporary maps during execution.") % ",".join(maps))
+
+    def __del__(self):
+        # removes temporary mask, used for import transparent or warped temp_map
+        if self.cleanup_mask:
+            # clear temporary mask, which was set by module      
+            if grass.run_command('r.mask',
+                                 quiet = True,
+                                 flags = 'r') != 0:  
+                grass.fatal(_('%s failed') % 'r.mask')
+            
+            # restore original mask, if exists 
+            if grass.find_file(self.opt_output + self.original_mask_suffix, element = 'cell', mapset = '.' )['name']:
+                if grass.run_command('g.copy',
+                                     quiet = True,
+                                     rast =  self.opt_output + self.original_mask_suffix + ',MASK') != 0:
+                    grass.fatal(_('%s failed') % 'g.copy')
+        
+        
+        # remove temporary created rasters
+        if self.cleanup_layers: 
+            maps = []
+            for suffix in ('.red', '.green', '.blue', '.alpha', self.original_mask_suffix):
+                rast = self.opt_output + suffix
+                if grass.find_file(rast, element = 'cell', mapset = '.')['file']:
+                    maps.append(rast)
+            
+            if maps:
+                grass.run_command('g.remove',
+                                  quiet = True,
+                                  flags = 'f',
+                                  rast  = ','.join(maps))
+        
+        # delete environmental variable which overrides region 
+        if 'GRASS_REGION' in os.environ.keys():
+            os.environ.pop('GRASS_REGION')
+
+    def ImportMapIntoGRASS(self, raster): 
+        """!Import raster into GRASS.
+        """
+
         grass.message(_("Importing raster map into GRASS..."))
+
+        if not raster:
+            grass.warning(_("Nothing to import.\nNo data has been downloaded from wms server."))
+            return
+
         # importing temp_map into GRASS
         if grass.run_command('r.in.gdal',
                              quiet = True,
-                             input = temp_warpmap,
-                             output = self.o_output) != 0:
+                             input = raster,
+                             output = self.opt_output) != 0:
             grass.fatal(_('%s failed') % 'r.in.gdal')
         
         # information for destructor to cleanup temp_layers, created
@@ -327,17 +449,21 @@
         self.cleanup_layers = True
         
         # setting region for full extend of imported raster
-        os.environ['GRASS_REGION'] = grass.region_env(rast = self.o_output + '.red')
-        
+        if grass.find_file(self.opt_output + '.red', element = 'cell', mapset = '.')['file']:
+            region_map = self.opt_output + '.red'
+        else:
+            region_map = self.opt_output
+        os.environ['GRASS_REGION'] = grass.region_env(rast = region_map)
+          
         # mask created from alpha layer, which describes real extend
         # of warped layer (may not be a rectangle), also mask contains
         # transparent parts of raster
-        if grass.find_file( self.o_output + '.alpha', element = 'cell', mapset = '.' )['name']:
+        if grass.find_file( self.opt_output + '.alpha', element = 'cell', mapset = '.' )['name']:
             # saving current mask (if exists) into temp raster
             if grass.find_file('MASK', element = 'cell', mapset = '.' )['name']:
                 if grass.run_command('g.copy',
                                      quiet = True,
-                                     rast = 'MASK,' + self.o_output + self.original_mask_suffix) != 0:    
+                                     rast = 'MASK,' + self.opt_output + self.original_mask_suffix) != 0:    
                     grass.fatal(_('%s failed') % 'g.copy')
             
             # info for destructor
@@ -347,50 +473,92 @@
                                  overwrite = True,
                                  maskcats = "0",
                                  flags = 'i',
-                                 input = self.o_output + '.alpha') != 0: 
+                                 input = self.opt_output + '.alpha') != 0: 
                 grass.fatal(_('%s failed') % 'r.mask')
         
-        if grass.run_command('r.composite',
-                             quiet = True,
-                             red = self.o_output + '.red',
-                             green = self.o_output +  '.green',
-                             blue = self.o_output + '.blue',
-                             output = self.o_output ) != 0:
+        #TODO one band + alpha band?
+        if grass.find_file(self.opt_output + '.red', element = 'cell', mapset = '.')['file']:
+            if grass.run_command('r.composite',
+                                 quiet = True,
+                                 red = self.opt_output + '.red',
+                                 green = self.opt_output +  '.green',
+                                 blue = self.opt_output + '.blue',
+                                 output = self.opt_output ) != 0:
                 grass.fatal(_('%s failed') % 'r.composite')
-        
-        grass.try_remove(temp_warpmap)
-        grass.try_remove(self.temp_map) 
 
-    def _flipBbox(self, bbox):
-        """ 
-        flips items in dictionary 
-        value flips between this keys:
-        maxy -> maxx
-        maxx -> maxy
-        miny -> minx
-        minx -> miny
-        @return copy of bbox with fliped cordinates
-        """  
-        temp_bbox = dict(bbox)
-        new_bbox = {}
-        new_bbox['maxy'] = temp_bbox['maxx']
-        new_bbox['miny'] = temp_bbox['minx']
-        new_bbox['maxx'] = temp_bbox['maxy']
-        new_bbox['minx'] = temp_bbox['miny']
 
-        return new_bbox
+class WMSDriversInfo:
+    def __init__(self):
+        """!Provides information about driver parameters.
+        """
 
-    def _tempfile(self):
-        """!Create temp_file and append list self.temp_files_to_cleanup 
-            with path of file 
-     
-        @return string path to temp_file
+        # format labels
+        self.f_labels = ["geotiff", "tiff", "png", "jpeg", "gif"]
+
+        # form for request
+        self.formats = ["image/geotiff", "image/tiff", "image/png", "image/jpeg", "image/gif"]
+
+    def GetDrvProperties(self, driver):
+        """!Get information about driver parameters.
         """
-        temp_file = grass.tempfile()
-        if temp_file is None:
-            grass.fatal(_("Unable to create temporary files"))
-        
-        # list of created tempfiles for destructor
-        self.temp_files_to_cleanup.append(temp_file)
-        
-        return temp_file
+        if driver == 'WMS_GDAL':
+            return self._GDALDrvProperties()
+        if 'WMS' in driver:
+            return self._WMSProperties()
+        if 'WMTS' in driver:
+            return self._WMTSProperties()
+        if 'OnEarth' in driver:
+            return self._OnEarthProperties()
+
+
+    def _OnEarthProperties(self):
+
+        props = {}
+        props['ignored_flags'] = ['o']
+        props['ignored_params'] = ['bgcolor', 'styles', 'capfile_output', 
+                                   'format', 'srs', 'wms_version']
+        props['req_multiple_layers'] = False
+
+        return props
+
+    def _WMSProperties(self):
+
+        props = {}
+        props['ignored_params'] = ['capfile']
+        props['ignored_flags'] = []
+        props['req_multiple_layers'] = True
+
+        return props
+
+    def _WMTSProperties(self):
+
+        props = {}
+        props['ignored_flags'] = ['o']
+        props['ignored_params'] = ['urlparams', 'bgcolor', 'wms_version']
+        props['req_multiple_layers'] = False
+
+        return props
+
+    def _GDALDrvProperties(self):
+
+        props = {}
+        props['ignored_flags'] = []
+        props['ignored_params'] = ['urlparams', 'bgcolor', 'capfile', 'capfile_output',
+                                    'username', 'password']
+        props['req_multiple_layers'] = True
+
+        return props
+
+    def GetFormatLabel(self, format):
+        """!Convert format request form to value in parameter 'format'.
+        """
+        if format in self.formats:
+            return self.f_labels[self.formats.index(format)]
+        return None
+
+    def GetFormat(self, label):
+        """!Convert value in parameter 'format' to format request form.
+        """
+        if label in self.f_labels:
+            return self.formats[self.f_labels.index(label)]
+        return None

Added: grass-addons/grass6/raster/r.in.wms2/wms_cap_parsers.py
===================================================================
--- grass-addons/grass6/raster/r.in.wms2/wms_cap_parsers.py	                        (rev 0)
+++ grass-addons/grass6/raster/r.in.wms2/wms_cap_parsers.py	2012-12-28 17:14:22 UTC (rev 54451)
@@ -0,0 +1,635 @@
+"""!
+ at brief Parsers for WMS/WMTS/NASA OnEarth capabilities files.
+
+List of classes:
+ - wms_cap_parsers::BaseCapabilitiesTree
+ - wms_cap_parsers::WMSXMLNsHandler
+ - wms_cap_parsers::WMSCapabilitiesTree
+ - wms_cap_parsers::WMTSXMLNsHandler
+ - wms_cap_parsers::WMTSCapabilitiesTree
+ - wms_cap_parsers::OnEarthCapabilitiesTree
+
+(C) 2012 by the GRASS Development Team
+
+This program is free software under the GNU General Public License
+(>=v2). Read the file COPYING that comes with GRASS for details.
+
+ at author Stepan Turek <stepan.turek seznam.cz> (Mentor: Martin Landa)
+"""
+
+import xml.etree.ElementTree as etree
+import grass.script as grass 
+
+class BaseCapabilitiesTree(etree.ElementTree):
+    def __init__(self, cap_file):
+        """!Initialize xml.etree.ElementTree
+        """
+        try:
+            etree.ElementTree.__init__(self, file = cap_file)
+        except etree.ParseError:
+            raise etree.ParseError(_("Unable to parse XML file"))
+        except IOError as error:
+            raise etree.ParseError(_("Unabble to open XML file '%s'.\n%s\n" % (cap_file, error)))
+
+        if self.getroot() is None:
+            raise etree.ParseError(_("Root node was not found.")) 
+        
+class WMSXMLNsHandler:
+    def __init__(self, caps):
+        """!Handle XML namespaces according to WMS version of capabilities.
+        """
+        self.namespace = "{http://www.opengis.net/wms}"
+        
+        if caps.getroot().find("Service") is not None:
+            self.use_ns = False
+        elif caps.getroot().find(self.namespace + "Service") is not None:
+            self.use_ns = True
+        else:
+            raise etree.ParseError(_("Unable to parse capabilities file.\n\
+                                      Tag <%s> was not found.") % "Service")
+    
+    def Ns(self, tag_name):
+        """!Add namespace to tag_name according to version
+        """
+        if self.use_ns:
+            tag_name = self.namespace + tag_name
+        return tag_name
+
+class WMSCapabilitiesTree(BaseCapabilitiesTree):
+    def __init__(self, cap_file, force_version = None):
+        """!Parses WMS capabilities file.
+            If the capabilities file cannot be parsed if it raises xml.etree.ElementTree.ParseError.
+
+        The class manges inheritance in 'Layer' elements. Inherited elements 
+        are added to 'Layer' element.
+        The class also removes elements which are in invalid form and are needed 
+        by wxGUI capabilities dialog.
+
+        @param cap_file - capabilities file        
+        @param force_version - force capabilities file version (1.1.1, 1.3.0)
+        """
+        BaseCapabilitiesTree.__init__(self, cap_file)
+        self.xml_ns = WMSXMLNsHandler(self)
+
+        grass.debug('Checking WMS capabilities tree.', 4)
+        
+        if not "version" in self.getroot().attrib:
+            raise etree.ParseError(_("Missing version attribute root node "
+                                     "in Capabilities XML file"))
+        else:
+            wms_version = self.getroot().attrib["version"]
+        
+        if wms_version == "1.3.0":
+            self.proj_tag = "CRS"
+        else:
+            self.proj_tag = "SRS"
+
+        if force_version is not None:
+            if wms_version != force_version:
+                raise etree.ParseError(_("WMS server does not support '%s' version.") % wms_version)
+
+        capability = self._find(self.getroot(), "Capability")
+        root_layer = self._find(capability, "Layer")
+
+        self._checkFormats(capability)
+        self._checkLayerTree(root_layer)
+
+        grass.debug('Check of WMS capabilities tree was finished.', 4)
+
+    def _checkFormats(self, capability):
+        """!Check if format element is defined.
+        """        
+        request = self._find(capability, "Request")
+        get_map = self._find(request, "GetMap")
+        formats = self._findall(get_map, "Format")
+ 
+    def _checkLayerTree(self, parent_layer, first = True):
+        """!Recursively check layer tree and manage inheritance in the tree
+        """
+        if first:
+            self._initLayer(parent_layer, None)
+        
+        layers = parent_layer.findall((self.xml_ns.Ns("Layer")))
+        
+        for l in layers:
+            self._initLayer(l, parent_layer)
+            self._checkLayerTree(l, False)
+        
+    def _initLayer(self, layer, parent_layer):
+        """Inherit elements from parent layer
+
+        @param layer - <Layer> element which inherits
+        @param parent_layer - <Layer> element which is inherited from 
+        """
+        if parent_layer is not None:
+            replaced_elements = [ ["EX_GeographicBoundingBox", "replace"],
+                                  ["Attribution", "replace"],
+                                  ["MinScaleDenominator", "replace"],
+                                  ["MaxScaleDenominator", "replace"],
+                                  ["AuthorityURL", "add"]]
+            
+            for element in replaced_elements:
+                elems = layer.findall(self.xml_ns.Ns(element[0]))
+
+                if len(elems) != 0 or element[1] == "add":
+                    for e in parent_layer.findall(self.xml_ns.Ns(element[0])):
+                        layer.append(e)
+            
+            inh_arguments = ["queryable", "cascaded", "opaque",
+                             "noSubsets", "fixedWidth", "fixedHeight"]
+            
+            for attr in parent_layer.attrib:
+                if attr not in layer.attrib and attr in inh_arguments:
+                    layer.attrib[attr] = parent_layer.attrib[attr]
+            
+            self._inhNotSame(self.proj_tag, "element_content", layer, parent_layer)
+            self._inhNotSame("BoundingBox", "attribute", layer, parent_layer, self.proj_tag)
+
+            # remove invalid Styles 
+            styles = layer.findall(self.xml_ns.Ns('Style'))
+            for s in styles:
+                s_name = s.find(self.xml_ns.Ns('Name'))
+                if s_name is None or not s_name.text:
+                    grass.debug('Removed invalid <Style> element.', 4)
+                    layer.remove(s)
+
+            self._inhNotSame("Style", "child_element_content", layer, parent_layer, "Name")
+            self._inhNotSame("Dimension", "attribute", layer, parent_layer, "name")
+
+    def _inhNotSame(self, element_name, cmp_type, layer, parent_layer, add_arg = None):
+        """Inherit elements which have unique values.
+
+        @param element_name - name of inherited element
+        @param cmp_type - 'element_content' - compared value is text of <Layer> element
+        @param cmp_type - 'child_element_content' - compared value is text of a child of the <Layer> element
+        @param cmp_type - 'attribute' - compared value is text of the <Layer> element attribute
+        @param layer - <Layer> element which inherits
+        @param parent_layer - <Layer> element which is inherited from 
+        @param add_arg - name of child element or attribute
+        """
+        elem = layer.findall(self.xml_ns.Ns(element_name))
+
+        parent_elems = []
+        if parent_layer is not None:
+            parent_elems = parent_layer.findall(self.xml_ns.Ns(element_name))
+        
+        for par_elem in parent_elems:
+            parent_cmp_text = None
+            if cmp_type == "attribute":
+                if add_arg in par_elem.attrib:
+                    parent_cmp_text = par_elem.attrib[add_arg];
+
+            elif cmp_type == "element_content":
+                parent_cmp_text = par_elem.text
+                
+            elif cmp_type == "child_element_content":
+                parent_cmp = par_elem.find(self.xml_ns.Ns(add_arg))
+                if parent_cmp is not None:
+                    parent_cmp_text = parent_cmp.text
+            
+            if parent_cmp_text is None:
+                continue
+            
+            is_there = False
+            for elem in elem:
+                cmp_text = None
+                if cmp_type == "attribute":
+                    if add_arg in elem.attrib:
+                        cmp_text = elem.attrib[add_arg]
+                
+                elif cmp_type == "element_content":
+                    cmp_text = elem.text
+                
+                elif cmp_type == "child_element_content":
+                    cmp = elem.find(self.xml_ns.Ns(add_arg))
+                    if cmp is not None:
+                        cmp_text = cmp.text
+                
+                if cmpt_text is None or \
+                   cmp_text.lower() == parent_cmp_text.lower():
+                    is_there = True
+                    break
+            
+            if not is_there:
+                layer.append(par_elem)
+
+    def _find(self, etreeElement, tag):
+        """!Find child element.
+            If the element is not found it raises xml.etree.ElementTree.ParseError.  
+        """
+        res = etreeElement.find(self.xml_ns.Ns(tag))
+
+        if res is None:
+            raise etree.ParseError(_("Unable to parse capabilities file. \n\
+                                      Tag <%s> was not found.") % tag)
+
+        return res
+
+    def _findall(self, etreeElement, tag):
+        """!Find all children element.
+            If no element is found it raises xml.etree.ElementTree.ParseError.  
+        """
+        res = etreeElement.findall(self.xml_ns.Ns(tag))
+
+        if not res:
+            raise etree.ParseError(_("Unable to parse capabilities file. \n\
+                                      Tag <%s> was not found.") % tag)
+
+        return res
+
+    def getprojtag(self):
+        """!Return projection tag according to version of capabilities (CRS/SRS).
+        """
+        return self.proj_tag
+
+    def getxmlnshandler(self):
+        """!Return WMSXMLNsHandler object.
+        """
+        return self.xml_ns
+
+class WMTSXMLNsHandler:
+    """!Handle XML namespaces which are used in WMTS capabilities file.
+    """
+    def NsWmts(self, tag):
+        """!Add namespace.
+        """
+        return "{http://www.opengis.net/wmts/1.0}" + tag
+
+    def NsOws(self, tag):
+        """!Add namespace.
+        """
+        return "{http://www.opengis.net/ows/1.1}" + tag
+
+class WMTSCapabilitiesTree(BaseCapabilitiesTree):
+    def __init__(self, cap_file):
+        """!Parses WMTS capabilities file.
+            If the capabilities file cannot be parsed it raises xml.etree.ElementTree.ParseError.
+
+        The class also removes elements which are in invalid form and are needed 
+        by wxGUI capabilities dialog or for creation of GetTile request by GRASS WMS library.
+
+        @param cap_file - capabilities file        
+        """
+        BaseCapabilitiesTree.__init__(self, cap_file)
+        self.xml_ns = WMTSXMLNsHandler()
+        
+        grass.debug('Checking WMTS capabilities tree.', 4)
+
+        contents = self._find(self.getroot(), 'Contents', self.xml_ns.NsWmts)
+
+        tile_mat_sets = self._findall(contents, 'TileMatrixSet', self.xml_ns.NsWmts)
+
+        for mat_set in tile_mat_sets:
+            if not self._checkMatSet(mat_set):
+                grass.debug('Removed invalid <TileMatrixSet> element.', 4)
+                contents.remove(mat_set)
+
+        # are there any <TileMatrixSet> elements after the check
+        self._findall(contents, 'TileMatrixSet', self.xml_ns.NsWmts)
+
+        layers = self._findall(contents, 'Layer', self.xml_ns.NsWmts)
+        for l in layers:
+            if not self._checkLayer(l):
+                grass.debug('Removed invalid <Layer> element.', 4)
+                contents.remove(l)
+
+        # are there any <Layer> elements after the check
+        self._findall(contents, 'Layer', self.xml_ns.NsWmts)
+
+        grass.debug('Check of WMTS capabilities tree was finished.', 4)
+
+    def _checkMatSet(self, mat_set):
+        """!Check <TileMatrixSet>.
+        """
+        mat_set_id = mat_set.find(self.xml_ns.NsOws('Identifier'))
+        if mat_set_id is None or not mat_set_id.text:
+            return False
+
+        mat_set_srs = mat_set.find(self.xml_ns.NsOws('SupportedCRS'))
+        if mat_set_srs is None or \
+           not mat_set_srs.text:
+            return False
+
+        tile_mats = mat_set.findall(self.xml_ns.NsWmts('TileMatrix'))
+        if not tile_mats:
+            return False
+
+        for t_mat in tile_mats:
+            if not self._checkMat(t_mat):
+               grass.debug('Removed invalid <TileMatrix> element.', 4)
+               mat_set.remove(t_mat)
+
+        tile_mats = mat_set.findall(self.xml_ns.NsWmts('TileMatrix'))
+        if not tile_mats:
+            return False
+
+        return True
+
+    def _checkMat(self, t_mat):        
+        """!Check <TileMatrix>.
+        """
+        def _checkElement(t_mat, e, func):
+            element = t_mat.find(self.xml_ns.NsWmts(e))
+            if element is None or not element.text: 
+                return False
+
+            try:
+                e = func(element.text)
+            except ValueError:
+                return False
+
+            if e < 0:
+                return False
+            return True
+
+        for e, func in [['ScaleDenominator', float], 
+                        ['TileWidth', int], 
+                        ['TileHeight', int]]:
+            if not _checkElement(t_mat, e, func):
+                return False
+                        
+        tile_mat_id = t_mat.find(self.xml_ns.NsOws('Identifier'))
+        if tile_mat_id is None or not tile_mat_id.text:
+            return False
+
+        tl_str = t_mat.find(self.xml_ns.NsWmts('TopLeftCorner'))
+        if tl_str is None or not tl_str.text:
+            return False
+
+        tl = tl_str.text.split(' ')
+        if len(tl) < 2:
+            return False
+
+        for t in tl:              
+            try:
+                t = float(t)
+            except ValueError:
+                return False
+        return True
+    
+    def _checkLayer(self, layer):
+        """!Check <Layer> element.
+        """
+        layer_id = layer.find(self.xml_ns.NsOws('Identifier'))
+        if layer_id is None or not layer_id.text:
+            return False
+
+        mat_set_links = layer.findall(self.xml_ns.NsWmts('TileMatrixSetLink'))
+        if not mat_set_links:
+            return False
+
+        styles = layer.findall(self.xml_ns.NsWmts('Style'))
+        if not styles:
+            return False
+
+        for s in styles:
+            s_name = s.find(self.xml_ns.NsOws('Identifier'))
+            if s_name is None or not s_name.text:
+                grass.debug('Removed invalid <Style> element.', 4)
+                layer.remove(s_name)
+
+        contents = self.getroot().find(self.xml_ns.NsWmts('Contents'))
+        mat_sets = contents.findall(self.xml_ns.NsWmts('TileMatrixSet'))
+
+        for link in  mat_set_links:
+            # <TileMatrixSetLink> does not point to existing  <TileMatrixSet>
+            if not self._checkMatSetLink(link, mat_sets):
+                grass.debug('Removed invalid <TileMatrixSetLink> element.', 4)
+                layer.remove(link)
+
+        return True
+
+    def _checkMatSetLink(self, link, mat_sets):
+        """!Check <TileMatrixSetLink> element.
+        """
+        mat_set_link_id = link.find(self.xml_ns.NsWmts('TileMatrixSet')).text
+        found = False
+
+        for mat_set in mat_sets:
+            mat_set_id = mat_set.find(self.xml_ns.NsOws('Identifier')).text
+
+            if mat_set_id != mat_set_link_id:
+                continue
+
+            # the link points to existing <TileMatrixSet>
+            found = True
+
+            tile_mat_set_limits = link.find(self.xml_ns.NsWmts('TileMatrixSetLimits'))
+            if tile_mat_set_limits is None:
+                continue
+
+            tile_mat_limits = tile_mat_set_limits.findall(self.xml_ns.NsWmts('TileMatrixLimits'))
+            for limit in tile_mat_limits:
+                if not self._checkMatSetLimit(limit):
+                    grass.debug('Removed invalid <TileMatrixLimits> element.', 4)
+                    tile_mat_limits.remove(limit)
+
+            # are there any <TileMatrixLimits> elements after the check
+            tile_mat_limits = tile_mat_set_limits.findall(self.xml_ns.NsWmts('TileMatrixLimits'))
+            if not tile_mat_limits:
+                grass.debug('Removed invalid <TileMatrixSetLimits> element.', 4)
+                link.remove(tile_mat_set_limits)
+          
+        if not found:
+            return False
+
+        return True
+
+    def _checkMatSetLimit(self, limit):
+        """!Check <TileMatrixLimits> element.
+        """
+        limit_tile_mat = limit.find(self.xml_ns.NsWmts('TileMatrix'))
+        if limit_tile_mat is None or not limit_tile_mat.text:
+            return False
+
+        for i in ['MinTileRow', 'MaxTileRow', 'MinTileCol', 'MaxTileCol']:
+            i_tag = limit.find(self.xml_ns.NsWmts(i))
+            if i_tag is None:
+                return False
+            try:
+                int(i_tag.text)
+            except ValueError:
+                return False
+        return True
+
+    def _find(self, etreeElement, tag, ns = None):
+        """!Find child element.
+            If the element is not found it raises xml.etree.ElementTree.ParseError.  
+        """
+        if not ns:
+            res = etreeElement.find(tag)
+        else:
+            res = etreeElement.find(ns(tag))
+
+        if res is None:
+            raise etree.ParseError(_("Unable to parse capabilities file. \n\
+                                      Tag '%s' was not found.") % tag)
+
+        return res
+
+    def _findall(self, etreeElement, tag, ns = None):
+        """!Find all children element.
+            If no element is found it raises xml.etree.ElementTree.ParseError.  
+        """
+        if not ns:
+            res = etreeElement.findall(tag)
+        else:
+            res = etreeElement.findall(ns(tag))
+
+        if not res:
+            raise etree.ParseError(_("Unable to parse capabilities file. \n\
+                                      Tag '%s' was not found.") % tag)
+
+        return res
+
+    def getxmlnshandler(self):
+        """!Return WMTSXMLNsHandler object.
+        """
+        return self.xml_ns
+
+class OnEarthCapabilitiesTree(BaseCapabilitiesTree):
+    def __init__(self, cap_file):
+        """!Parse NASA OnEarth tile service file.
+            If the file cannot be parsed it raises xml.etree.ElementTree.ParseError.
+
+        The class also removes elements which are in invalid form and are needed 
+        by wxGUI capabilities dialog or for creation of GetMap request by GRASS WMS library.
+
+        @param cap_file - capabilities file        
+        """
+        BaseCapabilitiesTree.__init__(self, cap_file)
+
+        grass.debug('Checking OnEarth capabilities tree.', 4)
+
+        self._checkLayerTree(self.getroot())
+
+        grass.debug('Check if OnEarth capabilities tree was finished.', 4)
+
+    def _checkLayerTree(self, parent_layer, first = True):
+        """!Recursively check layer tree.
+        """
+        if first:
+            tiled_patterns = self._find(parent_layer, 'TiledPatterns')
+            layers = tiled_patterns.findall('TiledGroup')
+            layers += tiled_patterns.findall('TiledGroups')
+            parent_layer = tiled_patterns
+        else:
+            layers = parent_layer.findall('TiledGroup')
+            layers += parent_layer.findall('TiledGroups')
+
+        for l in layers:
+            if not self._checkLayer(l):
+                grass.debug(('Removed invalid <%s> element.' % l.tag), 4)
+                parent_layer.remove(l)
+            if l.tag == 'TiledGroups':
+               self._checkLayerTree(l, False)
+
+    def _find(self, etreeElement, tag):
+        """!Find child element.
+            If the element is not found it raises xml.etree.ElementTree.ParseError.  
+        """
+        res = etreeElement.find(tag)
+
+        if res is None:
+            raise  etree.ParseError(_("Unable to parse tile service file. \n\
+                                       Tag <%s> was not found.") % tag)
+
+        return res
+
+    def _checkLayer(self, layer):
+        """!Check <TiledGroup>/<TiledGroups> elements.
+        """
+        if layer.tag == 'TiledGroups':
+            return True
+            
+        name = layer.find('Name')
+        if name is None or not name.text:
+            return False
+
+        t_patts = layer.findall('TilePattern')
+
+        for patt in t_patts:
+            urls = self._getUrls(patt)
+            for url in urls:
+                if not self.gettilepatternurldata(url):
+                    urls.remove(url)
+
+            # check if there are any vaild urls
+            if not urls:
+                grass.debug('<TilePattern>  was removed. It has no valid url.', 4)
+                layer.remove(patt)
+            patt.text = '\n'.join(urls)
+
+        t_patts = layer.findall('TilePattern')
+        if not t_patts:
+            return False
+
+        return True
+
+    def _getUrls(self, tile_pattern):
+        """!Get all urls from tile pattern.
+        """
+        urls = []
+        if  tile_pattern.text is not None:
+            tile_patt_lines = tile_pattern.text.split('\n')
+
+            for line in tile_patt_lines:
+                if 'request=GetMap' in line:
+                    urls.append(line.strip())
+        return urls
+
+    def gettilepatternurldata(self, url):
+        """!Parse url string in Tile Pattern.
+        """
+        par_url = bbox = width = height = None 
+
+        bbox_idxs = self.geturlparamidxs(url, "bbox=")
+        if bbox_idxs is None:
+            return None
+
+        par_url = [url[:bbox_idxs[0] - 1], url[bbox_idxs[1]:]]
+
+        bbox = url[bbox_idxs[0] + len('bbox=') : bbox_idxs[1]]
+        bbox_list = bbox.split(',')
+        if len(bbox_list) < 4:
+            return None
+
+        try:
+            bbox = map(float, bbox.split(','))
+        except ValueError:
+            return None
+
+        width_idxs = self.geturlparamidxs(url, "width=")
+        if width_idxs is None:
+            return None
+
+        try:
+            width = int(url[width_idxs[0] + len('width=') : width_idxs[1]])
+        except  ValueError:
+            return None
+
+        height_idxs = self.geturlparamidxs(url, "height=")
+        if height_idxs is None:
+            return None
+
+        try:
+            height = int(url[height_idxs[0] + len('height=') : height_idxs[1]])
+        except  ValueError:
+            return None
+
+        if height < 0 or width < 0:
+            return None
+
+        return par_url, bbox, width, height
+
+    def geturlparamidxs(self, params_str, param_key):
+        """!Find start and end index of parameter and it's value in url string
+        """
+        start_i = params_str.lower().find(param_key)
+        if start_i < 0: 
+            return None
+        end_i = params_str.find("&", start_i)
+        if end_i < 0:
+            end_i = len(params_str)
+
+        return (start_i, end_i) 
\ No newline at end of file


Property changes on: grass-addons/grass6/raster/r.in.wms2/wms_cap_parsers.py
___________________________________________________________________
Added: svn:mime-type
   + text/x-python
Added: svn:eol-style
   + native

Modified: grass-addons/grass6/raster/r.in.wms2/wms_drv.py
===================================================================
--- grass-addons/grass6/raster/r.in.wms2/wms_drv.py	2012-12-28 17:09:00 UTC (rev 54450)
+++ grass-addons/grass6/raster/r.in.wms2/wms_drv.py	2012-12-28 17:14:22 UTC (rev 54451)
@@ -1,175 +1,167 @@
-import grass.script as grass
+"""!
+ at brief WMS, WMTS and NASA OnEarth drivers implemented in GRASS using GDAL Python bindings. 
 
+List of classes:
+ - wms_drv::WMSDrv
+ - wms_drv::BaseRequestMgr
+ - wms_drv::WMSRequestMgr
+ - wms_drv::WMTSRequestMgr
+ - wms_drv::OnEarthRequestMgr
+
+(C) 2012 by the GRASS Development Team
+
+This program is free software under the GNU General Public License
+(>=v2). Read the file COPYING that comes with GRASS for details.
+
+ at author Stepan Turek <stepan.turek seznam.cz> (Mentor: Martin Landa)
+"""
+
+import grass.script as grass 
+
 try:
     from osgeo import gdal
     from osgeo import gdalconst 
 except:
     grass.fatal(_("Unable to load GDAL python bindings"))
 
-from urllib2 import urlopen
-
 import numpy as Numeric
 Numeric.arrayrange = Numeric.arange
 
+from math import pi, floor
+from urllib2 import HTTPError
+from httplib import HTTPException
+from xml.etree.ElementTree import ParseError
+
 from wms_base import WMSBase
 
+from wms_cap_parsers import WMTSCapabilitiesTree, OnEarthCapabilitiesTree
+
 class WMSDrv(WMSBase):
     def _download(self):
         """!Downloads data from WMS server using own driver
         
-        @return temp_map with stored downloaded data
+        @return temp_map with downloaded data
         """ 
         grass.message(_("Downloading data from WMS server..."))
-        
 
-        proj = self.projection_name + "=EPSG:"+ str(self.o_srs)
-        url = self.o_mapserver_url + "REQUEST=GetMap&VERSION=%s&LAYERS=%s&WIDTH=%s&HEIGHT=%s&STYLES=%s&BGCOLOR=%s&TRANSPARENT=%s" %\
-                  (self.o_wms_version, self.o_layers, self.tile_cols, self.tile_rows, self.o_styles, self.o_bgcolor, self.transparent)
-        url += "&" +proj+ "&" + "FORMAT=" + self.mime_format
-        
-        if self.o_urlparams != "":
-            url +="&" + self.o_urlparams
-        
-        cols = int(self.region['cols'])
-        rows = int(self.region['rows'])
-        
-        # computes parameters of tiles 
-        num_tiles_x = cols / self.tile_cols 
-        last_tile_x_size = cols % self.tile_cols
-        tile_x_length =  float(self.tile_cols) / float(cols ) * (self.bbox['maxx'] - self.bbox['minx']) 
-        
-        last_tile_x = False
-        if last_tile_x_size != 0:
-            last_tile_x = True
-            num_tiles_x = num_tiles_x + 1
-        
-        num_tiles_y = rows / self.tile_rows 
-        last_tile_y_size = rows % self.tile_rows
-        tile_y_length =  float(self.tile_rows) / float(rows) * (self.bbox['maxy'] - self.bbox['miny']) 
-        
-        last_tile_y = False
-        if last_tile_y_size != 0:
-            last_tile_y = True
-            num_tiles_y = num_tiles_y + 1
-        
-        # each tile is downloaded and written into temp_map 
-        tile_bbox = dict(self.bbox)
-        tile_bbox['maxx'] = self.bbox['minx']  + tile_x_length
-        
-        tile_to_temp_map_size_x = self.tile_cols
-        for i_x in range(num_tiles_x):
-            # set bbox for tile i_x,i_y (E, W)
-            if i_x != 0:
-                tile_bbox['maxx'] += tile_x_length 
-                tile_bbox['minx'] += tile_x_length            
-            
-            if i_x == num_tiles_x - 1 and last_tile_x:
-                tile_to_temp_map_size_x = last_tile_x_size 
-            
-            tile_bbox['maxy'] = self.bbox['maxy']                    
-            tile_bbox['miny'] = self.bbox['maxy'] - tile_y_length 
-            tile_to_temp_map_size_y = self.tile_rows       
-            
-            for i_y in range(num_tiles_y):
-                # set bbox for tile i_x,i_y (N, S)
-                if i_y != 0:
-                    tile_bbox['miny'] -= tile_y_length 
-                    tile_bbox['maxy'] -= tile_y_length
-                
-                if i_y == num_tiles_y - 1 and last_tile_y:
-                    tile_to_temp_map_size_y = last_tile_y_size 
-                
-                if self.flip_coords:
-                    # flips coordinates if WMS strandard is 1.3.0 and 
-                    # projection is geographic (see:wms_base.py _computeBbox)
-                    query_bbox = dict(self._flipBbox(tile_bbox))
+        if not self.params['capfile']:
+            self.cap_file = self._fetchCapabilities(self.params)
+        else:
+            self.cap_file = self.params['capfile']
+
+        # initialize correct manager according to chosen OGC service
+        if self.params['driver'] == 'WMTS_GRASS':
+            req_mgr = WMTSRequestMgr(self.params, self.bbox, self.region, self.proj_srs, self.cap_file)
+        elif self.params['driver'] == 'WMS_GRASS':
+            req_mgr = WMSRequestMgr(self.params, self.bbox, self.region, self.tile_size, self.proj_srs)
+        elif self.params['driver'] == 'OnEarth_GRASS':
+            req_mgr = OnEarthRequestMgr(self.params, self.bbox, self.region, self.proj_srs, self.cap_file)
+
+        # get information about size in pixels and bounding box of raster, where all tiles will be joined
+        map_region = req_mgr.GetMapRegion()
+
+        init = True
+        temp_map = None
+
+        # iterate through all tiles and download them
+        while True:
+
+            # get url for request the tile and information for placing the tile into raster with other tiles
+            tile = req_mgr.GetNextTile()
+
+            # if last tile has been already downloaded 
+            if not tile:
+                break
+
+            # url for request the tile
+            query_url = tile[0]
+
+            # the tile size and offset in pixels for placing it into raster where tiles are joined
+            tile_ref = tile[1]
+            grass.debug(query_url, 2)
+            try: 
+                wms_data = self._fetchDataFromServer(query_url, self.params['username'], self.params['password'])
+            except (IOError, HTTPException), e:
+                if HTTPError == type(e) and e.code == 401:
+                    grass.fatal(_("Authorization failed to '%s' when fetching data.") % self.params['url'])
                 else:
-                    query_bbox = tile_bbox
+                    grass.fatal(_("Unable to fetch data from: '%s'") % self.params['url'])
 
-                query_url = url + "&" + "BBOX=%s,%s,%s,%s" % ( query_bbox['minx'],  query_bbox['miny'],  query_bbox['maxx'],  query_bbox['maxy'])
-                grass.debug(query_url)
-                try: 
-                    wms_data = urlopen(query_url)
-                except IOError:
-                    grass.fatal(_("Unable to fetch data from mapserver"))
+            temp_tile = self._tempfile()
                 
-                temp_tile = self._tempfile()
+            # download data into temporary file
+            try:
+                temp_tile_opened = open(temp_tile, 'w')
+                temp_tile_opened.write(wms_data.read())
+            except IOError:
+                grass.fatal(_("Unable to write data into tempfile"))
+            finally:
+                temp_tile_opened.close()
                 
-                # download data into temporary file
+            tile_dataset_info = gdal.Open(temp_tile, gdal.GA_ReadOnly) 
+            if tile_dataset_info is None:
+                # print error xml returned from server
                 try:
-                    temp_tile_opened = open(temp_tile, 'w')
-                    temp_tile_opened.write(wms_data.read())
+                    error_xml_opened = open(temp_tile, 'r')
+                    err_str = error_xml_opened.read()     
                 except IOError:
-                    grass.fatal(_("Unable to write data into tempfile"))
+                    grass.fatal(_("Unable to read data from tempfile"))
                 finally:
-                    temp_tile_opened.close()
-                
-                tile_dataset_info = gdal.Open(temp_tile, gdal.GA_ReadOnly) 
-                if tile_dataset_info is None:
-                    # print error xml returned from server
-                    try:
-                        error_xml_opened = open(temp_tile, 'r')
-                        err_str = error_xml_opened.read()     
-                    except IOError:
-                        grass.fatal(_("Unable to read data from tempfile"))
-                    finally:
-                        error_xml_opened.close()
+                    error_xml_opened.close()
 
-                    if  err_str is not None:
-                        grass.fatal(_("WMS server error: %s") %  err_str)
-                    else:
-                        grass.fatal(_("WMS server unknown error") )
+                if  err_str is not None:
+                    grass.fatal(_("WMS server error: %s") %  err_str)
+                else:
+                    grass.fatal(_("WMS server unknown error") )
                 
-                band = tile_dataset_info.GetRasterBand(1) 
-                cell_type_func = band.__swig_getmethods__["DataType"]#??
-                bands_number_func = tile_dataset_info.__swig_getmethods__["RasterCount"]
+            temp_tile_pct2rgb = None
+            if tile_dataset_info.RasterCount == 1 and \
+               tile_dataset_info.GetRasterBand(1).GetRasterColorTable() is not None:
+                # expansion of color table into bands 
+                temp_tile_pct2rgb = self._tempfile()
+                tile_dataset = self._pct2rgb(temp_tile, temp_tile_pct2rgb)
+            else: 
+                tile_dataset = tile_dataset_info
                 
-                ##### see original r.in.wms - file gdalwarp.py line 117 ####
-                temp_tile_pct2rgb = None
-                if bands_number_func(tile_dataset_info) == 1 and band.GetRasterColorTable() is not None:
-                    # expansion of color table into bands 
-                    temp_tile_pct2rgb = self._tempfile()
-                    tile_dataset = self._pct2rgb(temp_tile, temp_tile_pct2rgb)
-                else: 
-                    tile_dataset = tile_dataset_info
-                
-                # initialization of temp_map_dataset, where all tiles are merged
-                if i_x == 0 and i_y == 0:
-                    temp_map = self._tempfile()
+            # initialization of temp_map_dataset, where all tiles are merged
+            if init:
+                temp_map = self._tempfile()
                     
-                    driver = gdal.GetDriverByName(self.gdal_drv_format)
-                    metadata = driver.GetMetadata()
-                    if not metadata.has_key(gdal.DCAP_CREATE) or \
-                           metadata[gdal.DCAP_CREATE] == 'NO':
-                        grass.fatal(_('Driver %s does not supports Create() method') % drv_format)  
-                    
-                    self.temp_map_bands_num = bands_number_func(tile_dataset)
-                    temp_map_dataset = driver.Create(temp_map, int(cols), int(rows),
-                                                     self.temp_map_bands_num, cell_type_func(band));
+                driver = gdal.GetDriverByName(self.gdal_drv_format)
+                metadata = driver.GetMetadata()
+                if not metadata.has_key(gdal.DCAP_CREATE) or \
+                       metadata[gdal.DCAP_CREATE] == 'NO':
+                    grass.fatal(_('Driver %s does not supports Create() method') % drv_format)  
+                self.temp_map_bands_num = tile_dataset.RasterCount
+                temp_map_dataset = driver.Create(temp_map, map_region['cols'], map_region['rows'],
+                                                 self.temp_map_bands_num, 
+                                                 tile_dataset.GetRasterBand(1).DataType)
+                init = False
                 
-                # tile written into temp_map
-                tile_to_temp_map = tile_dataset.ReadRaster(0, 0, tile_to_temp_map_size_x, tile_to_temp_map_size_y,
-                                                                 tile_to_temp_map_size_x, tile_to_temp_map_size_y)
+            # tile is written into temp_map
+            tile_to_temp_map = tile_dataset.ReadRaster(0, 0, tile_ref['sizeX'], tile_ref['sizeY'],
+                                                             tile_ref['sizeX'], tile_ref['sizeY'])
                 
-                temp_map_dataset.WriteRaster(self.tile_cols * i_x, self.tile_rows * i_y,
-                                             tile_to_temp_map_size_x,  tile_to_temp_map_size_y, tile_to_temp_map) 
+            temp_map_dataset.WriteRaster(tile_ref['t_cols_offset'], tile_ref['t_rows_offset'],
+                                         tile_ref['sizeX'],  tile_ref['sizeY'], tile_to_temp_map) 
                 
-                tile_dataset = None
-                tile_dataset_info = None
-                grass.try_remove(temp_tile)
-                grass.try_remove(temp_tile_pct2rgb)    
+            tile_dataset = None
+            tile_dataset_info = None
+            grass.try_remove(temp_tile)
+            grass.try_remove(temp_tile_pct2rgb)    
         
+        if not temp_map:
+            return temp_map
         # georeferencing and setting projection of temp_map
         projection = grass.read_command('g.proj', 
                                         flags = 'wf',
-                                        epsg =self.o_srs).rstrip('\n')
+                                        epsg =self.params['srs']).rstrip('\n')
         temp_map_dataset.SetProjection(projection)
-        
 
+        pixel_x_length = (map_region['maxx'] - map_region['minx']) / int(map_region['cols'])
+        pixel_y_length = (map_region['miny'] - map_region['maxy']) / int(map_region['rows'])
 
-        pixel_x_length = (self.bbox['maxx'] - self.bbox['minx']) / int(cols)
-        pixel_y_length = (self.bbox['miny'] - self.bbox['maxy']) / int(rows)
-        geo_transform = [ self.bbox['minx'] , pixel_x_length  , 0.0 ,  self.bbox['maxy'] , 0.0 , pixel_y_length ] 
+        geo_transform = [map_region['minx'] , pixel_x_length  , 0.0 , map_region['maxy'] , 0.0 , pixel_y_length] 
         temp_map_dataset.SetGeoTransform(geo_transform )
         temp_map_dataset = None
         
@@ -220,3 +212,633 @@
                 tif_ds.GetRasterBand(iBand+1).WriteArray(dst_data,0,iY)
         
         return tif_ds       
+
+class BaseRequestMgr:
+    """!Base class for request managers. 
+    """
+    def _computeRequestData(self, bbox, tl_corner, tile_span, tile_size, mat_num_bbox):
+        """!Initialize data needed for iteration through tiles. Used by WMTS_GRASS and OnEarth_GRASS drivers.
+        """ 
+        epsilon = 1e-15
+
+        # request data bbox specified in row and col number 
+        self.t_num_bbox = {}
+
+        self.t_num_bbox['min_col'] = int(floor((bbox['minx'] - tl_corner['minx']) / tile_span['x'] + epsilon))
+        self.t_num_bbox['max_col'] = int(floor((bbox['maxx'] - tl_corner['minx']) / tile_span['x'] - epsilon))
+
+        self.t_num_bbox['min_row'] = int(floor((tl_corner['maxy'] - bbox['maxy']) / tile_span['y'] + epsilon))
+        self.t_num_bbox['max_row'] = int(floor((tl_corner['maxy'] - bbox['miny']) / tile_span['y'] - epsilon))
+
+
+        # Does required bbox intersects bbox of data available on server?
+        self.intersects = False
+        for col in ['min_col', 'max_col']:
+            for row in ['min_row', 'max_row']:
+                if (self.t_num_bbox['min_row'] <= self.t_num_bbox[row] and self.t_num_bbox[row] <= mat_num_bbox['max_row']) and \
+                   (self.t_num_bbox['min_col'] <= self.t_num_bbox[col] and self.t_num_bbox[col] <= mat_num_bbox['max_col']):
+                    self.intersects = True
+                    
+        if not self.intersects:
+            grass.warning(_('Region is out of server data extend.'))
+            self.map_region = None
+            return
+
+        # crop request bbox to server data bbox extend 
+        if self.t_num_bbox['min_col'] < (mat_num_bbox['min_col']):
+            self.t_num_bbox['min_col']  = int(mat_num_bbox['min_col'])
+
+        if self.t_num_bbox['max_col'] > (mat_num_bbox['max_col']):
+            self.t_num_bbox['max_col']  = int(mat_num_bbox['max_col'])
+
+        if self.t_num_bbox['min_row'] < (mat_num_bbox['min_row']):
+            self.t_num_bbox['min_row'] = int(mat_num_bbox['min_row']) 
+
+        if self.t_num_bbox['max_row'] > (mat_num_bbox['max_row']):
+            self.t_num_bbox['max_row'] = int(mat_num_bbox['max_row']) 
+
+        num_tiles = (self.t_num_bbox['max_col'] - self.t_num_bbox['min_col'] + 1) * (self.t_num_bbox['max_row'] - self.t_num_bbox['min_row'] + 1) 
+        grass.message(_('Fetching %d tiles with %d x %d pixel size per tile...') % (num_tiles, tile_size['x'], tile_size['y']))
+
+        # georeference of raster, where tiles will be merged
+        self.map_region = {}
+        self.map_region['minx'] = self.t_num_bbox['min_col'] * tile_span['x'] + tl_corner['minx']
+        self.map_region['maxy'] = tl_corner['maxy'] - (self.t_num_bbox['min_row']) * tile_span['y']
+
+        self.map_region['maxx'] = (self.t_num_bbox['max_col'] + 1) * tile_span['x'] + tl_corner['minx']
+        self.map_region['miny'] = tl_corner['maxy'] - (self.t_num_bbox['max_row'] + 1) * tile_span['y']
+
+        # size of raster, where tiles will be merged
+        self.map_region['cols'] = int(tile_size['x'] * (self.t_num_bbox['max_col'] -  self.t_num_bbox['min_col'] + 1))
+        self.map_region['rows'] = int(tile_size['y'] * (self.t_num_bbox['max_row'] -  self.t_num_bbox['min_row'] + 1))
+
+        # hold information about current column and row during iteration 
+        self.i_col = self.t_num_bbox['min_col']
+        self.i_row = self.t_num_bbox['min_row'] 
+
+        # bbox for first tile request
+        self.query_bbox = { 
+                            'minx' : tl_corner['minx'],
+                            'maxy' : tl_corner['maxy'],
+                            'maxx' : tl_corner['minx'] + tile_span['x'],
+                            'miny' : tl_corner['maxy'] - tile_span['y'],
+                          }
+
+        self.tile_ref = {
+                          'sizeX' : tile_size['x'],
+                          'sizeY' : tile_size['y']
+                        }
+
+    def _isGeoProj(self, proj):
+        """!Is it geographic projection? 
+        """       
+        if (proj.find("+proj=latlong")  != -1 or \
+            proj.find("+proj=longlat")  != -1):
+
+            return True
+        return False
+
+class WMSRequestMgr(BaseRequestMgr):
+    def __init__(self, params, bbox, region, tile_size, proj_srs, cap_file = None):
+        """!Initialize data needed for iteration through tiles.
+        """
+        self.version = params['wms_version']
+        proj = params['proj_name'] + "=EPSG:"+ str(params['srs'])
+        self.url = params['url'] + ("?SERVICE=WMS&REQUEST=GetMap&VERSION=%s&LAYERS=%s&WIDTH=%s&HEIGHT=%s&STYLES=%s&BGCOLOR=%s&TRANSPARENT=%s" % \
+                  (params['wms_version'], params['layers'], tile_size['cols'], tile_size['rows'], params['styles'], \
+                   params['bgcolor'], params['transparent']))
+        self.url += "&" +proj+ "&" + "FORMAT=" + params['format']
+
+        self.bbox = bbox
+        self.proj_srs = proj_srs
+        self.tile_rows = tile_size['rows']
+        self.tile_cols = tile_size['cols']
+
+        if params['urlparams'] != "":
+            self.url += "&" + params['urlparams']
+        
+        cols = int(region['cols'])
+        rows = int(region['rows'])
+        
+        # computes parameters of tiles 
+        self.num_tiles_x = cols / self.tile_cols 
+        self.last_tile_x_size = cols % self.tile_cols
+        self.tile_length_x =  float(self.tile_cols) / float(cols) * (self.bbox['maxx'] - self.bbox['minx']) 
+        
+        self.last_tile_x = False
+        if self.last_tile_x_size != 0:
+            self.last_tile_x = True
+            self.num_tiles_x = self.num_tiles_x + 1
+        
+        self.num_tiles_y = rows / self.tile_rows 
+        self.last_tile_y_size = rows % self.tile_rows
+        self.tile_length_y =  float(self.tile_rows) / float(rows) * (self.bbox['maxy'] - self.bbox['miny']) 
+        
+        self.last_tile_y = False
+        if self.last_tile_y_size != 0:
+            self.last_tile_y = True
+            self.num_tiles_y = self.num_tiles_y + 1
+        
+        self.tile_bbox = dict(self.bbox)
+        self.tile_bbox['maxx'] = self.bbox['minx']  + self.tile_length_x
+
+        self.i_x = 0 
+        self.i_y = 0
+
+        self.map_region = self.bbox
+        self.map_region['cols'] = cols
+        self.map_region['rows'] = rows
+
+    def GetMapRegion(self):
+        """!Get size in pixels and bounding box of raster where all tiles will be merged.
+        """
+        return self.map_region
+
+    def GetNextTile(self):
+        """!Get url for tile request from server and information for merging the tile with other tiles
+        """
+
+        tile_ref = {}
+
+        if self.i_x >= self.num_tiles_x:
+            return None
+            
+        tile_ref['sizeX'] = self.tile_cols
+        if self.i_x == self.num_tiles_x - 1 and self.last_tile_x:
+            tile_ref['sizeX'] = self.last_tile_x_size
+         
+        # set bbox for tile (N, S)
+        if self.i_y != 0:
+            self.tile_bbox['miny'] -= self.tile_length_y 
+            self.tile_bbox['maxy'] -= self.tile_length_y
+        else:
+            self.tile_bbox['maxy'] = self.bbox['maxy']
+            self.tile_bbox['miny'] = self.bbox['maxy'] - self.tile_length_y
+
+        tile_ref['sizeY'] = self.tile_rows
+        if self.i_y == self.num_tiles_y - 1 and self.last_tile_y:
+            tile_ref['sizeY'] = self.last_tile_y_size 
+
+        if self._isGeoProj(self.proj_srs) and self.version == "1.3.0":                
+            query_bbox = self._flipBbox(self.tile_bbox, self.proj_srs, self.version)
+        else:
+            query_bbox = self.tile_bbox
+        query_url = self.url + "&" + "BBOX=%s,%s,%s,%s" % ( query_bbox['minx'],  query_bbox['miny'],  query_bbox['maxx'],  query_bbox['maxy'])
+
+        tile_ref['t_cols_offset'] = int(self.tile_cols * self.i_x)
+        tile_ref['t_rows_offset'] = int(self.tile_rows * self.i_y)
+
+        if self.i_y >= self.num_tiles_y - 1:
+            self.i_y = 0
+            self.i_x += 1
+            # set bbox for next tile (E, W)      
+            self.tile_bbox['maxx'] += self.tile_length_x 
+            self.tile_bbox['minx'] += self.tile_length_x 
+        else:
+            self.i_y += 1
+
+        return query_url, tile_ref
+
+    def _flipBbox(self, bbox, proj, version):
+        """ 
+        Flips coordinates if WMS standard is 1.3.0 and 
+        projection is geographic.
+
+        value flips between this keys:
+        maxy -> maxx
+        maxx -> maxy
+        miny -> minx
+        minx -> miny
+        @return copy of bbox with flipped coordinates
+        """  
+
+        temp_bbox = dict(bbox)
+        new_bbox = {}
+        new_bbox['maxy'] = temp_bbox['maxx']
+        new_bbox['miny'] = temp_bbox['minx']
+        new_bbox['maxx'] = temp_bbox['maxy']
+        new_bbox['minx'] = temp_bbox['miny']
+
+        return new_bbox
+
+
+class WMTSRequestMgr(BaseRequestMgr):
+    def __init__(self, params, bbox, region, proj_srs, cap_file = None):
+        """!Initializes data needed for iteration through tiles.
+        """
+
+        self.proj_srs = proj_srs
+        self.meters_per_unit = None
+
+        # constant defined in WMTS standard (in meters)
+        self.pixel_size = 0.00028
+
+        # parse capabilities file
+        try:
+            # checks all elements needed by this class,
+            # invalid elements are removed
+            cap_tree = WMTSCapabilitiesTree(cap_file)
+        except ParseError as error:
+            grass.fatal(_("Unable to parse tile service file.\n%s\n") % str(error))
+        self.xml_ns = cap_tree.getxmlnshandler()
+
+        root = cap_tree.getroot()
+
+        # get layer tile matrix sets with required projection
+        mat_sets = self._getMatSets(root, params['layers'], params['srs'])  #[[TileMatrixSet, TileMatrixSetLink], ....]
+
+        # TODO: what if more tile matrix sets have required srs (returned more than 1)?
+        mat_set = mat_sets[0][0]
+        mat_set_link = mat_sets[0][1]
+        params['tile_matrix_set'] = mat_set.find(self.xml_ns.NsOws('Identifier')).text
+
+        # find tile matrix with resolution closest and smaller to wanted resolution 
+        tile_mat  = self._findTileMats(mat_set.findall(self.xml_ns.NsWmts('TileMatrix')), region, bbox)
+
+        # get extend of data available on server expressed in max/min rows and cols of tile matrix
+        mat_num_bbox = self._getMatSize(tile_mat, mat_set_link)
+
+        # initialize data needed for iteration through tiles
+        self._computeRequestData(tile_mat, params, bbox, mat_num_bbox)
+
+    def GetMapRegion(self):
+        """!Get size in pixels and bounding box of raster where all tiles will be merged.
+        """
+        return self.map_region
+
+    def _getMatSets(self, root, layer_name, srs):
+        """!Get matrix sets which are available for chosen layer and have required EPSG.
+        """
+
+        contents = root.find(self.xml_ns.NsWmts('Contents'))
+        layers = contents.findall(self.xml_ns.NsWmts('Layer'))
+
+        ch_layer = None
+        for layer in layers:
+            layer_id = layer.find(self.xml_ns.NsOws('Identifier')).text
+            if layer_id == layer_name:
+                ch_layer = layer 
+                break  
+
+        if ch_layer is None:
+            grass.fatal(_("Layer '%s' was not found in capabilities file") % layer_name)
+
+        mat_set_links = ch_layer.findall(self.xml_ns.NsWmts('TileMatrixSetLink'))
+
+        suitable_mat_sets = []
+        tileMatrixSets = contents.findall(self.xml_ns.NsWmts('TileMatrixSet'))
+
+        for link in  mat_set_links:
+            mat_set_link_id = link.find(self.xml_ns.NsWmts('TileMatrixSet')).text
+            for mat_set in tileMatrixSets:
+                mat_set_id = mat_set.find(self.xml_ns.NsOws('Identifier')).text 
+                if mat_set_id != mat_set_link_id:
+                    continue
+                mat_set_srs = mat_set.find(self.xml_ns.NsOws('SupportedCRS')).text
+                if mat_set_srs.lower() == ("EPSG:"+ str(srs)).lower():
+                    suitable_mat_sets.append([mat_set, link])
+
+        if not suitable_mat_sets:
+            grass.fatal(_("Layer '%s' is not available with %s code.") % (layer_name,  "EPSG:" + str(srs)))
+
+        return suitable_mat_sets # [[TileMatrixSet, TileMatrixSetLink], ....]
+
+    def _findTileMats(self, tile_mats, region, bbox):
+        """!Find best tile matrix set for requested resolution.
+        """        
+        scale_dens = []
+
+        scale_dens.append((bbox['maxy'] - bbox['miny']) / region['rows'] * self._getMetersPerUnit()  / self.pixel_size)
+        scale_dens.append((bbox['maxx'] - bbox['minx']) / region['cols'] * self._getMetersPerUnit() / self.pixel_size)
+
+        scale_den = min(scale_dens)
+
+        first = True
+        for t_mat in tile_mats:
+            mat_scale_den = float(t_mat.find(self.xml_ns.NsWmts('ScaleDenominator')).text)
+            if first:
+                best_scale_den = mat_scale_den
+                best_t_mat = t_mat
+                first = False
+                continue
+                
+            best_diff = best_scale_den - scale_den
+            mat_diff = mat_scale_den - scale_den
+            if (best_diff < mat_diff  and  mat_diff < 0) or \
+               (best_diff > mat_diff  and  best_diff > 0):
+                best_t_mat = t_mat
+                best_scale_den = mat_scale_den
+
+        return best_t_mat
+
+    def _getMetersPerUnit(self):
+        """!Get coefficient which allows to convert units of request projection into meters.
+        """  
+        if self.meters_per_unit:
+            return self.meters_per_unit
+
+        # for geographic projection
+        if self._isGeoProj(self.proj_srs):
+            proj_params = self.proj_srs.split(' ')
+            for param in proj_params:
+                if '+a' in param:
+                    a = float(param.split('=')[1])
+                    break
+            equator_perim = 2 * pi * a
+            # meters per degree on equator
+            self.meters_per_unit = equator_perim / 360 
+
+        # other units
+        elif '+to_meter' in self.proj_srs:
+            proj_params = self.proj_srs.split(' ')
+            for param in proj_params:
+                if '+to_meter' in param:
+                    self.meters_per_unit = 1/float(param.split('=')[1])
+                    break
+        # coordinate system in meters        
+        else:
+            self.meters_per_unit = 1
+
+        return self.meters_per_unit
+
+    def _getMatSize(self, tile_mat, mat_set_link):
+        """!Get rows and cols extend of data available on server for chosen layer and tile matrix.
+        """
+
+        # general tile matrix size
+        mat_num_bbox = {}
+        mat_num_bbox['min_col'] = mat_num_bbox['min_row'] = 0
+        mat_num_bbox['max_col'] = int(tile_mat.find(self.xml_ns.NsWmts('MatrixWidth')).text) - 1
+        mat_num_bbox['max_row'] = int(tile_mat.find(self.xml_ns.NsWmts('MatrixHeight')).text) - 1
+
+        # get extend restriction in TileMatrixSetLink for the tile matrix, if exists
+        tile_mat_set_limits = mat_set_link.find((self.xml_ns.NsWmts('TileMatrixSetLimits')))
+        if tile_mat_set_limits is None:
+            return mat_num_bbox
+
+        tile_mat_id = tile_mat.find(self.xml_ns.NsOws('Identifier')).text
+        tile_mat_limits = tile_mat_set_limits.findall(self.xml_ns.NsWmts('TileMatrixLimits'))
+
+        for limit in tile_mat_limits:
+            limit_tile_mat = limit.find(self.xml_ns.NsWmts('TileMatrix'))
+            limit_id = limit_tile_mat.text
+
+            if limit_id == tile_mat_id:
+                for i in [['min_row', 'MinTileRow'], ['max_row', 'MaxTileRow'], \
+                          ['min_col', 'MinTileCol'], ['max_col', 'MaxTileCol']]:
+                    i_tag = limit.find(self.xml_ns.NsWmts(i[1]))
+
+                    mat_num_bbox[i[0]] = int(i_tag.text)
+                break
+        return mat_num_bbox
+
+    def _computeRequestData(self, tile_mat, params, bbox, mat_num_bbox):
+        """!Initialize data needed for iteration through tiles.
+        """  
+        scale_den = float(tile_mat.find(self.xml_ns.NsWmts('ScaleDenominator')).text)
+
+        pixel_span = scale_den * self.pixel_size / self._getMetersPerUnit()
+
+        tl_str = tile_mat.find(self.xml_ns.NsWmts('TopLeftCorner')).text.split(' ')
+
+        tl_corner = {}
+        tl_corner['minx'] = float(tl_str[0])
+        tl_corner['maxy'] = float(tl_str[1])
+
+        tile_span = {}
+        self.tile_size = {}
+
+        self.tile_size['x'] = int(tile_mat.find(self.xml_ns.NsWmts('TileWidth')).text)
+        tile_span['x'] = pixel_span * self.tile_size['x']
+        
+        self.tile_size['y'] = int(tile_mat.find(self.xml_ns.NsWmts('TileHeight')).text)
+        tile_span['y'] = pixel_span * self.tile_size['y']
+
+        self.url = params['url'] + ("?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&" \
+                                    "LAYER=%s&STYLE=%s&FORMAT=%s&TILEMATRIXSET=%s&TILEMATRIX=%s" % \
+                                   (params['layers'], params['styles'], params['format'],
+                                    params['tile_matrix_set'], tile_mat.find(self.xml_ns.NsOws('Identifier')).text ))
+
+        BaseRequestMgr._computeRequestData(self, bbox, tl_corner, tile_span, self.tile_size, mat_num_bbox)
+
+    def GetNextTile(self):
+        """!Get url for tile request from server and information for merging the tile with other tiles.
+        """
+        if not self.intersects or self.i_col > self.t_num_bbox['max_col']:
+            return None 
+                
+        query_url = self.url + "&TILECOL=%i&TILEROW=%i" % (int(self.i_col), int(self.i_row)) 
+
+        self.tile_ref['t_cols_offset'] = int(self.tile_size['x'] * (self.i_col - self.t_num_bbox['min_col']))
+        self.tile_ref['t_rows_offset'] = int(self.tile_size['y'] * (self.i_row - self.t_num_bbox['min_row']))
+
+        if self.i_row >= self.t_num_bbox['max_row']:
+            self.i_row =  self.t_num_bbox['min_row']
+            self.i_col += 1
+        else:
+            self.i_row += 1 
+
+        return query_url, self.tile_ref
+
+class OnEarthRequestMgr(BaseRequestMgr):
+    def __init__(self, params, bbox, region, proj_srs, tile_service):
+        """!Initializes data needed for iteration through tiles.
+        """
+        try:
+            # checks all elements needed by this class,
+            # invalid elements are removed
+            self.cap_tree = OnEarthCapabilitiesTree(tile_service)
+        except ParseError as error:
+            grass.fatal(_("Unable to parse tile service file.\n%s\n") % str(error))
+
+        root = self.cap_tree.getroot()
+
+        # parse tile service file and get needed data for making tile requests
+        url, self.tile_span, t_patt_bbox, self.tile_size = self._parseTileService(root, bbox, region, params)
+        self.url = url
+        self.url[0] = params['url'] + '?' + url[0]
+        # initialize data needed for iteration through tiles
+        self._computeRequestData(bbox, t_patt_bbox, self.tile_span, self.tile_size)
+
+    def GetMapRegion(self):
+        """!Get size in pixels and bounding box of raster where all tiles will be merged.
+        """
+        return self.map_region
+
+    def _parseTileService(self, root, bbox, region, params):
+        """!Get data from tile service file
+        """
+        tiled_patterns = root.find('TiledPatterns')
+        tile_groups = self._getAllTiledGroup(tiled_patterns)
+        if not tile_groups:
+            grass.fatal(_("Unable to parse tile service file. \n No tag '%s' was found.") % 'TiledGroup')
+
+        req_group = None
+        for group in tile_groups:
+            name = group.find('Name')
+            if name.text == params['layers']:
+                req_group = group
+                break
+
+        if req_group is None:
+            grass.fatal(_("Tiled group '%s' was not found in tile service file") % params['layers'])
+
+        group_t_patts = req_group.findall('TilePattern')
+        best_patt = self._parseTilePattern(group_t_patts, bbox, region)
+
+        urls = best_patt.text.split('\n')
+        if params['urlparams']:
+            url = self._insTimeToTilePatternUrl(params['urlparams'], urls)
+        else:
+            url = urls[0]
+            for u in urls:
+                if not 'time=${' in u:
+                    url = u
+
+        url, t_bbox, width, height = self.cap_tree.gettilepatternurldata(url)
+        tile_span = {}
+        tile_span['x'] = abs(t_bbox[0] - t_bbox[2])
+        tile_span['y'] = abs(t_bbox[1] - t_bbox[3])
+                   
+        tile_pattern_bbox = req_group.find('LatLonBoundingBox')
+
+        t_patt_bbox = {}
+        for s in ['minx', 'miny', 'maxx', 'maxy']:
+            t_patt_bbox[s] = float(tile_pattern_bbox.get(s))
+
+        tile_size = {}
+        tile_size['x'] = width
+        tile_size['y'] = height
+
+        return url, tile_span, t_patt_bbox, tile_size
+
+    def _getAllTiledGroup(self, parent, tiled_groups = None):
+        """!Get all 'TileGroup' elements
+        """
+        if not tiled_groups:
+            tiled_groups = []
+
+        tiled_groups += parent.findall('TiledGroup')
+        new_groups = parent.findall('TiledGroups')
+
+        for group in new_groups:
+            self._getAllTiledGroup(group, tiled_groups)
+        return tiled_groups
+
+    def _parseTilePattern(self, group_t_patts, bbox, region):
+        """!Find best tile pattern for requested resolution.
+        """
+        res = {}
+        res['y'] = (bbox['maxy'] - bbox['miny']) / region['rows']
+        res['x'] = (bbox['maxx'] - bbox['minx']) / region['cols']
+
+        if res['x'] < res['y']:
+            comp_res = 'x'
+        else:
+            comp_res = 'y'
+
+        t_res = {}
+        best_patt = None
+        for pattern in group_t_patts:
+            url, t_bbox, width, height = self.cap_tree.gettilepatternurldata(pattern.text.split('\n')[0])
+
+            t_res['x'] = abs(t_bbox[0] - t_bbox[2]) / width
+            t_res['y'] = abs(t_bbox[1] - t_bbox[3]) / height
+
+            if best_patt is None:
+                best_res = t_res[comp_res]
+                best_patt = pattern
+                first = False
+                continue
+            
+            best_diff = best_res - res[comp_res]
+            tile_diff = t_res[comp_res] - res[comp_res]
+
+            if (best_diff < tile_diff  and  tile_diff < 0) or \
+               (best_diff > tile_diff  and  best_diff > 0):
+
+                best_res = t_res[comp_res]
+                best_patt = pattern
+
+        return best_patt
+
+    def _insTimeToTilePatternUrl(self, url_params, urls):
+        """!Time can be variable in some urls in OnEarth TMS. 
+            Insert requested time from 'urlparams' into the variable if any url of urls contains the variable.
+        """
+        url = None
+        not_sup_params = []
+        url_params_list = url_params.split('&')
+
+        for param in  url_params_list:
+            try:
+                k, v = param.split('=')
+            except ValueError:
+                grass.warning(_("Wrong form of parameter '%s' in '%s'. \n \
+                                 The parameter was ignored.") % (param, 'urlparams'))
+
+            if k != 'time':
+                not_sup_params.append(k)
+                continue
+
+            has_time_var = False
+            for url in urls: 
+                url_p_idxs = self.geturlparamidxs(url, k)
+                if not url_p_idxs:
+                    continue
+
+                url_p_value = url[url_p_idxs[0] + len(k + '=') : url_p_idxs[1]]
+
+                if url_p_value[:2] == '${'  and \
+                   url_p_value[len(url_p_value) - 1] == '}':
+                   url = url[:url_p_idxs[0]] + param + url[url_p_idxs[1]:]   
+                   has_time_var = True
+                   break
+
+            if not has_time_var:
+                grass.warning(_("Parameter '%s' in '%s' is not variable in tile pattern url.") % (k, 'urlparams'))
+
+        if not_sup_params:
+                grass.warning(_("%s driver supports only '%s' parameter in '%s'. Other parameters are ignored.") % \
+                               ('OnEarth GRASS', 'time', 'urlparams'))
+
+        return url
+
+    def _computeRequestData(self, bbox, t_patt_bbox, tile_span, tile_size):
+        """!Initialize data needed for iteration through tiles.
+        """  
+        epsilon = 1e-15
+        mat_num_bbox = {}
+
+        mat_num_bbox['min_row'] = mat_num_bbox['min_col'] = 0
+        mat_num_bbox['max_row'] = floor((t_patt_bbox['maxy'] - t_patt_bbox['miny'])/ tile_span['y'] + epsilon)  
+        mat_num_bbox['max_col'] = floor((t_patt_bbox['maxx'] - t_patt_bbox['minx'])/ tile_span['x'] + epsilon)  
+
+        BaseRequestMgr._computeRequestData(self, bbox, t_patt_bbox, self.tile_span, self.tile_size, mat_num_bbox)
+
+
+    def GetNextTile(self):
+        """!Get url for tile request from server and information for merging the tile with other tiles
+        """
+        if self.i_col > self.t_num_bbox['max_col']:
+            return None 
+
+        x_offset = self.tile_span['x'] * self.i_col
+        y_offset = self.tile_span['y'] * self.i_row
+
+        query_url = self.url[0] + "&" + "bbox=%s,%s,%s,%s" % (float(self.query_bbox['minx'] + x_offset),  
+                                                              float(self.query_bbox['miny'] - y_offset),  
+                                                              float(self.query_bbox['maxx'] + x_offset),  
+                                                              float(self.query_bbox['maxy'] - y_offset)) + self.url[1]
+
+        self.tile_ref['t_cols_offset'] = int(self.tile_size['y'] * (self.i_col - self.t_num_bbox['min_col']))
+        self.tile_ref['t_rows_offset'] = int(self.tile_size['x'] * (self.i_row - self.t_num_bbox['min_row']))
+
+        if self.i_row >= self.t_num_bbox['max_row']:
+            self.i_row =  self.t_num_bbox['min_row']
+            self.i_col += 1
+        else:
+            self.i_row += 1 
+
+        return query_url, self.tile_ref
+

Modified: grass-addons/grass6/raster/r.in.wms2/wms_gdal_drv.py
===================================================================
--- grass-addons/grass6/raster/r.in.wms2/wms_gdal_drv.py	2012-12-28 17:09:00 UTC (rev 54450)
+++ grass-addons/grass6/raster/r.in.wms2/wms_gdal_drv.py	2012-12-28 17:14:22 UTC (rev 54451)
@@ -1,5 +1,20 @@
+"""!
+ at brief GDAL WMS driver. 
+
+List of classes:
+ - wms_drv::NullDevice
+ - wms_drv::WMSGdalDrv
+
+(C) 2012 by the GRASS Development Team
+
+This program is free software under the GNU General Public License
+(>=v2). Read the file COPYING that comes with GRASS for details.
+
+ at author Stepan Turek <stepan.turek seznam.cz> (Mentor: Martin Landa)
+"""
+
 import os
-import grass.script as grass
+import grass.script as grass 
 
 try:
     from osgeo import gdal
@@ -29,25 +44,25 @@
         service.set("name","WMS")
         
         version = etree.SubElement(service, "Version")
-        version.text =self.o_wms_version
+        version.text =self.params['wms_version']
         
         server_url = etree.SubElement(service, "ServerUrl")
-        server_url.text =self.o_mapserver_url
+        server_url.text =self.params['url']
         
-        srs = etree.SubElement(service, self.projection_name)   
-        srs.text = 'EPSG:' + str(self.o_srs)
+        srs = etree.SubElement(service, self.params['proj_name'])   
+        srs.text = 'EPSG:' + str(self.params['srs'])
         
         image_format = etree.SubElement(service, "ImageFormat")
-        image_format.text = self.mime_format
+        image_format.text = self.params['format']
         
         image_format = etree.SubElement(service, "Transparent")
-        image_format.text = self.transparent
+        image_format.text = self.params['transparent']
         
         layers = etree.SubElement(service, "Layers")
-        layers.text = self.o_layers
+        layers.text = self.params['layers']
         
         styles = etree.SubElement(service, "Styles")
-        styles.text = self.o_styles
+        styles.text = self.params['styles']
         
         data_window = etree.SubElement(gdal_wms, "DataWindow")
         
@@ -75,10 +90,10 @@
         block_size_x.text = str(self.temp_map_bands_num)
         
         block_size_x = etree.SubElement(gdal_wms, "BlockSizeX")
-        block_size_x.text = str(self.tile_cols) 
+        block_size_x.text = str(self.tile_size['cols']) 
         
         block_size_y = etree.SubElement(gdal_wms, "BlockSizeY")
-        block_size_y.text = str(self.tile_rows)
+        block_size_y.text = str(self.tile_size['rows'])
         
         xml_file = self._tempfile()
         
@@ -97,9 +112,11 @@
 
         # GDAL WMS driver does not flip geographic coordinates 
         # according to WMS standard 1.3.0.
-        if self.flip_coords and self.o_wms_version == "1.3.0":
+        if ("+proj=latlong" in self.proj_srs or \
+            "+proj=longlat" in self.proj_srs) and \
+            self.params['wms_version'] == "1.3.0":
             grass.warning(_("If module will not be able to fetch the data in this\
-                           geographic projection, \n try flag -d or use WMS version 1.1.1."))
+                           geographic projection, \n try 'WMS_GRASS' driver or use WMS version 1.1.1."))
 
         self._debug("_download", "started")
         



More information about the grass-commit mailing list