Changeset 807

Show
Ignore:
Timestamp:
05/15/07 02:16:20 (2 years ago)
Author:
sgillies
Message:

Rewrite of the IGeoItemSimple adapter for annotable AT content

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • Geographer/trunk/configure.zcml

    r745 r807  
    44  > 
    55 
    6   <adapter 
    7     for=".interfaces.IGeoreferenceable" 
    8     provides=".interfaces.IGeoItemSimple" 
    9     factory=".geo.GeoItemSimple" 
     6  <utility 
     7    component=".geo.geometryFactory" 
     8    name="geographer.Geometry" 
    109    /> 
    1110 
    12   <adapter 
    13     for=".interfaces.IGeoserializable" 
    14     provides=".interfaces.IGeoCollectionSimple" 
    15     factory=".geo.GeoCollectionSimple" 
    16     /> 
    17  
     11  <adapter factory=".geo.GeoContentSimple"/> 
    1812 
    1913  <five:implements 
     
    2216    /> 
    2317 
    24   <five:implements 
    25     class="Products.ATContentTypes.content.base.BaseContent" 
    26     interface=".interfaces.IGeoserializable" 
    27     /> 
    28      
    29   <five:implements 
    30     class="Products.ATContentTypes.content.folder.ATFolder" 
    31     interface=".interfaces.IGeoserializable" 
    32     /> 
    33  
    34   <five:implements 
    35     class="Products.ATContentTypes.content.topic.ATTopic" 
    36     interface=".interfaces.IGeoserializable" 
    37     /> 
    38  
    39   <include package=".browser"/> 
    40  
    4118</configure> 
    4219 
  • Geographer/trunk/geo.py

    r804 r807  
    2828# =========================================================================== 
    2929 
     30from zope.component import adapts, createObject 
     31from zope.component.factory import Factory 
    3032from zope.interface import implements 
    31 from zope.app.annotation.interfaces import IAnnotations 
    32 from persistent.dict import PersistentDict 
     33from zope.schema.fieldproperty import FieldProperty 
    3334 
    34 from Products.ATContentTypes.interface.topic import IATTopic 
    35 from Products.ATContentTypes.interface.folder import IATFolder 
     35try: 
     36    from zope.annotation.interfaces import IAnnotations 
     37# For Zope 2 
     38except: 
     39    from zope.app.annotation.interfaces import IAnnotations 
    3640 
    37 from Products.Geographer.interfaces \ 
    38     import IGeoreferenceable, IGeoserializable, \ 
    39         IGeoItemSimple, IGeoCollectionSimple 
     41from Products.Geographer.interfaces import IGeometry, IGeoItemSimple 
     42from Products.Geographer.interfaces import IGeoreferenceable 
    4043 
    41 ANNO_KEY = 'geocoder.simple.item' 
    4244 
    43 class GeoItemSimple(object): 
    44      
    45     """Python expression of a GeoRSS simple item. 
     45# Geometry class and factory 
     46 
     47class Geometry(object): 
     48 
     49    """A simple geometry. 
     50    """ 
     51    implements(IGeometry) 
     52 
     53    type = FieldProperty(IGeometry['type']) 
     54    coordinates = FieldProperty(IGeometry['coordinates']) 
     55    crs = FieldProperty(IGeometry['crs']) 
     56 
     57 
     58geometryFactory = Factory( 
     59    Geometry, 
     60    title=u'Create a new geometry property', 
     61    ) 
     62 
     63 
     64# Geo-referenced item 
     65 
     66ANNO_KEY = 'geographer.geoitem' 
     67 
     68class GeoContentSimple(object): 
     69 
     70    """Provides geo-referencing annotation and the Python feature protocol. 
     71 
     72    Useful for AT content types, could serve as a template for Zope 3 content 
     73    types. 
    4674    """ 
    4775    implements(IGeoItemSimple) 
    48     
     76    adapts(IGeoreferenceable) 
     77 
    4978    def __init__(self, context): 
    5079        """Initialize adapter.""" 
    5180        self.context = context 
    5281        annotations = IAnnotations(context) 
    53         self.georef = annotations.get(ANNO_KEY, None) 
    54         if not self.georef: 
    55             annotations[ANNO_KEY] = PersistentDict() 
    56             self.georef = annotations[ANNO_KEY] 
    57             self.georef['srs'] = 'EPSG:4326' 
    58             self.georef['geometryType'] = None 
    59             self.georef['spatialCoordinates'] = None 
     82        self.geometry = annotations.get(ANNO_KEY) 
     83        if not self.geometry: 
     84            annotations[ANNO_KEY] = createObject(u'geographer.Geometry') 
     85            self.geometry = annotations[ANNO_KEY] 
     86            self.geometry.type = 'Point' 
     87            self.geometry.coordinates = () 
    6088 
    61     def getSRS(self): 
    62         return self.georef['srs'] 
     89    def getInfo(self): 
     90        context = self.context 
     91        title = context.Title() 
     92        description = context.Description() 
    6393 
    64     def setSRS(self, srs): 
    65         try: 
    66             assert (srs.startswith('EPSG') or srs.find('proj') >= 0) 
    67         except AssertionError: 
    68             raise ValueError, \ 
    69             "%s is invalid. Spatial reference system definition must be in EPSG or PROJ.4 form" % (srs) 
    70         self.georef['srs'] = srs 
    71          
    72     def getGeometryType(self): 
    73         return self.georef['geometryType'] 
     94        return { 
     95            'id': context.getId(), 
     96            'properties': { 
     97                'title': unicode(title), 
     98                'description': unicode(description), 
     99                'uri': context.absolute_url(), 
     100                }, 
     101            'geometry': { 
     102                'type': self.geometry.type, 
     103                'coordinates': self.geometry.coordinates, 
     104                } 
     105            } 
    74106 
    75     def setGeometryType(self, geomtype): 
    76         if geomtype not in ['point', 'line', 'polygon', 'box']: 
    77             raise ValueError, \ 
    78             """%s is invalid. Supported geometry types are: 'point', 'line', 'polygon', 'box'""" % (geomtype) 
    79         self.georef['geometryType'] = geomtype 
     107    info = property(getInfo,) 
    80108 
    81     def getSpatialCoordinates(self): 
    82         values = [float(v) for v in self.georef['spatialCoordinates'].split()] 
    83         nvalues = len(values) 
    84         npoints = nvalues/3 
    85         coords = [] 
    86         for i in range(npoints): 
    87             coords.append(tuple(values[3*i:3*i+3])) 
    88         return tuple(coords) 
    89  
    90     def setGeometry(self, geomtype, coords): 
    91         # set geometry type 
    92         if geomtype not in ['point', 'line', 'polygon', 'box']: 
    93             raise ValueError, \ 
    94             """%s is invalid. Supported geometry types are: 'point', 'line', 'polygon', 'box'""" % (geomtype) 
    95         self.georef['geometryType'] = geomtype 
    96  
    97         # check number of points against geometry type 
    98         try: 
    99             if geomtype == 'point': assert len(coords) == 1 
    100             if geomtype == 'line': assert len(coords) >= 2 
    101             if geomtype == 'polygon': assert len(coords) >= 4 
    102             if geomtype == 'box': assert len(coords) == 2 
    103         except AssertionError: 
    104             raise ValueError, \ 
    105             "Number of coordinates %d is inconsistent with geometry type %s" \ 
    106             % (len(coords), geomtype) 
    107              
    108         value = '' 
    109         for point in coords: 
    110             if len(point) == 3: 
    111                 value = ' '.join([value, "%f %f %f" % point]) 
    112             elif len(point) == 2: 
    113                 value = ' '.join([value, "%f %f 0.0" % point]) 
    114             else: 
    115                 raise ValueError, \ 
    116                 "Insufficient number of ordinates: %s" % str(point) 
    117         self.georef['spatialCoordinates'] = value.lstrip() 
    118          
    119     def isGeoreferenced(self): 
    120         """Return True if the object is "on the map".""" 
    121         g = self.georef 
    122         return bool(g['geometryType']) and bool(g['spatialCoordinates']) 
    123          
    124     def getInfo(self, dims=3): 
    125         """Return an informative dict.""" 
    126         g = self.georef 
    127         context = self.context 
    128         info = {'srs':                  g['srs'], 
    129                 'geometryType':         g['geometryType'] 
    130                } 
    131         if dims == 3: 
    132             info['spatialCoordinates'] = g['spatialCoordinates'] 
    133         elif dims == 2: 
    134             values = g['spatialCoordinates'].split() 
    135             nvalues = len(values) 
    136             npoints = nvalues/3 
    137             coords = [] 
    138             for i in range(npoints): 
    139                 coords.extend(values[3*i:3*i+2]) 
    140             info['spatialCoordinates'] = ' '.join(coords) 
    141         else: 
    142             raise ValueError, "Invalid dims: %d" % (dims) 
    143  
    144         # Content objects 
    145         info.update( 
    146                {'id':           context.getId(), 
    147                 'title':        context.title_or_id(), 
    148                 'description':  context.getProperty('description', 
    149                                     'No description'), 
    150                 'url':          context.absolute_url(),} 
    151             ) 
    152         return info 
    153  
    154  
    155 class GeoCollectionSimple(object): 
    156      
    157     """Adapter for Folderish collections of GeoItemSimple. 
    158     """ 
    159     implements(IGeoCollectionSimple) 
    160      
    161     def __init__(self, context): 
    162         """Initialize.""" 
    163         self.context = context 
    164          
    165     def geoItems(self): 
    166         if IATTopic.providedBy(self.context): 
    167             for ob in self.context.queryCatalog(): 
    168                 try: 
    169                     item = IGeoItemSimple(ob.getObject()) 
    170                     assert(item.isGeoreferenced()) 
    171                 except: 
    172                     continue 
    173                 yield item 
    174         elif IATFolder.providedBy(self.context):  
    175             for ob in self.context.listFolderContents(): 
    176                 try: 
    177                     item = IGeoItemSimple(ob) 
    178                     assert(item.isGeoreferenced()) 
    179                 except: 
    180                     continue 
    181                 yield item 
    182         else: 
    183             try: 
    184                 item = IGeoItemSimple(self.context) 
    185                 assert(item.isGeoreferenced()) 
    186             except: 
    187                 pass 
    188             yield item 
    189  
    190     def getItemsInfo(self): 
    191         infos = [] 
    192         for item in self.geoItems(): 
    193             infos.append(item.getInfo()) 
    194         return infos 
    195  
    196     def getBoundingBox(self): 
    197         raise NotImplementedError 
    198          
  • Geographer/trunk/interfaces.py

    r806 r807  
    3232# For Zope 2 
    3333except ImportError: 
    34     from zope.annotation.interfaces import IAttributeAnnotatable 
     34    from zope.app.annotation.interfaces import IAttributeAnnotatable 
    3535     
     36from zope.dublincore.interfaces import IDCDescriptiveProperties 
     37 
    3638from zope.interface import Interface 
    37 from zope.schema import Choice, Dict, Id, Object, TextLine, Tuple 
     39from zope.schema import BytesLine, Choice, Dict, Id, Object, Tuple, URI 
    3840 
    3941# Marker interfaces. These are on the "for" side of a Zope3 adapter. 
     
    6264# Interfaces to be provided by adapters. 
    6365 
    64 class IGeometryProperty(Interface): 
     66class IGeometry(Interface): 
    6567 
    6668    """A geometry property with a geographic or projected coordinate system. 
     
    7173    # TODO: geometries other than points 
    7274    type = Choice( 
    73         values=("Point"), 
     75        values=("Point",), 
    7476        title=u"Geometry Type", 
    7577        description=u"The name of the geometry type. See " 
    7678        "http://code.google.com/apis/kml/documentation/kml_tags_21.html", 
    77         required=True, 
     79        required=False, 
     80        default="Point", 
    7881        ) 
    7982 
     
    8184        title=u"Geometry Coordinates", 
    8285        description=u"A sequence of coordinate tuples", 
    83         required=True, 
    8486        value_type=Tuple(title=u"Coordinates"), 
     87        required=False, 
     88        default=(), 
    8589        ) 
    8690 
    87     crs = TextLine( 
     91    crs = BytesLine( 
    8892        title=u"Coordinate Reference System", 
    8993        description=u"The PROJ.4 format definition of a coordinate reference " 
    9094        "system", 
    9195        required=False, 
    92         default=u"epsg:4326", 
     96        default="epsg:4326", 
    9397        ) 
    9498 
    9599 
    96 class IGeoItemSimple(Interface): 
     100class IGeoItemSimple(IDCDescriptiveProperties): 
    97101 
    98102    """A simple georeferenced object, analogous to an item in GeoRSS, or a 
     
    101105    See http://georss.org/simple.html for an explanation of the simple GeoRSS 
    102106    profile. 
     107 
     108    IDCDescriptiveProperties defines 'title' and 'description'. 
    103109    """ 
    104110 
     
    109115        ) 
    110116 
    111     properties = Dict
    112         title=u"Item Properties", 
    113         description=u"Non-geometry properties and metadata", 
     117    uri = URI
     118        title=u"URI", 
     119        description=u"Uniform Resource Identifier", 
    114120        required=False, 
    115         default={}, 
    116121        ) 
    117122 
    118123    geometry = Object( 
    119         IGeometryProperty
     124        IGeometry
    120125        title=u"Geometry", 
    121126        description=u"The item's geometry", 
     
    124129        ) 
    125130 
    126      
     131    info = Dict( 
     132        title=u"Info Dictionary", 
     133        description=u"Dictionary which provides the Python feature protocol", 
     134        readonly=True, 
     135        ) 
     136 
    127137class IGeoCollectionSimple(Interface): 
    128138 
  • Geographer/trunk/tests/adapters.txt

    r803 r807  
    1 Sanity check 
     1Adapter tests 
     2============= 
    23 
    3     >>> bool(1) 
    4     True 
     4  >>> from Products.Geographer.interfaces import IGeoItemSimple 
    55 
     6Target: an AT content object 
     7---------------------------- 
     8 
     9Add a new document and geo-reference it 
     10 
     11  >>> self.folder.invokeFactory('Document', id='document', title=u'A Document', 
     12  ...   description=u'This is a document') 
     13  'document' 
     14  >>> item = IGeoItemSimple(self.folder.document) 
     15  >>> item 
     16  <Products.Geographer.geo.GeoContentSimple object at ...> 
     17   
     18Set geometry properties 
     19 
     20  >>> item.geometry.type = 'Point' 
     21  >>> item.geometry.coordinates = ((0.0, 0.0, 0.0),) 
     22 
     23Check info 
     24 
     25  >>> item.info 
     26  {'geometry': {'type': 'Point', 'coordinates': ((0.0, 0.0, 0.0),)}, 'id': 'document', 'properties': {'uri': 'http://nohost/plone/Members/test_user_1_/document', 'description': u'This is a document', 'title': u'A Document'}} 
     27