Package eoxserver :: Package resources :: Package coverages :: Module data
[hide private]
[frames] | no frames]

Source Code for Module eoxserver.resources.coverages.data

   1  #------------------------------------------------------------------------------- 
   2  # $Id: data.py 2240 2013-02-19 19:56:56Z schindlerf $ 
   3  # 
   4  # Project: EOxServer <http://eoxserver.org> 
   5  # Authors: Stephan Krause <stephan.krause@eox.at> 
   6  #          Stephan Meissl <stephan.meissl@eox.at> 
   7  # 
   8  #------------------------------------------------------------------------------- 
   9  # Copyright (C) 2011 EOX IT Services GmbH 
  10  # 
  11  # Permission is hereby granted, free of charge, to any person obtaining a copy 
  12  # of this software and associated documentation files (the "Software"), to deal 
  13  # in the Software without restriction, including without limitation the rights 
  14  # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell  
  15  # copies of the Software, and to permit persons to whom the Software is  
  16  # furnished to do so, subject to the following conditions: 
  17  # 
  18  # The above copyright notice and this permission notice shall be included in all 
  19  # copies of this Software or works derived from this Software. 
  20  # 
  21  # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
  22  # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
  23  # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
  24  # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
  25  # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
  26  # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 
  27  # THE SOFTWARE. 
  28  #------------------------------------------------------------------------------- 
  29   
  30  import os.path 
  31  import logging 
  32   
  33  from eoxserver.contrib import gdal 
  34  from eoxserver.core.system import System 
  35  from eoxserver.core.records import ( 
  36      RecordWrapper, RecordWrapperFactoryInterface, RecordWrapperFactory 
  37  ) 
  38  from eoxserver.core.exceptions import InternalError 
  39  from eoxserver.backends.cache import CacheFileWrapper 
  40  from eoxserver.resources.coverages.exceptions import ( 
  41      EngineError, MetadataException 
  42  ) 
  43  from eoxserver.resources.coverages.models import ( 
  44      DataSource, DataPackage, LocalDataPackage, RemoteDataPackage, 
  45      RasdamanDataPackage, TileIndex 
  46  ) 
  47  from eoxserver.resources.coverages.interfaces import ( 
  48      DataSourceInterface, DataPackageInterface, TileIndexInterface 
  49  ) 
  50  from eoxserver.resources.coverages.geo import GeospatialMetadata 
  51  from eoxserver.resources.coverages.formats import getFormatRegistry 
  52   
  53   
  54  logger = logging.getLogger(__name__) 
  55   
  56  #------------------------------------------------------------------------------- 
  57  # Data source wrappers 
  58  #------------------------------------------------------------------------------- 
  59   
60 -class DataSourceWrapper(RecordWrapper):
61 """ 62 This class implements :class:`~.DataSourceInterface`. It inherits from 63 :class:`~.RecordWrapper`. 64 65 .. method:: setAttrs(**kwargs) 66 67 :class:`DataSourceWrapper` defines two attributes that can be assigned 68 to an instance: 69 70 * ``location``: the location of the data source (a local or remote path 71 to a directory) 72 * ``search_pattern``: the search pattern defined for the data source 73 (optional) 74 75 .. method:: sync 76 77 See :meth:`.RecordWrapper.sync`. 78 79 .. method:: getRecord 80 81 See :meth:`.RecordWrapper.getRecord` 82 """ 83 84 REGISTRY_CONF = { 85 "name": "Data Source Wrapper", 86 "impl_id": "resources.coverages.data.DataSourceWrapper", 87 "factory_ids": ( 88 "resources.coverages.data.DataSourceFactory", 89 ) 90 } 91
92 - def __init__(self):
93 self.record = None 94 95 self.location = None 96 self.search_pattern = None
97
98 - def getType(self):
99 """ 100 Returns ``"data_source"``. 101 """ 102 103 return "data_source"
104
105 - def detect(self):
106 """ 107 Detect files at the location that match the given search pattern. 108 Returns a list of locations. If no location has been defined yet, 109 return an empty list. 110 """ 111 112 if self.record: 113 location = System.getRegistry().getFromFactory( 114 factory_id="backends.factories.LocationFactory", 115 params={ 116 "record": self.record.location 117 } 118 ) 119 120 return location.detect(self.record.search_pattern) 121 elif self.location: 122 return self.location.detect(self.search_pattern) 123 else: 124 return []
125
126 - def contains(self, wrapper):
127 """ 128 Check if the DataSource contains a coverage with a certain ID 129 """ 130 res_id = wrapper.getModel().pk 131 132 if self.record: 133 return self.record.coveragerecord_set.filter(pk=res_id).count() > 0 134 else: 135 return False
136
137 - def _validate_record(self, record):
138 if not isinstance(record, DataSource): 139 raise InternalError( 140 "Cannot assign '%s' object to data source record." % \ 141 record.__class__.__name__ 142 )
143 # use default _set_record method 144
145 - def _validate_attrs(self, **kwargs):
146 if "location" not in kwargs: 147 raise InternalError( 148 "Missing mandatory 'location' keyword argument when initializing data source wrapper." 149 )
150
151 - def _set_attrs(self, **kwargs):
152 self.location = kwargs["location"] 153 self.search_pattern = kwargs.get("search_pattern")
154
155 - def _fetch_unique_record(self):
156 return self._fetch()
157 158 # use default _fetch method 159
160 - def _get_query(self, fields=None):
161 query = {} 162 163 if fields is None or "location" in fields: 164 if self.location: 165 query["location"] = self.location.getRecord() 166 else: 167 query["location"] = None 168 169 if fields is None or "search_pattern" in fields: 170 query["search_pattern"] = self.search_pattern 171 172 return query
173
174 - def _get_query_set(self, query):
175 return DataSource.objects.filter(**query)
176
177 - def _create_record(self):
178 if self.location: 179 location_record = self.location.getRecord() 180 else: 181 raise InternalError( 182 "Cannot create data source without location." 183 ) 184 185 self.record = DataSource.objects.create( 186 location=location_record, 187 search_pattern=self.search_pattern 188 )
189 190 DataSourceWrapperImplementation = \ 191 DataSourceInterface.implement(DataSourceWrapper) 192 193 #------------------------------------------------------------------------------- 194 # Data package wrappers 195 #------------------------------------------------------------------------------- 196
197 -class DataPackageWrapper(RecordWrapper):
198 """ 199 This is the common base class for data package wrappers. It derives from 200 :class:`~.RecordWrapper`. 201 202 .. method:: setAttrs(**kwargs) 203 204 :class:`DataPackageWrapper` defines three attributes that can be assigned 205 to any data package instance: 206 207 * ``location``: the location of the data; the location type depends on 208 the concrete data package subclass 209 * ``metadata_location``: the location of the metadata; the location type 210 depends on the concrete data package subclass 211 * ``metadata_format_name``: the name of the metadata format; can be 212 derived automatically from the metadata using :meth:`readEOMetadata` 213 214 .. method:: sync 215 216 See :meth:`.RecordWrapper.sync`. 217 218 .. method:: getRecord 219 220 See :meth:`.RecordWrapper.getRecord` 221 """ 222
223 - def __init__(self):
224 super(DataPackageWrapper, self).__init__() 225 226 self.metadata_format_name = None 227 self.location = None 228 self.metadata_location = None
229
230 - def open(self):
231 """ 232 Open the underlying dataset with GDAL and return a 233 :class:`osgeo.gdal.Dataset` object. This method raises 234 :exc:`~.EngineError` if GDAL was not able to open the dataset. It 235 raises :exc:`~.DataAccessError` if the dataset could not be made 236 accessible to GDAL (e.g. download of a remote FTP resource failed). 237 """ 238 239 self.prepareAccess() 240 241 gdal_str = self.getGDALDatasetIdentifier() 242 243 try: 244 ds = gdal.Open(str(gdal_str)) 245 246 if ds is None: 247 raise EngineError( 248 "Could not open GDAL Dataset '%s'." % gdal_str 249 ) 250 else: 251 return ds 252 253 except Exception, e: 254 raise EngineError( 255 "Could not open GDAL Dataset '%s'. Error message: '%s'" % ( 256 gdal_str, str(e) 257 ) 258 )
259
260 - def getSourceFormat(self):
261 """ 262 Return the source data file format. 263 """ 264 return str( self.record.source_format )
265
266 - def getLocation(self):
267 """ 268 Return the location of the data, i.e. an object implementing 269 :class:`~.LocationInterface`. The location type depends on the 270 concrete data package subclass. 271 """ 272 273 if self.record: 274 return System.getRegistry().getFromFactory( 275 "backends.factories.LocationFactory", 276 params = { 277 "record": self.record.data_location 278 } 279 ) 280 else: 281 return self.location
282
283 - def getMetadataLocation(self):
284 """ 285 Return the location of the metadata, i.e. an object implementing 286 :class:`~.LocationInterface`. The location type depends on the 287 concrete data package subclass. 288 """ 289 290 if self.record and self.record.metadata_location: 291 return System.getRegistry().getFromFactory( 292 "backends.factories.LocationFactory", 293 params = { 294 "record": self.record.metadata_location 295 } 296 ) 297 elif not self.record: 298 return self.metadata_location 299 elif not self.metadata_location: 300 return None
301
302 - def getCoverages(self):
303 """ 304 Return the coverages that use this data package. 305 """ 306 307 if self.record: 308 return System.getRegistry().bind( 309 "resources.coverages.wrappers.EOCoverageFactory" 310 ).find( 311 impl_ids = [ 312 "resources.coverages.wrappers.RectifiedDatasetWrapper", 313 "resources.coverages.wrappers.ReferenceableDatasetWrapper" 314 # TODO: add plain coverages 315 ], 316 filter_exprs = [ 317 System.getRegistry().getFromFactory( 318 factory_id = "resources.coverages.filters.CoverageExpressionFactory", 319 params = { 320 "op_name": "attr", 321 "operands": ("data_package", "=", self.record.pk) 322 } 323 ) 324 ] 325 ) 326 else: 327 return []
328
329 - def readGeospatialMetadata(self, default_srid=None):
330 """ 331 Read geospatial metadata from the underlying dataset. The return value 332 is a :class:`~.GeospatialMetadata` instance. The method accepts an 333 optional integer ``default_srid`` argument which predefines the 334 output SRID if it cannot be retrieved from the dataset; see 335 :meth:`~.GeospatialMetadata.readFromDataset`. 336 337 The dataset is opened using :meth:`open`; it may raise 338 :exc:`~.DataAccessError` or :exc:`~.EngineError` in the error cases 339 described there. 340 """ 341 342 ds = self.open() 343 344 return GeospatialMetadata.readFromDataset(ds, default_srid)
345
346 - def readEOMetadata(self):
347 """ 348 Read EO Metadata from the metadata location and return an 349 :class:`~.EOMetadata` instance. :exc:`~.DataAccessError` may be raised 350 if the metadata location cannot be made accessible (e.g. an XML 351 metadata file cannot be retrieved from a remote location). 352 :exc:`~.MetadataException` will be raised if the metadata cannot be 353 read (e.g. because a metadata file does not contain valid XML). 354 """ 355 356 if self.getMetadataLocation(): 357 self._prepare_metadata_access() 358 359 try: 360 md_location = self._get_accessible_metadata_location() 361 362 md_reader = System.getRegistry().findAndBind( 363 intf_id = "resources.coverages.interfaces.EOMetadataReader", 364 params = { 365 "location": md_location 366 #"resources.coverages.interfaces.location_type": md_location.getType(), 367 #"resources.coverages.interfaces.encoding_type": "xml" # TODO: make this configurable 368 } 369 ) 370 371 eo_metadata = md_reader.readEOMetadata(md_location) 372 373 self.metadata_format_name = \ 374 eo_metadata.getMetadataFormat().getName() 375 376 self._post_metadata_access() 377 except: 378 self._post_metadata_access() 379 raise MetadataException("") 380 381 return eo_metadata 382 else: 383 return None
384
385 - def prepareAccess(self):
386 """ 387 Prepare access to the underlying dataset. This makes the underlying 388 dataset accessible so that :meth:`getAccessibleLocation` and 389 :meth:`getGDALDatasetIdentifier` can yield meaningful results. Concrete 390 subclasses have to override this. By default :exc:`~.InternalError` is 391 raised. 392 """ 393 394 raise InternalError("Not implemented.")
395
396 - def getAccessibleLocation(self):
397 """ 398 Get an accessible location of the underlying dataset. A previous 399 successful call to :meth:`prepareAccess` may be necessary for this 400 method to yield a meaningful result. Concrete subclasses have to 401 override this. By default :exc:`~.InternalError` is raised. 402 """ 403 404 raise InternalError("Not implemented.")
405
406 - def getGDALDatasetIdentifier(self):
407 """ 408 Get a GDAL dataset identifier for the underlying dataset, i.e. the 409 string to be passed on to the :func:`gdal.Open` function. A previous 410 successful call to :meth:`prepareAccess` may be necessary for this 411 method to yield a meaningful result. Concrete subclasses have to 412 override this. By default :exc:`~.InternalError` is raised. 413 """ 414 415 raise InternalError("Not implemented.")
416
417 - def _validate_attrs(self, **kwargs):
418 if not "location" in kwargs: 419 raise InternalError( 420 "When assigning attributes to a local data package you must either provide a 'location' keyword argument" 421 )
422
423 - def _set_attrs(self, **kwargs):
424 # set attributes 425 426 self.location = kwargs["location"] 427 428 self.metadata_location = kwargs.get("metadata_location") 429 self.metadata_format_name = kwargs.get("metadata_format_name")
430
431 - def _fetch_unique_record(self):
432 # no uniqueness constraints apply 433 434 return None
435
436 - def _get_query(self, fields=None):
437 query = {} 438 439 if fields is None or "location" in fields: 440 query["data_location"] = self.getLocation().getRecord() 441 442 if fields is None or "metadata_location" in fields: 443 metadata_location = self.getMetadataLocation() 444 445 if metadata_location: 446 query["metadata_location"] = metadata_location.getRecord() 447 else: 448 query["metadata_location"] = None 449 450 451 if fields is None or "metadata_format_name" in fields: 452 query["metadata_format_name"] = self.metadata_format_name 453 454 return query
455
456 - def _prepare_metadata_access(self):
457 # called by :meth:`readEOMetadata` to prepare access to a remote 458 # metadata resource 459 460 raise InternalError("Not implemented.")
461
462 - def _get_accessible_metadata_location(self):
463 # called by :meth:`readEOMetadata` to retrieve an accessible location 464 # for a metadata resource 465 466 raise InternalError("Not implemented.")
467
468 - def _post_metadata_access(self):
469 # called by :meth:`readEOMetadata` to clean up after metadata decoding 470 471 raise InternalError("Not implemented.")
472
473 -class LocalDataPackageWrapper(DataPackageWrapper):
474 """ 475 This is a wrapper for data packages stored in files on the local file 476 system. It inherits from :class:`DataPackageWrapper`. See there for the 477 inherited methods. 478 """ 479 480 REGISTRY_CONF = { 481 "name": "Local Data Package Wrapper", 482 "impl_id": "resources.coverages.data.LocalDataPackageWrapper", 483 "factory_ids": ( 484 "resources.coverages.data.DataPackageFactory", 485 ) 486 } 487
488 - def __init__(self, **kwargs):
489 super(LocalDataPackageWrapper, self).__init__(**kwargs) 490 self.source_format = None
491
492 - def getDataStructureType(self):
493 """ 494 Returns ``"file"``. 495 """ 496 497 return "file"
498
499 - def getType(self):
500 """ 501 Returns ``"local"``. 502 """ 503 504 return "local"
505
506 - def prepareAccess(self):
507 """ 508 Nothing to be done here as locations on the local file system are 509 accessible by themselves. 510 """ 511 pass
512
513 - def getAccessibleLocation(self):
514 """ 515 Returns the same as :meth:`getLocation`. 516 """ 517 return self.getLocation()
518
519 - def getGDALDatasetIdentifier(self):
520 """ 521 Returns the path to the data file. 522 523 .. note:: This does not account for data formats where the dataset is 524 structured into subdatasets. This is future work 525 """ 526 return os.path.abspath(self.getLocation().getPath())
527
528 - def _validate_record(self, record):
529 # raise :exc:`~.InternalError` if the model record is not of type 530 # :class:`~.LocalDataPackage`. 531 532 if record.data_package_type != "local": 533 raise InternalError( 534 "Cannot assign '%s' type data package record to local data package." %\ 535 record.data_package_type 536 )
537
538 - def _set_record(self, record):
539 # be sure to set the model record to the :class:`~.LocalDataPackage` 540 # instance (the model record instance may be an instance of 541 # the superclass :class:`~.DataPackage` as well) 542 543 if isinstance(record, LocalDataPackage): 544 self.record = record 545 else: 546 self.record = record.localdatapackage
547
548 - def _set_attrs(self, **kwargs):
549 super(LocalDataPackageWrapper, self)._set_attrs(**kwargs) 550 self.source_format = kwargs.get("source_format") 551 552 if self.source_format is None: 553 driver_name = "GDAL/" + self.open().GetDriver().ShortName 554 frmt = getFormatRegistry().getFormatsByDriver(driver_name)[0] 555 self.source_format = frmt.mimeType
556
557 - def _get_query_set(self, query):
558 return LocalDataPackage.objects.filter(**query)
559
560 - def _create_record(self):
561 # create a model record from the instance attributes 562 563 if self.metadata_location: 564 metadata_location_record = self.metadata_location.getRecord() 565 else: 566 metadata_location_record = None 567 568 self.record = LocalDataPackage.objects.create( 569 data_package_type = LocalDataPackage.DATA_PACKAGE_TYPE, 570 data_location = self.location.getRecord(), 571 metadata_location = metadata_location_record, 572 metadata_format_name = self.metadata_format_name, 573 source_format = self.source_format 574 )
575
576 - def _prepare_metadata_access(self):
577 # nothing to be done here 578 579 pass
580
582 # return local metadata file location 583 584 return self.getMetadataLocation()
585
586 - def _post_metadata_access(self):
587 # nothing to be done here 588 589 pass
590 591 LocalDataPackageWrapperImplementation = \ 592 DataPackageInterface.implement(LocalDataPackageWrapper) 593
594 -class RemoteDataPackageWrapper(DataPackageWrapper):
595 """ 596 This is a wrapper for data stored in a remote repository accessible via 597 FTP. It inherits from :class:`DataPackageWrapper`. See there for the 598 inherited methods. 599 600 This class wraps not only the (remote) locations of data and metadata, but 601 also :class:`~.CacheFileWrapper` instances for locally cached copies of the 602 respective files. 603 604 .. method:: initialize(**kwargs) 605 606 In addition to the attributes declared in 607 :meth:`DataPackageWrapper.initialize` this method accepts an optional 608 ``cache_file`` keyword argument which is expected to be an instance of 609 :class:`~.CacheFileWrapper`. 610 """ 611 612 REGISTRY_CONF = { 613 "name": "Remote Data Package Wrapper", 614 "impl_id": "resources.coverages.data.RemoteDataPackageWrapper", 615 "factory_ids": ( 616 "resources.coverages.data.DataPackageFactory", 617 ) 618 } 619
620 - def __init__(self):
621 super(RemoteDataPackageWrapper, self).__init__() 622 623 self.cache_file = None 624 self.md_cache_file = None
625
626 - def getDataStructureType(self):
627 """ 628 Returns ``"file"``. 629 """ 630 631 return "file"
632
633 - def getType(self):
634 """ 635 Returns ``"remote"``. 636 """ 637 638 return "remote"
639
640 - def prepareAccess(self):
641 """ 642 Loads a remote data file into the local cache, if necessary. Never 643 omit the call to :meth:`prepareAccess` when attempting to access a 644 remote dataset, subsequent method calls to :meth:`open`, 645 :meth:`getAccessibleLocation` and :meth:`getGDALDatasetIdentifier` may 646 fail. 647 """ 648 649 if not self.cache_file: 650 if self.record and self.record.cache_file: 651 self.cache_file = CacheFileWrapper(self.record.cache_file) 652 653 self.cache_file.access() 654 else: 655 remote_location = self.getLocation() 656 657 self.cache_file = CacheFileWrapper.create( 658 os.path.basename(remote_location.getPath()) 659 ) 660 661 self.cache_file.copy(remote_location) 662 663 if self.record: 664 self.record.cache_file = self.cache_file.getModel() 665 666 self.record.save() 667 else: 668 self.cache_file.access()
669
670 - def getAccessibleLocation(self):
671 """ 672 Returns the location of the locally cached data file. 673 """ 674 675 if self.cache_file: 676 return self.cache_file.getLocation() 677 else: 678 raise InternalError( 679 "Cache file not present or not initialized. Please do always call prepareAccess() before accessing a remote data package." 680 )
681
682 - def getGDALDatasetIdentifier(self):
683 """ 684 Returns the path to the location of the locally cached data file. 685 """ 686 687 return os.path.abspath(self.getAccessibleLocation().getPath())
688
689 - def _validate_record(self, record):
690 # check if the model record points to a remote data package 691 692 if record.data_package_type != "remote": 693 raise InternalError( 694 "Cannot assign '%s' type data package record to remote data package." %\ 695 record.data_package_type 696 )
697
698 - def _set_record(self, record):
699 # be sure to set the model record to the :class:`~.RemoteDataPackage` 700 # instance 701 702 if isinstance(record, RemoteDataPackage): 703 self.record = record 704 else: 705 self.record = record.remotedatapackage
706
707 - def _set_attrs(self, **kwargs):
708 # set attributes; raises :exc:`~.InternalError` if no ``location`` 709 # keyword argument is given 710 711 super(RemoteDataPackageWrapper, self)._set_attrs(**kwargs) 712 713 self.cache_file = kwargs.get("cache_file") 714 self.source_format = kwargs.get("source_format") 715 716 if self.source_format is None: 717 driver_name = "GDAL/" + self.open().GetDriver().ShortName 718 frmt = getFormatRegistry().getFormatsByDriver(driver_name)[0] 719 self.source_format = frmt.mimeType
720
721 - def _get_query(self, fields=None):
722 query = super(RemoteDataPackageWrapper, self)._get_query(fields) 723 724 if fields is None or "cache_file" in fields: 725 if self.cache_file: 726 query["cache_file"] = self.cache_file.getModel() 727 else: 728 query["cache_file"] = None 729 730 return query
731
732 - def _get_query_set(self, query):
733 return RemoteDataPackage.objects.filter(**query)
734
735 - def _create_record(self):
736 # create :class:`~.RemoteDataPackage` resource 737 738 739 if self.cache_file: 740 cache_file_record = self.cache_file.getModel() 741 else: 742 cache_file_record = None 743 744 if self.metadata_location: 745 metadata_location_record = self.metadata_location.getRecord() 746 else: 747 metadata_location_record = None 748 749 self.record = RemoteDataPackage.objects.create( 750 data_package_type = RemoteDataPackage.DATA_PACKAGE_TYPE, 751 data_location = self.location.getRecord(), 752 metadata_location = metadata_location_record, 753 metadata_format_name = self.metadata_format_name, 754 cache_file = cache_file_record, 755 source_format = self.source_format 756 )
757
758 - def _prepare_metadata_access(self):
759 # fetch a local copy of the metadata file (create a cache file for it) 760 761 remote_location = self.getMetadataLocation() 762 763 self.md_cache_file = CacheFileWrapper.create( 764 os.path.basename(remote_location.getPath()) 765 ) 766 767 self.md_cache_file.copy(remote_location)
768
770 # return the location of the locally cached metadata file 771 772 if self.md_cache_file: 773 return self.md_cache_file.getLocation() 774 else: 775 raise InternalError( 776 "Cannot access metadata cache file. Did you call _prepare_metadata_access()?" 777 )
778
779 - def _post_metadata_access(self):
780 # remove the locally cached metadata file and the corresponding 781 # database record 782 783 if self.md_cache_file: 784 self.md_cache_file.purge() 785 786 del self.md_cache_file
787 788 RemoteDataPackageWrapperImplementation = \ 789 DataPackageInterface.implement(RemoteDataPackageWrapper) 790
791 -class RasdamanDataPackageWrapper(DataPackageWrapper):
792 """ 793 This is a wrapper for rasdaman data packages. It inherits from 794 :class:`DataPackageWrapper`. See there for the inherited methods. 795 """ 796 797 REGISTRY_CONF = { 798 "name": "Rasdaman Data Package Wrapper", 799 "impl_id": "resources.coverages.data.RasdamanDataPackageWrapper", 800 "factory_ids": ( 801 "resources.coverages.data.DataPackageFactory", 802 ) 803 } 804
805 - def getSourceFormat(self):
806 """ 807 Return the source data file format. 808 """ 809 return None
810
811 - def getDataStructureType(self):
812 """ 813 Returns ``"rasdaman_array"``. 814 """ 815 816 return "rasdaman_array"
817
818 - def getType(self):
819 """ 820 Returns ``"rasdaman_array"``. 821 """ 822 823 return "rasdaman"
824
825 - def prepareAccess(self):
826 """ 827 Nothing to be done here. Though not necessarily local the rasdaman 828 data is always accessible in the sense that its always possible to 829 connect to it without further preconditions. 830 """ 831 832 pass
833
834 - def getAccessibleLocation(self):
835 """ 836 Return the rasdaman array location. 837 """ 838 839 return self.getLocation()
840
841 - def getGDALDatasetIdentifier(self):
842 """ 843 Returns a connection string to the rasdaman database combined with 844 a query indicating the given dataset. This is the format GDAL expects 845 for reading data from a rasdaman array. 846 """ 847 848 location = self.getLocation() 849 850 rasdaman_strs = ["rasdaman:"] 851 852 rasdaman_strs.append("host='%s'" % location.getHost()) 853 854 if location.getPort() is not None: 855 rasdaman_strs.append("port='%d'" % location.getPort()) 856 857 # TODO: check if this is a valid parameter 858 #if location.getDBName() is not None: 859 # rasdaman_strs.append( 860 # "dbname='%s'" % location.getDBName() 861 # ) 862 863 if location.getUser() is not None: 864 rasdaman_strs.append("user='%s'" % location.getUser()) 865 866 if location.getPassword() is not None: 867 rasdaman_strs.append( 868 "password='%s'" % location.getPassword() 869 ) 870 871 872 if location.getOID(): 873 rasdaman_strs.append( 874 "query='select ( a [$x_lo:$x_hi,$y_lo:$y_hi] ) from %s as a where oid(a)=%f'" %\ 875 (location.getCollection(), location.getOID()) 876 877 ) 878 else: 879 rasdaman_strs.append( 880 "query='select ( a [$x_lo:$x_hi,$y_lo:$y_hi] ) from %s as a'" % location.getCollection() 881 ) 882 883 return " ".join(rasdaman_strs)
884
885 - def _validate_record(self, record):
886 # check if the model record points to a rasdaman array data package. 887 888 if record.data_package_type != "rasdaman": 889 raise InternalError( 890 "Cannot assign '%s' type data package record to rasdaman data package." %\ 891 record.data_package_type 892 )
893
894 - def _set_record(self, record):
895 # be sure to set the model record to a :class:`RasdamanDataPackage` 896 # instance 897 898 if isinstance(record, RasdamanDataPackage): 899 self.record = record 900 else: 901 self.record = record.rasdamandatapackage
902
903 - def _get_query_set(self, query):
904 return RasdamanDataPackage.objects.filter(**query)
905
906 - def _create_record(self):
907 # create a :class:`RasdamanDataPackage` model record. 908 909 if self.metadata_location: 910 metadata_location_record = self.metadata_location.getRecord() 911 else: 912 metadata_location_record = None 913 914 self.record = RasdamanDataPackage.objects.create( 915 data_package_type = RasdamanDataPackage.DATA_PACKAGE_TYPE, 916 data_location = self.location.getRecord(), 917 metadata_location = metadata_location_record, 918 metadata_format_name = self.metadata_format_name 919 )
920
921 - def _prepare_metadata_access(self):
922 # nothing to be done here; metadata is expected to reside on the local 923 # file system 924 925 pass
926
927 - def _get_accessible_metadata_location(self):
928 # return the location of the metadata file on the local file system 929 930 return self.getMetadataLocation()
931
932 - def _post_metadata_access(self):
933 # nothing to be done here 934 935 pass
936 937 RasdamanDataPackageWrapperImplementation = \ 938 DataPackageInterface.implement(RasdamanDataPackageWrapper) 939 940 #------------------------------------------------------------------------------- 941 # Tile index wrapper 942 #------------------------------------------------------------------------------- 943 944
945 -class TileIndexWrapper(RecordWrapper):
946 """ 947 This class wraps a tile index. It inherits from 948 :class:`~.RecordWrapper`. 949 950 .. method:: initialize(**kwargs) 951 952 Apart from the mandatory ``record`` keyword argument, this method accepts 953 a ``storage_dir`` argument which will be saved as instance attribute. 954 An :exc:`~.InternalError` will be raised if neither of the two is given. 955 The ``storage_dir`` denotes the path to the local directory where to 956 find a tile index shape file as well as the actual tiles (usually 957 stored in a directory tree under that directory). 958 """ 959 960 REGISTRY_CONF = { 961 "name": "Tile Index Wrapper", 962 "impl_id": "resources.coverages.data.TileIndexWrapper", 963 "factory_ids": ( 964 "resources.coverages.data.TileIndexFactory", 965 ) 966 } 967
968 - def __init__(self):
969 super(TileIndexWrapper, self).__init__() 970 971 self.storage_dir = None
972
973 - def getSourceFormat(self):
974 """ 975 Return the source data file format. 976 """ 977 # TODO: implement proper source format for tile arrays 978 return "image/tiff"
979
980 - def getType(self):
981 """ 982 Returns ``"index"``. 983 """ 984 985 return "index"
986 987
988 - def getDataStructureType(self):
989 """ 990 Returns ``"index"``. 991 """ 992 993 return "index"
994
995 - def getStorageDir(self):
996 """ 997 Returns the path to the directory where to find the tile index shape 998 file as well as the actual tiles. 999 """ 1000 1001 if self.record: 1002 return self.record.storage_dir 1003 else: 1004 return self.storage_dir
1005
1006 - def getShapeFilePath(self):
1007 """ 1008 Returns the path to the tile index shape file. 1009 """ 1010 1011 return os.path.join(self.getStorageDir(), "tindex.shp")
1012
1013 - def _validate_record(self, record):
1014 if not isinstance(record, TileIndex): 1015 raise InternalError( 1016 "Cannot assign '%s' record to tile index wrapper." %\ 1017 record.__class__.__name__ 1018 )
1019 1020 # use default _set_record implementation 1021
1022 - def _validate_attrs(self, **kwargs):
1023 if "storage_dir" not in kwargs: 1024 raise InternalError( 1025 "'storage_dir' keyword arguments needed to initialize tile index." 1026 )
1027
1028 - def _set_attrs(self, **kwargs):
1029 self.storage_dir = kwargs["storage_dir"]
1030
1031 - def _fetch_unique_record(self):
1032 # no uniqueness constraints apply 1033 1034 return None
1035
1036 - def _get_query(self, fields=None):
1037 query = {} 1038 1039 if fields is None or "storage_dir" in query: 1040 query["storage_dir"] = self.getStorageDir() 1041 1042 return query
1043
1044 - def _get_query_set(self, query):
1045 return TileIndex.objects.filter(**query)
1046
1047 - def _create_record(self):
1048 self.record = TileIndex.objects.create(storage_dir = self.storage_dir)
1049 1050 TileIndexWrapperImplementation = \ 1051 TileIndexInterface.implement(TileIndexWrapper) 1052 1053 #------------------------------------------------------------------------------- 1054 # Factories 1055 #------------------------------------------------------------------------------- 1056
1057 -class DataSourceFactory(RecordWrapperFactory):
1058 """ 1059 This is a factory for :class:`DataSourceWrapper` objects. It inherits from 1060 :class:`~.RecordWrapperFactory`. 1061 """ 1062 REGISTRY_CONF = { 1063 "name": "Data Source Factory", 1064 "impl_id": "resources.coverages.data.DataSourceFactory", 1065 "binding_method": "direct" 1066 } 1067
1068 - def _get_record_by_pk(self, pk):
1069 return DataSource.objects.get(pk=pk)
1070
1071 - def _get_record_wrapper(self, record):
1072 wrapper = DataSourceWrapper() 1073 wrapper.setRecord(record) 1074 1075 return wrapper
1076 1077 DataSourceFactoryImplementation = \ 1078 RecordWrapperFactoryInterface.implement(DataSourceFactory) 1079
1080 -class DataPackageFactory(RecordWrapperFactory):
1081 """ 1082 This factory gives access to data package wrappers. It inherits from 1083 :class:`~.RecordWrapperFactory`. 1084 """ 1085 1086 REGISTRY_CONF = { 1087 "name": "Data Package Factory", 1088 "impl_id": "resources.coverages.data.DataPackageFactory", 1089 "binding_method": "direct" 1090 } 1091
1092 - def _get_record_by_pk(self, pk):
1093 return DataPackage.objects.get(pk=pk)
1094
1095 - def _get_record_wrapper(self, record):
1096 wrapper = self.impls[record.data_package_type]() 1097 wrapper.setRecord(record) 1098 1099 return wrapper
1100 1101 DataPackageFactoryImplementation = \ 1102 RecordWrapperFactoryInterface.implement(DataPackageFactory) 1103
1104 -class TileIndexFactory(RecordWrapperFactory):
1105 """ 1106 This is a factory for :class:`TileIndexWrapper` objects. It inherits from 1107 :class:`~.RecordWrapperFactory`. 1108 """ 1109 1110 REGISTRY_CONF = { 1111 "name": "Tile Index Factory", 1112 "impl_id": "resources.coverages.data.TileIndexFactory", 1113 "binding_method": "direct" 1114 } 1115
1116 - def _get_record_by_pk(self, pk):
1117 return TileIndex.objects.get(pk=pk)
1118
1119 - def _get_record_wrapper(self, record):
1120 wrapper = TileIndexWrapper() 1121 wrapper.setRecord(record) 1122 1123 return wrapper
1124 1125 TileIndexFactoryImplementation = \ 1126 RecordWrapperFactoryInterface.implement(TileIndexFactory) 1127