wms_base.py 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817
  1. """!
  2. @brief Preparation of parameters for drivers, which download it, and managing downloaded data.
  3. List of classes:
  4. - wms_base::WMSBase
  5. - wms_base::GRASSImporter
  6. - wms_base::WMSDriversInfo
  7. (C) 2012-2019 by the GRASS Development Team
  8. This program is free software under the GNU General Public License
  9. (>=v2). Read the file COPYING that comes with GRASS for details.
  10. @author Stepan Turek <stepan.turek seznam.cz> (Mentor: Martin Landa)
  11. """
  12. import os
  13. from math import ceil
  14. import base64
  15. try:
  16. from urllib2 import Request, urlopen, HTTPError
  17. from httplib import HTTPException
  18. except ImportError:
  19. from urllib.request import Request, urlopen
  20. from urllib.error import HTTPError
  21. from http.client import HTTPException
  22. import grass.script as grass
  23. from grass.exceptions import CalledModuleError
  24. class WMSBase(object):
  25. def __init__(self):
  26. # these variables are information for destructor
  27. self.temp_files_to_cleanup = []
  28. self.params = {}
  29. self.tile_size = {"bbox": None}
  30. self.temp_map = None
  31. self.temp_warpmap = None
  32. def __del__(self):
  33. # tries to remove temporary files, all files should be
  34. # removed before, implemented just in case of unexpected
  35. # stop of module
  36. for temp_file in self.temp_files_to_cleanup:
  37. grass.try_remove(temp_file)
  38. def _debug(self, fn, msg):
  39. grass.debug("%s.%s: %s" % (self.__class__.__name__, fn, msg))
  40. def _initializeParameters(self, options, flags):
  41. self._debug("_initialize_parameters", "started")
  42. # initialization of module parameters (options, flags)
  43. self.params["driver"] = options["driver"]
  44. drv_info = WMSDriversInfo()
  45. driver_props = drv_info.GetDrvProperties(options["driver"])
  46. self._checkIgnoeredParams(options, flags, driver_props)
  47. self.params["capfile"] = options["capfile"].strip()
  48. for key in ["url", "layers", "styles", "method"]:
  49. self.params[key] = options[key].strip()
  50. self.flags = flags
  51. if self.flags["o"]:
  52. self.params["transparent"] = "FALSE"
  53. else:
  54. self.params["transparent"] = "TRUE"
  55. for key in ["password", "username", "urlparams"]:
  56. self.params[key] = options[key]
  57. if (self.params["password"] and self.params["username"] == "") or (
  58. self.params["password"] == "" and self.params["username"]
  59. ):
  60. grass.fatal(
  61. _(
  62. "Please insert both %s and %s parameters or none of them."
  63. % ("password", "username")
  64. )
  65. )
  66. self.params["bgcolor"] = options["bgcolor"].strip()
  67. if (
  68. options["format"] == "jpeg"
  69. and "format" not in driver_props["ignored_params"]
  70. ):
  71. if not flags["o"] and "WMS" in self.params["driver"]:
  72. grass.warning(_("JPEG format does not support transparency"))
  73. self.params["format"] = drv_info.GetFormat(options["format"])
  74. if not self.params["format"]:
  75. self.params["format"] = self.params["format"]
  76. # TODO: get srs from Tile Service file in OnEarth_GRASS driver
  77. self.params["srs"] = int(options["srs"])
  78. if self.params["srs"] <= 0 and "srs" not in driver_props["ignored_params"]:
  79. grass.fatal(_("Invalid EPSG code %d") % self.params["srs"])
  80. self.params["wms_version"] = options["wms_version"]
  81. if (
  82. "CRS" in GetSRSParamVal(self.params["srs"])
  83. and self.params["wms_version"] == "1.1.1"
  84. ):
  85. self.params["wms_version"] = "1.3.0"
  86. grass.warning(
  87. _(
  88. "WMS version <1.3.0> will be used, because version <1.1.1> does not support <%s>projection"
  89. )
  90. % GetSRSParamVal(self.params["srs"])
  91. )
  92. if self.params["wms_version"] == "1.3.0":
  93. self.params["proj_name"] = "CRS"
  94. else:
  95. self.params["proj_name"] = "SRS"
  96. # read projection info
  97. self.proj_location = grass.read_command("g.proj", flags="jf").rstrip("\n")
  98. self.proj_location = self._modifyProj(self.proj_location)
  99. self.source_epsg = str(GetEpsg(self.params["srs"]))
  100. self.target_epsg = None
  101. target_crs = grass.parse_command("g.proj", flags="g", delimiter="=")
  102. if "epsg" in target_crs.keys():
  103. self.target_epsg = target_crs["epsg"]
  104. if self.source_epsg != self.target_epsg:
  105. grass.warning(
  106. _(
  107. "SRS differences: WMS source EPSG %s != location EPSG %s (use srs=%s to adjust)"
  108. )
  109. % (self.source_epsg, self.target_epsg, self.target_epsg)
  110. )
  111. self.proj_srs = grass.read_command(
  112. "g.proj", flags="jf", epsg=str(GetEpsg(self.params["srs"]))
  113. )
  114. self.proj_srs = self.proj_srs.rstrip("\n")
  115. self.proj_srs = self._modifyProj(self.proj_srs)
  116. if not self.proj_srs or not self.proj_location:
  117. grass.fatal(_("Unable to get projection info"))
  118. self.region = options["region"]
  119. min_tile_size = 100
  120. maxcols = int(options["maxcols"])
  121. if maxcols <= min_tile_size:
  122. grass.fatal(_("Maxcols must be greater than 100"))
  123. maxrows = int(options["maxrows"])
  124. if maxrows <= min_tile_size:
  125. grass.fatal(_("Maxrows must be greater than 100"))
  126. # setting optimal tile size according to maxcols and maxrows constraint
  127. # and region cols and rows
  128. self.tile_size["cols"] = int(
  129. self.region["cols"] / ceil(self.region["cols"] / float(maxcols))
  130. )
  131. self.tile_size["rows"] = int(
  132. self.region["rows"] / ceil(self.region["rows"] / float(maxrows))
  133. )
  134. # default format for GDAL library
  135. self.gdal_drv_format = "GTiff"
  136. self._debug("_initialize_parameters", "finished")
  137. def _modifyProj(self, proj):
  138. """!Modify proj.4 string for usage in this module"""
  139. # add +wktext parameter to avoid droping of +nadgrids parameter (if presented) in gdalwarp
  140. if "+nadgrids=" in proj and " +wktext" not in proj:
  141. proj += " +wktext"
  142. return proj
  143. def _checkIgnoeredParams(self, options, flags, driver_props):
  144. """!Write warnings for set parameters and flags, which chosen driver does not use."""
  145. not_relevant_params = []
  146. for i_param in driver_props["ignored_params"]:
  147. if (
  148. i_param in options
  149. and options[i_param]
  150. and i_param not in ["srs", "wms_version", "format"]
  151. ): # params with default value
  152. not_relevant_params.append("<" + i_param + ">")
  153. if len(not_relevant_params) > 0:
  154. grass.warning(
  155. _(
  156. "These parameter are ignored: %s\n\
  157. %s driver does not support the parameters."
  158. % (",".join(not_relevant_params), options["driver"])
  159. )
  160. )
  161. not_relevant_flags = []
  162. for i_flag in driver_props["ignored_flags"]:
  163. if flags[i_flag]:
  164. not_relevant_flags.append("<" + i_flag + ">")
  165. if len(not_relevant_flags) > 0:
  166. grass.warning(
  167. _(
  168. "These flags are ignored: %s\n\
  169. %s driver does not support the flags."
  170. % (",".join(not_relevant_flags), options["driver"])
  171. )
  172. )
  173. def GetMap(self, options, flags):
  174. """!Download data from WMS server."""
  175. self._initializeParameters(options, flags)
  176. self.bbox = self._computeBbox()
  177. self.temp_map = self._download()
  178. if not self.temp_map:
  179. return
  180. self._reprojectMap()
  181. return self.temp_warpmap
  182. def _fetchCapabilities(self, options):
  183. """!Download capabilities from WMS server"""
  184. cap_url = options["url"].strip()
  185. if "?" in cap_url:
  186. cap_url += "&"
  187. else:
  188. cap_url += "?"
  189. if "WMTS" in options["driver"]:
  190. cap_url += "SERVICE=WMTS&REQUEST=GetCapabilities&VERSION=1.0.0"
  191. elif "OnEarth" in options["driver"]:
  192. cap_url += "REQUEST=GetTileService"
  193. else:
  194. cap_url += (
  195. "SERVICE=WMS&REQUEST=GetCapabilities&VERSION=" + options["wms_version"]
  196. )
  197. if options["urlparams"]:
  198. cap_url += "&" + options["urlparams"]
  199. grass.debug("Fetching capabilities file.\n%s" % cap_url)
  200. try:
  201. cap = self._fetchDataFromServer(
  202. cap_url, options["username"], options["password"]
  203. )
  204. except (IOError, HTTPException) as e:
  205. if isinstance(e, HTTPError) and e.code == 401:
  206. grass.fatal(
  207. _("Authorization failed to <%s> when fetching capabilities")
  208. % options["url"]
  209. )
  210. else:
  211. msg = _("Unable to fetch capabilities from <{0}>. Reason: ").format(
  212. options["url"]
  213. )
  214. if hasattr(e, "reason"):
  215. msg += "{0}".format(e.reason)
  216. else:
  217. msg += "{0}".format(e)
  218. grass.fatal(msg)
  219. grass.debug("Fetching capabilities OK")
  220. return grass.decode(cap.read())
  221. def _fetchDataFromServer(self, url, username=None, password=None):
  222. """!Fetch data from server"""
  223. request = Request(url)
  224. if username and password:
  225. base64string = base64.encodestring("%s:%s" % (username, password)).replace(
  226. "\n", ""
  227. )
  228. request.add_header("Authorization", "Basic %s" % base64string)
  229. try:
  230. return urlopen(request)
  231. except ValueError as error:
  232. grass.fatal("%s" % error)
  233. def GetCapabilities(self, options):
  234. """!Get capabilities from WMS server"""
  235. cap = self._fetchCapabilities(options)
  236. capfile_output = options["capfile_output"].strip()
  237. # save to file
  238. if capfile_output:
  239. try:
  240. with open(capfile_output, "w") as temp:
  241. temp.write(cap)
  242. return
  243. except IOError as error:
  244. grass.fatal(
  245. _("Unable to open file '%s'.\n%s\n" % (capfile_output, error))
  246. )
  247. # print to output
  248. print(cap)
  249. def _computeBbox(self):
  250. """!Get region extent for WMS query (bbox)"""
  251. self._debug("_computeBbox", "started")
  252. bbox_region_items = {"maxy": "n", "miny": "s", "maxx": "e", "minx": "w"}
  253. bbox = {}
  254. if self.proj_srs == self.proj_location: # TODO: do it better
  255. for bbox_item, region_item in bbox_region_items.items():
  256. bbox[bbox_item] = self.region[region_item]
  257. # if location projection and wms query projection are
  258. # different, corner points of region are transformed into wms
  259. # projection and then bbox is created from extreme coordinates
  260. # of the transformed points
  261. else:
  262. for bbox_item, region_item in bbox_region_items.items():
  263. bbox[bbox_item] = None
  264. temp_region = self._tempfile()
  265. try:
  266. temp_region_opened = open(temp_region, "w")
  267. temp_region_opened.write(
  268. "%f %f\n%f %f\n%f %f\n%f %f\n"
  269. % (
  270. self.region["e"],
  271. self.region["n"],
  272. self.region["w"],
  273. self.region["n"],
  274. self.region["w"],
  275. self.region["s"],
  276. self.region["e"],
  277. self.region["s"],
  278. )
  279. )
  280. except IOError:
  281. grass.fatal(_("Unable to write data into tempfile"))
  282. finally:
  283. temp_region_opened.close()
  284. points = grass.read_command(
  285. "m.proj",
  286. flags="d",
  287. proj_out=self.proj_srs,
  288. proj_in=self.proj_location,
  289. input=temp_region,
  290. quiet=True,
  291. ) # TODO: stdin
  292. grass.try_remove(temp_region)
  293. if not points:
  294. grass.fatal(_("Unable to determine region, %s failed") % "m.proj")
  295. points = points.splitlines()
  296. if len(points) != 4:
  297. grass.fatal(_("Region definition: 4 points required"))
  298. for point in points:
  299. try:
  300. point = list(map(float, point.split("|")))
  301. except ValueError:
  302. grass.fatal(_("Reprojection of region using m.proj failed."))
  303. if not bbox["maxy"]:
  304. bbox["maxy"] = point[1]
  305. bbox["miny"] = point[1]
  306. bbox["maxx"] = point[0]
  307. bbox["minx"] = point[0]
  308. continue
  309. if bbox["maxy"] < point[1]:
  310. bbox["maxy"] = point[1]
  311. elif bbox["miny"] > point[1]:
  312. bbox["miny"] = point[1]
  313. if bbox["maxx"] < point[0]:
  314. bbox["maxx"] = point[0]
  315. elif bbox["minx"] > point[0]:
  316. bbox["minx"] = point[0]
  317. self._debug("_computeBbox", "finished -> %s" % bbox)
  318. # Ordering of coordinates axis of geographic coordinate
  319. # systems in WMS 1.3.0 is flipped. If self.tile_size['flip_coords'] is
  320. # True, coords in bbox need to be flipped in WMS query.
  321. return bbox
  322. def _reprojectMap(self):
  323. """!Reproject data using gdalwarp if needed"""
  324. # reprojection of raster
  325. do_reproject = True
  326. if (
  327. self.source_epsg is not None
  328. and self.target_epsg is not None
  329. and self.source_epsg == self.target_epsg
  330. ):
  331. do_reproject = False
  332. # TODO: correctly compare source and target crs
  333. if do_reproject and self.proj_srs == self.proj_location:
  334. do_reproject = False
  335. if do_reproject:
  336. grass.message(_("Reprojecting raster..."))
  337. self.temp_warpmap = grass.tempfile() + ".tif"
  338. if int(os.getenv("GRASS_VERBOSE", "2")) <= 2:
  339. nuldev = open(os.devnull, "w+")
  340. else:
  341. nuldev = None
  342. if self.params["method"] == "nearest":
  343. gdal_method = "near"
  344. elif self.params["method"] == "linear":
  345. gdal_method = "bilinear"
  346. else:
  347. gdal_method = self.params["method"]
  348. # RGB rasters - alpha layer is added for cropping edges of projected raster
  349. try:
  350. if self.temp_map_bands_num == 3:
  351. ps = grass.Popen(
  352. [
  353. "gdalwarp",
  354. "-s_srs",
  355. "%s" % self.proj_srs,
  356. "-t_srs",
  357. "%s" % self.proj_location,
  358. "-r",
  359. gdal_method,
  360. "-dstalpha",
  361. self.temp_map,
  362. self.temp_warpmap,
  363. ],
  364. stdout=nuldev,
  365. )
  366. # RGBA rasters
  367. else:
  368. ps = grass.Popen(
  369. [
  370. "gdalwarp",
  371. "-s_srs",
  372. "%s" % self.proj_srs,
  373. "-t_srs",
  374. "%s" % self.proj_location,
  375. "-r",
  376. gdal_method,
  377. self.temp_map,
  378. self.temp_warpmap,
  379. ],
  380. stdout=nuldev,
  381. )
  382. ps.wait()
  383. except OSError as e:
  384. grass.fatal(
  385. "%s \nThis can be caused by missing %s utility. " % (e, "gdalwarp")
  386. )
  387. if nuldev:
  388. nuldev.close()
  389. if ps.returncode != 0:
  390. grass.fatal(_("%s failed") % "gdalwarp")
  391. grass.try_remove(self.temp_map)
  392. # raster projection is same as projection of location
  393. else:
  394. self.temp_warpmap = self.temp_map
  395. self.temp_files_to_cleanup.remove(self.temp_map)
  396. return self.temp_warpmap
  397. def _tempfile(self):
  398. """!Create temp_file and append list self.temp_files_to_cleanup
  399. with path of file
  400. @return string path to temp_file
  401. """
  402. temp_file = grass.tempfile()
  403. if temp_file is None:
  404. grass.fatal(_("Unable to create temporary files"))
  405. # list of created tempfiles for destructor
  406. self.temp_files_to_cleanup.append(temp_file)
  407. return temp_file
  408. class GRASSImporter:
  409. def __init__(self, opt_output, cleanup_bands):
  410. self.cleanup_mask = False
  411. self.cleanup_bands = cleanup_bands
  412. # output map name
  413. self.opt_output = opt_output
  414. # suffix for existing mask (during overriding will be saved
  415. # into raster named:self.opt_output + this suffix)
  416. self.original_mask_suffix = "_temp_MASK"
  417. # check names of temporary rasters, which module may create
  418. maps = []
  419. for suffix in (".red", ".green", ".blue", ".alpha", self.original_mask_suffix):
  420. rast = self.opt_output + suffix
  421. if grass.find_file(rast, element="cell", mapset=".")["file"]:
  422. maps.append(rast)
  423. if len(maps) != 0:
  424. grass.fatal(
  425. _(
  426. "Please change output name, or change names of these rasters: %s, "
  427. "module needs to create this temporary maps during execution."
  428. )
  429. % ",".join(maps)
  430. )
  431. def __del__(self):
  432. # removes temporary mask, used for import transparent or warped temp_map
  433. if self.cleanup_mask:
  434. # clear temporary mask, which was set by module
  435. try:
  436. grass.run_command("r.mask", quiet=True, flags="r")
  437. except CalledModuleError:
  438. grass.fatal(_("%s failed") % "r.mask")
  439. # restore original mask, if exists
  440. if grass.find_file(
  441. self.opt_output + self.original_mask_suffix, element="cell", mapset="."
  442. )["name"]:
  443. try:
  444. mask_copy = self.opt_output + self.original_mask_suffix
  445. grass.run_command("g.copy", quiet=True, raster=mask_copy + ",MASK")
  446. except CalledModuleError:
  447. grass.fatal(_("%s failed") % "g.copy")
  448. # remove temporary created rasters
  449. maps = []
  450. rast = self.opt_output + ".alpha"
  451. if grass.find_file(rast, element="cell", mapset=".")["file"]:
  452. maps.append(rast)
  453. if self.cleanup_bands:
  454. for suffix in (".red", ".green", ".blue", self.original_mask_suffix):
  455. rast = self.opt_output + suffix
  456. if grass.find_file(rast, element="cell", mapset=".")["file"]:
  457. maps.append(rast)
  458. if maps:
  459. grass.run_command(
  460. "g.remove", quiet=True, flags="fb", type="raster", name=",".join(maps)
  461. )
  462. # delete environmental variable which overrides region
  463. if "GRASS_REGION" in os.environ.keys():
  464. os.environ.pop("GRASS_REGION")
  465. maplist = grass.read_command(
  466. "g.list",
  467. type="raster",
  468. pattern="%s*" % (self.opt_output),
  469. mapset=".",
  470. separator=",",
  471. ).rstrip("\n")
  472. if len(maplist) == 0:
  473. grass.fatal(_("WMS import failed, nothing imported"))
  474. else:
  475. for raster in maplist.split(","):
  476. grass.raster_history(raster, overwrite=True)
  477. grass.run_command(
  478. "r.support", map=raster, description="generated by r.in.wms"
  479. )
  480. grass.message(_("<%s> created.") % raster)
  481. def ImportMapIntoGRASS(self, raster):
  482. """!Import raster into GRASS."""
  483. # importing temp_map into GRASS
  484. try:
  485. # do not use -o flag !
  486. grass.run_command(
  487. "r.in.gdal",
  488. flags="o",
  489. quiet=True,
  490. overwrite=True,
  491. input=raster,
  492. output=self.opt_output,
  493. )
  494. except CalledModuleError:
  495. grass.fatal(_("%s failed") % "r.in.gdal")
  496. # information for destructor to cleanup temp_layers, created
  497. # with r.in.gdal
  498. # setting region for full extend of imported raster
  499. if grass.find_file(self.opt_output + ".red", element="cell", mapset=".")[
  500. "file"
  501. ]:
  502. region_map = self.opt_output + ".red"
  503. else:
  504. region_map = self.opt_output
  505. os.environ["GRASS_REGION"] = grass.region_env(rast=region_map)
  506. # mask created from alpha layer, which describes real extend
  507. # of warped layer (may not be a rectangle), also mask contains
  508. # transparent parts of raster
  509. if grass.find_file(self.opt_output + ".alpha", element="cell", mapset=".")[
  510. "name"
  511. ]:
  512. # saving current mask (if exists) into temp raster
  513. if grass.find_file("MASK", element="cell", mapset=".")["name"]:
  514. try:
  515. mask_copy = self.opt_output + self.original_mask_suffix
  516. grass.run_command("g.copy", quiet=True, raster="MASK," + mask_copy)
  517. except CalledModuleError:
  518. grass.fatal(_("%s failed") % "g.copy")
  519. # info for destructor
  520. self.cleanup_mask = True
  521. try:
  522. grass.run_command(
  523. "r.mask",
  524. quiet=True,
  525. overwrite=True,
  526. maskcats="0",
  527. flags="i",
  528. raster=self.opt_output + ".alpha",
  529. )
  530. except CalledModuleError:
  531. grass.fatal(_("%s failed") % "r.mask")
  532. if not self.cleanup_bands:
  533. # use the MASK to set NULL vlues
  534. for suffix in (".red", ".green", ".blue"):
  535. rast = self.opt_output + suffix
  536. if grass.find_file(rast, element="cell", mapset=".")["file"]:
  537. grass.run_command(
  538. "g.rename",
  539. rast="%s,%s" % (rast, rast + "_null"),
  540. quiet=True,
  541. )
  542. grass.run_command(
  543. "r.mapcalc",
  544. expression="%s = %s" % (rast, rast + "_null"),
  545. quiet=True,
  546. )
  547. grass.run_command(
  548. "g.remove",
  549. type="raster",
  550. name="%s" % (rast + "_null"),
  551. flags="f",
  552. quiet=True,
  553. )
  554. # TODO one band + alpha band?
  555. if (
  556. grass.find_file(self.opt_output + ".red", element="cell", mapset=".")[
  557. "file"
  558. ]
  559. and self.cleanup_bands
  560. ):
  561. try:
  562. grass.run_command(
  563. "r.composite",
  564. quiet=True,
  565. overwrite=True,
  566. red=self.opt_output + ".red",
  567. green=self.opt_output + ".green",
  568. blue=self.opt_output + ".blue",
  569. output=self.opt_output,
  570. )
  571. except CalledModuleError:
  572. grass.fatal(_("%s failed") % "r.composite")
  573. class WMSDriversInfo:
  574. def __init__(self):
  575. """!Provides information about driver parameters."""
  576. # format labels
  577. self.f_labels = ["geotiff", "tiff", "png", "jpeg", "gif", "png8"]
  578. # form for request
  579. self.formats = [
  580. "image/geotiff",
  581. "image/tiff",
  582. "image/png",
  583. "image/jpeg",
  584. "image/gif",
  585. "image/png8",
  586. ]
  587. self.srs = ("epsg", "ogc")
  588. def GetDrvProperties(self, driver):
  589. """!Get information about driver parameters."""
  590. if driver == "WMS_GDAL":
  591. return self._GDALDrvProperties()
  592. if "WMS" in driver:
  593. return self._WMSProperties()
  594. if "WMTS" in driver:
  595. return self._WMTSProperties()
  596. if "OnEarth" in driver:
  597. return self._OnEarthProperties()
  598. def _OnEarthProperties(self):
  599. props = {}
  600. props["ignored_flags"] = ["o"]
  601. props["ignored_params"] = [
  602. "bgcolor",
  603. "styles",
  604. "capfile_output",
  605. "format",
  606. "srs",
  607. "wms_version",
  608. ]
  609. props["req_multiple_layers"] = False
  610. return props
  611. def _WMSProperties(self):
  612. props = {}
  613. props["ignored_params"] = ["capfile"]
  614. props["ignored_flags"] = []
  615. props["req_multiple_layers"] = True
  616. return props
  617. def _WMTSProperties(self):
  618. props = {}
  619. props["ignored_flags"] = ["o"]
  620. props["ignored_params"] = ["urlparams", "bgcolor", "wms_version"]
  621. props["req_multiple_layers"] = False
  622. return props
  623. def _GDALDrvProperties(self):
  624. props = {}
  625. props["ignored_flags"] = []
  626. props["ignored_params"] = [
  627. "urlparams",
  628. "bgcolor",
  629. "capfile",
  630. "capfile_output",
  631. "username",
  632. "password",
  633. ]
  634. props["req_multiple_layers"] = True
  635. return props
  636. def GetFormatLabel(self, format):
  637. """!Convert format request form to value in parameter 'format'."""
  638. if format in self.formats:
  639. return self.f_labels[self.formats.index(format)]
  640. return None
  641. def GetFormat(self, label):
  642. """!Convert value in parameter 'format' to format request form."""
  643. if label in self.f_labels:
  644. return self.formats[self.f_labels.index(label)]
  645. return None
  646. def GetSrs(self):
  647. """!Get supported srs prefixes (e.g. epsg/crs)
  648. @todo filter according to version and driver params
  649. """
  650. return self.srs
  651. # TODO move to utils?
  652. def GetSRSParamVal(srs):
  653. """!Decides whether to use CRS or EPSG prefix according to srs number."""
  654. if srs in [84, 83, 27]:
  655. return "OGC:CRS{}".format(srs)
  656. else:
  657. return "EPSG:{}".format(srs)
  658. def GetEpsg(srs):
  659. """
  660. @return EPSG number
  661. If srs is CRS number, return EPSG number which corresponds to CRS number.
  662. """
  663. if srs == 84:
  664. return 4326
  665. if srs == 83:
  666. return 4269
  667. if srs == 27:
  668. return 4267
  669. return srs