OWS.py 12 KB

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