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