OWS.py 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432
  1. #!/usr/bin/env python
  2. # coding=utf-8
  3. import urlparse
  4. import urllib
  5. from lxml import objectify
  6. try:
  7. from lxml import etree
  8. except:
  9. from xml.etree import ElementTree as etree
  10. import os,sys
  11. import tempfile
  12. import logging
  13. import ConfigParser
  14. import md5
  15. import mapscript
  16. from string import Template
  17. from osgeo import osr
  18. from osgeo import ogr
  19. import OWSExceptions
  20. from owslib import crs as CRS
  21. import shutil
  22. class OWS:
  23. capabilities = None
  24. url = None
  25. requestUrl = None
  26. mapobj = None
  27. qstring = None
  28. owsns11 = "http://www.opengis.net/ows/1.1"
  29. owsns = "http://www.opengis.net/ows"
  30. cachedir = None
  31. mapfileName = "mapfile.map"
  32. config = None
  33. parsedUrl = None
  34. service = None
  35. mapfilename = None
  36. def __init__(self,url=None,qstring=None,configFile=None):
  37. self.requestUrl = url
  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("Proxy4OWS","cachedir")))
  48. logging.debug("%s initialized"%self.service)
  49. if url:
  50. self.url = url
  51. logging.debug("OWS.py::__init__()::url: '%s'" % url)
  52. if qstring:
  53. self.qstring = qstring
  54. self.__getCapabilities()
  55. def __getCapabilitiesFromFile(self,service,version):
  56. if os.path.exists(os.path.join(self.cachedir,"capabilities.xml")):
  57. logging.debug("OWS.py:: Using cached version of capabilities document")
  58. capafile = open(os.path.join(self.cachedir,"capabilities.xml"))
  59. return service(self.url,xml=capafile.read(),version=version)
  60. else:
  61. return None
  62. def __getCapabilities(self):
  63. if self.url:
  64. logging.debug("OWS.py::__getCapabilities::self.url: '%s'" % self.url)
  65. self.parsedUrl = urlparse.urlparse(self.url)
  66. self.__getCacheDir()
  67. if self.service == "WFS":
  68. from owslib.wfs import WebFeatureService
  69. # FIXME Make default version configurable
  70. try:
  71. # raise "2.0.0 not supported"
  72. # FIXME continue to 1.1.0
  73. self.capabilities = self.__getCapabilitiesFromFile(WebFeatureService,"2.0.0")
  74. if self.capabilities == None and self.url:
  75. logging.debug("OWS.py:: Downloading new capabilities file for WFS 2.0.0")
  76. self.capabilities = WebFeatureService(url=self.url,version="2.0.0")
  77. except Exception,e:
  78. try:
  79. self.capabilities = self.__getCapabilitiesFromFile(WebFeatureService,"1.1.0")
  80. if self.capabilities == None and self.url:
  81. logging.debug("OWS.py:: Downloading new capabilities file for WFS 1.1.0")
  82. self.capabilities = WebFeatureService(url=self.url,version="1.1.0")
  83. except:
  84. self.capabilities = self.__getCapabilitiesFromFile(WebFeatureService,"1.0.0")
  85. if self.capabilities == None and self.url:
  86. logging.debug("OWS.py:: Downloading new capabilities file for WFS 1.0.0")
  87. self.capabilities = WebFeatureService(url=self.url,version="1.0.0")
  88. elif self.service == "WCS":
  89. from owslib.wcs import WebCoverageService
  90. self.capabilities = self.__getCapabilitiesFromFile(WebCoverageService,"1.0.0")
  91. if self.capabilities == None and self.url:
  92. logging.debug("OWS.py:: Downloading new capabilities file for WCS 1.0.0")
  93. self.capabilities = WebCoverageService(url=self.url,version="1.0.0")
  94. def getParams(self):
  95. params = urlparse.parse_qs(self.qstring)
  96. logging.debug(str(params))
  97. if not "VERSION" in params.keys() and "version" in params.keys():
  98. params["VERSION"] = params.pop("version")
  99. if not "LAYERS" in params.keys() and "layers" in params.keys():
  100. params["LAYERS"] = params.pop("layers")
  101. if not "FORMAT" in params.keys() and "format" in params.keys():
  102. params["FORMAT"] = params.pop("format")
  103. if not "STYLES" in params.keys() and "styles" in params.keys():
  104. params["STYLES"] = params.pop("styles")
  105. if not "TIME" in params.keys() and "time" in params.keys():
  106. params["TIME"] = params.pop("time")
  107. #else:
  108. # params["STYLES"] = ['']
  109. if not "TRANSPARENT" in params.keys() and "transparent" in params.keys():
  110. params["TRANSPARENT"] = params.pop("transparent")
  111. return params
  112. def getOnlineResource(self,onlineresource,mapfilename=None):
  113. o = urlparse.urlparse(onlineresource)
  114. params = urlparse.parse_qs(o.query,keep_blank_values=True)
  115. params["owsUrl"] = self.url
  116. params["owsService"] = self.service
  117. params["map"] = self.getMapfileLocation(mapfilename)
  118. location = urlparse.urlunparse((o[0],o[1],o[2],o[3],urllib.urlencode(params,True),o[5]))
  119. logging.debug("Setting OnlineResource to %s"% location)
  120. return location
  121. def getMapfileLocation(self,mapfilename=None):
  122. # save the map if possible
  123. if mapfilename:
  124. mapfilename.replace("..","") # remove potential path change
  125. # mapfile must end with .map and cachedir must be at the
  126. # beginning of the mapfile name
  127. if mapfilename.endswith(".map") and \
  128. mapfilename.find(self.cachedir) == 0:
  129. return mapfilename
  130. else:
  131. # do not save anything
  132. return
  133. # save to new location otherwice
  134. else:
  135. return os.path.join(self.cachedir,self.mapfileName)
  136. def __getCacheDir(self):
  137. dirname = os.path.join(self.config.get("Proxy4OWS","cachedir"),
  138. "%s-%s" % (self.service, md5.new(self.url).hexdigest()))
  139. self.cachedir = dirname
  140. # get existing cache dir
  141. if not os.path.isdir(dirname):
  142. os.mkdir(dirname)
  143. logging.debug("Cachedir %s created" % dirname)
  144. os.chmod(dirname, 0777)
  145. open(os.path.join(self.cachedir,"url.txt"),"w").write(self.url)
  146. else:
  147. logging.debug("Cachedir %s found" % dirname)
  148. return self.cachedir
  149. def dispatch(self):
  150. """Dispatch given request
  151. """
  152. request = mapscript.OWSRequest()
  153. request.loadParams()
  154. mapobj = None
  155. self.request=request.getValueByName("REQUEST")
  156. # if no 'map' parameter in URL, create new mapfile
  157. if not request.getValueByName("map"):
  158. logging.debug("Creating new mapfile")
  159. mapobj = self.makeMap()
  160. else:
  161. # there is 'map' parameter in URL and the file exists, load it
  162. logging.debug("Using existing mapfile %s" % request.getValueByName("map"))
  163. if os.path.isfile(request.getValueByName("map")):
  164. mapobj = mapscript.mapObj(request.getValueByName("map"))
  165. # there is 'map' parameter in URL BUT the file does not exist:
  166. # create
  167. else:
  168. logging.debug("Mapfile not found: creating new mapfile")
  169. mapobj = self.makeMap(request.getValueByName("map"))
  170. # mapobj.getLayerByName(request.getValueByName("layers")).metadata.get("wfs_filter")
  171. logging.debug("Calling OWSDispatch")
  172. res = mapobj.OWSDispatch(request)
  173. if mapscript.MS_DONE == res:
  174. raise OWSExceptions.NoValidRequest("No valid OWS Request")
  175. elif mapscript.MS_FAILURE == res:
  176. pass
  177. #raise OWSExceptions.RequestFailed("Request failed")
  178. def getMapObj(self,mapfilename=None):
  179. if self.url is not None and self.capabilities is None:
  180. self.__getCapabilities()
  181. # nothing has changed in the capabilities document since last
  182. # request ?
  183. if os.path.exists(os.path.join(self.cachedir,"capabilities.xml")) and\
  184. "_capabilities" in dir(self.capabilities):
  185. oldCapsFile = open(os.path.join(self.cachedir,"capabilities.xml"))
  186. oldCaps = oldCapsFile.read()
  187. oldCapsFile.close()
  188. # the capabilities document is up-to-date, load existing
  189. # mapfile
  190. newXml = etree.tostring(self.capabilities._capabilities)
  191. if md5.new(oldCaps).hexdigest() == md5.new(newXml).hexdigest():
  192. newCapsFile = open(os.path.join(self.cachedir,"capabilities.xml"),"w")
  193. newCapsFile.write(newXml)
  194. mapfilename = self.getMapfileLocation()
  195. if os.path.exists(mapfilename):
  196. logging.debug("Capabilities unchanged, using existing mapfile %s" % self.getMapfileLocation())
  197. return mapscript.mapObj(mapfilename)
  198. # finally
  199. # clear existing cached files
  200. logging.debug("Cached capabilities document is outdated, creating new one")
  201. shutil.rmtree(self.cachedir)
  202. # reate new one
  203. self.__getCacheDir()
  204. return self._createNewMapObj(mapfilename)
  205. def _createNewMapObj(self,mapfilename=None):
  206. mapobj = mapscript.mapObj()
  207. mapobj.setMetaData("wms_onlineresource",self.getOnlineResource(self.config.get("MapServer","onlineresource"),mapfilename))
  208. logging.debug("Setting SRS to %s"%self.config.get("MapServer","srs"))
  209. mapobj.setMetaData("wms_srs",self.config.get("MapServer","srs"))
  210. mapobj.setProjection("init=epsg:4326")
  211. mapobj.setSize(500,500)
  212. mapobj.setExtent(-180,-90,90,180)
  213. mapobj.shapepath = self.cachedir
  214. mapobj.setMetaData("wms_encoding","utf-8")
  215. errfile = self.config.get("MapServer","errorfile")
  216. logging.debug("Setting ERRORFILE to %s"%errfile)
  217. if not os.path.exists(errfile) and errfile != "stderr":
  218. tmp = open(errfile,"w")
  219. tmp.close()
  220. # file
  221. if os.access(errfile, os.W_OK):
  222. mapobj.setConfigOption("MS_ERRORFILE",errfile)
  223. # stderr
  224. elif errfile == "stderr":
  225. mapobj.setConfigOption("MS_ERRORFILE",errfile)
  226. # no error file set
  227. else:
  228. logging.warning("Cannot set ERRORFILE to %s: %s " % (errfile,"Write access denided"))
  229. logging.debug("Setting IMAGEPATH to %s"%self.config.get("MapServer","imagepath"))
  230. mapobj.web.imagepath=self.config.get("MapServer","imagepath")
  231. mapobj.setMetaData("ows_enable_request","*")
  232. return mapobj
  233. def saveMapfile(self,mapobj,mapfilename):
  234. self.mapfilename = self.getMapfileLocation(mapfilename)
  235. if self.mapfilename:
  236. # save mapfile ONLY if GetCapabilities requested - it makes no
  237. # sense for other cases
  238. logging.info("Saving mapfile to %s" % self.mapfilename)
  239. mapobj.save(self.mapfilename)
  240. # cache capabilities document
  241. if "_capabilities" in dir(self.capabilities):
  242. logging.info("Saving service Capabilities to %s" % os.path.join(self.cachedir,"capabilities.xml"))
  243. open(os.path.join(self.cachedir,"capabilities.xml"),"w").write(etree.tostring(self.capabilities._capabilities))
  244. else:
  245. logging.info("Mapfile NOT saved")
  246. return self.mapfilename
  247. def getLayerUrl(self):
  248. layerurl = self.url
  249. if self.url.find("?") > -1:
  250. if not self.url.endswith("?") and\
  251. not self.url.endswith("&"):
  252. layerurl += "&"
  253. else:
  254. layerurl += "?"
  255. return layerurl
  256. def createLayerDefinitionFile(self, name,
  257. templatefile,time="",target=None):
  258. """
  259. Depreacted
  260. to be removed
  261. """
  262. layerurl = self.getLayerUrl()
  263. defFileName = None
  264. if target:
  265. defFileName = target
  266. else:
  267. defFileName = os.path.join(self.cachedir,'%s.%s'%(name,self.service.lower()))
  268. if time:
  269. time = "<DefaultTime>"+time+"</DefaultTime>"
  270. if not os.path.isfile(defFileName):
  271. open(defFileName,'w').write(
  272. Template(open(templatefile).read()).substitute(
  273. dict(url= layerurl,
  274. name=name,time=time,extras="&BAND=1,2,3")))
  275. # FIXME always takes band 1,2,3 ^^
  276. logging.debug("Created %s layer definition file" % defFileName)
  277. else:
  278. logging.debug("Using existing layer definition file %s" % defFileName)
  279. return defFileName
  280. def getLayerExtent(self,layer,crs=None):
  281. """Get extent of layer in form of minx, miny, maxx,maxy
  282. :returns: [minx, miny, maxx, maxy]
  283. """
  284. bbox = None
  285. if layer.boundingBoxWGS84:
  286. dest = osr.SpatialReference()
  287. dest.ImportFromEPSG(crs.code)
  288. source = osr.SpatialReference()
  289. source.ImportFromEPSG(4326)
  290. bbox = []
  291. # TODO rewrite this using ogr CoordinateTransformation
  292. # http://www.gdal.org/ogr/osr_tutorial.html
  293. # WELL: it does NOT seem to be THAT better
  294. geom = ogr.CreateGeometryFromWkt("""POINT(%s %s)""" % (layer.boundingBoxWGS84[0],layer.boundingBoxWGS84[1]),source)
  295. geom.TransformTo(dest)
  296. bbox.append(geom.GetX())
  297. bbox.append(geom.GetY())
  298. geom = ogr.CreateGeometryFromWkt("""POINT(%s %s)""" % (layer.boundingBoxWGS84[2],layer.boundingBoxWGS84[3]),source)
  299. geom.TransformTo(dest)
  300. bbox.append(geom.GetX())
  301. bbox.append(geom.GetY())
  302. logging.debug("Setting extent for layer <%s> to %s" %\
  303. (layer.id,bbox))
  304. return bbox
  305. def getService(configFile=None):
  306. qstring = os.environ["QUERY_STRING"]
  307. params = urlparse.parse_qs(qstring)
  308. urlparam = None
  309. serviceparam = None
  310. for p in params:
  311. if p.lower() == "owsurl":
  312. v = params[p]
  313. #del params[p]
  314. #params["owsUrl"] = v
  315. urlparam = v
  316. if p.lower() == "owsservice":
  317. v = params[p]
  318. #del params[p]
  319. #params["owsService"] = v
  320. serviceparam = v
  321. if urlparam:
  322. params["owsUrl"] = urlparam
  323. if serviceparam:
  324. params["owsService"] = serviceparam
  325. if "owsUrl" in params.keys() and\
  326. "owsService" in params.keys():
  327. owsUrl = urllib.unquote(params["owsUrl"][0])
  328. if params["owsService"][0].lower() == "wfs":
  329. from wfs import WFS
  330. logging.debug("OWS.py::getService()::owsUrl: '%s'" % owsUrl)
  331. return WFS(owsUrl,qstring,configFile = configFile )
  332. elif params["owsService"][0].lower() == "wcs":
  333. from wcs import WCS
  334. return WCS(owsUrl,qstring, configFile = configFile)
  335. elif params["owsService"][0].lower() == "wms":
  336. from wms import WMS
  337. return WMS(owsUrl,qstring, configFile = configFile)
  338. else:
  339. raise OWSExceptions.MissingParameterValue("""owsUrl or owsService""")