1#!/usr/local/bin/python3.8 2 3############################################################################ 4# 5# MODULE: v.in.wfs 6# AUTHOR(S): Markus Neteler. neteler itc it 7# Hamish Bowman 8# Converted to Python by Glynn Clements 9# PURPOSE: WFS support 10# COPYRIGHT: (C) 2006-2012 Markus Neteler and the GRASS Development Team 11# 12# This program is free software under the GNU General 13# Public License (>=v2). Read the file COPYING that 14# comes with GRASS for details. 15# 16# GetFeature example: 17# http://mapserver.gdf-hannover.de/cgi-bin/grassuserwfs?REQUEST=GetFeature&SERVICE=WFS&VERSION=1.0.0 18############################################################################# 19 20# 21# TODO: suggest to depend on the OWSLib for OGC web service needs 22# http://pypi.python.org/pypi/OWSLib 23# 24 25#%Module 26#% description: Imports GetFeature from a WFS server. 27#% keyword: vector 28#% keyword: import 29#% keyword: OGC web services 30#% keyword: OGC WFS 31#%end 32#%option 33#% key: url 34#% type: string 35#% description: Base URL starting with 'http' and ending in '?' 36#% required: yes 37#%end 38#%option G_OPT_V_OUTPUT 39#%end 40#%option 41#% key: name 42#% type: string 43#% description: Comma separated names of data layers to download 44#% multiple: yes 45#% required: no 46#%end 47#%option 48#% key: srs 49#% type: string 50#% label: Specify alternate spatial reference system (example: EPSG:4326) 51#% description: The given code must be supported by the server, consult the capabilities file 52#% required: no 53#%end 54#%option 55#% key: maximum_features 56#% type: integer 57#% label: Maximum number of features to download 58#% description: (default: unlimited) 59#%end 60#%option 61#% key: start_index 62#% type: integer 63#% label: Skip earlier feature IDs and start downloading at this one 64#% description: (default: start with the first feature) 65#%end 66#%option 67#% key: username 68#% type: string 69#% required: no 70#% multiple: no 71#% label: Username or file with username or environment variable name with username 72#%end 73#%option 74#% key: password 75#% type: string 76#% required: no 77#% multiple: no 78#% label: Password or file with password or environment variable name with password 79#%end 80#%flag 81#% key: l 82# todo #% description: List available layers and exit 83#% description: Download server capabilities to 'wms_capabilities.xml' in the current directory and exit 84#% suppress_required: yes 85#%end 86#%flag 87#% key: r 88#% description: Restrict fetch to features which touch the current region 89#%end 90 91 92import os 93import sys 94from grass.script.utils import try_remove 95from grass.script import core as grass 96try: 97 from urllib2 import urlopen, URLError, HTTPError 98 from urllib2 import build_opener, install_opener 99 from urllib2 import HTTPPasswordMgrWithDefaultRealm, HTTPBasicAuthHandler 100except ImportError: 101 from urllib.request import urlopen 102 from urllib.request import build_opener, install_opener 103 from urllib.request import HTTPPasswordMgrWithDefaultRealm, HTTPBasicAuthHandler 104 from urllib.error import URLError, HTTPError 105 106 107def main(): 108 out = options['output'] 109 wfs_url = options['url'] 110 111 request_base = 'REQUEST=GetFeature&SERVICE=WFS&VERSION=1.0.0' 112 wfs_url += request_base 113 114 if options['name']: 115 wfs_url += '&TYPENAME=' + options['name'] 116 117 if options['srs']: 118 wfs_url += '&SRS=' + options['srs'] 119 120 if options['maximum_features']: 121 wfs_url += '&MAXFEATURES=' + options['maximum_features'] 122 if int(options['maximum_features']) < 1: 123 # GTC Invalid WFS maximum features parameter 124 grass.fatal(_("Invalid maximum number of features")) 125 126 if options['start_index']: 127 wfs_url += '&STARTINDEX=' + options['start_index'] 128 if int(options['start_index']) < 1: 129 # GTC Invalid WFS start index parameter 130 grass.fatal(_('Features begin with index "1"')) 131 132 if flags['r']: 133 bbox = grass.read_command("g.region", flags='w').split('=')[1] 134 wfs_url += '&BBOX=' + bbox 135 136 if flags['l']: 137 wfs_url = options['url'] + 'REQUEST=GetCapabilities&SERVICE=WFS&VERSION=1.0.0' 138 139 tmp = grass.tempfile() 140 tmpxml = tmp + '.xml' 141 142 grass.debug(wfs_url) 143 144 # Set user and password if given 145 if options['username'] and options['password']: 146 grass.message(_("Setting username and password...")) 147 if os.path.isfile(options['username']): 148 with open(options['username']) as f: 149 filecontent = f.read() 150 user = filecontent.strip() 151 elif options['username'] in os.environ: 152 user = os.environ[options['username']] 153 else: 154 user = options['username'] 155 if os.path.isfile(options['password']): 156 with open(options['password']) as f: 157 filecontent = f.read() 158 pw = filecontent.strip() 159 elif options['password'] in os.environ: 160 pw = os.environ[options['password']] 161 else: 162 pw = options['password'] 163 164 passmgr = HTTPPasswordMgrWithDefaultRealm() 165 passmgr.add_password(None, wfs_url,user, pw) 166 authhandler = HTTPBasicAuthHandler(passmgr) 167 opener = build_opener(authhandler) 168 install_opener(opener) 169 170 # GTC Downloading WFS features 171 grass.message(_("Retrieving data...")) 172 try: 173 inf = urlopen(wfs_url) 174 except HTTPError as e: 175 # GTC WFS request HTTP failure 176 grass.fatal(_("The server couldn't fulfill the request.\nError code: %s") % e.code) 177 except URLError as e: 178 # GTC WFS request network failure 179 grass.fatal(_("Failed to reach the server.\nReason: %s") % e.reason) 180 181 outf = open(tmpxml, 'wb') 182 while True: 183 s = inf.read() 184 if not s: 185 break 186 outf.write(s) 187 inf.close() 188 outf.close() 189 190 if flags['l']: 191 import shutil 192 if os.path.exists('wms_capabilities.xml'): 193 grass.fatal(_('A file called "wms_capabilities.xml" already exists here')) 194 # os.move() might fail if the temp file is on another volume, so we copy instead 195 shutil.copy(tmpxml, 'wms_capabilities.xml') 196 try_remove(tmpxml) 197 sys.exit(0) 198 199 grass.message(_("Importing data...")) 200 try: 201 grass.run_command('v.in.ogr', flags='o', input=tmpxml, output=out) 202 grass.message(_("Vector map <%s> imported from WFS.") % out) 203 except: 204 grass.message(_("WFS import failed")) 205 finally: 206 try_remove(tmpxml) 207 208 209if __name__ == "__main__": 210 options, flags = grass.parser() 211 main() 212