OWS.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348
  1. #!/usr/bin/env python
  2. # coding=utf-8
  3. import urlparse
  4. import urllib
  5. from lxml import objectify
  6. from lxml import etree
  7. import os,sys
  8. import tempfile
  9. import logging
  10. import ConfigParser
  11. import md5
  12. import mapscript
  13. from string import Template
  14. from osgeo import osr
  15. from osgeo import ogr
  16. import OWSExceptions
  17. from owslib import crs as CRS
  18. class OWS:
  19. capabilities = None
  20. url = None
  21. requestUrl = None
  22. mapobj = None
  23. qstring = None
  24. owsns11 = "http://www.opengis.net/ows/1.1"
  25. owsns = "http://www.opengis.net/ows"
  26. cachedir = None
  27. mapfileName = "mapfile.map"
  28. config = None
  29. parsedUrl = None
  30. service = None
  31. mapfilename = None
  32. def __init__(self,url=None,qstring=None,configFile=None):
  33. self.requestUrl = url
  34. if url:
  35. self.url = url
  36. logging.debug("OWS.py::__init__()::url: '%s'" % url)
  37. self.__getCapabilities()
  38. if qstring:
  39. self.qstring = qstring
  40. configFiles = [ os.path.join(os.path.dirname(__file__),"config.cfg") ]
  41. if sys.platform == "win32":
  42. pass # TODO Default conf. file on windows platform
  43. elif sys.platform == "linux2":
  44. configFiles.append("/etc/owsviewer.cfg")
  45. if configFile:
  46. configFiles.append(configFile)
  47. self.config = ConfigParser.ConfigParser()
  48. self.config.read(configFiles)
  49. logging.debug("Creating cachedir for %s in %s" % (self.url,self.config.get("OWSProxy","cachedir")))
  50. logging.debug("%s initialized"%self.service)
  51. def __getCapabilities(self):
  52. logging.debug("OWS.py::__getCapabilities::self.url: '%s'" % self.url)
  53. self.parsedUrl = urlparse.urlparse(self.url)
  54. if self.service == "WFS":
  55. from owslib.wfs import WebFeatureService
  56. try:
  57. self.capabilities = WebFeatureService(url=self.url,version="1.1.0")
  58. except:
  59. self.capabilities = WebFeatureService(url=self.url,version="1.0.0")
  60. logging.debug("OWS Capabilities: %s",self.capabilities)
  61. elif self.service == "WCS":
  62. from owslib.wcs import WebCoverageService
  63. self.capabilities = WebCoverageService(url=self.url,version="1.0.0")
  64. def getParams(self):
  65. params = urlparse.parse_qs(self.qstring)
  66. if not "VERSION" in params.keys():
  67. params["VERSION"] = params.pop("version")
  68. if not "LAYERS" in params.keys():
  69. params["LAYERS"] = params.pop("layers")
  70. if not "FORMAT" in params.keys():
  71. params["FORMAT"] = params.pop("format")
  72. if not "STYLES" in params.keys():
  73. params["STYLES"] = params.pop("styles")
  74. if not "TRANSPARENT" in params.keys():
  75. params["TRANSPARENT"] = params.pop("transparent")
  76. return params
  77. def getOnlineResource(self,onlineresource,mapfilename=None):
  78. o = urlparse.urlparse(onlineresource)
  79. params = urlparse.parse_qs(o.query,keep_blank_values=True)
  80. params["owsUrl"] = self.url
  81. params["owsService"] = self.service
  82. params["map"] = self.getMapfileLocation(mapfilename)
  83. location = urlparse.urlunparse((o[0],o[1],o[2],o[3],urllib.urlencode(params,True),o[5]))
  84. logging.debug("Setting OnlineResource to %s"% location)
  85. return location
  86. def getMapfileLocation(self,mapfilename=None):
  87. # save the map if possible
  88. if mapfilename:
  89. mapfilename.replace("..","") # remove potential path change
  90. # mapfile must end with .map and cachedir must be at the
  91. # beginning of the mapfile name
  92. if mapfilename.endswith(".map") and \
  93. mapfilename.find(self.cachedir) == 0:
  94. return mapfilename
  95. else:
  96. # do not save anything
  97. return
  98. # save to new location otherwice
  99. else:
  100. return os.path.join(self.cachedir,self.mapfileName)
  101. def __getCacheDir(self):
  102. self.cachedir = tempfile.mkdtemp(prefix="%s-%s"%(self.service,
  103. md5.new(self.url).hexdigest()),
  104. dir=self.config.get("OWSProxy","cachedir"))
  105. os.chmod(self.cachedir, 0777)
  106. logging.debug("Cachedir %s created" % self.cachedir)
  107. open(os.path.join(self.cachedir,"url.txt"),"w").write(self.url)
  108. return self.cachedir
  109. def dispatch(self):
  110. """Dispatch given request
  111. """
  112. request = mapscript.OWSRequest()
  113. request.loadParams()
  114. mapobj = None
  115. self.request=request.getValueByName("REQUEST")
  116. # if no 'map' parameter in URL, create new mapfile
  117. if not request.getValueByName("map"):
  118. logging.debug("Creating new mapfile")
  119. mapobj = self.makeMap()
  120. else:
  121. # there is 'map' parameter in URL and the file exists, load it
  122. logging.debug("Using existing mapfile %s" % request.getValueByName("map"))
  123. if os.path.isfile(request.getValueByName("map")):
  124. mapobj = mapscript.mapObj(request.getValueByName("map"))
  125. # there is 'map' parameter in URL BUT the file does not exist:
  126. # create
  127. else:
  128. mapobj = self.makeMap(request.getValueByName("map"))
  129. # WFS Filter encoding, if available
  130. if request.getValueByName("fes"):
  131. self.setFilter(mapobj,request)
  132. # mapobj.getLayerByName(request.getValueByName("layers")).metadata.get("wfs_filter")
  133. # here is still fine, but it fails on dispatch:
  134. res = mapobj.OWSDispatch(request)
  135. if mapscript.MS_DONE == res:
  136. raise OWSExceptions.NoValidRequest("No valid OWS Request")
  137. elif mapscript.MS_FAILURE == res:
  138. pass
  139. #raise OWSExceptions.RequestFailed("Request failed")
  140. def getMapObj(self,mapfilename=None):
  141. self.__getCacheDir()
  142. if self.url is not None and self.capabilities is None:
  143. self.__getCapabilities()
  144. mapobj = mapscript.mapObj()
  145. mapobj.setMetaData("wms_onlineresource",self.getOnlineResource(self.config.get("MapServer","onlineresource"),mapfilename))
  146. logging.debug("Setting SRS to %s"%self.config.get("MapServer","srs"))
  147. mapobj.setMetaData("wms_srs",self.config.get("MapServer","srs"))
  148. mapobj.setProjection("init=epsg:4326")
  149. mapobj.setSize(500,500)
  150. mapobj.setExtent(-180,-90,90,180)
  151. mapobj.shapepath = self.cachedir
  152. mapobj.setMetaData("wms_encoding","utf-8")
  153. errfile = self.config.get("MapServer","errorfile")
  154. logging.debug("Setting ERRORFILE to %s"%errfile)
  155. if not os.path.exists(errfile) and errfile != "stderr":
  156. tmp = open(errfile,"w")
  157. tmp.close()
  158. # file
  159. if os.access(errfile, os.W_OK):
  160. mapobj.setConfigOption("MS_ERRORFILE",errfile)
  161. # stderr
  162. elif errfile == "stderr":
  163. mapobj.setConfigOption("MS_ERRORFILE",errfile)
  164. # no error file set
  165. else:
  166. logging.warning("Cannot set ERRORFILE to %s: %s " % (errfile,"Write access denided"))
  167. logging.debug("Setting IMAGEPATH to %s"%self.config.get("MapServer","imagepath"))
  168. mapobj.web.imagepath=self.config.get("MapServer","imagepath")
  169. mapobj.setMetaData("ows_enable_request","*")
  170. return mapobj
  171. def saveMapfile(self,mapobj,mapfilename):
  172. self.mapfilename = self.getMapfileLocation(mapfilename)
  173. if self.mapfilename:
  174. # save mapfile ONLY if GetCapabilities requested - it makes no
  175. # sense for other cases
  176. logging.info("Saving mapfile to %s" % self.mapfilename)
  177. mapobj.save(self.mapfilename)
  178. else:
  179. logging.info("Mapfile NOT saved")
  180. return self.mapfilename
  181. def getLayerUrl(self):
  182. layerurl = self.url
  183. if self.url.find("?") > -1:
  184. if not self.url.endswith("?") and\
  185. not self.url.endswith("&"):
  186. layerurl += "&"
  187. else:
  188. layerurl += "?"
  189. return layerurl
  190. def createLayerDefinitionFile(self, name,
  191. templatefile,time="",target=None):
  192. layerurl = self.getLayerUrl()
  193. defFileName = None
  194. if target:
  195. defFileName = target
  196. else:
  197. defFileName = os.path.join(self.cachedir,'%s.%s'%(name,self.service.lower()))
  198. if time:
  199. time = "<DefaultTime>"+time+"</DefaultTime>"
  200. open(defFileName,'w').write(
  201. Template(open(templatefile).read()).substitute(
  202. dict(url= layerurl,
  203. name=name,time=time,extras="&BAND=1,2,3")))
  204. # FIXME always takes band 1,2,3 ^^
  205. logging.debug("Created %s layer definition file" % defFileName)
  206. return defFileName
  207. def getLayerExtent(self,layer,crs=None):
  208. """Get extent of layer in form of minx, miny, maxx,maxy
  209. :returns: [minx, miny, maxx, maxy]
  210. """
  211. bbox = None
  212. if layer.boundingBoxWGS84:
  213. dest = osr.SpatialReference()
  214. dest.ImportFromEPSG(crs.code)
  215. source = osr.SpatialReference()
  216. source.ImportFromEPSG(4326)
  217. bbox = []
  218. # TODO rewrite this using ogr CoordinateTransformation
  219. # http://www.gdal.org/ogr/osr_tutorial.html
  220. # WELL: it does NOT seem to be THAT better
  221. geom = ogr.CreateGeometryFromWkt("""POINT(%s %s)""" % (layer.boundingBoxWGS84[0],layer.boundingBoxWGS84[1]),source)
  222. geom.TransformTo(dest)
  223. bbox.append(geom.GetX())
  224. bbox.append(geom.GetY())
  225. geom = ogr.CreateGeometryFromWkt("""POINT(%s %s)""" % (layer.boundingBoxWGS84[2],layer.boundingBoxWGS84[3]),source)
  226. geom.TransformTo(dest)
  227. bbox.append(geom.GetX())
  228. bbox.append(geom.GetY())
  229. logging.debug("Setting extent for layer <%s> to %s" %\
  230. (layer.id,bbox))
  231. return bbox
  232. def setFilter(self,mapobj,request):
  233. """ Set WFS filter encoding
  234. """
  235. # get the layer
  236. layerobj = mapobj.getLayerByName(request.getValueByName("layers"))
  237. # get the filter
  238. fes = request.getValueByName("fes")
  239. logging.debug("FES received from HSLayers: %s" % fes)
  240. # cut off the opening and closing <Filter> tag
  241. # - this is needed for mapserver
  242. root = etree.XML(fes)
  243. msFilter = ""
  244. for child in root:
  245. msFilter += etree.tostring(child)
  246. logging.debug("Setting the filter %s" % msFilter)
  247. # set the filter
  248. layerobj.setMetaData("wfs_filter",msFilter)
  249. def getService(configFile=None):
  250. qstring = os.environ["QUERY_STRING"]
  251. params = urlparse.parse_qs(qstring)
  252. for p in params:
  253. if p.lower() == "owsurl":
  254. v = params[p]
  255. del params[p]
  256. params["owsUrl"] = v
  257. if p.lower() == "owsservice":
  258. v = params[p]
  259. del params[p]
  260. params["owsService"] = v
  261. if "owsUrl" in params.keys() and\
  262. "owsService" in params.keys():
  263. owsUrl = urllib.unquote(params["owsUrl"][0])
  264. if params["owsService"][0].lower() == "wfs":
  265. from wfs import WFS
  266. logging.debug("OWS.py::getService()::owsUrl: '%s'" % owsUrl)
  267. return WFS(owsUrl,qstring,configFile = configFile )
  268. elif params["owsService"][0].lower() == "wcs":
  269. from wcs import WCS
  270. return WCS(owsUrl,qstring, configFile = configFile)
  271. elif params["owsService"][0].lower() == "wms":
  272. from wms import WMS
  273. return WMS(owsUrl,qstring, configFile = configFile)
  274. else:
  275. raise OWSExceptions.MissingParameterValue("""owsUrl or owsService""")