Changeset 1072

Show
Ignore:
Timestamp:
04/02/08 09:18:23
Author:
domlowe
Message:

Updated WFS and tests to new interfaces

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • OWSLib/branches/wcstemp/owslib/wfs.py

    r660 r1072  
    4747    """ 
    4848     
     49    def __getitem__(self,name): 
     50        ''' check contents dictionary to allow dict like access to service layers''' 
     51        if name in self.__getattribute__('contents').keys(): 
     52            return self.__getattribute__('contents')[name] 
     53        else: 
     54            raise KeyError, "No content named %s" % name 
     55     
     56     
    4957    def __init__(self, url, version='1.0.0', xml=None): 
    5058        """Initialize.""" 
     
    5260        self.version = version 
    5361        self._capabilities = None 
     62        reader = WFSCapabilitiesReader(self.version) 
    5463        if xml: 
    55             reader = WFSCapabilitiesReader(self.version) 
    56             self._capabilities = ServiceMetadata(reader.readString(xml)) 
    57          
    58     def _getcapproperty(self): 
    59         if not self._capabilities: 
    60             reader = WFSCapabilitiesReader(self.version) 
    61             self._capabilities = ServiceMetadata(reader.read(self.url)) 
    62         return self._capabilities 
    63     capabilities = property(_getcapproperty, None) 
    64              
     64            self._capabilities = reader.readString(xml) 
     65        else: 
     66            self._capabilities = reader.read(self.url) 
     67        self._buildMetadata() 
     68     
     69    def _buildMetadata(self): 
     70        '''set up capabilities metadata objects: ''' 
     71         
     72        #serviceIdentification metadata 
     73        serviceelem=self._capabilities.find(nspath('Service')) 
     74        self.identification=ServiceIdentification(serviceelem, self.version)   
     75     
     76        #serviceProvider metadata 
     77        self.provider=ServiceProvider(serviceelem)    
     78         
     79        #serviceOperations metadata  
     80        self.operations=[] 
     81        for elem in self._capabilities.find(nspath('Capability/Request')).getchildren(): 
     82            self.operations.append(OperationMetadata(elem)) 
     83                    
     84        #serviceContents metadata: our assumption is that services use a top-level  
     85        #layer as a metadata organizer, nothing more.  
     86         
     87        self.contents={}  
     88        featuretypelist=self._capabilities.find(nspath('FeatureTypeList')) 
     89        features = self._capabilities.findall(nspath('FeatureTypeList/FeatureType')) 
     90        for feature in features: 
     91            cm=ContentMetadata(feature, featuretypelist) 
     92            self.contents[cm.id]=cm        
     93         
     94        #exceptions 
     95        self.exceptions = [f.text for f \ 
     96                in self._capabilities.findall('Capability/Exception/Format')] 
     97       
    6598    def getcapabilities(self): 
    6699        """Request and return capabilities document from the WFS as a  
    67         file-like object.""" 
     100        file-like object. 
     101        NOTE: this is effectively redundant now""" 
    68102        reader = WFSCapabilitiesReader(self.version) 
    69103        return urlopen(reader.capabilities_url(self.url)) 
     104     
     105    def items(self): 
     106        '''supports dict-like items() access''' 
     107        items=[] 
     108        for item in self.contents: 
     109            items.append((item,self.contents[item])) 
     110        return items 
    70111     
    71112    def getfeature(self, typename=None, filter=None, bbox=None, featureid=None, 
     
    99140        3) featureid (direct access to known features) 
    100141        """ 
    101         md = self.capabilities 
    102         base_url = md.getOperationByName('{http://www.opengis.net/wfs}GetFeature').methods[method]['url'] 
     142        base_url = self.getOperationByName('{http://www.opengis.net/wfs}GetFeature').methods[method]['url'] 
    103143        request = {'service': 'WFS', 'version': self.version, 'request': 'GetFeature'} 
    104144         
     
    149189            return u 
    150190 
    151  
    152 class ServiceMetadata(object): 
    153     """Abstraction for WFS metadata. 
    154      
    155     Implements IServiceMetadata. 
    156     """ 
    157  
    158     def __init__(self, infoset): 
    159         """Initialize from an element tree.""" 
    160         self._root = infoset.getRoot() 
    161         # properties 
    162         self.service = self._root.find(nspath('Service/Name')).text 
    163         self.title = self._root.find(nspath('Service/Title')).text 
    164         self.abstract = self._root.find(nspath('Service/Abstract')).text 
    165         self.link = self._root.find(nspath('Service/OnlineResource')).text 
    166          
    167         # operations [] 
    168         self.operations = [] 
    169         for elem in self._root.findall(nspath('Capability/Request/*')): 
    170             self.operations.append(OperationMetadata(elem)) 
    171  
    172         # contents: our assumption is that services use a top-level layer 
    173         # as a metadata organizer, nothing more. 
    174         self.contents = [] 
    175         top = self._root.find(nspath('FeatureTypeList')) 
    176         for elem in self._root.findall(nspath('FeatureTypeList/FeatureType')): 
    177             self.contents.append(ContentMetadata(elem, top)) 
    178          
    179         # keywords 
    180         self.keywords = [] 
    181  
    182         self.provider = ContactMetadata(self._root.find('Service/ContactInformation')) 
    183  
    184     def getContentByName(self, name): 
    185         """Return a named content item.""" 
    186         for item in self.contents: 
    187             if item.name == name: 
    188                 return item 
    189         raise KeyError, "No content named %s" % name 
    190  
    191191    def getOperationByName(self, name): 
    192192        """Return a named content item.""" 
     
    196196        raise KeyError, "No operation named %s" % name 
    197197 
     198class ServiceIdentification(object): 
     199    ''' Implements IServiceIdentificationMetadata ''' 
     200     
     201    def __init__(self, infoset, version): 
     202        self._root=infoset 
     203        self.type = self._root.find(nspath('Name')).text 
     204        self.version = version 
     205        self.title = self._root.find(nspath('Title')).text 
     206        abstract = self._root.find(nspath('Abstract')) 
     207        if abstract is not None: 
     208            self.abstract = abstract.text 
     209        else: 
     210            self.abstract = None 
     211        self.keywords = [f.text for f in self._root.findall(nspath('Keywords'))] 
     212        accessconstraints=self._root.find(nspath('AccessConstraints')) 
     213        if accessconstraints is not None: 
     214            self.accessconstraints = accessconstraints.text 
     215        else: 
     216            accessconstraints=None 
     217        fees = self._root.find(nspath('Fees')) 
     218        if fees is not None: 
     219            self.fees = fees.text 
     220              
     221class ServiceProvider(object): 
     222    ''' Implements IServiceProviderMetatdata ''' 
     223    def __init__(self, infoset): 
     224        self._root=infoset 
     225        name=self._root.find(nspath('ContactInformation/ContactPersonPrimary/ContactOrganization')) 
     226        if name is not None: 
     227            self.name=name.text 
     228        else: 
     229            self.name=None 
     230        self.url=self._root.find(nspath('OnlineResource')).attrib.get('{http://www.w3.org/1999/xlink}href', '') 
     231        if self.url == '': 
     232            self.url=self._root.find(nspath('OnlineResource')).text 
     233        #contact metadata 
     234        contact = self._root.find('ContactInformation') 
     235        ## sometimes there is a contact block that is empty, so make 
     236        ## sure there are children to parse 
     237        if contact is not None and contact.getchildren(): 
     238            self.contact = ContactMetadata(contact) 
     239        else: 
     240            self.contact = None 
     241 
     242class ContactMetadata: 
     243    """Abstraction for contact details advertised in GetCapabilities. 
     244    Not fully tested due to lack of Contact info in test capabilities doc. 
     245    """ 
     246    def __init__(self, elem): 
     247        self.name = elem.find(nspath('ContactPersonPrimary/ContactPerson')).text 
     248        self.email = elem.find(nspath('ContactElectronicMailAddress')).text  
     249 
     250        self.address = self.city = self.region = None 
     251        self.postcode = self.country = None 
     252 
     253        address = elem.find(nspath('ContactAddress')) 
     254        if address is not None: 
     255            street = address.find(nspath('Address')) 
     256            if street is not None: self.address = street.text 
     257 
     258            city = address.find(nspath('City')) 
     259            if city is not None: self.city = city.text 
     260 
     261            region = address.find(nspath('StateOrProvince')) 
     262            if region is not None: self.region = region.text 
     263 
     264            postcode = address.find(nspath('PostCode')) 
     265            if postcode is not None: self.postcode = postcode.text 
     266 
     267            country = address.find(nspath('Country')) 
     268            if country is not None: self.country = country.text 
     269 
     270        organization = elem.find(nspath('ContactPersonPrimary/ContactOrganization')) 
     271        if organization is not None: self.organization = organization.text 
     272        else:self.organization = None 
     273 
     274        position = elem.find(nspath('ContactPosition')) 
     275        if position is not None: self.position = position.text 
     276        else: self.position = None 
     277 
    198278 
    199279class ContentMetadata: 
     
    205285    def __init__(self, elem, parent): 
    206286        """.""" 
    207         self.name = elem.find(nspath('Name')).text 
     287        self.id = elem.find(nspath('Name')).text 
    208288        self.title = elem.find(nspath('Title')).text 
    209289        # bboxes 
     
    249329 
    250330 
    251 class ContactMetadata: 
    252     """Abstraction for contact details advertised in GetCapabilities. 
    253     """ 
    254     # TODO: refactor with class from wfs 
    255  
    256     def __init__(self, elem): 
    257         self.name = None 
    258         self.email = None 
    259  
    260         if elem: 
    261             self.name = elem.find('ContactPersonPrimary/ContactPerson').text 
    262             self.organization = elem.find('ContactPersonPrimary/ContactOrganization').text 
    263             address = elem.find('ContactAddress') 
    264             if address is not None: 
    265                 try:     
    266                     self.address = address.find('Address').text 
    267                     self.city = address.find('City').text 
    268                     self.region = address.find('StateOrProvince').text 
    269                     self.postcode = address.find('Postcode').text 
    270                     self.country = address.find('Country').text 
    271                 except: pass 
    272             self.email = elem.find('ContactElectronicMailAddress').text  
    273  
    274  
    275 class WFSCapabilitiesInfoset(object): 
    276     """High-level container for WFS Capabilities based on lxml.etree 
    277     """ 
    278  
    279     def __init__(self, infoset): 
    280         """Initialize""" 
    281         self._infoset = infoset 
    282  
    283     # 
    284     # XML Node accessors 
    285     #  
    286      
    287     def getRoot(self): 
    288         """ 
    289         Returns the root node of the capabilities document. 
    290         """ 
    291         return self._infoset 
    292  
    293     def getServiceNode(self): 
    294         """ 
    295         Returns the <Service> node of the capabilities document. 
    296         """ 
    297         return self.getRoot().find(nspath('Service')) 
    298  
    299     def getCapabilitiesNode(self): 
    300         """ 
    301         Returns the <Capability> node of the capabilities document. 
    302         """ 
    303         return self.getRoot().find(nspath('Capability')) 
    304  
    305     def getFeatureTypeNode(self): 
    306         """ 
    307         Returns the <FeatureTypeList> node of the capabilities document. 
    308         """ 
    309         return self.getRoot().find(nspath('FeatureTypeList')) 
    310  
    311     def getFilterNode(self): 
    312         """ 
    313         Returns the <Filter_Capabilities> node of the capabilities document. 
    314         """ 
    315         return self.getRoot().find(nspath('Filter_Capabilities')) 
    316  
    317     # 
    318     # Info accessors 
    319     # 
    320  
    321     def getServiceInfo(self): 
    322         """ 
    323         Returns the WFS Service information packed in a dictionary. 
    324         """ 
    325         service = self.getServiceNode() 
    326         info = {} 
    327         for tag in ('Name', 'Title', 'Abstract', 'Keyword', 'OnlineResource', 
    328                     'Fees', 'AccessConstraints'): 
    329             info[tag.lower()] = service.findtext(nspath(tag)) 
    330         return info 
    331  
    332     def getCapabilityInfo(self): 
    333         """ 
    334         Returns the WFS Capability information packed in a dictionary. 
    335         """ 
    336         # Simplify the resource URLs, favoring GET over POST. 
    337         # Assume GML2. 
    338         capabilities = self.getCapabilitiesNode() 
    339         info = {} 
    340          
    341         for key, path_id in [('capabilities', 'GetCapabilities'), 
    342                              ('description', 'DescribeFeatureType'), 
    343                              ('features', 'GetFeature')]: 
    344             get_path = nspath('Request/%s/DCPType/HTTP/Get' % path_id) 
    345             post_path = nspath('Request/%s/DCPType/HTTP/Post' % path_id) 
    346              
    347             node = capabilities.find(get_path) or capabilities.find(post_path) 
    348             info[key] = node.get('onlineResource', None) 
    349         return info 
    350  
    351     def getFeatureTypeInfo(self): 
    352         """ 
    353         Returns the WFS Feature type information as a list of dictionaries. 
    354         """ 
    355         # Assume XMLSchema is used as the schema description language. 
    356         info = [] 
    357         for featuretype in self.getFeatureTypeNode().getiterator(nspath('FeatureType')): 
    358             entry = {} 
    359             # Loop over simple text nodes 
    360             for tag in ('Name', 'Title', 'Abstract', 'SRS'): 
    361                 entry[tag.lower()] = featuretype.findtext(nspath(tag)) 
    362  
    363             # LatLongBoundingBox 
    364             entry['latlongboundingbox'] = [] 
    365             for latlong in featuretype.findall(nspath('LatLongBoundingBox')): 
    366                 entry['latlongboundingbox'].append('%s,%s,%s,%s' % (latlong.get('minx'), 
    367                                                                     latlong.get('miny'), 
    368                                                                     latlong.get('maxx'), 
    369                                                                     latlong.get('maxy'))) 
    370              
    371             # MetadataURL 
    372             entry['metadataurl'] = [] 
    373             for metadataurl in featuretype.findall(nspath('MetadataURL')): 
    374                 entry['metadataurl'].append(metadataurl.text) 
    375  
    376             info.append(entry) 
    377         return info 
    378  
    379  
    380331class WFSCapabilitiesReader(object): 
    381332    """Read and parse capabilities document into a lxml.etree infoset 
     
    417368        request = self.capabilities_url(url) 
    418369        u = urlopen(request) 
    419         return WFSCapabilitiesInfoset(etree.fromstring(u.read())) 
     370        return etree.fromstring(u.read()) 
    420371 
    421372    def readString(self, st): 
     
    427378        if not isinstance(st, str): 
    428379            raise ValueError("String must be of type string, not %s" % type(st)) 
    429         return WFSCapabilitiesInfoset(etree.fromstring(st)
    430      
     380        return etree.fromstring(st
     381     
  • OWSLib/branches/wcstemp/owslib/wms.py

    r1071 r1072  
    210210        abstract = self._root.find('Abstract') 
    211211        if abstract is not None: 
    212                 self.abstract = self._root.find('Abstract').text 
     212                self.abstract = abstract.text 
    213213        else: 
    214214                self.abstract = None 
  • OWSLib/branches/wcstemp/tests/MapServerWFSCapabilities.txt

    r799 r1072  
    1111Test capabilities 
    1212 
    13     >>> wfs.capabilities.service 
     13Service Identification: 
     14 
     15    >>> wfs.identification.type 
    1416    'MapServer WFS' 
    1517 
    16     >>> wfs.capabilities.title 
     18    >>> wfs.identification.version 
     19    '1.0' 
     20     
     21    >>> wfs.identification.title 
    1722    'Bird Studies Canada WMS/WFS Server' 
    1823     
    19     >>> wfs.capabilities.abstract 
     24    >>> wfs.identification.abstract 
    2025    'Bird Studies Canada WMS/WFS Server for bird distribution and abundance data, and related information.  Bird Studies Canada gratefully acknowledges the support of Environment Canada - Canadian Information System for the Environment in developing this service.' 
    2126     
    22     >>> wfs.capabilities.link 
     27    >>> wfs.identification.keywords 
     28    ['\n    birds\n    distribution\n    abundance\n    conservation\n    sites\n    monitoring\n    populations\n    canada\n  '] 
     29 
     30    >>> wfs.identification.accessconstraints 
     31    'None' 
     32 
     33    >>> wfs.identification.fees 
     34    'None' 
     35 
     36Service Provider: 
     37 
     38    >>> wfs.provider.name 
     39 
     40    >>> wfs.provider.url 
    2341    'http://geodiscover.cgdi.ca/gdp/search?action=entrySummary&entryType=webService&entryId=3920&entryLang=en&portal=gdp' 
    2442     
    25     >>> wfs.capabilities.keywords 
    26     [] 
    27      
    28     >>> p = wfs.capabilities.provider 
    29     >>> p.name 
    30      
    31     >>> p.email 
    32      
     43    #TODO: test contact info: 
     44    #>>> wfs.provider.contact.name 
     45 
    3346     
    3447Test available content layers 
    3548 
    36     >>> [layer.name for layer in wfs.capabilities.contents] 
    37     ['IBA', 'CBC_PT', 'CBC_PY', 'MMP', 'CLLS', 'OBBA_SQUARE', 'OBBA_REGION', 'OBBA_BLOCK', 'OWLS', 'BBS_PT'] 
     49    >>> wfs.contents.keys() 
     50    ['CBC_PT', 'OBBA_REGION', 'IBA', 'MMP', 'CBC_PY', 'OBBA_SQUARE', 'BBS_PT', 'OWLS', 'OBBA_BLOCK', 'CLLS'] 
    3851     
    39     >>> [layer.title for layer in wfs.capabilities.contents] 
    40     ['Canadian Important Bird Areas', 'Canadian Christmas Bird Count Locations', 'Canadian Christmas Bird Count Locations', 'Marsh Monitoring Program Route Locations', 'Canadian Lakes Loon Survey Locations', 'Ontario Breeding Bird Atlas 10 km Squares', 'Ontario Breeding Bird Atlas Administrative Regions', 'Ontario Breeding Bird Atlas 100 km Blocks', 'Nocturnal Owl Survey Locations', 'Breeding Bird Survey Route Start Points'] 
     52    >>> [wfs[layer].title for layer in wfs.contents] 
     53    ['Canadian Christmas Bird Count Locations', 'Ontario Breeding Bird Atlas Administrative Regions', 'Canadian Important Bird Areas', 'Marsh Monitoring Program Route Locations', 'Canadian Christmas Bird Count Locations', 'Ontario Breeding Bird Atlas 10 km Squares', 'Breeding Bird Survey Route Start Points', 'Nocturnal Owl Survey Locations', 'Ontario Breeding Bird Atlas 100 km Blocks', 'Canadian Lakes Loon Survey Locations'] 
    4154     
    4255Test single item accessor 
    4356 
    44     >>> wfs.capabilities.getContentByName('IBA').title 
     57    >>> wfs['IBA'].title 
    4558    'Canadian Important Bird Areas' 
    4659     
    47     >>> wfs.capabilities.getContentByName('IBA').boundingBox 
     60    >>> wfs['IBA'].boundingBox 
    4861     
    49     >>> wfs.capabilities.getContentByName('IBA').boundingBoxWGS84 
     62    >>> wfs['IBA'].boundingBoxWGS84 
    5063    (-141.238, 41.671799999999998, -52.667000000000002, 78.105900000000005) 
    5164     
    52     >>> wfs.capabilities.getContentByName('IBA').crsOptions 
     65    >>> wfs['IBA'].crsOptions 
    5366    ['EPSG:4326'] 
    5467     
    55     >>> wfs.capabilities.getContentByName('IBA').verbOptions 
     68    >>> wfs['IBA'].verbOptions 
    5669    ['{http://www.opengis.net/wfs}Query'] 
    5770     
    5871Expect a KeyError for invalid names 
    5972 
    60     >>> wfs.capabilities.getContentByName('utterly bogus').title 
     73    >>> wfs['utterly bogus'].title 
    6174    Traceback (most recent call last): 
    6275    ... 
     
    6578Test operations 
    6679 
    67     >>> [op.name for op in wfs.capabilities.operations] 
     80    >>> [op.name for op in wfs.operations] 
    6881    ['{http://www.opengis.net/wfs}GetCapabilities', '{http://www.opengis.net/wfs}DescribeFeatureType', '{http://www.opengis.net/wfs}GetFeature'] 
    6982     
    70     >>> wfs.capabilities.getOperationByName('{http://www.opengis.net/wfs}GetFeature').methods 
     83    >>> wfs.getOperationByName('{http://www.opengis.net/wfs}GetFeature').methods 
    7184    {'{http://www.opengis.net/wfs}Get': {'url': 'http://www.bsc-eoc.org/cgi-bin/bsc_ows.asp?'}, '{http://www.opengis.net/wfs}Post': {'url': 'http://www.bsc-eoc.org/cgi-bin/bsc_ows.asp?'}} 
    7285     
    73     >>> wfs.capabilities.getOperationByName('{http://www.opengis.net/wfs}GetFeature').formatOptions 
     86    >>> wfs.getOperationByName('{http://www.opengis.net/wfs}GetFeature').formatOptions 
    7487    ['{http://www.opengis.net/wfs}GML2', '{http://www.opengis.net/wfs}GML3'] 
     88 
     89 
     90Test exceptions 
     91 
     92    >>> wfs.exceptions 
     93    [] 
    7594 
    7695Lastly, test the getcapabilities method