Skip to content

Commit 7378165

Browse files
committed
release version 0.3
1 parent 5a517b7 commit 7378165

19 files changed

+4375
-3634
lines changed

EOxWCSClient/cmdline_wcs_client.py

-397
This file was deleted.

EOxWCSClient/wcs_client.py

+61-53
Original file line numberDiff line numberDiff line change
@@ -88,9 +88,6 @@
8888
global crs_url
8989
crs_url = 'http://www.opengis.net/def/crs/EPSG/0/'
9090

91-
global namespacemap
92-
namespacemap = {"wcs": "http://www.opengis.net/wcs/2.0", "wcseo": "http://www.opengis.net/wcseo/1.0", "crs": "http://www.opengis.net/wcs/service-extension/crs/1.0", "gml" : "http://www.opengis.net/gml/3.2", "gmlcov" : "http://www.opengis.net/gmlcov/1.0", "ogc" : "http://www.opengis.net/ogc", "ows" : "http://www.opengis.net/ows/2.0", "swe" : "http://www.opengis.net/swe/2.0", "int" : "http://www.opengis.net/WCS_service-extension_interpolation/1.0", "eop" : "http://www.opengis.net/eop/2.0", "om" : "http://www.opengis.net/om/2.0"}
93-
9491

9592
# sets a storage location in case the user doesn't provide one (to be on the save side) - eg. for error msgs.
9693
global temp_storage
@@ -294,14 +291,17 @@ def _set_base_desceocoverageset(self):
294291
'request': '&request=',
295292
'server_url': '',
296293
'eoID': '&eoID=',
297-
'subset_lon': '&subset=Long,'+crs_url+'4326(',
298-
'subset_lat': '&subset=Lat,'+crs_url+'4326(',
294+
# 'subset_lon': '&subset=Long,'+crs_url+'4326(', #@@
295+
# 'subset_lat': '&subset=Lat,'+crs_url+'4326(', #@@
296+
'subset_lon': '&subset=Long(',
297+
'subset_lat': '&subset=Lat(',
299298
'subset_time': '&subset=phenomenonTime(%22',
300299
'containment': '&containment=',
301300
'section': '&section=',
302301
'count': '&count=',
303302
'IDs_only': False}
304303

304+
#print "base_desceocoverageset", base_desceocoverageset
305305
return base_desceocoverageset
306306

307307

@@ -323,7 +323,8 @@ def _set_base_getcov(self):
323323
'subset_y': '&subset=',
324324
'rangesubset': '&rangesubset=',
325325
'outputcrs': '&outputcrs='+crs_url,
326-
'interpolation': '&interpolation=',
326+
'interpolation': '&interpolation=', #@@
327+
# 'interpolation': '&interpolation=nearest-neighbour', #@@
327328
'mediatype': '&mediatype=',
328329
'mask': '&mask=polygon,'+crs_url,
329330
'size_x': '&',
@@ -463,7 +464,7 @@ def DescribeEOCoverageSet(self, input_params):
463464
#/* GetCoverage() */
464465
#/************************************************************************/
465466

466-
def GetCoverage(self, input_params):
467+
def GetCoverage(self, input_params, use_wcs_GCo_call):
467468
"""
468469
Creates a GetCoverage request url based on the input_parameters
469470
and executes the request.
@@ -513,27 +514,29 @@ def GetCoverage(self, input_params):
513514

514515
# provide the same functionality for input as for the cmd-line
515516
# (to get around the url-notation for input)
516-
if input_params.has_key('subset_x') and input_params['subset_x'].startswith('epsg'):
517+
if input_params.has_key('subset_x') and input_params['subset_x'] is not None and input_params['subset_x'].startswith('epsg') :
517518
crs = input_params['subset_x'].split(':')[1].split(' ')[0]
518519
label = input_params['subset_x'].split(':')[1].split(' ')[1]
519520
coord = input_params['subset_x'].split(':')[1].split(' ')[2]
520-
out = label+','+crs_url+crs+'('+coord
521+
# out = label+','+crs_url+crs+'('+coord #@@
522+
out = label+'('+coord
521523
input_params['subset_x'] = out
522-
elif input_params.has_key('subset_x') and (input_params['subset_x'].startswith('pix') or input_params['subset_x'].startswith('ori')):
524+
elif input_params.has_key('subset_x') and input_params['subset_x'] is not None and (input_params['subset_x'].startswith('pix') or input_params['subset_x'].startswith('ori')):
523525
label = input_params['subset_x'].split(' ')[1]
524526
coord = input_params['subset_x'].split(' ')[2]
525527
out = label+'('+coord
526528
input_params['subset_x'] = out
527529
else:
528530
pass
529531

530-
if input_params.has_key('subset_y') and input_params['subset_y'].startswith('epsg'):
532+
if input_params.has_key('subset_y') and input_params['subset_y'] is not None and input_params['subset_y'].startswith('epsg'):
531533
crs = input_params['subset_y'].split(':')[1].split(' ')[0]
532534
label = input_params['subset_y'].split(':')[1].split(' ')[1]
533535
coord = input_params['subset_y'].split(':')[1].split(' ')[2]
534-
out = label+','+crs_url+crs+'('+coord
536+
# out = label+','+crs_url+crs+'('+coord #@@
537+
out = label+'('+coord
535538
input_params['subset_y'] = out
536-
elif input_params.has_key('subset_y') and (input_params['subset_y'].startswith('pix') or input_params['subset_y'].startswith('ori')):
539+
elif input_params.has_key('subset_y') and input_params['subset_y'] is not None and (input_params['subset_y'].startswith('pix') or input_params['subset_y'].startswith('ori')):
537540
label = input_params['subset_y'].split(' ')[1]
538541
coord = input_params['subset_y'].split(' ')[2]
539542
out = label+'('+coord
@@ -542,15 +545,15 @@ def GetCoverage(self, input_params):
542545
pass
543546

544547

545-
if input_params.has_key('size_x'):
548+
if input_params.has_key('size_x') and input_params['size_x'] is not None:
546549
if input_params['size_x'].startswith('siz'):
547550
out = "size="+input_params['size_x'].split(" ")[1]+"("+input_params['size_x'].split(" ")[2]
548551
input_params['size_x'] = out
549552
elif input_params['size_x'].startswith('res'):
550553
out = "resolution="+input_params['size_x'].split(" ")[1]+"("+input_params['size_x'].split(" ")[2]
551554
input_params['size_x'] = out
552555

553-
if input_params.has_key('size_y'):
556+
if input_params.has_key('size_y') and input_params['size_y'] is not None:
554557
if input_params['size_y'].startswith('siz'):
555558
out = "size="+input_params['size_y'].split(" ")[1]+"("+input_params['size_y'].split(" ")[2]
556559
input_params['size_y'] = out
@@ -579,17 +582,17 @@ def _parse_xml(self, in_xml):
579582
"""
580583
join_xml = ''.join(in_xml)
581584
tree = etree.fromstring(join_xml)
582-
tag_ids = tree.xpath("wcs:CoverageDescriptions/wcs:CoverageDescription/wcs:CoverageId/text()", namespaces=namespacemap)
583-
#print tag_ids
584-
585-
## TODO - map each axis and crs to the respective coverage, abd show them accordingly to the selection
586-
587-
# also read out the gml:Envelope axisLabels and srsName - use only first returned entry
588-
axis_labels = tree.xpath("wcs:CoverageDescriptions/wcs:CoverageDescription/gml:boundedBy/gml:Envelope/@axisLabels|wcs:CoverageDescriptions/wcs:CoverageDescription/gml:boundedBy/gml:EnvelopeWithTimePeriod/@axisLabels", namespaces=namespacemap)
585+
try:
586+
tag_ids = tree.xpath("wcs:CoverageDescriptions/wcs:CoverageDescription/wcs:CoverageId/text()", namespaces=tree.nsmap)
587+
#print tag_ids
588+
except etree.XPathEvalError:
589+
raise IndexError
590+
591+
axis_labels = tree.xpath("wcs:CoverageDescriptions/wcs:CoverageDescription/gml:boundedBy/gml:Envelope/@axisLabels|wcs:CoverageDescriptions/wcs:CoverageDescription/gml:boundedBy/gml:EnvelopeWithTimePeriod/@axisLabels", namespaces=tree.nsmap)
589592
#print 'AxisLabels: ', type(axis_labels),len(axis_labels), axis_labels
590593
axis_labels = axis_labels[0].encode().split(" ")
591594
#print axis_labels
592-
offered_crs = tree.xpath("wcs:CoverageDescriptions/wcs:CoverageDescription/gml:boundedBy/gml:Envelope/@srsName|wcs:CoverageDescriptions/wcs:CoverageDescription/gml:boundedBy/gml:EnvelopeWithTimePeriod/@srsName", namespaces=namespacemap)
595+
offered_crs = tree.xpath("wcs:CoverageDescriptions/wcs:CoverageDescription/gml:boundedBy/gml:Envelope/@srsName|wcs:CoverageDescriptions/wcs:CoverageDescription/gml:boundedBy/gml:EnvelopeWithTimePeriod/@srsName", namespaces=tree.nsmap)
593596
offered_crs = os.path.basename(offered_crs[0])
594597
#print offered_crs
595598
if len(axis_labels) == 0:
@@ -611,34 +614,40 @@ def _execute_xml_request(self, http_request, IDs_only=False):
611614
Output: prints out the submitted http_request or Error_XML in case of failure
612615
"""
613616
# print "I'm in "+sys._getframe().f_code.co_name
614-
print http_request
617+
print 'REQUEST: ',http_request #@@
615618

616619
try:
617-
# access the url
618-
request_handle = urllib2.urlopen(http_request)
619-
# read the content of the url
620-
result_xml = request_handle.read()
620+
# create a request object,
621+
request_handle = urllib2.Request(http_request, headers={'User-Agent': 'Python-urllib/2.7,QgsWcsClient-plugin'})
622+
response = urllib2.urlopen(request_handle)
623+
xml_result = response.read()
624+
status = response.code
625+
#headers = response.headers.dict
626+
#print 'HEADERS ', headers
627+
#print 'XML-ResponseStatus: ', status
628+
621629

622-
## TODO ## to change the User-agent header --> change the above to the following
623-
# create a request object, this construct doesn't need the close-statements (-> line 633 / 633) anymore
624-
#request_handle = urllib2.Request(http_request, headers={'User-Agent': 'Python-urllib/2.6,QgsWcsClient-plugin'})
625-
#result_xml = urllib2.urlopen(request_handle).read()
626630

627631
# extract only the CoverageIDs and provide them as a list for further usage
628632
if IDs_only == True:
629-
cids, axis_labels, offered_crs = self._parse_xml(result_xml)
630-
request_handle.close()
631-
return cids, axis_labels, offered_crs
633+
try:
634+
cids, axis_labels, offered_crs = self._parse_xml(xml_result)
635+
# request_handle.close()
636+
return cids, axis_labels, offered_crs
637+
except IndexError:
638+
raise IndexError
632639
else:
633-
request_handle.close()
634-
return result_xml
640+
return xml_result
635641

636642
except urllib2.URLError, url_ERROR:
637643
if hasattr(url_ERROR, 'reason'):
638-
print '\n', time.strftime("%Y-%m-%dT%H:%M:%S%Z"), "- ERROR: Server not accessible -", url_ERROR.reason
644+
print '\n', time.strftime("%Y-%m-%dT%H:%M:%S%Z"), "- ERROR: Server not accessible -" , url_ERROR.reason
645+
err_msg=['ERROR', url_ERROR.read()]
646+
return err_msg
639647

640648
try:
641649
print url_ERROR.read(), '\n'
650+
642651
except:
643652
pass
644653

@@ -669,7 +678,7 @@ def _execute_getcov_request(self, http_request, input_params):
669678
Returns: HttpCode (if success)
670679
"""
671680
# print "I'm in "+sys._getframe().f_code.co_name
672-
print http_request
681+
print 'REQUEST:', http_request
673682

674683
now = time.strftime('_%Y%m%dT%H%M%S')
675684

@@ -689,35 +698,34 @@ def _execute_getcov_request(self, http_request, input_params):
689698
else:
690699
out_format_ext = input_params['format']
691700

692-
#if not (input_params['coverageID'].endswith('tif') or input_params['coverageID'].endswith('tiff') or \
693-
#input_params['coverageID'].endswith('jpeg') or input_params['coverageID'].endswith('jpg') or \
694-
#input_params['coverageID'].endswith('gif')):
695-
#out_coverageID = input_params['coverageID']+now+'.'+out_format_ext # input_params['format']
696701

697-
#else:
698-
#out_coverageID = input_params['coverageID']
699-
#@@
700702
out_coverageID = input_params['coverageID']+now+'.'+out_format_ext # input_params['format']
701703

702704
if input_params.has_key('output') and input_params['output'] is not None:
703705
outfile = input_params['output']+dsep+out_coverageID
704706
else:
705707
outfile = temp_storage+dsep+out_coverageID
706708

709+
print 'REQUEST-GetCov: ',http_request #@@
707710

708711
try:
709-
request_handle = urllib2.urlopen(http_request)
710-
status = request_handle.code
712+
request_handle = urllib2.Request(http_request, headers={'User-Agent': 'Python-urllib/2.7,QgsWcsClient-plugin'})
713+
response = urllib2.urlopen(request_handle)
714+
result = response.read()
715+
status = response.code
716+
#headers = response.headers.dict
717+
#print 'HEADERS ', headers
718+
#print 'GCov-Status: ', status
711719

712720
try:
713721
file_getcov = open(outfile, 'w+b')
714-
file_getcov.write(request_handle.read())
722+
file_getcov.write(result)
715723
file_getcov.flush()
716724
os.fsync(file_getcov.fileno())
717725
file_getcov.close()
718-
request_handle.close()
719726
return status
720727

728+
721729
except IOError as (errno, strerror):
722730
print "I/O error({0}): {1}".format(errno, strerror)
723731
except:
@@ -756,7 +764,7 @@ def _merge_dicts(self, input_params, procedure_dict):
756764

757765
request_dict = {}
758766
for k, v in input_params.iteritems():
759-
#print k,' -- ',v
767+
#print 'TTTT: ', k,' -- ',v
760768
# make sure there is always a version set
761769
if k == 'version' and (v == '' or v == None):
762770
v = '2.0.0'
@@ -768,11 +776,10 @@ def _merge_dicts(self, input_params, procedure_dict):
768776
# (which got inserted for argparse() to handle negativ input values correctly)
769777
request_dict[k] = str(procedure_dict[k])+str(v).strip()
770778

771-
772779
# get the basic request settings
773780
base_request = self._set_base_request()
774781
request_dict.update(base_request)
775-
#print 'request_dict ',request_dict
782+
776783
return request_dict
777784

778785

@@ -787,6 +794,7 @@ def _create_request(self, input_params, procedure_dict):
787794
# print "I'm in "+sys._getframe().f_code.co_name
788795

789796
request_dict = self._merge_dicts(input_params, procedure_dict)
797+
#print 'Request_Dict: ', request_dict #@@
790798

791799
# this doesn't look so nice, but this way I can control the order within the generated request
792800
http_request = ''

Makefile

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
#
44
# A OGC WCS 2.0/EO-WCS Client
55
# -------------------
6-
# begin : 2014-06-26
6+
# begin : 2014-06-26; 2017-04-10
77
# copyright : (C) 2014 by Christian Schiller / EOX IT Services GmbH, Vienna, Austria
88
99
# ***************************************************************************/

README.md

+36-3
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,45 @@ QgsWcsClient2
33

44
A QGis WCS2.0/EO-WCS Plugin
55

6-
A tool to download (and subset in space and time) a series of raster data
6+
A tool to download (and subset in space and time) a time-series of raster data and get the data in your desired file-format and projection.
77

8-
The WCS 2.0/EO-WCS Client allows to specify an Area-Of-Interest and a Time-Of-Interest and then access/download the raster data (coverages) from OGC WCS-2.0 compliant servers.
8+
The WCS 2.0/EO-WCS Client allows to specify an Area-Of-Interest and a Time-Of-Interest and then access/download the raster data (coverages) from OGC WCS-2.0 and WCS-2.0/EO-WCS compliant servers.
99
Unlike WMS, WCS enables the access to the original data (and not just to portrayals).
1010
The tool also handles the EO-WCS Application profile which allows to access/download a full time-series of coverages with just a few clicks. For multi-band EO-imagery the bands of interest can also be selected/sub-setted and their order in the output can be chosen.
11-
The downloaded coverages are directly loaded as layers into QGIS
11+
The downloaded coverages are directly loaded as layers into QGIS.
1212

1313
Requirements: This tool requires the python lxml-module to be pre-installed.
14+
[ Help for installation in Windows (thankfully provided by hkristen) can by found at: https://github.com/EOX-A/QgsWcsClient2/issues/8 ]
15+
16+
It would be more than welcome to receive accessible EO-WCS server-urls for additional testing.
17+
18+
19+
Added Features & Fixed Bugs:
20+
2017-05-03 - verison 0.3:
21+
- added Button to import WCS-Urls already stored in native QGis settings
22+
- added Button to sort the Server_List
23+
- enabled resizing of QgsWcsClient2-Client Window
24+
- added "WGS84 UpperCorner & WGS84LowerCorner" (BoundingBox) fields to GetCapabilities and DescribeEOCoverageSet Results-View
25+
- added a uniq "Browser tag" to be submitted with the requests, to verify in the access log-files that the Qgis-plugin was used
26+
(Identifies now with User-Agent => 'Python-urllib/2.7,QgsWcsClient-plugin')
27+
- enabled support to access https:// sites
28+
- config_server.pkl file (containing the server entries) will not get overwritten during update/re-installatinon anymore
29+
(a default one will only be installed if it is not available at plugin startup)
30+
- added possibility to view full GetCapabilities XML response-document (also made more clear to view DescribeEOCoverageSet XML)
31+
(now all are accessible => GetCap, DescCov, DescEOCov: just use copy/paste to save them)
32+
- better error checking e.g. for http errors -> warning messages (e.g. for redirects, automatic redirection is not supported to
33+
minimize security issues)
34+
35+
- fixed xml_parsing error
36+
- fixed issue with "offered CRS"
37+
- fixed issue "offered interpolation"
38+
- fixed issue if no coverage was found in selected time-period
39+
- removed the "striping/alternatingRowColors" from the Coverage-listings
40+
- fixed issue with "no data selected" fixed for "DescribeEOCoverage" Request
41+
- fixed various the xml-namespace handling issues
42+
- fixed issue with the "clock" icon shown permanently
43+
- fixed Time selection (BeginTime/EndTime) for DescribeEOCoverageSet (2.5 D coverages i.e 2D plus Time), added plausability check
44+
- fixed issue with associateing the corresponding axisLabel / CRS etc. with each coverage (once DescribeCoverage is executed for a specific
45+
Coverage)
46+
1447

__init__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
A QGIS plugin
66
A OGC WCS 2.0/EO-WCS Client
77
-------------------
8-
begin : 2014-06-26
8+
begin : 2014-06-26; 2017-04-10
99
copyright : (C) 2014 by Christian Schiller / EOX IT Services GmbH, Vienna, Austria
1010
email : christian dot schiller at eox dot at
1111
***************************************************************************/

0 commit comments

Comments
 (0)