OWS.py 12 KB

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