#!/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 from owslib import crs as CRS import shutil from xml.etree import ElementTree 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 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("Proxy4OWS","cachedir"))) logging.debug("%s initialized"%self.service) if url: self.url = url logging.debug("OWS.py::__init__()::url: '%s'" % url) self.__getCapabilities() if qstring: self.qstring = qstring def __getCapabilities(self): self.__getCacheDir() 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 try: self.capabilities = WebFeatureService(url=self.url,version="1.1.0") except: self.capabilities = WebFeatureService(url=self.url,version="1.0.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") # cache capabilities document open(os.path.join(self.cachedir,"capabilities.xml"),"w").write(ElementTree.tostring(self.capabilities._capabilities)) 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): dirname = os.path.join(self.config.get("Proxy4OWS","cachedir"), "%s-%s" % (self.service, md5.new(self.url).hexdigest())) # get existing cache dir if not os.path.isdir(dirname): os.mkdir(dirname) logging.debug("Cachedir %s created" % dirname) os.chmod(dirname, 0777) open(os.path.join(self.cachedir,"url.txt"),"w").write(self.url) else: logging.debug("Cachedir %s found" % dirname) self.cachedir = dirname 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): if self.url is not None and self.capabilities is None: self.__getCapabilities() # nothing has changed in the capabilities document since last # request ? if os.path.exists(os.path.join(self.cachedir,"capabilities.xml")): oldCapsFile = open(os.path.join(self.cachedir,"capabilities.xml")) oldCaps = oldCapsFile.read() oldCapsFile.close() # the capabilities document is up-to-date, load existing # mapfile newXml = ElementTree.tostring(self.capabilities._capabilities) if md5.new(oldCaps).hexdigest() == md5.new(newXml).hexdigest(): newCapsFile = open(os.path.join(self.cachedir,"capabilities.xml"),"w") newCapsFile.write(newXml) logging.debug("Capabilities unchanged, using existing mapfile %s" % self.getMapfileLocation()) return mapscript.mapObj(self.getMapfileLocation()) # finally # clear existing cached files logging.debug("Cached capabilities document is outdated, creating new one") shutil.rmtree(self.cachedir) # reate new one self.__getCacheDir() return self._createNewMapObj(mapfilename) def _createNewMapObj(self,mapfilename=None): 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+"" if not os.path.isfile(defFileName): 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) else: logging.debug("Using existing layer definition file %s" % defFileName) return defFileName def getLayerExtent(self,layer,crs=None): """Get extent of layer in form of minx, miny, maxx,maxy :returns: [minx, miny, maxx, maxy] """ bbox = None if layer.boundingBoxWGS84: dest = osr.SpatialReference() dest.ImportFromEPSG(crs.code) source = osr.SpatialReference() source.ImportFromEPSG(4326) bbox = [] # TODO rewrite this using ogr CoordinateTransformation # http://www.gdal.org/ogr/osr_tutorial.html # WELL: it does NOT seem to be THAT better 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""")