__init__.py 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408
  1. #!/usr/bin/env python
  2. # coding=utf-8
  3. from OWS import OWS
  4. import mapscript
  5. import cgi
  6. from lxml import objectify
  7. import urllib
  8. import urlparse
  9. import logging
  10. from osgeo import ogr,gdal,osr
  11. from owslib.crs import Crs
  12. import os,sys
  13. import re
  14. import shutil
  15. import tempfile
  16. class WFS(OWS):
  17. service = "WFS"
  18. wfsns = "http://www.opengis.net/wfs"
  19. layerDefFile = None
  20. lyrobj = None
  21. wfs = None
  22. def __init__(self,url=None,qstring=None,configFile=None):
  23. OWS.__init__(self,url,qstring,configFile)
  24. def dispatch(self):
  25. """Dispatch given request
  26. """
  27. request = mapscript.OWSRequest()
  28. request.loadParams()
  29. typename = request.getValueByName("layers")
  30. if typename:
  31. layer = self.capabilities.contents[typename]
  32. self.request=request.getValueByName("REQUEST")
  33. # if no 'map' parameter in URL, create new mapfile
  34. if not request.getValueByName("map"):
  35. logging.debug("Creating new mapfile")
  36. self.mapobj = self.makeMap()
  37. else:
  38. # there is 'map' parameter in URL and the file exists, load it
  39. logging.debug("Using existing mapfile %s" % request.getValueByName("map"))
  40. if os.path.isfile(request.getValueByName("map")):
  41. self.mapobj = mapscript.mapObj(request.getValueByName("map"))
  42. # there is 'map' parameter in URL BUT the file does not exist:
  43. # create
  44. else:
  45. self.mapobj = self.makeMap(request.getValueByName("map"))
  46. if self.mapobj:
  47. self.mapfilename = request.getValueByName("map")
  48. # download data
  49. if self.request.upper() == "GETMAP":
  50. dataFile = self.getData(request,typename,layer)
  51. if dataFile:
  52. #layer.data = dataFile.name
  53. pass
  54. res = self.mapobj.OWSDispatch(request)
  55. layerobj = self.mapobj.getLayerByName(typename)
  56. if mapscript.MS_DONE == res:
  57. raise OWSExceptions.NoValidRequest("No valid OWS Request")
  58. elif mapscript.MS_FAILURE == res:
  59. pass
  60. #raise OWSExceptions.RequestFailed("Request failed")
  61. def getData(self,request,typename,layer):
  62. """Download data from WFS server and store them to local harddrive
  63. """
  64. fes = request.getValueByName("fes")
  65. bbox = request.getValueByName("bbox").split(",")
  66. import sys
  67. print >>sys.stderr, "###########",bbox
  68. featureid = request.getValueByName("featureid")
  69. featureversion = request.getValueByName("featureversion")
  70. datadir = os.path.join(self.cachedir,"cache")
  71. layer = self.capabilities.contents[typename]
  72. crs = self.__getLayerCrs(layer.crsOptions)
  73. version = request.getValueByName("version")
  74. # get propper bbox for the WFS request
  75. if version == "1.3.0":
  76. requestCrs = request.getValueByName("crs")
  77. else:
  78. requestCrs = request.getValueByName("srs")
  79. requestCrs = Crs(requestCrs)
  80. bbox = self.__adjustBBox(bbox,requestCrs, crs,version)
  81. # clear dir
  82. outfn = None
  83. if os.path.isdir(datadir):
  84. if os.path.isfile(os.path.join(datadir,"%s.gml"%typename)):
  85. # find out bbox of the data
  86. bboxfile = os.path.join(datadir,"%s.bbox"%typename)
  87. filterfile = os.path.join(datadir,"%s.filter"%typename)
  88. if os.path.isfile(bboxfile) and not os.path.isfile(filterfile)\
  89. and not fes:
  90. # convert "string" to [floats] as [minx,miny,maxx,maxy]
  91. storedbbox = map(lambda x: float(x), open(bboxfile).read().split(","))
  92. if storedbbox[0] <= bbox[0] and\
  93. storedbbox[1] <= bbox[1] and \
  94. storedbbox[2] >= bbox[2] and \
  95. storedbbox[3] >= bbox[3]:
  96. logging.info(
  97. "Using cached file for type [%s] with bbox [%f,%f,%f,%f], not downloading new data"%\
  98. (typename, bbox[0],bbox[1],bbox[2],bbox[3]))
  99. # setting output file name
  100. outfn = os.path.join(datadir,"%s.gml"%typename)
  101. else:
  102. # remove pre-cached file with only little area
  103. logging.info("Removing pre-cached files %s.*"% typename)
  104. self.__clear(datadir,typename)
  105. else:
  106. os.mkdir(os.path.join(self.cachedir,"cache"))
  107. # create new cache file, if it does not exist yet
  108. # download the data from the server
  109. if outfn == None:
  110. # clear, just to be sure
  111. self.__clear(datadir,typename)
  112. # create cached bbox
  113. outbbox = open(os.path.join(datadir,"%s.bbox"%typename),"w")
  114. outbbox.write("%f,%f,%f,%f"%(bbox[0],bbox[1],bbox[2],bbox[3]))
  115. outbbox.close()
  116. # create cached filter
  117. if fes:
  118. outfes = open(os.path.join(datadir,"%s.filter"%typename),"w")
  119. outfes.write(fes)
  120. outfes.close()
  121. # create chace file
  122. outfn= os.path.join(datadir,"%s.gml"%typename)
  123. cacheFile = open(outfn,"w")
  124. # download feature from WFS
  125. logging.debug("Downloading data [%s] from bbox [%f,%f,%f,%f]"%\
  126. (typename, bbox[0],bbox[1],bbox[2],bbox[3]))
  127. bbox.append(crs.getcode())
  128. import sys
  129. print >>sys.stderr, typename, bbox, fes, crs.getcode()
  130. feature = self.capabilities.getfeature(
  131. typename=typename,
  132. bbox=bbox,
  133. filter=fes,
  134. srsname=crs.getcode())
  135. self.capabilities
  136. cacheFile.write(feature.read())
  137. cacheFile.close()
  138. # set layer connection
  139. layerobj = self.mapobj.getLayerByName(typename)
  140. layerobj.connection = os.path.abspath(outfn)
  141. # data are downloaded
  142. # make sure, they have propper axis order - x,y
  143. if self.capabilities.version == "1.1.0" and crs.axisorder == "yx":
  144. logging.debug("Setting GML_INVERT_AXIS_ORDER_IF_LAT_LONG variable to YES")
  145. gdal.SetConfigOption("GML_INVERT_AXIS_ORDER_IF_LAT_LONG","YES")
  146. gdal.SetConfigOption("GML_CONSIDER_EPSG_AS_URN","YES")
  147. # >>> from osgeo import ogr
  148. # >>> from osgeo import gdal
  149. # >>> gdal.SetConfigOption("GML_INVERT_AXIS_ORDER_IF_LAT_LONG","YES")
  150. # >>> gdal.SetConfigOption("GML_CONSIDER_EPSG_AS_URN","YES")
  151. # >>> ind = ogr.Open("data.xml")
  152. # >>> outd = ogr.GetDriverByName("GML")
  153. # >>> outf = outd.CopyDataSource(ind,"out4.xml")
  154. # >>> outf.Destroy()
  155. logging.info("Connection of [%s] set to %s" % (typename,layerobj.connection))
  156. return outfn
  157. def __adjustBBox(self,bbox,src,target,version):
  158. """ adjust bounding coordinates and axis order
  159. """
  160. # swap axis, if needed
  161. if version == "1.3.0" and src.axisorder == "yx":
  162. bbox[0],bbox[1] = bbox[1],bbox[0]
  163. bbox[2],bbox[3] = bbox[3],bbox[2]
  164. # coordinate transformation
  165. projsrc = osr.SpatialReference()
  166. projsrc.ImportFromEPSG(src.code)
  167. projtarget = osr.SpatialReference()
  168. projtarget.ImportFromEPSG(target.code)
  169. trans = osr.CoordinateTransformation(projsrc,projtarget)
  170. bbox[0],bbox[1],z = trans.TransformPoint(float(bbox[0]),float(bbox[1]))
  171. bbox[2],bbox[3],z = trans.TransformPoint(float(bbox[2]),float(bbox[3]))
  172. return bbox
  173. def setFilter(self,mapobj,request):
  174. """ Set WFS filter encoding
  175. """
  176. # get the layer
  177. layerobj = mapobj.getLayerByName(request.getValueByName("layers"))
  178. # get the filter
  179. logging.debug("FES received from HSLayers: %s" % fes)
  180. # cut off the opening and closing <Filter> tag
  181. # - this is needed for mapserver
  182. #root = etree.XML(fes)
  183. #msFilter = ""
  184. #for child in root:
  185. # msFilter += etree.tostring(child)
  186. #logging.debug("Setting the filter %s" % msFilter)
  187. # set the filter
  188. layerobj.setMetaData("wfs_filter",fes)
  189. #layerobj.setMetaData("wfs_filter",msFilter)
  190. # save the mapfile - debugging
  191. mapobj.save("mapfile.fes")
  192. def makeMap(self,mapfilename=None):
  193. mapobj = self.getMapObj(mapfilename)
  194. # mapobjects exists, do not do any new layers
  195. if mapobj.numlayers:
  196. return mapobj
  197. self.layerDefFile = self.createLayerDefinitionFile("wfs",
  198. os.path.join( os.path.dirname(__file__), "templates",'wfs.xml'))
  199. ds = ogr.Open(self.layerDefFile)
  200. self.setMapName(mapobj)
  201. # load mapfile SYMBOLs
  202. symbolPath = os.path.join( os.path.dirname(__file__), "symbols.txt")
  203. symbolsLoaded = mapobj.setSymbolSet(symbolPath)
  204. if symbolsLoaded == mapscript.MS_SUCCESS:
  205. logging.debug("Symbols loaded from %s",symbolPath)
  206. else:
  207. logging.debug("Error loading symbols from %s",symbolPath)
  208. logging.debug(self.capabilities.contents)
  209. srss = []
  210. for name in self.capabilities.contents:
  211. mapobj.setMetaData("wms_srs",self.config.get("MapServer","srs"))
  212. mapobj.setMetaData("wfs_srs",self.config.get("MapServer","srs"))
  213. mapobj.setMetaData("wfs_connectiontimeout","90")
  214. layer = self.capabilities.contents[name]
  215. logging.debug("Creating layer %s" % name)
  216. srss = srss+filter(lambda y: not y in srss,layer.crsOptions)
  217. lyrobj = mapscript.layerObj(mapobj)
  218. #lyrobj.name = name.replace(":","_")
  219. lyrobj.name = name
  220. lyrobj.title = layer.title
  221. if layer.title:
  222. lyrobj.setMetaData("wms_title",layer.title)
  223. lyrobj.setMetaData("wfs_title",layer.title)
  224. #if layer.abstract:
  225. # lyrobj.setMetaData("ows_abstract", layer.abstract)
  226. logging.debug("WFS version %s",self.capabilities.version)
  227. lyrobj.setMetaData("gml_include_items","all")
  228. lyrobj.setConnectionType(mapscript.MS_OGR,'')
  229. lyrobj.data = re.sub(r".*:","",name)
  230. crs = self.__getLayerCrs(layer.crsOptions)
  231. if ds:
  232. ogrLayer = ds.GetLayerByName(name)
  233. extent = self.getLayerExtent(layer,crs)
  234. if extent:
  235. lyrobj.setMetaData("wms_extent","%s %s %s %s" % \
  236. (extent[0],extent[1],extent[2],extent[3]))
  237. lyrobj.setMetaData("wfs_extent","%s %s %s %s" % \
  238. (extent[0],extent[1],extent[2],extent[3]))
  239. lyrobj.type = self._getLayerType(ogrLayer)
  240. else:
  241. mapobj.removeLayer(mapobj.numlayers-1)
  242. logging.debug("No ogrDataSource found")
  243. continue
  244. lyrobj.setProjection(crs.getcode())
  245. #lyrobj.setProjection(layer.crsOptions[0].getcode())
  246. lyrobj.dump = mapscript.MS_TRUE
  247. lyrobj.template = "foo"
  248. cls = mapscript.classObj(lyrobj)
  249. style = mapscript.styleObj(cls)
  250. style.outlinecolor=mapscript.colorObj(134,81,0)
  251. style.color=mapscript.colorObj(238,153,0)
  252. style.size=5
  253. style.width=5
  254. if lyrobj.type == mapscript.MS_LAYER_POINT:
  255. style.symbol = 1
  256. ## overwrite already set SRSs
  257. #if len(srss) > 0:
  258. # logging.debug("Overwriting SRS option")
  259. # mapobj.setMetaData("wms_srs"," ".join(srss))
  260. self.saveMapfile(mapobj,mapfilename)
  261. return mapobj
  262. def getGeomName(self,geomname):
  263. if geomname.find("LINE") > -1:
  264. return mapscript.MS_LAYER_LINE
  265. elif geomname.find("POLYGON") > -1:
  266. return mapscript.MS_LAYER_POLYGON
  267. else:
  268. return mapscript.MS_LAYER_POINT
  269. def setMapName(self,mapobj):
  270. mapobj.name = self.config.get("MapServer","name")
  271. if self.capabilities.identification.title:
  272. mapobj.setMetaData("wms_title",self.capabilities.identification.title)
  273. mapobj.setMetaData("wfs_title",self.capabilities.identification.title)
  274. if self.capabilities.identification.abstract:
  275. mapobj.setMetaData("wms_abstract",self.capabilities.identification.abstract)
  276. mapobj.setMetaData("wfs_abstract",self.capabilities.identification.abstract)
  277. def _getLayerType(self,layer):
  278. """Returns MS layer type based on ogr.Layer.GetGeomType
  279. with ogr:
  280. wkbGeometryCollection = 7
  281. wkbGeometryCollection25D = -2147483641
  282. wkbLineString = 2
  283. wkbLineString25D = -2147483646
  284. wkbLinearRing = 101
  285. wkbMultiLineString = 5
  286. wkbMultiLineString25D = -2147483643
  287. wkbMultiPoint = 4
  288. wkbMultiPoint25D = -2147483644
  289. wkbMultiPolygon = 6
  290. wkbMultiPolygon25D = -2147483642
  291. wkbNDR = 1
  292. wkbNone = 100
  293. wkbPoint = 1
  294. wkbPoint25D = -2147483647
  295. wkbPolygon = 3
  296. wkbPolygon25D = -2147483645
  297. wkbUnknown = 0
  298. """
  299. geomType = layer.GetGeomType()
  300. if geomType == 0: # unknown
  301. # brutal force way
  302. f = layer.GetNextFeature()
  303. if f:
  304. gr = f.GetGeometryRef()
  305. geomType = gr.GetGeometryType()
  306. if geomType in [ogr.wkbPolygon,
  307. ogr.wkbMultiPolygon,
  308. ogr.wkbLinearRing]:
  309. return mapscript.MS_LAYER_POLYGON
  310. elif geomType in [ogr.wkbLineString,
  311. ogr.wkbMultiLineString]:
  312. return mapscript.MS_LAYER_LINE
  313. else:
  314. return mapscript.MS_LAYER_POINT
  315. def __getLayerCrs(self,crss):
  316. """
  317. Returns bests (non-degree) coordinate system of the layer, which is
  318. available.
  319. Ofcourse, sometimes, there is no other option, there EPSG:4326, but
  320. take somethign else, if you can
  321. """
  322. for crs in crss:
  323. if crs.getcode() == "EPSG:4326":
  324. return crs
  325. return crss[0]
  326. def __clear(self,datadir,typename):
  327. """Remove all cached files with following typename
  328. """
  329. for i in os.listdir(datadir):
  330. if i.find(typename) == 0:
  331. logging.debug("Removing pre-cached file %s"% i)
  332. os.remove(os.path.join(datadir,i))