1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32 import re
33 import os.path
34 import logging
35 from lxml import etree
36 import tempfile
37 import mimetypes
38 from cStringIO import StringIO
39
40 from django.test import Client
41 from django.conf import settings
42
43 from eoxserver.core.system import System
44 from eoxserver.core.util.xmltools import XMLDecoder
45 from eoxserver.core.util.multiparttools import (
46 mpUnpack, getMimeType, getMultipartBoundary
47 )
48 from eoxserver.contrib import gdal, osr
49 from eoxserver.testing.core import (
50 EOxServerTestCase, BASE_FIXTURES
51 )
52 from eoxserver.testing.xcomp import xmlCompareFiles
53
54
55 logger = logging.getLogger(__name__)
56
57
58 mimetypes.init()
59 mimetypes.init()
60
61
62 RE_MIME_TYPE_XML = re.compile("^text/xml|application/(?:[a-z]+\+)?xml$",re.IGNORECASE)
63
64
65
66
67
69 gt = ds.GetGeoTransform()
70 size_x = ds.RasterXSize
71 size_y = ds.RasterYSize
72
73 return (gt[0],
74 gt[3] + size_x * gt[5],
75 gt[0] + size_y * gt[1],
76 gt[3])
77
79 gt = ds.GetGeoTransform()
80 return (gt[1], abs(gt[5]))
81
83 return s.partition(';')[0].strip().lower()
84
85
86
87
89
90 """ Main base class for testing the OWS interface
91 of EOxServer.
92 """
93
94 fixtures = BASE_FIXTURES + ["testing_coverages.json", "testing_asar.json"]
95
97 super(OWSTestCase,self).setUp()
98
99 logger.info("Starting Test Case: %s" % self.__class__.__name__)
100
101 rq = self.getRequest()
102
103 if ( len(rq) == 2 ):
104 request, req_type = rq
105 headers = {}
106 else:
107 request, req_type, headers = rq
108
109 client = Client()
110
111 if req_type == "kvp":
112 self.response = client.get('/ows?%s' % request, {}, **headers)
113
114 elif req_type == "xml":
115 self.response = client.post('/ows', request, "text/xml", {}, **headers)
116
117 else:
118 raise Exception("Invalid request type '%s'." % req_type)
119
121 value = System.getConfig().getConfigValue("testing", config_key)
122 if value is None:
123 return default
124 elif value.lower() in ("yes", "y", "true", "on"):
125 return True
126 elif value.lower() in ("no", "n", "false", "off"):
127 return False
128 else:
129 return default
130
132 raise Exception("Not implemented.")
133
136
138 return os.path.join(settings.PROJECT_DIR,"responses")
139
141 return os.path.join(settings.PROJECT_DIR,"data")
142
144 return "%s.%s" % (self.__class__.__name__, self.getFileExtension(file_type))
145
148
150 return os.path.join(settings.PROJECT_DIR, "expected")
151
153 return "%s.%s" % (self.__class__.__name__, self.getFileExtension(file_type))
154
156 raise Exception("Not implemented.")
157
159 """
160 Helper function for the basic XML tree comparison to be used by `testXMLComparison`.
161 """
162 expected_path = os.path.join( self.getExpectedFileDir(), self.getExpectedFileName(suffix) )
163 response_path = os.path.join( self.getResponseFileDir(), self.getResponseFileName(suffix) )
164
165
166 if response is None : response = self.getXMLData()
167
168
169 if not os.path.isfile( expected_path ) :
170 with file(response_path, 'w') as fid : fid.write(response)
171 self.skipTest( "Missing the expected XML response '%s'." % expected_path )
172
173
174 try:
175 xmlCompareFiles( expected_path , StringIO(response) )
176 except Exception as e :
177 with file(response_path, 'w') as fid : fid.write(response)
178 self.fail( "Response returned in '%s' is not equal to expected response in '%s'. REASON: %s " % \
179 ( response_path , expected_path , str(e) ) )
180
181
183 """
184 Helper function for the `testBinaryComparisonRaster` function.
185 """
186 expected_path = os.path.join(self.getExpectedFileDir(), self.getExpectedFileName(file_type))
187 response_path = os.path.join(self.getResponseFileDir(), self.getResponseFileName(file_type))
188
189 try:
190 f = open(expected_path, 'r')
191 expected = f.read()
192 f.close()
193 except IOError:
194 expected = None
195
196 actual_response = None
197 if Data is None:
198 if file_type in ("raster", "html"):
199 actual_response = self.getResponseData()
200 elif file_type == "xml":
201 actual_response = self.getXMLData()
202 else:
203 self.fail("Unknown file_type '%s'." % file_type)
204 else:
205 actual_response = Data
206
207 if expected != actual_response:
208 if self.getFileExtension("raster") in ("hdf", "nc"):
209 self.skipTest("Skipping binary comparison for HDF or NetCDF file '%s'." % expected_path)
210 f = open(response_path, 'w')
211 f.write(actual_response)
212 f.close()
213
214 if expected is None:
215 self.skipTest("Expected response in '%s' is not present" % expected_path)
216 else:
217 self.fail("Response returned in '%s' is not equal to expected response in '%s'." % (
218 response_path, expected_path)
219 )
220
222 logger.info("Checking HTTP Status ...")
223
224 self.assertEqual(self.response.status_code, 200)
225
226
228 """
229 Base class for test cases that expect a raster as response.
230 """
231
234
239
240
242 """
243 Extended RasterTestCases that open the result with GDAL and
244 perform several tests.
245 """
246
255
257 _, self.tmppath = tempfile.mkstemp("." + self.getFileExtension("raster"))
258 f = open(self.tmppath, "w")
259 f.write(self.getResponseData())
260 f.close()
261 gdal.AllRegister()
262
263 exp_path = os.path.join(self.getExpectedFileDir(), self.getExpectedFileName("raster"))
264
265 try:
266 self.res_ds = gdal.Open(self.tmppath, gdal.GA_ReadOnly)
267 except RuntimeError, e:
268 self.fail("Response could not be opened with GDAL. Error was %s" % e)
269
270 try:
271 self.exp_ds = gdal.Open(exp_path, gdal.GA_ReadOnly)
272 except RuntimeError:
273 self.skipTest("Expected response in '%s' is not present" % exp_path)
274
277 self._openDatasets()
278 self.assertEqual((self.res_ds.RasterXSize, self.res_ds.RasterYSize),
279 (self.exp_ds.RasterXSize, self.exp_ds.RasterYSize))
280
281 - def testExtent(self):
282 self._openDatasets()
283 EPSILON = 1e-8
284
285 res_extent = extent_from_ds(self.res_ds)
286 exp_extent = extent_from_ds(self.exp_ds)
287
288 self.assert_(
289 max([
290 abs(res_extent[i] - exp_extent[i]) for i in range(0, 4)
291 ]) < EPSILON
292 )
293
295 self._openDatasets()
296 res_resolution = resolution_from_ds(self.res_ds)
297 exp_resolution = resolution_from_ds(self.exp_ds)
298 self.assertAlmostEqual(res_resolution[0], exp_resolution[0], delta=exp_resolution[0]/10)
299 self.assertAlmostEqual(res_resolution[1], exp_resolution[1], delta=exp_resolution[1]/10)
300
302 self._openDatasets()
303 self.assertEqual(self.res_ds.RasterCount, self.exp_ds.RasterCount)
304
307 self._openDatasets()
308 self.assertEqual((self.res_ds.RasterXSize, self.res_ds.RasterYSize),
309 (self.exp_ds.RasterXSize, self.exp_ds.RasterYSize))
310
312 self._openDatasets()
313 self.assertEqual(self.res_ds.RasterCount, self.exp_ds.RasterCount)
314
316 self._openDatasets()
317 self.assertEqual(self.res_ds.GetGCPCount(), self.exp_ds.GetGCPCount())
318
320 self._openDatasets()
321
322 res_proj = self.res_ds.GetGCPProjection()
323 if not res_proj:
324 self.fail("Response Dataset has no GCP Projection defined")
325 res_srs = osr.SpatialReference(res_proj)
326
327 exp_proj = self.exp_ds.GetGCPProjection()
328 if not exp_proj:
329 self.fail("Expected Dataset has no GCP Projection defined")
330 exp_srs = osr.SpatialReference(exp_proj)
331
332 self.assert_(res_srs.IsSame(exp_srs))
333
335 """
336 Base class for test cases that expects XML output, which is parsed
337 and validated against a schema definition.
338 """
339
342
344 logger.info("Validating XML ...")
345
346 if XMLData is None:
347 doc = etree.XML(self.getXMLData())
348 else:
349 doc = etree.XML(XMLData)
350 schema_locations = doc.get("{http://www.w3.org/2001/XMLSchema-instance}schemaLocation")
351 locations = schema_locations.split()
352
353
354 schema_def = etree.Element("schema", attrib={
355 "elementFormDefault": "qualified",
356 "version": "1.0.0",
357 }, nsmap={
358 None: "http://www.w3.org/2001/XMLSchema"
359 }
360 )
361
362 for ns, location in zip(locations[::2], locations[1::2]):
363 if location == "../owsCoverages.xsd":
364 location = "http://schemas.opengis.net/wcs/1.1/wcsAll.xsd"
365 etree.SubElement(schema_def, "import", attrib={
366 "namespace": ns,
367 "schemaLocation": location
368 }
369 )
370
371
372 schema = etree.XMLSchema(etree.XML(etree.tostring(schema_def)))
373
374 try:
375 schema.assertValid(doc)
376 except etree.Error as e:
377 self.fail(str(e))
378
381
383 """
384 Mixin class for XML test cases that uses XML schematrons for validation.
385 Use the `schematron_locations`
386 """
387 schematron_locations = ()
388
390 errors = []
391 doc = etree.XML(self.getXMLData())
392
393 schematron_def = etree.Element("schema", attrib={
394 "queryBinding": "xslt2",
395 }, nsmap={
396 None: "http://purl.oclc.org/dsdl/schematron"
397 }
398 )
399 etree.SubElement(schematron_def, "pattern")
400
401
402
403
404
405
406
407
408
409
410 schematron = etree.Schematron(etree.XML(etree.tostring(schematron_def)))
411
412 try:
413 schematron.assertValid(doc)
414 except etree.DocumentInvalid, e:
415 errors.append(str(e))
416 except etree.SchematronValidateError:
417 self.skipTest("Schematron Testing is not enabled.")
418
419 if len(errors):
420 self.fail(str(errors))
421
422
424 """
425 Exception test cases expect the request to fail and examine the
426 exception response.
427 """
428
431
434
436 return "/ows:Exception/@exceptionCode"
437
442
449
451 """
452 HTML test cases expect to receive HTML text.
453 """
454
457
460
462 """
463 Multipart tests combine XML and raster tests and split the response
464 into a xml and a raster part which are examined separately.
465 """
466
474
475
477 """ remove variable parts of selected XML elements text and attributes """
478
479
480
481 N0 = "{http://www.opengis.net/gml/3.2}rangeSet/" \
482 "{http://www.opengis.net/gml/3.2}File/" \
483 "{http://www.opengis.net/gml/3.2}rangeParameters"
484
485 N1 = "{http://www.opengis.net/gml/3.2}rangeSet/" \
486 "{http://www.opengis.net/gml/3.2}File/" \
487 "{http://www.opengis.net/gml/3.2}fileReference"
488
489 N2 = "{http://www.opengis.net/wcs/1.1}Coverage/" \
490 "{http://www.opengis.net/ows/1.1}Reference"
491
492 HREF = "{http://www.w3.org/1999/xlink}href"
493
494
495
496 def cropFileName( name ) :
497 base , _ , ext = name.rpartition(".")
498 base , _ , _ = base.rpartition("_")
499 return "%s.%s"%(base,ext)
500
501 def changeHref( e ) :
502 if e is not None : e.set( HREF , cropFileName( e.get( HREF ) ) )
503
504 def changeText( e ) :
505 if e is not None: e.text = cropFileName( e.text )
506
507
508 xml = etree.parse( StringIO( cbuffer ) )
509
510
511
512
513 changeHref( xml.find( N0 ) )
514 changeText( xml.find( N1 ) )
515
516 changeHref( xml.find( N2 ) )
517
518
519 return etree.tostring( xml , encoding="UTF-8" , xml_declaration=True )
520
521 - def _unpackMultipartContent( self , response ) :
522
523 content = response.content
524 content_type = response['Content-Type']
525
526
527 if getMimeType( content_type ) \
528 not in ( "multipart/mixed" , "multipart/related" ) :
529 raise Exception , "Received content is neither mixed nor related multipart! Content-Type: %s" % content_type
530
531
532 boundary = getMultipartBoundary( content_type )
533
534
535 for header,offset,size in mpUnpack(content,boundary) :
536 match = RE_MIME_TYPE_XML.match( _getMime(header['content-type']) )
537 if match is not None :
538
539 self.xmlData = content[offset:(offset+size)]
540 else :
541
542 self.imageData = content[offset:(offset+size)]
543
544
554
555
557 if part == "xml":
558 return "xml"
559 elif part == "raster":
560 return "tif"
561 elif part == "TransactionDescribeCoverage":
562 return "TransactionDescribeCoverage.xml"
563 elif part == "TransactionDeleteCoverage":
564 return "TransactionDeleteCoverage.xml"
565 elif part == "TransactionDescribeCoverageDeleted":
566 return "TransactionDescribeCoverageDeleted.xml"
567 else:
568 return "dat"
569
573
577
583
589
590
591
592
593
595 """
596 Base class for WCS Transactional test cases.
597 """
598 ADDmetaFile = None
599 ADDtiffFile = None
600 isAsync = False
601 ID = None
602
604 super(WCSTransactionTestCase, self).setUp()
605 logger.debug("WCSTransactionTestCase for ID: %s" % self.ID)
606
607 if self.isAsync:
608 from eoxserver.resources.processes.tracker import (
609 dequeueTask, TaskStatus, startTask, stopTaskSuccessIfNotFinished
610 )
611
612
613 taskId = dequeueTask(1)[0]
614
615
616 pStatus = TaskStatus( taskId )
617 try:
618
619 requestType , requestID , requestHandler , inputs = startTask( taskId )
620
621 module , _ , funct = requestHandler.rpartition(".")
622 handler = getattr( __import__(module,fromlist=[funct]) , funct )
623
624 handler( pStatus , inputs )
625
626 stopTaskSuccessIfNotFinished( taskId )
627 except Exception as e :
628 pStatus.setFailure( unicode(e) )
629
630
631 request = "service=WCS&version=2.0.0&request=DescribeCoverage&coverageid=%s" % str( self.ID )
632 self.responseDescribeCoverage = self.client.get('/ows?%s' % request)
633
634
635 request = "service=WCS&version=2.0.0&request=GetCoverage&format=image/tiff&mediatype=multipart/mixed&coverageid=%s" % str( self.ID )
636 self.responseGetCoverage = self.client.get('/ows?%s' % request)
637
638
639 requestBegin = """<wcst:Transaction service="WCS" version="1.1"
640 xmlns:wcst="http://www.opengis.net/wcs/1.1/wcst"
641 xmlns:ows="http://www.opengis.net/ows/1.1"
642 xmlns:xlink="http://www.w3.org/1999/xlink"
643 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
644 xsi:schemaLocation="http://www.opengis.net/wcs/1.1/wcst http://schemas.opengis.net/wcst/1.1/wcstTransaction.xsd">
645 <wcst:InputCoverages>
646 <wcst:Coverage>
647 <ows:Identifier>"""
648 requestEnd = """</ows:Identifier>
649 <wcst:Action codeSpace=\"http://schemas.opengis.net/wcs/1.1.0/actions.xml\">
650 Delete
651 </wcst:Action>"
652 </wcst:Coverage>
653 </wcst:InputCoverages>
654 </wcst:Transaction>"""
655 request = requestBegin + self.ID + requestEnd
656 self.responseDeleteCoverage = self.client.post('/ows', request, "text/xml")
657
658
659 request = "service=WCS&version=2.0.0&request=DescribeCoverage&coverageid=%s" % str( self.ID )
660 self.responseDescribeCoverageDeleted = self.client.get('/ows?%s' % request)
661
663 requestBegin = """<wcst:Transaction service="WCS" version="1.1"
664 xmlns:wcst="http://www.opengis.net/wcs/1.1/wcst"
665 xmlns:ows="http://www.opengis.net/ows/1.1"
666 xmlns:xlink="http://www.w3.org/1999/xlink"
667 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
668 xsi:schemaLocation="http://www.opengis.net/wcs/1.1/wcst http://schemas.opengis.net/wcst/1.1/wcstTransaction.xsd">
669 <wcst:InputCoverages>
670 <wcst:Coverage>
671 <ows:Identifier>"""
672 requestMid1 = """</ows:Identifier>
673 <ows:Reference xlink:href="file:///"""
674 requestMid2 = """" xlink:role="urn:ogc:def:role:WCS:1.1:Pixels"/>"""
675 requestMid3 = """<ows:Metadata xlink:href="file:///"""
676 requestMid4 = """" xlink:role="http://www.opengis.net/eop/2.0/EarthObservation"/>"""
677 requestMid5 = """<wcst:Action codeSpace="http://schemas.opengis.net/wcs/1.1.0/actions.xml">Add</wcst:Action>
678 </wcst:Coverage>
679 </wcst:InputCoverages>"""
680 requestAsync = """<wcst:ResponseHandler>http://NOTUSED</wcst:ResponseHandler>"""
681 requestEnd = """</wcst:Transaction>"""
682
683 params = requestBegin + self.ID + requestMid1 + self.getDataFullPath(self.ADDtiffFile) + requestMid2
684 if self.ADDmetaFile is not None:
685 params += requestMid3 + self.getDataFullPath(self.ADDmetaFile) + requestMid4
686 params += requestMid5
687 if self.isAsync:
688 params += requestAsync
689 params += requestEnd
690 return (params, "xml")
691
694
696
697
698 tree = etree.fromstring(self.getXMLData())
699 for node in tree.findall("{http://www.opengis.net/wcs/1.1/wcst}RequestId"):
700 node.text = "identifier"
701 for node in tree.findall("{http://www.opengis.net/wcs/1.1/wcst}TimeStamp"):
702 node.text = "2011-01-01T00:00:00Z"
703 self.response.content = etree.tostring(tree, encoding="ISO-8859-1")
704 super(WCSTransactionTestCase, self).testXMLComparison()
705
707 """
708 Tests that the <ows:Identifier> in the XML request and response is the
709 same
710 """
711 logger.debug("IDCompare testResponseIdComparison for ID: %s" % self.ID)
712 self._testResponseIdComparison( self.ID , self.getXMLData() )
713
715 """
716 Tests that the inserted coverage is available in a DescribeCoverage
717 request
718 """
719
720 self.assertEqual(self.responseDescribeCoverage.status_code, 200)
721
723 self.testValidate(self.responseDescribeCoverage.content)
724
726
727 self._testXMLComparison( "TransactionDescribeCoverage" , self.responseDescribeCoverage.content )
728
730 """
731 Validate the inserted coverage via a GetCoverage request
732 """
733
734 self.assertEqual(self.responseGetCoverage.status_code, 200)
735
737 """
738 Test to delete the previously inserted coaverage
739 """
740
741 self.assertEqual(self.responseDeleteCoverage.status_code, 200)
742
745
747 tree = etree.fromstring(self.responseDeleteCoverage.content)
748 for node in tree.findall("{http://www.opengis.net/wcs/1.1/wcst}RequestId"):
749 node.text = "identifier"
750
751 self._testXMLComparison( "TransactionDeleteCoverage" , etree.tostring(tree, encoding="ISO-8859-1"))
752
754 """
755 Tests that the <ows:Identifier> in the XML request and response is the
756 same
757 """
758 logger.debug("IDCompare testResponseIdComparison for ID: %s" % self.ID)
759 self._testResponseIdComparison( self.ID , self.responseDeleteCoverage.content )
760
762 """
763 Tests that the deletec coverage is not longer available in a
764 DescribeCoverage request
765 """
766
767 self.assertEqual(self.responseDescribeCoverageDeleted.status_code, 404)
768
770 self.testValidate(self.responseDescribeCoverageDeleted.content)
771
773
774 self._testXMLComparison( "TransactionDescribeCoverageDeleted" , self.responseDescribeCoverageDeleted.content )
775
777 """
778 Tests that the <ows:Identifier> in the XML request and response is the
779 same
780 """
781 logger.debug("_testResponseIdComparison for ID: %s" % id)
782 tree = etree.fromstring( rcontent )
783 for node in tree.findall("{http://www.opengis.net/ows/1.1}Identifier"):
784 self.assertEqual( node.text, id )
785
790 """
791 WCS-T test cases for RectifiedGridCoverages
792 """
793
794
796 if self.isSetUp: return
797
798 self._unpackMultipartContent( self.responseGetCoverage )
799
800 self.isSetUp = True
801
804
809 """
810 WCS-T test cases for ReferenceableGridCoverages
811 """
812
813
815 if self.isSetUp: return
816
817 self._unpackMultipartContent( self.responseGetCoverage )
818
819 self.isSetUp = True
820
823
824
825
826
827
831
833 logger.info("Checking Coverage Ids ...")
834 decoder = XMLDecoder(self.getXMLData(), {
835 "coverageids": {"xml_location": "/wcs:CoverageDescriptions/wcs:CoverageDescription/wcs:CoverageId", "xml_type": "string[]"}
836 })
837
838 result_coverage_ids = decoder.getValue("coverageids")
839 expected_coverage_ids = self.getExpectedCoverageIds()
840 self.assertItemsEqual(result_coverage_ids, expected_coverage_ids)
841
842
843 for coverage_id in result_coverage_ids:
844 self.assertTrue(result_coverage_ids.count(coverage_id) == 1, "CoverageID %s is not unique." % coverage_id)
845
849
851 decoder = XMLDecoder(self.getXMLData(), {
852 "coverageids": {"xml_location": "/wcs:CoverageDescriptions/wcs:CoverageDescription/wcs:CoverageId", "xml_type": "string[]"}
853 })
854 coverage_ids = decoder.getValue("coverageids")
855 self.assertEqual(len(coverage_ids), self.getExpectedCoverageCount())
856
860
862 decoder = XMLDecoder(self.getXMLData(), {
863 "sections": {"xml_location": "/*", "xml_type": "tagName[]"}
864 })
865 sections = decoder.getValue("sections")
866 self.assertItemsEqual(sections, self.getExpectedSections())
867
870
871
872 tree = etree.fromstring(self.getXMLData())
873 for node in tree.findall("{http://www.opengis.net/gmlcov/1.0}metadata/" \
874 "{http://www.opengis.net/gmlcov/1.0}Extension/" \
875 "{http://www.opengis.net/wcseo/1.0}EOMetadata/" \
876 "{http://www.opengis.net/wcseo/1.0}lineage/" \
877 "{http://www.opengis.net/gml/3.2}timePosition"):
878 node.text = "2011-01-01T00:00:00Z"
879 self.xmlData = etree.tostring(tree, encoding="ISO-8859-1")
880
881 super(WCS20GetCoverageMultipartTestCase, self).testXMLComparison()
882
888
894
896 fixtures = BASE_FIXTURES + ["testing_rasdaman_coverages.json"]
897
899
900
901
902 gdal.AllRegister()
903 if gdal.GetDriverByName("RASDAMAN") is None:
904 self.skipTest("Rasdaman driver is not enabled.")
905
906 if not self.isRequestConfigEnabled("rasdaman_enabled"):
907 self.skipTest("Rasdaman tests are not enabled. Use the "
908 "configuration option 'rasdaman_enabled' to allow "
909 "rasdaman tests.")
910
911 super(RasdamanTestCaseMixIn, self).setUp()
912
913
914
915
916
918 layers = []
919 styles = []
920 crs = "epsg:4326"
921 bbox = (0, 0, 1, 1)
922 width = 100
923 height = 100
924 frmt = "image/jpeg"
925 time = None
926 dim_band = None
927
928 swap_axes = True
929
930 httpHeaders = None
931
933 return mimetypes.guess_extension(self.frmt, False)[1:]
934
936 params = "service=WMS&request=GetMap&version=1.1.1&" \
937 "layers=%s&styles=%s&srs=%s&bbox=%s&" \
938 "width=%d&height=%d&format=%s" % (
939 ",".join(self.layers), ",".join(self.styles), self.crs,
940 ",".join(map(str, self.bbox)),
941 self.width, self.height, self.frmt
942 )
943
944 if self.time:
945 params += "&time=%s" % self.time
946
947 if self.dim_band:
948 params += "&dim_band=%s" % self.dim_band
949
950 if self.httpHeaders is None:
951 return (params, "kvp")
952 else:
953 return (params, "kvp", self.httpHeaders)
954
956 layers = []
957 styles = []
958 crs = "epsg:4326"
959 bbox = (0, 0, 1, 1)
960 width = 100
961 height = 100
962 frmt = "image/jpeg"
963 time = None
964 dim_band = None
965
966 swap_axes = True
967
968 httpHeaders = None
969
971 return mimetypes.guess_extension(self.frmt, False)[1:]
972
974 bbox = self.bbox if not self.swap_axes else (
975 self.bbox[1], self.bbox[0],
976 self.bbox[3], self.bbox[2]
977 )
978
979 params = "service=WMS&request=GetMap&version=1.3.0&" \
980 "layers=%s&styles=%s&crs=%s&bbox=%s&" \
981 "width=%d&height=%d&format=%s" % (
982 ",".join(self.layers), ",".join(self.styles), self.crs,
983 ",".join(map(str, bbox)),
984 self.width, self.height, self.frmt
985 )
986
987 if self.time:
988 params += "&time=%s" % self.time
989
990 if self.dim_band:
991 params += "&dim_band=%s" % self.dim_band
992
993 if self.httpHeaders is None:
994 return (params, "kvp")
995 else:
996 return (params, "kvp", self.httpHeaders)
997
1000 return "/{http://www.opengis.net/ogc}ServiceException/@code"
1001