[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