#!/usr/bin/env python
# coding=utf-8
import urlparse
import urllib
from lxml import objectify
from lxml import etree
import os,sys
import tempfile
import logging
import ConfigParser
import md5
import mapscript
from string import Template
from osgeo import osr
from osgeo import ogr
import OWSExceptions
class OWS:
capabilities = None
url = None
requestUrl = None
mapobj = None
qstring = None
owsns11 = "http://www.opengis.net/ows/1.1"
owsns = "http://www.opengis.net/ows"
cachedir = None
mapfileName = "mapfile.map"
config = None
parsedUrl = None
service = None
mapfilename = None
def __init__(self,url=None,qstring=None,configFile=None):
self.requestUrl = url
if url:
self.url = url
logging.debug("OWS.py::__init__()::url: '%s'" % url)
self.__getCapabilities()
if qstring:
self.qstring = qstring
configFiles = [ os.path.join(os.path.dirname(__file__),"config.cfg") ]
if sys.platform == "win32":
pass # TODO Default conf. file on windows platform
elif sys.platform == "linux2":
configFiles.append("/etc/owsviewer.cfg")
if configFile:
configFiles.append(configFile)
self.config = ConfigParser.ConfigParser()
self.config.read(configFiles)
logging.debug("Creating cachedir for %s in %s" % (self.url,self.config.get("OWSProxy","cachedir")))
logging.debug("%s initialized"%self.service)
def __getCapabilities(self):
logging.debug("OWS.py::__getCapabilities::self.url: '%s'" % self.url)
self.parsedUrl = urlparse.urlparse(self.url)
if self.service == "WFS":
from owslib.wfs import WebFeatureService
self.capabilities = WebFeatureService(url=self.url,version="1.1.0")
logging.debug("OWS Capabilities: %s",self.capabilities)
elif self.service == "WCS":
from owslib.wcs import WebCoverageService
self.capabilities = WebCoverageService(url=self.url,version="1.0.0")
def getParams(self):
params = urlparse.parse_qs(self.qstring)
if not "VERSION" in params.keys():
params["VERSION"] = params.pop("version")
if not "LAYERS" in params.keys():
params["LAYERS"] = params.pop("layers")
if not "FORMAT" in params.keys():
params["FORMAT"] = params.pop("format")
if not "STYLES" in params.keys():
params["STYLES"] = params.pop("styles")
if not "TRANSPARENT" in params.keys():
params["TRANSPARENT"] = params.pop("transparent")
return params
def getOnlineResource(self,onlineresource,mapfilename=None):
o = urlparse.urlparse(onlineresource)
params = urlparse.parse_qs(o.query,keep_blank_values=True)
params["owsUrl"] = self.url
params["owsService"] = self.service
params["map"] = self.getMapfileLocation(mapfilename)
location = urlparse.urlunparse((o[0],o[1],o[2],o[3],urllib.urlencode(params,True),o[5]))
logging.debug("Setting OnlineResource to %s"% location)
return location
def getMapfileLocation(self,mapfilename=None):
# save the map if possible
if mapfilename:
mapfilename.replace("..","") # remove potential path change
# mapfile must end with .map and cachedir must be at the
# beginning of the mapfile name
if mapfilename.endswith(".map") and \
mapfilename.find(self.cachedir) == 0:
return mapfilename
else:
# do not save anything
return
# save to new location otherwice
else:
return os.path.join(self.cachedir,self.mapfileName)
def __getCacheDir(self):
self.cachedir = tempfile.mkdtemp(prefix="%s-%s"%(self.service,
md5.new(self.url).hexdigest()),
dir=self.config.get("OWSProxy","cachedir"))
os.chmod(self.cachedir, 0777)
logging.debug("Cachedir %s created" % self.cachedir)
open(os.path.join(self.cachedir,"url.txt"),"w").write(self.url)
return self.cachedir
def dispatch(self):
"""Dispatch given request
"""
request = mapscript.OWSRequest()
request.loadParams()
mapobj = None
self.request=request.getValueByName("REQUEST")
# if no 'map' parameter in URL, create new mapfile
if not request.getValueByName("map"):
logging.debug("Creating new mapfile")
mapobj = self.makeMap()
else:
# there is 'map' parameter in URL and the file exists, load it
logging.debug("Using existing mapfile %s" % request.getValueByName("map"))
if os.path.isfile(request.getValueByName("map")):
mapobj = mapscript.mapObj(request.getValueByName("map"))
# there is 'map' parameter in URL BUT the file does not exist:
# create
else:
mapobj = self.makeMap(request.getValueByName("map"))
# WFS Filter encoding, if available
if request.getValueByName("fes"):
self.setFilter(mapobj,request)
# mapobj.getLayerByName(request.getValueByName("layers")).metadata.get("wfs_filter")
# here is still fine, but it fails on dispatch:
res = mapobj.OWSDispatch(request)
if mapscript.MS_DONE == res:
raise OWSExceptions.NoValidRequest("No valid OWS Request")
elif mapscript.MS_FAILURE == res:
pass
#raise OWSExceptions.RequestFailed("Request failed")
def getMapObj(self,mapfilename=None):
self.__getCacheDir()
if self.url is not None and self.capabilities is None:
self.__getCapabilities()
mapobj = mapscript.mapObj()
mapobj.setMetaData("wms_onlineresource",self.getOnlineResource(self.config.get("MapServer","onlineresource"),mapfilename))
logging.debug("Setting SRS to %s"%self.config.get("MapServer","srs"))
mapobj.setMetaData("wms_srs",self.config.get("MapServer","srs"))
mapobj.setProjection("init=epsg:4326")
mapobj.setSize(500,500)
mapobj.setExtent(-180,-90,90,180)
mapobj.shapepath = self.cachedir
mapobj.setMetaData("wms_encoding","utf-8")
errfile = self.config.get("MapServer","errorfile")
logging.debug("Setting ERRORFILE to %s"%errfile)
if not os.path.exists(errfile) and errfile != "stderr":
tmp = open(errfile,"w")
tmp.close()
# file
if os.access(errfile, os.W_OK):
mapobj.setConfigOption("MS_ERRORFILE",errfile)
# stderr
elif errfile == "stderr":
mapobj.setConfigOption("MS_ERRORFILE",errfile)
# no error file set
else:
logging.warning("Cannot set ERRORFILE to %s: %s " % (errfile,"Write access denided"))
logging.debug("Setting IMAGEPATH to %s"%self.config.get("MapServer","imagepath"))
mapobj.web.imagepath=self.config.get("MapServer","imagepath")
mapobj.setMetaData("ows_enable_request","*")
return mapobj
def saveMapfile(self,mapobj,mapfilename):
self.mapfilename = self.getMapfileLocation(mapfilename)
if self.mapfilename:
# save mapfile ONLY if GetCapabilities requested - it makes no
# sense for other cases
logging.info("Saving mapfile to %s" % self.mapfilename)
mapobj.save(self.mapfilename)
else:
logging.info("Mapfile NOT saved")
return self.mapfilename
def getLayerUrl(self):
layerurl = self.url
if self.url.find("?") > -1:
if not self.url.endswith("?") and\
not self.url.endswith("&"):
layerurl += "&"
else:
layerurl += "?"
return layerurl
def createLayerDefinitionFile(self, name,
templatefile,time="",target=None):
layerurl = self.getLayerUrl()
defFileName = None
if target:
defFileName = target
else:
defFileName = os.path.join(self.cachedir,'%s.%s'%(name,self.service.lower()))
if time:
time = ""+time+""
open(defFileName,'w').write(
Template(open(templatefile).read()).substitute(
dict(url= layerurl,
name=name,time=time,extras="&BAND=1,2,3")))
# FIXME always takes band 1,2,3 ^^
logging.debug("Created %s layer definition file" % defFileName)
return defFileName
def getLayerExtent(self,layer,crs=None,wkt=None):
"""Get extent of layer in form of minx, miny, maxx,maxy
"""
bbox = None
if layer.boundingBoxWGS84:
dest = osr.SpatialReference()
if crs:
try:
crsSplit = crs.split(":")
epsg = int(crsSplit[len(crsSplit)-1])
dest.ImportFromEPSG(epsg)
except (ValueError):
logging.debug("Unable to parse crs '%s'" % crs)
else:
dest.ImportFromWkt(wkt)
source = osr.SpatialReference()
source.ImportFromEPSG(4326)
bbox = []
# TODO rewrite this using ogr CoordinateTransformation
# http://www.gdal.org/ogr/osr_tutorial.html
geom = ogr.CreateGeometryFromWkt("""POINT(%s %s)""" % (layer.boundingBoxWGS84[0],layer.boundingBoxWGS84[1]),source)
geom.TransformTo(dest)
bbox.append(geom.GetX())
bbox.append(geom.GetY())
geom = ogr.CreateGeometryFromWkt("""POINT(%s %s)""" % (layer.boundingBoxWGS84[2],layer.boundingBoxWGS84[3]),source)
geom.TransformTo(dest)
bbox.append(geom.GetX())
bbox.append(geom.GetY())
logging.debug("Setting extent for layer <%s> to %s" %\
(layer.id,bbox))
return bbox
def setFilter(self,mapobj,request):
""" Set WFS filter encoding
"""
# get the layer
layerobj = mapobj.getLayerByName(request.getValueByName("layers"))
# get the filter
fes = request.getValueByName("fes")
logging.debug("FES received from HSLayers: %s" % fes)
# cut off the opening and closing tag
# - this is needed for mapserver
root = etree.XML(fes)
msFilter = ""
for child in root:
msFilter += etree.tostring(child)
logging.debug("Setting the filter %s" % msFilter)
# set the filter
layerobj.setMetaData("wfs_filter",msFilter)
def getService(configFile=None):
qstring = os.environ["QUERY_STRING"]
params = urlparse.parse_qs(qstring)
for p in params:
if p.lower() == "owsurl":
v = params[p]
del params[p]
params["owsUrl"] = v
if p.lower() == "owsservice":
v = params[p]
del params[p]
params["owsService"] = v
if "owsUrl" in params.keys() and\
"owsService" in params.keys():
owsUrl = urllib.unquote(params["owsUrl"][0])
if params["owsService"][0].lower() == "wfs":
from wfs import WFS
logging.debug("OWS.py::getService()::owsUrl: '%s'" % owsUrl)
return WFS(owsUrl,qstring,configFile = configFile )
elif params["owsService"][0].lower() == "wcs":
from wcs import WCS
return WCS(owsUrl,qstring, configFile = configFile)
elif params["owsService"][0].lower() == "wms":
from wms import WMS
return WMS(owsUrl,qstring, configFile = configFile)
else:
raise OWSExceptions.MissingParameterValue("""owsUrl or owsService""")