1"""! 2@brief GDAL WMS driver. 3 4List of classes: 5 - wms_drv::NullDevice 6 - wms_drv::WMSGdalDrv 7 8(C) 2012-2021 by the GRASS Development Team 9 10This program is free software under the GNU General Public License 11(>=v2). Read the file COPYING that comes with GRASS for details. 12 13@author Stepan Turek <stepan.turek seznam.cz> (Mentor: Martin Landa) 14""" 15 16import os 17import grass.script as grass 18 19try: 20 from osgeo import gdal 21 from osgeo import gdalconst 22except: 23 grass.fatal(_("Unable to load GDAL Python bindings (requires package 'python-gdal' being installed)")) 24 25import xml.etree.ElementTree as etree 26 27from wms_base import WMSBase, GetSRSParamVal 28 29 30class NullDevice(): 31 32 def write(self, s): 33 pass 34 35 36class WMSGdalDrv(WMSBase): 37 def __init__(self, createopt): 38 super(WMSGdalDrv, self).__init__() 39 self.proxy = None 40 self.proxy_user_pw = None 41 self.createopt = createopt 42 43 def setProxy(self, proxy, proxy_user_pw=None): 44 """ Set the HTTP proxy and its user and password 45 46 @input proxy HTTP proxy with [IP address]:[port] 47 @input proxy_user_pw with [user name]:[password] 48 """ 49 self.proxy = proxy 50 self.proxy_user_pw = proxy_user_pw 51 52 def _createXML(self): 53 """!Create XML for GDAL WMS driver 54 55 @return path to XML file 56 """ 57 self._debug("_createXML", "started") 58 59 gdal_wms = etree.Element("GDAL_WMS") 60 service = etree.SubElement(gdal_wms, "Service") 61 name = etree.Element("name") 62 service.set("name", "WMS") 63 64 version = etree.SubElement(service, "Version") 65 version.text = self.params['wms_version'] 66 67 server_url = etree.SubElement(service, "ServerUrl") 68 server_url.text = self.params['url'] 69 70 srs = etree.SubElement(service, self.params['proj_name']) 71 srs.text = GetSRSParamVal(self.params['srs']) 72 73 image_format = etree.SubElement(service, "ImageFormat") 74 image_format.text = self.params['format'] 75 76 image_format = etree.SubElement(service, "Transparent") 77 image_format.text = self.params['transparent'] 78 79 layers = etree.SubElement(service, "Layers") 80 layers.text = self.params['layers'] 81 82 styles = etree.SubElement(service, "Styles") 83 styles.text = self.params['styles'] 84 85 data_window = etree.SubElement(gdal_wms, "DataWindow") 86 87 upper_left_x = etree.SubElement(data_window, "UpperLeftX") 88 upper_left_x.text = str(self.bbox['minx']) 89 90 upper_left_y = etree.SubElement(data_window, "UpperLeftY") 91 upper_left_y.text = str(self.bbox['maxy']) 92 93 lower_right_x = etree.SubElement(data_window, "LowerRightX") 94 lower_right_x.text = str(self.bbox['maxx']) 95 96 lower_right_y = etree.SubElement(data_window, "LowerRightY") 97 lower_right_y.text = str(self.bbox['miny']) 98 99 size_x = etree.SubElement(data_window, "SizeX") 100 size_x.text = str(self.region['cols']) 101 102 size_y = etree.SubElement(data_window, "SizeY") 103 size_y.text = str(self.region['rows']) 104 105 # RGB + alpha 106 self.temp_map_bands_num = 4 107 block_size_x = etree.SubElement(gdal_wms, "BandsCount") 108 block_size_x.text = str(self.temp_map_bands_num) 109 110 block_size_x = etree.SubElement(gdal_wms, "BlockSizeX") 111 block_size_x.text = str(self.tile_size['cols']) 112 113 block_size_y = etree.SubElement(gdal_wms, "BlockSizeY") 114 block_size_y.text = str(self.tile_size['rows']) 115 116 if self.params['username'] and self.params['password']: 117 user_password = etree.SubElement(gdal_wms, "UserPwd") 118 user_password.text = "%s:%s" % (self.params['username'], self.params['password']) 119 120 xml_file = self._tempfile() 121 122 etree.ElementTree(gdal_wms).write(xml_file) 123 124 self._debug("_createXML", "finished -> %s" % xml_file) 125 126 return xml_file 127 128 def _download(self): 129 """!Downloads data from WMS server using GDAL WMS driver 130 131 @return temp_map with stored downloaded data 132 """ 133 grass.message("Downloading data from WMS server...") 134 135 # GDAL WMS driver does not flip geographic coordinates 136 # according to WMS standard 1.3.0. 137 if ("+proj=latlong" in self.proj_srs or 138 "+proj=longlat" in self.proj_srs) and \ 139 self.params['wms_version'] == "1.3.0": 140 grass.warning(_("If module will not be able to fetch the data in this " + 141 "geographic projection, \n try 'WMS_GRASS' driver or use WMS version 1.1.1.")) 142 143 self._debug("_download", "started") 144 temp_map = self._tempfile() 145 146 xml_file = self._createXML() 147 148 # print xml file content for debug level 1 149 file = open(xml_file, "r") 150 grass.debug("WMS request XML:\n%s" % file.read(), 1) 151 file.close() 152 153 if self.proxy: 154 gdal.SetConfigOption('GDAL_HTTP_PROXY', str(self.proxy)) 155 if self.proxy_user_pw: 156 gdal.SetConfigOption('GDAL_HTTP_PROXYUSERPWD', str(self.proxy_user_pw)) 157 wms_dataset = gdal.Open(xml_file, gdal.GA_ReadOnly) 158 grass.try_remove(xml_file) 159 if wms_dataset is None: 160 grass.fatal(_("Unable to open GDAL WMS driver")) 161 162 self._debug("_download", "GDAL dataset created") 163 164 driver = gdal.GetDriverByName(self.gdal_drv_format) 165 if driver is None: 166 grass.fatal(_("Unable to find %s driver" % format)) 167 168 metadata = driver.GetMetadata() 169 if gdal.DCAP_CREATECOPY not in metadata or \ 170 metadata[gdal.DCAP_CREATECOPY] == 'NO': 171 grass.fatal(_('Driver %s supports CreateCopy() method.') % self.gdal_drv_name) 172 173 self._debug("_download", "calling GDAL CreateCopy...") 174 175 if self.createopt is None: 176 temp_map_dataset = driver.CreateCopy(temp_map, wms_dataset, 0) 177 else: 178 self._debug("_download", "Using GDAL createopt <%s>" % str(self.createopt)) 179 temp_map_dataset = driver.CreateCopy( 180 temp_map, wms_dataset, 0, self.createopt 181 ) 182 183 if temp_map_dataset is None: 184 grass.fatal(_("Incorrect WMS query")) 185 186 temp_map_dataset = None 187 wms_dataset = None 188 189 self._debug("_download", "finished") 190 191 return temp_map 192