[GRASS-SVN] r54454 - in grass/trunk/gui: scripts wxpython/core wxpython/gcp wxpython/gui_core wxpython/lmgr wxpython/mapdisp
svn_grass at osgeo.org
svn_grass at osgeo.org
Sat Dec 29 02:45:35 PST 2012
Author: martinl
Date: 2012-12-29 02:45:35 -0800 (Sat, 29 Dec 2012)
New Revision: 54454
Added:
grass/trunk/gui/scripts/d.wms.py
grass/trunk/gui/wxpython/core/ws.py
Modified:
grass/trunk/gui/wxpython/core/gconsole.py
grass/trunk/gui/wxpython/core/render.py
grass/trunk/gui/wxpython/core/utils.py
grass/trunk/gui/wxpython/gcp/mapdisplay.py
grass/trunk/gui/wxpython/gui_core/mapwindow.py
grass/trunk/gui/wxpython/lmgr/frame.py
grass/trunk/gui/wxpython/lmgr/layertree.py
grass/trunk/gui/wxpython/mapdisp/frame.py
grass/trunk/gui/wxpython/mapdisp/mapwindow.py
grass/trunk/gui/wxpython/mapdisp/statusbar.py
Log:
wxGUI: add support for WMS layers (work in progress)
author: Stepan Turek
Added: grass/trunk/gui/scripts/d.wms.py
===================================================================
--- grass/trunk/gui/scripts/d.wms.py (rev 0)
+++ grass/trunk/gui/scripts/d.wms.py 2012-12-29 10:45:35 UTC (rev 54454)
@@ -0,0 +1,208 @@
+#!/usr/bin/env python
+#
+############################################################################
+#
+# MODULE: d.wms
+#
+# AUTHOR(S): Stepan Turek <stepan.turek seznam.cz> (mentor: Martin Landa)
+#
+# PURPOSE: Wrapper for wxGUI to add WMS into layer tree
+#
+# COPYRIGHT: (C) 2012 by Stepan Turek, and 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.
+#
+#############################################################################
+
+#%module
+#% description: Downloads and displays data from WMS server.
+#% keywords: raster
+#% keywords: import
+#% keywords: wms
+#%end
+
+#%option
+#% key: url
+#% type: string
+#% description: URL of WMS server
+#% required: yes
+#%end
+
+#%option
+#% key: layers
+#% type: string
+#% description: Layers to request from WMS server
+#% multiple: yes
+#% required: yes
+#%end
+
+#%option
+#% key: map
+#% type: string
+#% description: Name for output WMS layer in the layer tree
+#% required : yes
+#%end
+
+#%option
+#% key: srs
+#% type: integer
+#% description: EPSG number of source projection for request
+#% answer:4326
+#% guisection: Request properties
+#%end
+
+#%option
+#% key: wms_version
+#% type: string
+#% description: WMS standard
+#% options: 1.1.1,1.3.0
+#% answer: 1.1.1
+#% guisection: Request properties
+#%end
+
+#%option
+#% key: format
+#% type: string
+#% description: Image format requested from the server
+#% options: geotiff,tiff,jpeg,gif,png
+#% answer: geotiff
+#% guisection: Request properties
+#%end
+
+#%option
+#% key: method
+#% type: string
+#% description: Reprojection method to use
+#% options:near,bilinear,cubic,cubicspline
+#% answer:near
+#% guisection: Request properties
+#%end
+
+#%option
+#% key: maxcols
+#% type:integer
+#% description: Maximum columns to request at a time
+#% answer:400
+#% guisection: Request properties
+#%end
+
+#%option
+#% key: maxrows
+#% type: integer
+#% description: Maximum rows to request at a time
+#% answer: 300
+#% guisection: Request properties
+#%end
+
+#%option
+#% key: urlparams
+#% type:string
+#% 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
+#% multiple: yes
+#% guisection: Map style
+#%end
+
+#%option
+#% key: bgcolor
+#% type: string
+#% description: Color of map background
+#% guisection: Map style
+#%end
+
+#%flag
+#% key: o
+#% description: Don't request transparent data
+#% guisection: Map style
+#%end
+
+#%option
+#% key: driver
+#% type:string
+#% description: Driver for communication with server
+#% descriptions: WMS_GDAL;Download data using GDAL WMS driver;WMS_GRASS;Download data using native GRASS-WMS driver;WMTS_GRASS;Download data using native GRASS-WMTS driver;OnEarth_GRASS;Download data using native GRASS-OnEarth driver;
+#% 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 to load
+
+import os
+import sys
+
+from grass.script import core as grass
+
+sys.path.append(os.path.join(os.getenv("GISBASE"), "etc", "r.in.wms"))
+
+def GetRegion():
+ """!Parse region from GRASS_REGION env var.
+ """
+ region = os.environ["GRASS_REGION"]
+ conv_reg_vals = {'east' : 'e',
+ 'north' : 'n',
+ 'west' : 'w',
+ 'south' : 's',
+ 'rows' : 'rows',
+ 'cols' : 'cols',
+ 'e-w resol' : 'ewres',
+ 'n-s resol' : 'nsres'}
+
+ keys_to_convert = conv_reg_vals.keys()
+
+ conv_region = {}
+ region = region.split(';')
+
+ for r in region:
+ r = r.split(':')
+ r[0] = r[0].strip()
+
+ if r[0] in keys_to_convert:
+ conv_region[conv_reg_vals[r[0]]] = float(r[1])
+
+ return conv_region
+
+def main():
+ options['region'] = GetRegion()
+
+ if 'GRASS' in options['driver']:
+ grass.debug("Using GRASS driver")
+ from wms_drv import WMSDrv
+ wms = WMSDrv()
+ elif 'GDAL' in options['driver']:
+ grass.debug("Using GDAL WMS driver")
+ from wms_gdal_drv import WMSGdalDrv
+ wms = WMSGdalDrv()
+
+ temp_map = wms.GetMap(options, flags)
+ os.rename(temp_map, os.environ["GRASS_PNGFILE"])
+
+ return 0
+
+if __name__ == "__main__":
+ options, flags = grass.parser()
+ sys.exit(main())
Property changes on: grass/trunk/gui/scripts/d.wms.py
___________________________________________________________________
Added: svn:mime-type
+ text/x-python
Added: svn:eol-style
+ native
Modified: grass/trunk/gui/wxpython/core/gconsole.py
===================================================================
--- grass/trunk/gui/wxpython/core/gconsole.py 2012-12-28 21:49:46 UTC (rev 54453)
+++ grass/trunk/gui/wxpython/core/gconsole.py 2012-12-29 10:45:35 UTC (rev 54454)
@@ -79,6 +79,8 @@
self.setDaemon(True)
+ self.requestCmd = None
+
self.receiver = receiver
self._want_abort_all = False
@@ -99,6 +101,10 @@
return CmdThread.requestId
+ def GetId(self):
+ """!Get id for next command"""
+ return CmdThread.requestId + 1
+
def SetId(self, id):
"""!Set starting id"""
CmdThread.requestId = id
@@ -135,7 +141,7 @@
time.sleep(.1)
self.requestCmd = vars()['callable'](*args, **kwds)
- if self._want_abort_all:
+ if self._want_abort_all and self.requestCmd is not None:
self.requestCmd.abort()
if self.requestQ.empty():
self._want_abort_all = False
@@ -197,11 +203,11 @@
"""!Abort command(s)"""
if abortall:
self._want_abort_all = True
- self.requestCmd.abort()
+ if self.requestCmd is not None:
+ self.requestCmd.abort()
if self.requestQ.empty():
self._want_abort_all = False
-
class GStdout:
"""!GConsole standard output
Modified: grass/trunk/gui/wxpython/core/render.py
===================================================================
--- grass/trunk/gui/wxpython/core/render.py 2012-12-28 21:49:46 UTC (rev 54453)
+++ grass/trunk/gui/wxpython/core/render.py 2012-12-29 10:45:35 UTC (rev 54454)
@@ -3,6 +3,9 @@
@brief Rendering map layers and overlays into map composition image.
+ at todo Implement RenderManager also for other layers (see WMS
+implementation for details)
+
Classes:
- render::Layer
- render::MapLayer
@@ -33,6 +36,7 @@
from grass.script import core as grass
from core import utils
+from core.ws import RenderWMSMgr
from core.gcmd import GException, GError, RunCommand
from core.debug import Debug
from core.settings import UserSettings
@@ -47,8 +51,10 @@
- For map layer use MapLayer class.
- For overlays use Overlay class.
+
+ @todo needs refactoring (avoid parent)
"""
- def __init__(self, type, cmd, name = None,
+ def __init__(self, type, cmd, parent, name = None,
active = True, hidden = False, opacity = 1.0):
"""!Create new instance
@@ -62,7 +68,24 @@
@param hidden layer is hidden, won't be listed in Layer Manager if True
@param opacity layer opacity <0;1>
"""
- self.type = type
+ self.parent = parent
+
+ # generated file for each layer
+ if USE_GPNMCOMP or type == 'overlay':
+ tmpfile = tempfile.mkstemp()[1]
+ self.maskfile = tmpfile + '.pgm'
+ if type == 'overlay':
+ self.mapfile = tmpfile + '.png'
+ else:
+ self.mapfile = tmpfile + '.ppm'
+ grass.try_remove(tmpfile)
+ else:
+ self.mapfile = self.maskfile = None
+
+ # stores class which manages rendering instead of simple command - e. g. wms
+ self.renderMgr = None
+
+ self.SetType(type)
self.name = name
if self.type == 'command':
@@ -76,25 +99,13 @@
self.hidden = hidden
self.opacity = opacity
- self.force_render = True
+ self.forceRender = True
Debug.msg (3, "Layer.__init__(): type=%s, cmd='%s', name=%s, " \
"active=%d, opacity=%d, hidden=%d" % \
(self.type, self.GetCmd(string = True), self.name, self.active,
self.opacity, self.hidden))
-
- # generated file for each layer
- if USE_GPNMCOMP or self.type == 'overlay':
- tmpfile = tempfile.mkstemp()[1]
- self.maskfile = tmpfile + '.pgm'
- if self.type == 'overlay':
- self.mapfile = tmpfile + '.png'
- else:
- self.mapfile = tmpfile + '.ppm'
- grass.try_remove(tmpfile)
- else:
- self.mapfile = self.maskfile = None
-
+
def __del__(self):
Debug.msg (3, "Layer.__del__(): layer=%s, cmd='%s'" %
(self.name, self.GetCmd(string = True)))
@@ -120,13 +131,12 @@
'vector','thememap','themechart',
'grid', 'geodesic', 'rhumb', 'labels',
'command', 'rastleg','maplegend',
- 'overlay')
+ 'overlay', 'wms')
if self.type not in layertypes:
raise GException(_("<%(name)s>: layer type <%(type)s> is not supported") % \
{'type' : self.type, 'name' : self.name})
- # start monitor
if self.mapfile:
os.environ["GRASS_PNGFILE"] = self.mapfile
@@ -135,10 +145,7 @@
if self.type == 'command':
read = False
for c in self.cmd:
- ret, msg = RunCommand(c[0],
- getErrorMsg = True,
- quiet = True,
- **c[1])
+ ret, msg = self._runCommand(c)
if ret != 0:
break
if not read:
@@ -146,11 +153,7 @@
os.environ["GRASS_PNG_READ"] = "FALSE"
else:
- ret, msg = RunCommand(self.cmd[0],
- getErrorMsg = True,
- quiet = True,
- **self.cmd[1])
-
+ ret, msg = self._runCommand(self.cmd)
if ret != 0:
sys.stderr.write(_("Command '%s' failed\n") % self.GetCmd(string = True))
if msg:
@@ -169,10 +172,27 @@
if self.mapfile and "GRASS_PNGFILE" in os.environ:
del os.environ["GRASS_PNGFILE"]
- self.force_render = False
+ self.forceRender = False
return self.mapfile
+ def _runCommand(self, cmd):
+ """!Run command to render data
+
+ @todo catch error for wms (?)
+ """
+ if self.type == 'wms':
+ ret = 0
+ msg = ''
+ self.renderMgr.Render(cmd)
+ else:
+ ret, msg = RunCommand(self.cmd[0],
+ getErrorMsg = True,
+ quiet = True,
+ **self.cmd[1])
+
+ return ret, msg
+
def GetCmd(self, string = False):
"""!Get GRASS command as list of string.
@@ -238,16 +258,21 @@
"""!Check if layer is activated for rendering"""
return self.active
- def SetType(self, type):
+ def SetType(self, ltype):
"""!Set layer type"""
- if type not in ('raster', '3d-raster', 'vector',
+ if ltype not in ('raster', '3d-raster', 'vector',
'overlay', 'command',
'shaded', 'rgb', 'his', 'rastarrow', 'rastnum','maplegend',
'thememap', 'themechart', 'grid', 'labels',
- 'geodesic','rhumb'):
- raise GException(_("Unsupported map layer type '%s'") % type)
+ 'geodesic','rhumb', 'wms'):
+ raise GException(_("Unsupported map layer type '%s'") % ltype)
- self.type = type
+ if ltype == 'wms' and not isinstance(self.renderMgr, RenderWMSMgr):
+ self.renderMgr = RenderWMSMgr(self, self.mapfile, self.maskfile)
+ elif self.type == 'wms' and ltype != 'wms':
+ self.renderMgr = None
+
+ self.type = ltype
def SetName(self, name):
"""!Set layer name"""
@@ -281,10 +306,24 @@
Debug.msg(3, "Layer.SetCmd(): cmd='%s'" % self.GetCmd(string = True))
# for re-rendering
- self.force_render = True
-
+ self.forceRender = True
+
+ def IsDownloading(self):
+ """!Is data downloading from web server e. g. wms"""
+ if self.renderMgr is None:
+ return False
+ else:
+ return self.renderMgr.IsDownloading()
+
+ def AbortDownload(self):
+ """!Abort downloading data"""
+ if self.renderMgr is None:
+ return
+ else:
+ self.renderMgr.Abort()
+
class MapLayer(Layer):
- def __init__(self, type, cmd, name = None,
+ def __init__(self, type, cmd, parent, name = None,
active = True, hidden = False, opacity = 1.0):
"""!Represents map layer in the map canvas
@@ -296,7 +335,7 @@
@param hidden layer is hidden, won't be listed in Layer Manager if True
@param opacity layer opacity <0;1>
"""
- Layer.__init__(self, type, cmd, name,
+ Layer.__init__(self, type, cmd, parent, name,
active, hidden, opacity)
def GetMapset(self):
@@ -314,7 +353,7 @@
return self.name
class Overlay(Layer):
- def __init__(self, id, type, cmd,
+ def __init__(self, id, type, cmd, parent,
active = True, hidden = True, opacity = 1.0):
"""!Represents overlay displayed in map canvas
@@ -326,9 +365,8 @@
@param hidden layer is hidden, won't be listed in Layer Manager if True
@param opacity layer opacity <0;1>
"""
- Layer.__init__(self, 'overlay', cmd, type,
+ Layer.__init__(self, 'overlay', cmd, parent, type,
active, hidden, opacity)
-
self.id = id
class Map(object):
@@ -362,6 +400,9 @@
self._initGisEnv() # g.gisenv
self.GetWindow()
+ # reference to mapWindow, which contains this Map instance
+ self.mapWin = None
+
# GRASS environment variable (for rendering)
self.env = {"GRASS_BACKGROUNDCOLOR" : "FFFFFF",
"GRASS_COMPRESSION" : "0",
@@ -376,6 +417,9 @@
# projection info
self.projinfo = self._projInfo()
+ # is some layer being downloaded?
+ self.downloading = False
+
def _runCommand(self, cmd, **kwargs):
"""!Run command in environment defined by self.gisrc if
defined"""
@@ -834,28 +878,37 @@
masks = list()
opacities = list()
# render map layers
- ilayer = 1
if overlaysOnly:
layers = self.overlays
else:
layers = self.layers + self.overlays
+ self.downloading = False
+ #event = wxUpdateProgressBar(layer = None, map = self)
+ # When using Event for progress update, the event is handled after
+ # rendering. Maybe there exists some better solution than calling
+ # the method directly.
+ mapWindow.GetProgressBar().UpdateProgress(None, self)
+ #wx.PostEvent(mapWindow, event)
for layer in layers:
# skip non-active map layers
if not layer or not layer.active:
continue
# render
- if force or layer.force_render:
+ if force or layer.forceRender:
if not layer.Render():
continue
-
+
+ if layer.IsDownloading():
+ self.downloading = True
if mapWindow:
# update progress bar
### wx.SafeYield(mapWindow)
- event = wxUpdateProgressBar(value = ilayer)
- wx.PostEvent(mapWindow, event)
-
+ mapWindow.GetProgressBar().UpdateProgress(layer, self)
+ #event = wxUpdateProgressBar(layer = layer, map = self)
+ #wx.PostEvent(mapWindow, event)
+
# skip map layers when rendering fails
if not os.path.exists(layer.mapfile):
continue
@@ -867,7 +920,6 @@
opacities.append(str(layer.opacity))
Debug.msg(3, "Map.Render() type=%s, layer=%s " % (layer.type, layer.name))
- ilayer += 1
return maps, masks, opacities
@@ -985,7 +1037,7 @@
l_opacity = 0
elif l_opacity > 1:
l_opacity = 1
- layer = MapLayer(type = type, name = name, cmd = command,
+ layer = MapLayer(type = type, name = name, cmd = command, parent = self,
active = l_active, hidden = l_hidden, opacity = l_opacity)
# add maplayer to the list of layers
@@ -1052,8 +1104,8 @@
layerNameList = ""
for layer in self.layers:
- if layer.name:
- layerNameList += layer.name + ','
+ if layer.GetName():
+ layerNameList += layer.GetName() + ','
Debug.msg (4, "Map.ReoderLayers(): layers=%s" % \
(layerNameList))
@@ -1192,7 +1244,7 @@
@return None on failure
"""
Debug.msg (2, "Map.AddOverlay(): cmd=%s, render=%d" % (command, l_render))
- overlay = Overlay(id = id, type = type, cmd = command,
+ overlay = Overlay(id = id, type = type, cmd = command, parent = self,
active = l_active, hidden = l_hidden, opacity = l_opacity)
# add maplayer to the list of layers
@@ -1300,5 +1352,17 @@
def RenderOverlays(self, force):
"""!Render overlays only (for nviz)"""
for layer in self.overlays:
- if force or layer.force_render:
+ if force or layer.forceRender:
layer.Render()
+
+ def SetParentMapWindow(self, mapWin):
+ """!Set reference to parent MapWindow"""
+ self.mapWin = mapWin
+
+ def GetParentMapWindow(self):
+ """!Get reference to parent MapWindow"""
+ return self.mapWin
+
+ def IsDownloading(self):
+ """!Is any layer downloading data from web server e. g. wms"""
+ return self.downloading
Modified: grass/trunk/gui/wxpython/core/utils.py
===================================================================
--- grass/trunk/gui/wxpython/core/utils.py 2012-12-28 21:49:46 UTC (rev 54453)
+++ grass/trunk/gui/wxpython/core/utils.py 2012-12-29 10:45:35 UTC (rev 54454)
@@ -462,32 +462,40 @@
return coef * (float(d) + fm + fs)
def GetCmdString(cmd):
- """
- Get GRASS command as string.
+ """!Get GRASS command as string.
- @param cmd GRASS command given as dictionary
+ @param cmd GRASS command given as tuple
@return command string
"""
- scmd = ''
+ return ' '.join(CmdTupleToList(cmd))
+
+def CmdTupleToList(cmd):
+ """!Convert command tuple to list.
+
+ @param cmd GRASS command given as tuple
+
+ @return command in list
+ """
+ cmdList = []
if not cmd:
- return scmd
+ return cmdList
- scmd = cmd[0]
+ cmdList.append(cmd[0])
if 'flags' in cmd[1]:
for flag in cmd[1]['flags']:
- scmd += ' -' + flag
+ cmdList.append('-' + flag)
for flag in ('verbose', 'quiet', 'overwrite'):
if flag in cmd[1] and cmd[1][flag] is True:
- scmd += ' --' + flag
+ cmdList.append('--' + flag)
for k, v in cmd[1].iteritems():
if k in ('flags', 'verbose', 'quiet', 'overwrite'):
continue
- scmd += ' %s=%s' % (k, v)
+ cmdList.append('%s=%s' % (k, v))
- return scmd
+ return cmdList
def CmdToTuple(cmd):
"""!Convert command list to tuple for gcmd.RunCommand()"""
Added: grass/trunk/gui/wxpython/core/ws.py
===================================================================
--- grass/trunk/gui/wxpython/core/ws.py (rev 0)
+++ grass/trunk/gui/wxpython/core/ws.py 2012-12-29 10:45:35 UTC (rev 54454)
@@ -0,0 +1,347 @@
+"""!
+ at package core.ws
+
+ at brief Fetching and preparation of web service data for rendering.
+
+Note: Currently only WMS is implemented.
+
+Classes:
+ - ws::RenderWMSMgr
+ - ws::StdErr
+ - ws::GDALRasterMerger
+
+(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 sys
+
+import wx
+
+from grass.script import core as grass
+
+from core import utils
+from core.debug import Debug
+
+from core.gconsole import CmdThread, EVT_CMD_DONE
+from core.gcmd import GException
+
+try:
+ haveGdal = True
+ from osgeo import gdal
+ from osgeo import gdalconst
+except ImportError:
+ haveGdal = False
+
+class RenderWMSMgr(wx.EvtHandler):
+ """!Fetch and prepare WMS data for rendering.
+
+ @todo statusbar: updtade by event or call method ???
+ """
+ def __init__(self, parent, mapfile, maskfile):
+ if not haveGdal:
+ sys.stderr.write(_("Unable to load GDAL Python bindings.\n"\
+ "WMS layers can not be displayed without the bindings.\n"))
+
+ self.parent = parent
+
+ # thread for d.wms commands
+ self.thread = CmdThread(self)
+ wx.EvtHandler.__init__(self)
+ self.Bind(EVT_CMD_DONE, self.OnDataFetched)
+
+ self.downloading = False
+ self.renderedRegion = None
+ self.updateMap = True
+
+ self.cmdStdErr = StdErr()
+
+ self.mapfile = mapfile
+ self.maskfile = maskfile
+ self.tempMap = grass.tempfile()
+ self.dstSize = {}
+
+ def __del__(self):
+ grass.try_remove(self.tempMap)
+
+ def Render(self, cmd):
+ """!If it is needed, download missing WMS data.
+
+ @todo lmgr deletes mapfile and maskfile when order of layers
+ was changed (drag and drop) - if deleted, fetch data again
+ """
+ if not haveGdal:
+ return
+
+ self.dstSize['cols'] = int(os.environ["GRASS_WIDTH"])
+ self.dstSize['rows'] = int(os.environ["GRASS_HEIGHT"])
+
+ region = self._getRegionDict()
+ self._fitAspect(region, self.dstSize)
+
+ self.updateMap = True
+ fetchData = False
+ zoomChanged = False
+
+ if self.renderedRegion is None:
+ fetchData = True
+ else:
+ for c in ['north', 'south', 'east', 'west']:
+ if self.renderedRegion and \
+ region[c] != self.renderedRegion[c]:
+ fetchData = True
+ break
+
+ for c in ['e-w resol', 'n-s resol']:
+ if self.renderedRegion and \
+ region[c] != self.renderedRegion[c]:
+ zoomChanged = True
+ break
+
+ if fetchData:
+ self.renderedRegion = region
+
+ grass.try_remove(self.mapfile)
+ grass.try_remove(self.tempMap)
+
+ self.currentPid = self.thread.GetId()
+ self.thread.abort()
+ self.downloading = True
+
+ cmdList = utils.CmdTupleToList(cmd)
+
+ if Debug.GetLevel() < 3:
+ cmdList.append('--quiet')
+
+ tempPngfile = os.environ["GRASS_PNGFILE"]
+ os.environ["GRASS_PNGFILE"] = self.tempMap
+
+ tempRegion = os.environ["GRASS_REGION"]
+ os.environ["GRASS_REGION"] = self._createRegionStr(region)
+
+ self.thread.RunCmd(cmdList, env = os.environ.copy(), stderr = self.cmdStdErr)
+
+ os.environ["GRASS_PNGFILE"] = tempPngfile
+ os.environ["GRASS_REGION"] = tempRegion
+
+ def OnDataFetched(self, event):
+ """!Fetch data
+
+ @todo ?
+ @todo needs refactoring - self.parent.parent.mapWin.UpdateMap
+ """
+ if event.pid != self.currentPid:
+ return
+ self.downloading = False
+ if not self.updateMap:
+ # TODO
+ self.parent.parent.GetParentMapWindow().frame.GetProgressBar().UpdateProgress(self.parent, self.parent.parent)
+ self.renderedRegion = None
+ return
+
+ self.mapMerger = GDALRasterMerger(targetFile = self.mapfile, region = self.renderedRegion,
+ bandsNum = 3, gdalDriver = 'PNM', fillValue = 0)
+ self.mapMerger.AddRasterBands(self.tempMap, {1 : 1, 2 : 2, 3 : 3})
+ del self.mapMerger
+
+ self.maskMerger = GDALRasterMerger(targetFile = self.maskfile, region = self.renderedRegion,
+ bandsNum = 1, gdalDriver = 'PNM', fillValue = 0)
+ #{4 : 1} alpha channel (4) to first and only channel (1) in mask
+ self.maskMerger.AddRasterBands(self.tempMap, {4 : 1})
+ del self.maskMerger
+
+ self.parent.parent.GetParentMapWindow().UpdateMap(render = True)
+
+ def _getRegionDict(self):
+ """!Parse string from GRASS_REGION env variable into dict.
+ """
+ region = {}
+ parsedRegion = os.environ["GRASS_REGION"].split(';')
+ for r in parsedRegion:
+ r = r.split(':')
+ r[0] = r[0].strip()
+ if len(r) < 2:
+ continue
+ try:
+ if r[0] in ['cols', 'rows']:
+ region[r[0]] = int(r[1])
+ else:
+ region[r[0]] = float(r[1])
+ except ValueError:
+ region[r[0]] = r[1]
+
+ return region
+
+ def _createRegionStr(self, region):
+ """!Create string for GRASS_REGION env variable from dict created by _getRegionDict.
+ """
+ regionStr = ''
+ for k, v in region.iteritems():
+ item = k + ': ' + str(v)
+ if regionStr:
+ regionStr += '; '
+ regionStr += item
+
+ return regionStr
+
+ def IsDownloading(self):
+ """!Is it downloading any data from WMS server?
+ """
+ return self.downloading
+
+ def _fitAspect(self, region, size):
+ """!Compute region parameters to have direction independent resolution.
+ """
+ if region['n-s resol'] > region['e-w resol']:
+ delta = region['n-s resol'] * size['cols'] / 2
+
+ center = (region['east'] - region['west'])/2
+
+ region['east'] = center + delta + region['west']
+ region['west'] = center - delta + region['west']
+ region['e-w resol'] = region['n-s resol']
+
+ else:
+ delta = region['e-w resol'] * size['rows'] / 2
+
+ center = (region['north'] - region['south'])/2
+
+ region['north'] = center + delta + region['south']
+ region['south'] = center - delta + region['south']
+ region['n-s resol'] = region['e-w resol']
+
+ def Abort(self):
+ """!Abort process"""
+ self.updateMap = False
+ self.thread.abort(abortall = True)
+
+class StdErr:
+ """!Redirect error output according to debug mode.
+
+ @todo: replace or move to the other module (gconsole???)
+ """
+ def flush(self):
+ pass
+
+ def write(self, s):
+ if "GtkPizza" in s:
+ return
+ if Debug.GetLevel() == 0:
+ message = ''
+ for line in s.splitlines():
+ if len(line) == 0:
+ continue
+ if 'GRASS_INFO_ERROR' in line:
+ message += line.split(':', 1)[1].strip() + '\n'
+ elif 'ERROR:' in line:
+ message = line
+ if message:
+ sys.stderr.write(message)
+ message = ''
+ sys.stderr.flush()
+
+ elif Debug.GetLevel() >= 1:
+ for line in s.splitlines():
+ if len(line) == 0:
+ continue
+ Debug.msg(3, line)
+
+class GDALRasterMerger:
+ """!Merge rasters.
+
+ Based on gdal_merge.py utility.
+ """
+ def __init__(self, targetFile, region, bandsNum, gdalDriver, fillValue = None):
+ """!Create raster for merging.
+ """
+ self.gdalDrvType = gdalDriver
+
+ nsRes = (region['south'] - region['north']) / region['rows']
+ ewRes = (region['east'] - region['west']) / region['cols']
+
+ self.tGeotransform = [region['west'], ewRes, 0, region['north'], 0, nsRes]
+
+ self.tUlx, self.tUly, self.tLrx, self.tLry = self._getCorners(self.tGeotransform, region)
+
+ driver = gdal.GetDriverByName(self.gdalDrvType)
+ self.tDataset = driver.Create(targetFile, region['cols'], region['rows'], bandsNum, gdal.GDT_Byte)
+
+ if fillValue is not None:
+ # fill raster bands with a constant value
+ for iBand in range(1, self.tDataset.RasterCount + 1):
+ self.tDataset.GetRasterBand(iBand).Fill(fillValue)
+
+ def AddRasterBands(self, sourceFile, sTBands):
+ """!Add raster bands from sourceFile into the merging raster.
+ """
+ sDataset = gdal.Open(sourceFile, gdal.GA_ReadOnly)
+ if sDataset is None:
+ return
+
+ sGeotransform = sDataset.GetGeoTransform()
+
+ sSize = {
+ 'rows' : sDataset.RasterYSize,
+ 'cols' : sDataset.RasterXSize
+ }
+
+ sUlx, sUly, sLrx, sLry = self._getCorners(sGeotransform, sSize)
+
+ # figure out intersection region
+ tIntsctUlx = max(self.tUlx,sUlx)
+ tIntsctLrx = min(self.tLrx,sLrx)
+ if self.tGeotransform[5] < 0:
+ tIntsctUly = min(self.tUly,sUly)
+ tIntsctLry = max(self.tLry,sLry)
+ else:
+ tIntsctUly = max(self.tUly,sUly)
+ tIntsctLry = min(self.tLry,sLry)
+
+ # do they even intersect?
+ if tIntsctUlx >= tIntsctLrx:
+ return
+ if self.tGeotransform[5] < 0 and tIntsctUly <= tIntsctLry:
+ return
+ if self.tGeotransform[5] > 0 and tIntsctUly >= tIntsctLry:
+ return
+
+
+ # compute target window in pixel coordinates.
+ tXoff = int((tIntsctUlx - self.tGeotransform[0]) / self.tGeotransform[1] + 0.1)
+ tYoff = int((tIntsctUly - self.tGeotransform[3]) / self.tGeotransform[5] + 0.1)
+ tXsize = int((tIntsctLrx - self.tGeotransform[0])/self.tGeotransform[1] + 0.5) - tXoff
+ tYsize = int((tIntsctLry - self.tGeotransform[3])/self.tGeotransform[5] + 0.5) - tYoff
+
+ if tXsize < 1 or tYsize < 1:
+ return
+
+ # Compute source window in pixel coordinates.
+ sXoff = int((tIntsctUlx - sGeotransform[0]) / sGeotransform[1])
+ sYoff = int((tIntsctUly - sGeotransform[3]) / sGeotransform[5])
+ sXsize = int((tIntsctLrx - sGeotransform[0]) / sGeotransform[1] + 0.5) - sXoff
+ sYsize = int((tIntsctLry - sGeotransform[3]) / sGeotransform[5] + 0.5) - sYoff
+
+ if sXsize < 1 or sYsize < 1:
+ return
+
+ for sBandNnum, tBandNum in sTBands.iteritems():
+ bandData = sDataset.GetRasterBand(sBandNnum).ReadRaster(sXoff, sYoff, sXsize,
+ sYsize, tXsize, tYsize, gdal.GDT_Byte)
+ self.tDataset.GetRasterBand(tBandNum).WriteRaster(tXoff, tYoff, tXsize, tYsize, bandData,
+ tXsize, tYsize, gdal.GDT_Byte)
+
+ def _getCorners(self, geoTrans, size):
+
+ ulx = geoTrans[0]
+ uly = geoTrans[3]
+ lrx = geoTrans[0] + size['cols'] * geoTrans[1]
+ lry = geoTrans[3] + size['rows'] * geoTrans[5]
+
+ return ulx, uly, lrx, lry
+
+ def __del__(self):
+ self.tDataset = None
Property changes on: grass/trunk/gui/wxpython/core/ws.py
___________________________________________________________________
Added: svn:mime-type
+ text/x-python
Added: svn:eol-style
+ native
Modified: grass/trunk/gui/wxpython/gcp/mapdisplay.py
===================================================================
--- grass/trunk/gui/wxpython/gcp/mapdisplay.py 2012-12-28 21:49:46 UTC (rev 54453)
+++ grass/trunk/gui/wxpython/gcp/mapdisplay.py 2012-12-29 10:45:35 UTC (rev 54454)
@@ -256,7 +256,7 @@
"""
Update progress bar info
"""
- self.GetProgressBar().SetValue(event.value)
+ self.GetProgressBar().UpdateProgress(event.layer, event.map)
event.Skip()
Modified: grass/trunk/gui/wxpython/gui_core/mapwindow.py
===================================================================
--- grass/trunk/gui/wxpython/gui_core/mapwindow.py 2012-12-28 21:49:46 UTC (rev 54453)
+++ grass/trunk/gui/wxpython/gui_core/mapwindow.py 2012-12-29 10:45:35 UTC (rev 54454)
@@ -39,6 +39,7 @@
def __init__(self, parent, giface, Map, frame, **kwargs):
self.parent = parent
self.Map = Map
+ self.Map.SetParentMapWindow(self)
self.frame = frame
self._giface = giface
Modified: grass/trunk/gui/wxpython/lmgr/frame.py
===================================================================
--- grass/trunk/gui/wxpython/lmgr/frame.py 2012-12-28 21:49:46 UTC (rev 54453)
+++ grass/trunk/gui/wxpython/lmgr/frame.py 2012-12-29 10:45:35 UTC (rev 54454)
@@ -608,7 +608,8 @@
'd.rhumbline' : 'rhumb',
'd.labels' : 'labels',
'd.barscale' : 'barscale',
- 'd.redraw' : 'redraw'}[command[0]]
+ 'd.redraw' : 'redraw',
+ 'd.wms' : 'wms'}[command[0]]
except KeyError:
GMessage(parent = self,
message = _("Command '%s' not yet implemented in the WxGUI. "
@@ -1502,8 +1503,7 @@
"""!Import data from OGC WMS server"""
from ogc_services.wms import WMSDialog
dlg = WMSDialog(parent = self)
- dlg.CenterOnScreen()
-
+ dlg.CenterOnScreen()
if dlg.ShowModal() == wx.ID_OK: # -> import layers
layers = dlg.GetLayers()
@@ -1529,7 +1529,7 @@
dlg.Destroy()
-
+
def OnShowAttributeTable(self, event, selection = None):
"""!Show attribute table of the given vector map layer
"""
Modified: grass/trunk/gui/wxpython/lmgr/layertree.py
===================================================================
--- grass/trunk/gui/wxpython/lmgr/layertree.py 2012-12-28 21:49:46 UTC (rev 54453)
+++ grass/trunk/gui/wxpython/lmgr/layertree.py 2012-12-29 10:45:35 UTC (rev 54454)
@@ -91,8 +91,10 @@
'addRast3d' : MetaIcon(img = 'layer-raster3d-add',
label = _('Add 3D raster map layer'),
desc = _('Note that 3D raster data are rendered only in 3D view mode')),
+ 'wsImport' : MetaIcon(img = 'layer-wms-add',
+ label = _('Add WMS layer.')),
'layerOptions' : MetaIcon(img = 'options',
- label = _('Set options')),
+ label = _('Set options'))
}
class LayerTree(treemixin.DragAndDrop, CT.CustomTreeCtrl):
@@ -224,6 +226,9 @@
trgif = LMIcons["addCmd"].GetBitmap(bmpsize)
self.cmd_icon = il.Add(trgif)
+
+ trgif = LMIcons["wsImport"].GetBitmap(bmpsize)
+ self.ws_icon = il.Add(trgif)
self.AssignImageList(il)
@@ -941,7 +946,10 @@
self.SetItemImage(layer, self.folder, CT.TreeItemIcon_Normal)
self.SetItemImage(layer, self.folder_open, CT.TreeItemIcon_Expanded)
self.SetItemText(layer, grouptext)
-
+ elif ltype == 'wms':
+ self.SetItemImage(layer, self.ws_icon)
+ self.SetItemText(layer, '%s %s' % (_('wms'), label))
+
self.first = False
if ltype != 'group':
@@ -1022,11 +1030,7 @@
else:
if ltype == 'group':
self.OnRenameLayer(None)
-
- # updated progress bar range (mapwindow statusbar)
- if checked:
- self.mapdisplay.GetProgressBar().SetRange(len(self.Map.GetListOfLayers(l_active = True)))
-
+
return layer
def PropertiesDialog(self, layer, show = True):
@@ -1104,7 +1108,10 @@
elif ltype == 'labels':
cmd = ['d.labels']
-
+
+ elif ltype == 'wms':
+ cmd = ['d.wms']
+
if cmd:
GUI(parent = self, centreOnParent = False).ParseCommand(cmd,
completed = (self.GetOptData,layer,params))
@@ -1156,9 +1163,6 @@
if self.mapdisplay.GetToolbar('vdigit'):
self.mapdisplay.toolbars['vdigit'].UpdateListOfLayers (updateTool = True)
- # update progress bar range (mapwindow statusbar)
- self.mapdisplay.GetProgressBar().SetRange(len(self.Map.GetListOfLayers(l_active = True)))
-
# here was some dead code related to layer and nviz
# however, in condition was rerender = False
# but rerender is alway True
@@ -1206,9 +1210,6 @@
# ignore when map layer is edited
self.Map.ChangeLayerActive(mapLayer, checked)
- # update progress bar range (mapwindow statusbar)
- self.mapdisplay.GetProgressBar().SetRange(len(self.Map.GetListOfLayers(l_active = True)))
-
# nviz
if self.lmgr.IsPaneShown('toolbarNviz') and \
self.GetPyData(item) is not None:
Modified: grass/trunk/gui/wxpython/mapdisp/frame.py
===================================================================
--- grass/trunk/gui/wxpython/mapdisp/frame.py 2012-12-28 21:49:46 UTC (rev 54453)
+++ grass/trunk/gui/wxpython/mapdisp/frame.py 2012-12-29 10:45:35 UTC (rev 54454)
@@ -426,7 +426,7 @@
def OnUpdateProgress(self, event):
"""!Update progress bar info
"""
- self.GetProgressBar().SetValue(event.value)
+ self.GetProgressBar().UpdateProgress(event.layer, event.map)
event.Skip()
Modified: grass/trunk/gui/wxpython/mapdisp/mapwindow.py
===================================================================
--- grass/trunk/gui/wxpython/mapdisp/mapwindow.py 2012-12-28 21:49:46 UTC (rev 54453)
+++ grass/trunk/gui/wxpython/mapdisp/mapwindow.py 2012-12-29 10:45:35 UTC (rev 54454)
@@ -579,7 +579,6 @@
@param renderVector re-render vector map layer enabled for editing (used for digitizer)
"""
start = time.clock()
-
self.resize = False
# was if self.Map.cmdfile and ...
@@ -587,14 +586,6 @@
render = True
#
- # initialize process bar (only on 'render')
- #
- if render or renderVector:
- self.frame.GetProgressBar().Show()
- if self.frame.GetProgressBar().GetRange() > 0:
- self.frame.GetProgressBar().SetValue(1)
-
- #
# render background image if needed
#
# update layer dictionary if there has been a change in layers
@@ -707,18 +698,6 @@
stop = time.clock()
- #
- # hide process bar
- #
- self.frame.GetProgressBar().Hide()
-
- #
- # update statusbar
- #
- ### self.Map.SetRegion()
- self.frame.StatusbarUpdate()
-
-
Debug.msg (1, "BufferedWindow.UpdateMap(): render=%s, renderVector=%s -> time=%g" % \
(render, renderVector, (stop-start)))
@@ -779,6 +758,9 @@
self.Draw(self.pdcDec, pdctype = 'clear')
self.Draw(self.pdcTmp, pdctype = 'clear')
+
+ for layer in self.Map.GetListOfLayers(l_active = True):
+ layer.AbortDownload()
def DragMap(self, moveto):
"""!Drag the entire map image for panning.
Modified: grass/trunk/gui/wxpython/mapdisp/statusbar.py
===================================================================
--- grass/trunk/gui/wxpython/mapdisp/statusbar.py 2012-12-28 21:49:46 UTC (rev 54453)
+++ grass/trunk/gui/wxpython/mapdisp/statusbar.py 2012-12-29 10:45:35 UTC (rev 54454)
@@ -21,6 +21,8 @@
- statusbar::SbRegionExtent
- statusbar::SbCompRegionExtent
- statusbar::SbProgress
+ - statusbar::SbRMSError
+ - statusbar::SbGoToGCP
(C) 2006-2011 by the GRASS Development Team
@@ -83,7 +85,7 @@
self._postInitialized = False
- self.progressbar = SbProgress(self.mapFrame, self.statusbar)
+ self.progressbar = SbProgress(self.mapFrame, self.statusbar, self)
self._hiddenItems = {}
@@ -116,7 +118,7 @@
def AddStatusbarItem(self, item):
"""!Adds item to statusbar
- If item position is 0, item is managed by choice.
+ If item position is 0, item is managed by choice.
@see AddStatusbarItemsByClass
"""
@@ -180,7 +182,9 @@
@see Update
"""
- self.statusbarItems[itemName].Show()
+ if self.statusbarItems[itemName].GetPosition() != 0 or \
+ not self.progressbar.IsShown():
+ self.statusbarItems[itemName].Show()
def _postInit(self):
"""!Post-initialization method
@@ -208,16 +212,20 @@
It always updates mask.
"""
+ self.progressbar.Update()
+
if not self._postInitialized:
self._postInit()
-
for item in self.statusbarItems.values():
if item.GetPosition() == 0:
- item.Hide()
+ if not self.progressbar.IsShown():
+ item.Hide()
else:
item.Update() # mask, render
-
- if self.choice.GetCount() > 0:
+
+ if self.progressbar.IsShown():
+ pass
+ elif self.choice.GetCount() > 0:
item = self.choice.GetClientData(self.choice.GetSelection())
item.Update()
@@ -233,7 +241,7 @@
widgets.append((item.GetPosition(), item.GetWidget()))
widgets.append((1, self.choice))
- widgets.append((0, self.progressbar.GetWidget()))
+ widgets.append((1, self.progressbar.GetWidget()))
for idx, win in widgets:
if not win:
@@ -242,8 +250,6 @@
if idx == 0: # show region / mapscale / process bar
# -> size
wWin, hWin = win.GetBestSize()
- if win == self.progressbar.GetWidget():
- wWin = rect.width - 6
# -> position
# if win == self.statusbarWin['region']:
# x, y = rect.x + rect.width - wWin, rect.y - 1
@@ -254,6 +260,8 @@
else: # choice || auto-rendering
x, y = rect.x, rect.y
w, h = rect.width, rect.height + 1
+ if win == self.progressbar.GetWidget():
+ wWin = rect.width - 6
if idx == 2: # mask
x += 5
y += 4
@@ -956,19 +964,21 @@
return self.mapFrame.GetMap().GetRegion() # computational region
-class SbProgress(SbItem):
+class SbProgress(SbTextItem):
"""!General progress bar to show progress.
Underlaying widget is wx.Gauge.
"""
- def __init__(self, mapframe, statusbar, position = 0):
+ def __init__(self, mapframe, statusbar, sbManager, position = 0):
SbItem.__init__(self, mapframe, statusbar, position)
self.name = 'progress'
-
+ self.sbManager = sbManager
# on-render gauge
self.widget = wx.Gauge(parent = self.statusbar, id = wx.ID_ANY,
- range = 0, style = wx.GA_HORIZONTAL)
+ range = 0, style = wx.GA_HORIZONTAL)
self.widget.Hide()
+
+ self.maps = {}
def GetRange(self):
"""!Returns progress range."""
@@ -978,7 +988,86 @@
"""!Sets progress range."""
self.widget.SetRange(range)
+ def IsShown(self):
+ """!Is progress bar shown
+ """
+ return self.widget.IsShown()
+
+ def UpdateProgress(self, layer, map):
+ """!Update progress"""
+
+ if map not in self.maps or layer is None:
+ # self.map holds values needed for progress info for every Render instance in mapframe
+ self.maps[map] = {'progresVal' : 0, # current progress value
+ 'downloading' : [], # layers, which are downloading data
+ 'rendered' : [], # already rendered layers
+ 'range' : len(map.GetListOfLayers(l_active = True))}
+ else:
+ if layer not in self.maps[map]['rendered']:
+ self.maps[map]['rendered'].append(layer)
+ if layer.IsDownloading() and \
+ layer not in self.maps[map]['downloading']:
+ self.maps[map]['downloading'].append(layer)
+ else:
+ self.maps[map]['progresVal'] += 1
+ if layer in self.maps[map]['downloading']:
+ self.maps[map]['downloading'].remove(layer)
+
+ self.Update(map)
+ self.sbManager.Update()
+
+ def Update(self, map = None):
+ """!Update statusbar"""
+ activeMap = self.mapFrame.GetMap()
+ if map is None:
+ map = activeMap
+ if map not in self.maps:
+ return
+ if map != activeMap:
+ return
+ # update progress bar
+ if self.maps[map]['range'] == self.maps[map]['progresVal']:
+ self.widget.Hide()
+ return
+ elif self.maps[map]['range'] > 0:
+ if self.widget.GetRange() != self.maps[map]['range']:
+ self.widget.SetRange(self.maps[map]['range'])
+ self.widget.Show()
+ else:
+ return
+
+ self.widget.SetValue(self.maps[map]['progresVal'])
+
+ # update statusbar text
+ st_text = ''
+ first = True
+ for layer in self.maps[map]['downloading']:
+ if first:
+ st_text += _("Downloading data...")
+ first = False
+ else:
+ st_text += ', '
+ st_text += layer.GetName()
+
+ if self.maps[map]['range'] != len(self.maps[map]['rendered']):
+ if st_text:
+ st_text = _('Rendering & ') + st_text
+ else:
+ st_text = _('Rendering...')
+
+ self.statusbar.SetStatusText(st_text, self.position)
+
+ def GetValue(self):
+ return self.widget.GetValue()
+
+ def GetWidget(self):
+ """!Returns underlaying winget.
+
+ @return widget or None if doesn't exist
+ """
+ return self.widget
+
class SbGoToGCP(SbItem):
"""!SpinCtrl to select GCP to focus on
@@ -1074,3 +1163,4 @@
{ 'forw' : self.mapFrame.GetFwdError(),
'back' : self.mapFrame.GetBkwError() })
SbTextItem.Show(self)
+
More information about the grass-commit
mailing list