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