cap_interface.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428
  1. """
  2. @package web_services.cap_interface
  3. @brief Provides common interface for GUI web_services.widgets to capabilities data of web services.
  4. List of classes:
  5. - cap_interface::CapabilitiesBase
  6. - cap_interface::LayerBase
  7. - cap_interface::WMSCapabilities
  8. - cap_interface::WMSLayer
  9. - cap_interface::WMTSCapabilities
  10. - cap_interface::WMTSLayer
  11. - cap_interface::OnEarthCapabilities
  12. - cap_interface::OnEarthLayer
  13. (C) 2012 by the GRASS Development Team
  14. This program is free software under the GNU General Public License
  15. (>=v2). Read the file COPYING that comes with GRASS for details.
  16. @author Stepan Turek <stepan.turek seznam.cz> (Mentor: Martin Landa)
  17. """
  18. import os
  19. import sys
  20. WMSLibPath = os.path.join(os.getenv("GISBASE"), "etc", "r.in.wms")
  21. if WMSLibPath not in sys.path:
  22. sys.path.append(WMSLibPath)
  23. # Import only after the path has been set up.
  24. from wms_cap_parsers import ( # noqa: E402
  25. WMSCapabilitiesTree,
  26. WMTSCapabilitiesTree,
  27. OnEarthCapabilitiesTree,
  28. )
  29. class CapabilitiesBase:
  30. def GetLayerByName(self, name):
  31. """Find layer by name
  32. """
  33. for layer in self.layers_by_id:
  34. if name == layer.GetLayerData("name"):
  35. return layer
  36. return None
  37. def GetRootLayer(self):
  38. """Get children layers
  39. """
  40. if self.layers_by_id:
  41. return self.layers_by_id[0]
  42. else:
  43. return None
  44. class LayerBase:
  45. def GetId(self):
  46. """Get layer id
  47. """
  48. return self.id
  49. def GetChildren(self):
  50. """Get children layers
  51. """
  52. return self.child_layers
  53. def GetLayerNode(self):
  54. """Get layer node
  55. """
  56. return self.layer_node
  57. def AddChildLayer(self, layer):
  58. """Add child layer
  59. """
  60. self.child_layers.append(layer)
  61. class WMSCapabilities(CapabilitiesBase, WMSCapabilitiesTree):
  62. def __init__(self, cap_file, force_version=None):
  63. """Create common interface for web_services.widgets to WMS
  64. capabilities data
  65. """
  66. # checks all elements needed for creation of GetMap requests
  67. # by r.in.wms/d.wms modules, invalid elements are removed
  68. WMSCapabilitiesTree.__init__(self, cap_file, force_version)
  69. self.cap_node = self.getroot().find(self.xml_ns.Ns("Capability"))
  70. self.root_layer = self.cap_node.find(self.xml_ns.Ns("Layer"))
  71. self.layers_by_id = {}
  72. self._initializeLayerTree(self.root_layer)
  73. def _initializeLayerTree(self, parent_layer, id=0):
  74. """Build tree, which represents layers
  75. """
  76. if id == 0:
  77. parent_layer = WMSLayer(parent_layer, id, self)
  78. self.layers_by_id[id] = parent_layer
  79. id += 1
  80. layer_nodes = parent_layer.GetLayerNode().findall((self.xml_ns.Ns("Layer")))
  81. for node in layer_nodes:
  82. layer = WMSLayer(node, id, self)
  83. parent_layer.AddChildLayer(layer)
  84. self.layers_by_id[id] = layer
  85. id += 1
  86. id = self._initializeLayerTree(layer, id)
  87. return id
  88. def GetFormats(self):
  89. """Get supported formats
  90. """
  91. request_node = self.cap_node.find(self.xml_ns.Ns("Request"))
  92. get_map_node = request_node.find(self.xml_ns.Ns("GetMap"))
  93. format_nodes = get_map_node.findall(self.xml_ns.Ns("Format"))
  94. formats = []
  95. for node in format_nodes:
  96. formats.append(node.text)
  97. return formats
  98. class WMSLayer(LayerBase):
  99. def __init__(self, layer_node, id, cap):
  100. """Common interface for web_services.widgets to WMS
  101. capabilities <Layer> element
  102. """
  103. self.id = id
  104. self.cap = cap
  105. self.child_layers = []
  106. self.layer_node = layer_node
  107. self.xml_ns = self.cap.getxmlnshandler()
  108. def GetLayerData(self, param):
  109. """Get layer data"""
  110. title = self.xml_ns.Ns("Title")
  111. name = self.xml_ns.Ns("Name")
  112. if param == 'title':
  113. title_node = self.layer_node.find(title)
  114. if title_node is not None:
  115. return title_node.text
  116. else:
  117. return None
  118. if param == 'name':
  119. name_node = self.layer_node.find(name)
  120. if name_node is not None:
  121. return name_node.text
  122. else:
  123. return None
  124. if param == 'format':
  125. return self.cap.GetFormats()
  126. if param == 'styles':
  127. styles = []
  128. style = self.xml_ns.Ns("Style")
  129. for style_node in self.layer_node.findall(style):
  130. style_name = ''
  131. style_title = ''
  132. if style_node.find(title) is not None:
  133. style_title = style_node.find(title).text
  134. if style_node.find(name) is not None:
  135. style_name = style_node.find(name).text
  136. styles.append({'title': style_title,
  137. 'name': style_name,
  138. 'isDefault': False})
  139. return styles
  140. if param == 'srs':
  141. projs_nodes = self.layer_node.findall(
  142. self.xml_ns.Ns(self.cap.getprojtag()))
  143. projs = []
  144. if projs_nodes is None:
  145. return projs
  146. for p in projs_nodes:
  147. projs.append(p.text.strip())
  148. return projs
  149. def IsRequestable(self):
  150. """Is it possible to use the layer for WMS GetMap request?
  151. """
  152. name = self.xml_ns.Ns("Name")
  153. name_node = self.layer_node.find(name)
  154. if name_node is not None:
  155. return True
  156. else:
  157. return False
  158. class WMTSCapabilities(CapabilitiesBase, WMTSCapabilitiesTree):
  159. def __init__(self, cap_file):
  160. """Create common interface for web_services.widgets to WMTS
  161. capabilities data
  162. """
  163. # checks all elements needed for creation of GetTile requests
  164. # by r.in.wms/d.wms modules, invalid elements are removed
  165. WMTSCapabilitiesTree.__init__(self, cap_file)
  166. contents = self._find(self.getroot(), 'Contents', self.xml_ns.NsWmts)
  167. layers = self._findall(contents, 'Layer', self.xml_ns.NsWmts)
  168. self.layers_by_id = {}
  169. id = 0
  170. root_layer = WMTSLayer(None, id, self)
  171. self.layers_by_id[id] = root_layer
  172. for layer_node in layers:
  173. id += 1
  174. self.layers_by_id[id] = WMTSLayer(layer_node, id, self)
  175. root_layer.child_layers.append(self.layers_by_id[id])
  176. class WMTSLayer(LayerBase):
  177. def __init__(self, layer_node, id, cap):
  178. """Common interface for web_services.widgets to WMTS
  179. capabilities <Layer> element
  180. """
  181. self.id = id
  182. self.cap = cap
  183. self.child_layers = []
  184. self.layer_node = layer_node
  185. self.xml_ns = self.cap.getxmlnshandler()
  186. self.projs = self._getProjs()
  187. def GetLayerData(self, param):
  188. """Get layer data
  189. """
  190. title = self.xml_ns.NsOws("Title")
  191. name = self.xml_ns.NsOws("Identifier")
  192. if self.layer_node is None and param in ['title', 'name']:
  193. return None
  194. elif self.layer_node is None:
  195. return []
  196. if param == 'title':
  197. title_node = self.layer_node.find(title)
  198. if title_node is not None:
  199. return title_node.text
  200. else:
  201. return None
  202. if param == 'name':
  203. name_node = self.layer_node.find(name)
  204. if name_node is not None:
  205. return name_node.text
  206. else:
  207. return None
  208. if param == 'styles':
  209. styles = []
  210. for style_node in self.layer_node.findall(
  211. self.xml_ns.NsWmts("Style")):
  212. style_name = ''
  213. style_title = ''
  214. if style_node.find(title) is not None:
  215. style_title = style_node.find(title).text
  216. if style_node.find(name) is not None:
  217. style_name = style_node.find(name).text
  218. is_def = False
  219. if 'isDefault' in style_node.attrib and\
  220. style_node.attrib['isDefault'] == 'true':
  221. is_def = True
  222. styles.append({'title': style_title,
  223. 'name': style_name,
  224. 'isDefault': is_def})
  225. return styles
  226. if param == 'format':
  227. formats = []
  228. for frmt in self.layer_node.findall(self.xml_ns.NsWmts('Format')):
  229. formats.append(frmt.text.strip())
  230. return formats
  231. if param == 'srs':
  232. return self.projs
  233. def _getProjs(self):
  234. """Get layer projections
  235. """
  236. layer_projs = []
  237. if self.layer_node is None:
  238. return layer_projs
  239. mat_set_links = self.layer_node.findall(
  240. self.xml_ns.NsWmts('TileMatrixSetLink'))
  241. contents = self.cap.getroot().find(self.xml_ns.NsWmts('Contents'))
  242. tileMatrixSets = contents.findall(self.xml_ns.NsWmts('TileMatrixSet'))
  243. for link in mat_set_links:
  244. mat_set_link_id = link.find(
  245. self.xml_ns.NsWmts('TileMatrixSet')).text
  246. if not mat_set_link_id:
  247. continue
  248. for mat_set in tileMatrixSets:
  249. mat_set_id = mat_set.find(self.xml_ns.NsOws('Identifier')).text
  250. if mat_set_id and mat_set_id != mat_set_link_id:
  251. continue
  252. mat_set_srs = mat_set.find(
  253. self.xml_ns.NsOws('SupportedCRS')).text.strip()
  254. layer_projs.append(mat_set_srs)
  255. return layer_projs
  256. def IsRequestable(self):
  257. """Is it possible to use the layer for WMTS request?
  258. """
  259. if self.layer_node is None:
  260. return False
  261. else:
  262. return True
  263. class OnEarthCapabilities(CapabilitiesBase, OnEarthCapabilitiesTree):
  264. def __init__(self, cap_file):
  265. """Create Common interface for web_services.widgets to
  266. NASA OnEarth tile service data (equivalent to WMS, WMTS
  267. capabilities data)
  268. """
  269. # checks all elements needed for creation of GetMap requests
  270. # by r.in.wms/d.wms modules, invalid elements are removed
  271. OnEarthCapabilitiesTree.__init__(self, cap_file)
  272. self.layers_by_id = {}
  273. self._initializeLayerTree(self.getroot())
  274. def _initializeLayerTree(self, parent_layer, id=0):
  275. """Build tree, which represents layers
  276. """
  277. if id == 0:
  278. tiled_patterns = parent_layer.find('TiledPatterns')
  279. layer_nodes = tiled_patterns.findall('TiledGroup')
  280. layer_nodes += tiled_patterns.findall('TiledGroups')
  281. parent_layer = OnEarthLayer(None, None, id, self)
  282. self.layers_by_id[id] = parent_layer
  283. id += 1
  284. else:
  285. layer_nodes = parent_layer.layer_node.findall('TiledGroup')
  286. layer_nodes += parent_layer.layer_node.findall('TiledGroups')
  287. for layer_node in layer_nodes:
  288. layer = OnEarthLayer(layer_node, parent_layer, id, self)
  289. self.layers_by_id[id] = layer
  290. id += 1
  291. parent_layer.child_layers.append(layer)
  292. if layer_node.tag == 'TiledGroups':
  293. id = self._initializeLayerTree(layer, id)
  294. return id
  295. class OnEarthLayer(LayerBase):
  296. def __init__(self, layer_node, parent_layer, id, cap):
  297. """Common interface for web_services.widgets to NASA Earth
  298. capabilities <TiledGroup>/<TiledGroups> element
  299. (equivalent to WMS, WMTS <Layer> element)
  300. """
  301. self.id = id
  302. self.cap = cap
  303. self.layer_node = layer_node
  304. self.child_layers = []
  305. self.parent_layer = parent_layer
  306. def IsRequestable(self):
  307. """Is it possible to use the layer for NASA OnEarth GetMap request?
  308. """
  309. if self.layer_node is None or \
  310. self.layer_node.tag == 'TiledGroups':
  311. return False
  312. else:
  313. return True
  314. def GetLayerData(self, param):
  315. """Get layer data
  316. """
  317. if self.layer_node is None and param in ['title', 'name']:
  318. return None
  319. elif self.layer_node is None:
  320. return []
  321. if param == 'title':
  322. title_node = self.layer_node.find("Title")
  323. if title_node is not None:
  324. return title_node.text
  325. else:
  326. return None
  327. if param == 'name':
  328. name_node = self.layer_node.find("Name")
  329. if name_node is not None:
  330. return name_node.text
  331. else:
  332. return None
  333. if param == 'styles':
  334. return []
  335. if param == 'format':
  336. return []
  337. if param == 'srs':
  338. return []