| Home | Trees | Indices | Help |
|
|---|
|
|
1 #------------------------------------------------------------------------------- 2 # $Id: common.py 2476 2013-05-14 09:58:42Z meissls $ 3 # 4 # Project: EOxServer <http://eoxserver.org> 5 # Authors: Stephan Krause <stephan.krause@eox.at> 6 # Stephan Meissl <stephan.meissl@eox.at> 7 # Martin Paces <martin.paces@eox.at> 8 # 9 #------------------------------------------------------------------------------- 10 # Copyright (C) 2011 EOX IT Services GmbH 11 # 12 # Permission is hereby granted, free of charge, to any person obtaining a copy 13 # of this software and associated documentation files (the "Software"), to deal 14 # in the Software without restriction, including without limitation the rights 15 # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 16 # copies of the Software, and to permit persons to whom the Software is 17 # furnished to do so, subject to the following conditions: 18 # 19 # The above copyright notice and this permission notice shall be included in all 20 # copies of this Software or works derived from this Software. 21 # 22 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 23 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 24 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 25 # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 26 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 27 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 28 # THE SOFTWARE. 29 #------------------------------------------------------------------------------- 30 31 import logging 32 import os 33 from uuid import uuid4 34 35 import mapscript 36 37 from eoxserver.core.system import System 38 from eoxserver.core.util.timetools import ( 39 isotime, getDateTime 40 ) 41 from eoxserver.core.exceptions import InternalError, InvalidParameterException 42 from eoxserver.contrib import gdal 43 from eoxserver.resources.coverages.filters import ( 44 BoundedArea, TimeInterval 45 ) 46 from eoxserver.resources.coverages.formats import getFormatRegistry 47 from eoxserver.resources.coverages import crss 48 from eoxserver.resources.coverages.dateline import ( 49 extent_crosses_dateline, wrap_extent_around_dateline 50 ) 51 52 from eoxserver.processing.gdal.reftools import create_temporary_vrt 53 from eoxserver.services.owscommon import OWSCommonConfigReader 54 from eoxserver.services.mapserver import MapServerOperationHandler 55 from eoxserver.services.exceptions import InvalidRequestException 56 from eoxserver.processing.gdal.vrt import create_simple_vrt 57 58 59 logger = logging.getLogger(__name__) 60 61 _stripDot = lambda s : s[1:] if s[0] == '.' else s 62 64 """ get the space separated list of CRS EPSG codes to be passed 65 to MapScript setMedata("wms_srs",...) method 66 """ 67 return " ".join(crss.getSupportedCRS_WMS(format_function=crss.asShortCode)) 6874 77 80 8310485 layer = mapscript.layerObj() 86 87 layer.name = self.getName() 88 layer.setMetaData("ows_title", self.getName()) 89 layer.setMetaData("wms_label", self.getName()) 90 layer.addProcessing("CLOSE_CONNECTION=CLOSE") 91 92 if self.group_name: 93 layer.group = self.group_name 94 layer.setMetaData("wms_group_title", self.group_name) 95 96 return layer97110 113120115 layer = super(WMSEmptyLayer, self).getMapServerLayer(req) 116 117 layer.setMetaData("wms_enable_request", "getmap") 118 119 return layer127 133 136 139 142207144 if len(self.coverage.getRangeType().bands) >= 3: 145 return [1, 2, 3] 146 else: 147 return [1, 1, 1]148150 bands = self.coverage.getRangeType().bands 151 152 if len(bands) in range(1, 5): # 1 to 4 153 return bands 154 else: 155 return bands[:3]156158 nil_values = [] 159 160 if len(bands) == 1: 161 if len(bands[0].nil_values) > 0: 162 nil_values = [ 163 int(bands[0].nil_values[0].value), 164 int(bands[0].nil_values[0].value), 165 int(bands[0].nil_values[0].value) 166 ] 167 168 layer.offsite = mapscript.colorObj(*nil_values) 169 170 if len(bands) == 3: 171 for band in bands: 172 if len(band.nil_values) > 0: 173 nil_values.append(int(band.nil_values[0].value)) 174 else: 175 return 176 177 layer.offsite = mapscript.colorObj(*nil_values)178180 if self.coverage.getRangeType().data_type != gdal.GDT_Byte: 181 layer.setProcessingKey("SCALE", "AUTO")182184 bands = self.getBandSelection(req) 185 186 if not self.isRGB() and not self.isGrayscale() and not self.isRGBA(): 187 layer.setProcessingKey("BANDS", "%d,%d,%d" % tuple(self.getBandIndices(req))) 188 189 self.setOffsiteColor(layer, bands) 190 191 self.setScale(layer)192194 layer = super(WMSCoverageLayer, self).getMapServerLayer(req) 195 196 layer.setMetaData("wms_enable_request", "getcapabilities,getmap") 197 198 for key, value in self.coverage.getLayerMetadata(): 199 layer.setMetaData(key, value) 200 201 layer.type = mapscript.MS_LAYER_RASTER 202 layer.setConnectionType(mapscript.MS_RASTER, '') 203 204 self.configureBands(layer, req) 205 206 return layer209235211 layer = super(WMSRectifiedDatasetLayer, self).getMapServerLayer(req) 212 213 # general rectified coverage metadata 214 srid = self.coverage.getSRID() 215 extent = self.coverage.getExtent() 216 217 layer.setProjection(crss.asProj4Str(srid)) 218 layer.setMetaData("ows_srs", crss.asShortCode(srid)) 219 layer.setMetaData("wms_srs", crss.asShortCode(srid)) 220 layer.setMetaData("wms_extent", "%.10g %.10g %.10g %.10g" % extent) 221 layer.setExtent(*extent) 222 223 # bind rectified dataset 224 connector = System.getRegistry().findAndBind( 225 intf_id = "services.mapserver.MapServerDataConnectorInterface", 226 params = { 227 "services.mapserver.data_structure_type": \ 228 self.coverage.getDataStructureType() 229 } 230 ) 231 232 layer = connector.configure(layer, self.coverage) 233 234 return layer237 241 242277 278 279244 layer = super(WMSWrappedRectifiedDatasetLayer, self).getMapServerLayer(req) 245 246 e = wrap_extent_around_dateline(self.coverage.getExtent(), 247 self.coverage.getSRID()) 248 249 layer.setMetaData("wms_extent", "%.10g %.10g %.10g %.10g" % e) 250 layer.setExtent(*e) 251 252 data_package = self.coverage.getData() 253 data_package.prepareAccess() 254 ds = gdal.Open(data_package.getGDALDatasetIdentifier()) 255 256 vrt_ds = create_simple_vrt(ds, self.vrt_path) 257 258 259 size_x = ds.RasterXSize 260 size_y = ds.RasterYSize 261 262 dx = abs(e[0] - e[2]) / size_x 263 dy = abs(e[1] - e[3]) / size_y 264 265 vrt_ds.SetGeoTransform([e[0], dx, 0, e[3], 0, -dy]) 266 267 vrt_ds = None 268 269 layer.data = self.vrt_path 270 271 return layer272 273283307285 layer = super(WMSReferenceableDatasetLayer, self).getMapServerLayer(req) 286 287 # general rectified coverage metadata 288 srid = self.coverage.getSRID() 289 layer.setProjection( crss.asProj4Str( srid ) ) 290 layer.setMetaData("ows_srs", crss.asShortCode( srid ) ) 291 layer.setMetaData("wms_srs", crss.asShortCode( srid ) ) 292 layer.setMetaData("wms_extent", "%.10g %.10g %.10g %.10g" \ 293 % self.coverage.getExtent()) 294 layer.setExtent(*self.coverage.getExtent()) 295 296 # project the dataset 297 vrt_path = self.rectify() 298 layer.data = vrt_path 299 self.temp_files.append(vrt_path) 300 301 return layer302341310 layer = super(WMSRectifiedStitchedMosaicLayer, self).getMapServerLayer(req) 311 312 connector = System.getRegistry().findAndBind( 313 intf_id = "services.mapserver.MapServerDataConnectorInterface", 314 params = { 315 "services.mapserver.data_structure_type": \ 316 self.coverage.getDataStructureType() 317 } 318 ) 319 320 layer = connector.configure(layer, self.coverage) 321 322 extent = self.coverage.getExtent() 323 srid = self.coverage.getSRID() 324 size = self.coverage.getSize() 325 resolution = ((extent[2]-extent[0]) / float(size[0]), 326 (extent[1]-extent[3]) / float(size[1])) 327 328 layer.setProjection( crss.asProj4Str( srid ) ) 329 layer.setMetaData("ows_srs", crss.asShortCode( srid ) ) 330 layer.setMetaData("wms_srs", crss.asShortCode( srid ) ) 331 332 layer.setExtent(*self.coverage.getExtent()) 333 layer.setMetaData("wms_extent", "%.10g %.10g %.10g %.10g" % extent) 334 layer.setMetaData("wms_resolution", "%.10g %.10g" % resolution) 335 layer.setMetaData("wms_size", "%d %d" % size) 336 337 layer.type = mapscript.MS_LAYER_RASTER 338 layer.setConnectionType(mapscript.MS_RASTER, '') 339 340 return layer347393349 return self.dataset_series.getEOID()350352 layer = super(WMSDatasetSeriesLayer, self).getMapServerLayer(req) 353 354 coverages = self.dataset_series.getEOCoverages() 355 356 layer.setMetaData("wms_extent", "%.10g %.10g %.10g %.10g" \ 357 % self.dataset_series.getWGS84Extent()) 358 layer.setExtent(*self.dataset_series.getWGS84Extent()) 359 360 time_extent = ",".join( 361 sorted(set(isotime(coverage.getBeginTime()) for coverage in coverages)) 362 ) 363 layer.setMetaData("wms_timeextent", time_extent) 364 365 srid = 4326 # TODO: source CRS of dataset series 366 layer.setProjection( crss.asProj4Str( srid ) ) 367 layer.setMetaData("ows_srs", crss.asShortCode( srid ) ) 368 layer.setMetaData("wms_srs", crss.asShortCode( srid ) ) 369 370 371 layer.type = mapscript.MS_LAYER_RASTER 372 373 layer.setConnectionType(mapscript.MS_RASTER, '') 374 layer.setMetaData("wms_enable_request", "*") 375 layer.status = mapscript.MS_ON 376 377 for key, value in self.dataset_series.getLayerMetadata(): 378 layer.setMetaData(key, value) 379 380 # use a dummy coverage to connect to 381 382 connector = System.getRegistry().findAndBind( 383 intf_id = "services.mapserver.MapServerDataConnectorInterface", 384 params = { 385 "services.mapserver.data_structure_type": \ 386 coverages[0].getDataStructureType() 387 } 388 ) 389 390 layer = connector.configure(layer, coverages[0]) 391 392 return layer395 PARAM_SCHEMA = { 396 "service": {"xml_location": "/@service", "xml_type": "string", "kvp_key": "service", "kvp_type": "string"}, 397 "version": {"xml_location": "/@version", "xml_type": "string", "kvp_key": "version", "kvp_type": "string"}, 398 "operation": {"xml_location": "/", "xml_type": "localName", "kvp_key": "request", "kvp_type": "string"} 399 } 400518402 super(WMSCommonHandler, self).__init__() 403 404 self.req = None 405 406 self.layers = [] 407 408 self.temp_files = []409411 self.req = req 412 self.req.setSchema(self.PARAM_SCHEMA) 413 414 try: 415 self.validateParams() 416 self.configureRequest() 417 self.configureMapObj() 418 self.createLayers() 419 self.addMapServerLayers() 420 response = self.postprocess(self.dispatch()) 421 finally: 422 self.cleanup() 423 424 return response425 428430 # set the default EPSG:4326 projection 431 # TODO: check whether this is really correct 432 self.map.setProjection( crss.asProj4Str( 4326 ) )433435 """ 436 This method configures the ``ms_req.map`` object (an 437 instance of ``mapscript.mapObj``) with parameters from the 438 config. This method can be overridden in order to implement more 439 sophisticated behaviour. 440 441 @param ms_req An :class:`MapServerRequest` object 442 443 @return None 444 """ 445 446 self.map.setMetaData("ows_onlineresource", OWSCommonConfigReader().getHTTPServiceURL() + "?") 447 448 # set (per-service) map projection 449 self._setMapProjection() 450 451 # set the (global) list of supported CRSes 452 self.map.setMetaData("ows_srs", getMSWMSSRSMD()) 453 self.map.setMetaData("wms_srs", getMSWMSSRSMD()) 454 455 # set all supported output formats 456 457 # retrieve the format registry 458 FormatRegistry = getFormatRegistry() 459 460 # define the supported formats 461 for sf in FormatRegistry.getSupportedFormatsWMS(): 462 # output format definition 463 of = mapscript.outputFormatObj( sf.driver, "custom" ) 464 of.name = sf.mimeType 465 of.mimetype = sf.mimeType 466 of.extension = _stripDot(sf.defaultExt) 467 #of.imagemode = mapscript.MS_IMAGEMODE_BYTE 468 of.imagemode = mapscript.MS_IMAGEMODE_RGBA 469 470 #add the format 471 self.map.appendOutputFormat( of ) 472 473 # set the formats supported by getMap WMS operation 474 self.map.setMetaData("wms_getmap_formatlist", 475 ",".join( 476 map(lambda f: f.mimeType, 477 FormatRegistry.getSupportedFormatsWMS()) 478 ) 479 )480 481 484486 logger.debug("Adding WMS coverage layer for coverage '%s'." 487 % coverage.getCoverageId()) 488 489 if coverage.getType() == "plain": 490 raise InternalError( 491 "Plain coverage WMS views are not yet implemented." 492 ) 493 elif coverage.getType() == "eo.rect_dataset": 494 return WMSRectifiedDatasetLayer(coverage) 495 elif coverage.getType() == "eo.ref_dataset": 496 return WMSReferenceableDatasetLayer(coverage) 497 elif coverage.getType() == "eo.rect_stitched_mosaic": 498 return WMSRectifiedStitchedMosaicLayer(coverage)499 502 506508 return layer.getMapServerLayer(self.req)509 512520 523 524 525550527 visible_expr = System.getRegistry().getFromFactory( 528 "resources.coverages.filters.CoverageExpressionFactory", 529 {"op_name": "attr", "operands": ("visible", "=", True)} 530 ) 531 532 cov_factory = System.getRegistry().bind("resources.coverages.wrappers.EOCoverageFactory") 533 534 for coverage in cov_factory.find(filter_exprs=[visible_expr]): 535 self.addLayer(self.createCoverageLayer(coverage)) 536 537 dss_factory = System.getRegistry().bind("resources.coverages.wrappers.DatasetSeriesFactory") 538 539 # TODO: find a more efficient way to do this check 540 for dataset_series in dss_factory.find(): 541 if len(dataset_series.getEOCoverages()) > 0: 542 self.layers.append(WMSDatasetSeriesLayer(dataset_series))543545 ms_layer = super(WMS1XGetCapabilitiesHandler, self).getMapServerLayer(layer) 546 547 ms_layer.status = mapscript.MS_ON 548 549 return ms_layer552 555 558 561763 764563 timestamps = time_param.split("/") 564 565 if len(timestamps) == 1: 566 try: 567 timestamp = getDateTime(timestamps[0]) 568 except InvalidParameterException: 569 raise InvalidRequestException( 570 "Invalid 'TIME' parameter format.", 571 "InvalidParameterValue", 572 "time" 573 ) 574 575 return System.getRegistry().getFromFactory( 576 "resources.coverages.filters.CoverageExpressionFactory", 577 { 578 "op_name": "time_slice", 579 "operands": (timestamp,) 580 } 581 ) 582 583 elif len(timestamps) == 2: 584 try: 585 time_intv = TimeInterval( 586 getDateTime(timestamps[0]), 587 getDateTime(timestamps[1]) 588 ) 589 except InvalidParameterException: 590 raise InvalidRequestException( 591 "Invalid 'TIME' parameter format.", 592 "InvalidParameterValue", 593 "time" 594 ) 595 596 return System.getRegistry().getFromFactory( 597 "resources.coverages.filters.CoverageExpressionFactory", 598 { 599 "op_name": "time_intersects", 600 "operands": (time_intv,) 601 } 602 ) 603 else: 604 raise InvalidRequestException( 605 "Invalid 'TIME' parameter format.", 606 "InvalidParameterValue", 607 "time" 608 )609611 try: 612 bbox = self.req.getParamValue("bbox") 613 except InvalidParameterException: 614 raise InvalidRequestException( 615 "Invalid BBOX parameter value", 616 "InvalidParameterValue", 617 "bbox" 618 ) 619 620 if len(bbox) != 4: 621 raise InvalidRequestException( 622 "Wrong number of arguments for 'BBOX' parameter", 623 "InvalidParameterValue", 624 "bbox" 625 ) 626 627 srid = self.getSRID() 628 629 area = self.getBoundedArea(srid, bbox) 630 631 filter_exprs = [] 632 633 # TODO sqlite assert ahead `GEOSCoordSeq_setOrdinate_r` 634 filter_exprs.append( 635 System.getRegistry().getFromFactory( 636 "resources.coverages.filters.CoverageExpressionFactory", 637 { 638 "op_name": "footprint_intersects_area", 639 "operands": (area,) 640 } 641 ) 642 ) 643 644 time_param = self.req.getParamValue("time") 645 646 if time_param is not None: 647 filter_exprs.append(self.getTimeFilterExpr(time_param)) 648 649 return filter_exprs650652 layer_names = self.req.getParamValue("layers") 653 654 if layer_names is None: 655 raise InvalidRequestException( 656 "Missing 'LAYERS' parameter", 657 "MissingParameterValue", 658 "layers" 659 ) 660 661 filter_exprs = self.getFilterExpressions() 662 663 for layer_name in layer_names: 664 self.createLayersForName(layer_name, filter_exprs)665667 dataset_series = System.getRegistry().getFromFactory( 668 "resources.coverages.wrappers.DatasetSeriesFactory", 669 {"obj_id": layer_name} 670 ) 671 if dataset_series is not None: 672 self.createDatasetSeriesLayers(dataset_series, filter_exprs) 673 else: 674 coverage = System.getRegistry().getFromFactory( 675 "resources.coverages.wrappers.EOCoverageFactory", 676 {"obj_id": layer_name} 677 ) 678 if coverage is not None: 679 if coverage.matches(filter_exprs): 680 # TODO: check if the coverage crosses the dateline 681 # if yes, add multiple layers 682 683 if extent_crosses_dateline(coverage.getExtent(), coverage.getSRID()): 684 logger.debug("Coverage %s crosses the dateline. Special layer setup." % coverage.getCoverageId()) 685 if coverage.getType() == "eo.rect_dataset": 686 coverage_id = coverage.getCoverageId() 687 688 unwrapped_coverage_layer = WMSRectifiedDatasetLayer(coverage) 689 unwrapped_coverage_layer.name = coverage_id + "_unwrapped" 690 691 wrapped_extent = wrap_extent_around_dateline( 692 coverage.getExtent(), coverage.getSRID() 693 ) 694 vrt_path = str("/vsimem/%s/%s.vrt" % (str(uuid4()), coverage_id)) # str to avoid unicode issues 695 wrapped_coverage_layer = WMSWrappedRectifiedDatasetLayer(coverage, vrt_path, wrapped_extent) 696 wrapped_coverage_layer.name = coverage_id + "_wrapped" 697 698 unwrapped_coverage_layer.setGroup(coverage_id) 699 wrapped_coverage_layer.setGroup(coverage_id) 700 701 self.addLayer(unwrapped_coverage_layer) 702 self.addLayer(wrapped_coverage_layer) 703 else: 704 raise NotImplementedError( 705 "WMS for dateline crossing datasets are not " 706 "implemented." 707 ) 708 else: 709 self.addLayer(self.createCoverageLayer(coverage)) 710 else: 711 self.addLayer(WMSEmptyLayer(coverage.getCoverageId())) 712 else: 713 raise InvalidRequestException( 714 "No coverage or dataset series with EO ID '%s' found" % layer_name, 715 "LayerNotDefined", "layers" 716 )717 721 722 coverages = dataset_series.getEOCoverages(filter_exprs) 723 eoid = dataset_series.getEOID() 724 725 if len(coverages) == 0: 726 layer = WMSEmptyLayer(eoid) 727 728 self.addLayer(layer) 729 730 coverages.sort(key=_get_begin_time) 731 732 for coverage in coverages: 733 if extent_crosses_dateline(coverage.getExtent(), coverage.getSRID()): 734 logger.debug("Coverage %s crosses the dateline. Special layer setup." % coverage.getCoverageId()) 735 if coverage.getType() == "eo.rect_dataset": 736 coverage_id = coverage.getCoverageId() 737 738 unwrapped_coverage_layer = WMSRectifiedDatasetLayer(coverage) 739 unwrapped_coverage_layer.name = coverage_id + "_unwrapped" 740 741 wrapped_extent = wrap_extent_around_dateline( 742 coverage.getExtent(), coverage.getSRID() 743 ) 744 vrt_path = str("/vsimem/%s/%s.vrt" % (str(uuid4()), coverage_id)) # str to avoid unicode issues 745 wrapped_coverage_layer = WMSWrappedRectifiedDatasetLayer(coverage, vrt_path, wrapped_extent) 746 wrapped_coverage_layer.name = coverage_id + "_wrapped" 747 748 unwrapped_coverage_layer.setGroup(eoid) 749 wrapped_coverage_layer.setGroup(eoid) 750 751 self.addLayer(unwrapped_coverage_layer) 752 self.addLayer(wrapped_coverage_layer) 753 else: 754 raise NotImplementedError( 755 "WMS for dateline crossing datasets are not " 756 "implemented." 757 ) 758 759 else: 760 layer = self.createCoverageLayer(coverage) 761 layer.setGroup(eoid) 762 self.addLayer(layer)766 # TODO: more performant solution based on hashes 767 for other_layer in self.layers: 768 if other_layer.getName() == layer.getName(): 769 return 770 771 self.layers.append(layer)772774 775 srs = self.req.getParamValue(self.getSRSParameterName()) 776 777 if srs is None: 778 raise InvalidRequestException("Missing '%s' parameter" 779 % self.getSRSParameterName().upper(), "MissingParameterValue", 780 self.getSRSParameterName()) 781 782 srid = crss.parseEPSGCode(srs,(crss.fromURL,crss.fromURN, 783 crss.fromShortCode)) 784 785 if srid is None: 786 raise InvalidRequestException("Invalid '%s' parameter value" 787 % self.getSRSParameterName().upper(), "InvalidCRS" , 788 self.getSRSParameterName()) 789 790 return srid791793 ms_layer = super(WMS1XGetMapHandler, self).getMapServerLayer(layer) 794 795 ms_layer.status = mapscript.MS_DEFAULT 796 797 return ms_layer798
| Home | Trees | Indices | Help |
|
|---|
| Generated by Epydoc 3.0.1 on Wed May 15 14:50:23 2013 | http://epydoc.sourceforge.net |