root/PrimaGIS/trunk/PrimaGISLayer.py

Revision 795, 14.2 KB (checked in by dokai, 4 years ago)

- Refactored to use the PCL namespaces and OWSLib
- Updated MochiKit to 1.3.1
- Removed the in-product testrunner in favor of the Zope test runner

  • Property Id set to CHANGES.txt
  • Property svn:keywords set to Id
Line 
1# -*- coding: ISO-8859-1 -*-
2##############################################################################
3#
4# Copyright (c) 2005-2006 Kai Hänninen <kai.hanninen@mbconcert.fi>
5#
6# This file is part of PrimaGIS.
7#
8# PrimaGIS is free software; you can redistribute it and/or modify
9# it under the terms of the GNU General Public License as published by
10# the Free Software Foundation; either version 2 of the License, or
11# (at your option) any later version.
12#
13# PrimaGIS is distributed in the hope that it will be useful,
14# but WITHOUT ANY WARRANTY; without even the implied warranty of
15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16# GNU General Public License for more details.
17#
18# You should have received a copy of the GNU General Public License
19# along with PrimaGIS; if not, write to the Free Software
20# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21#
22# $Id$
23#
24##############################################################################
25try:
26    import pkg_resources
27    try:
28        pkg_resources.require('PCL-Core')
29        pkg_resources.require('OWSLib')
30    except pkg_resources.DistributionNotFound:
31        pass
32except ImportError:
33    pass
34
35from zope.interface import implements, directlyProvides
36
37from Products.Archetypes.public import Schema
38from Products.Archetypes.public import BooleanWidget, BooleanField
39from Products.Archetypes.public import StringField, StringWidget, SelectionWidget
40from Products.Archetypes.public import DisplayList, registerType
41from Products.Archetypes.utils import shasattr
42
43from Products.CMFCore import permissions
44from Products.CMFCore.utils import getToolByName
45
46from Products.PrimaGIS import log
47from Products.PrimaGIS.layer import BaseLayer, BaseLayerSchema, OWSSchema
48from Products.PrimaGIS.config import PROJECTNAME
49from Products.PrimaGIS.interfaces import IPrimaGISLayer
50from Products.PrimaGIS.widget import DataSourceSelectionWidget
51
52from AccessControl import ClassSecurityInfo
53from cartography import styles
54from cartography.geometry import Point
55from cartography.context.interfaces import IOWSMapRequest
56from cartography.data.interfaces import IOWSStore, IFeatureStore
57from owslib import wms, wfs
58
59
60# Compile the PrimaGISLayer schema
61PrimaGISLayerSchema =  BaseLayerSchema.copy() + \
62                    Schema((
63                        StringField(
64                            'dataSourceIdentifier',
65                            required=True,
66                            widget=DataSourceSelectionWidget(
67                                label="Data source",
68                                label_msgid="label_data_source",
69                                description=("Select the data source for this "
70                                             "layer by first selecting the "
71                                             "datastore and then the appropriate "
72                                             "feature type."),
73                                description_msgid="help_data_source",
74                                i18n_domain="primagis"
75                                ),
76                            ),
77                        )) + \
78                    OWSSchema.copy()
79
80class PrimaGISLayer(BaseLayer):
81    """
82    This object represents a layer in a PrimaGIS.
83    A layer uses data from a connected data store and applies (optional) styling to
84    the features.
85    """
86    implements(IPrimaGISLayer)
87    schema = PrimaGISLayerSchema
88    allowed_content_types = ['TextSymbolizer', 'PolygonSymbolizer',
89                             'PointSymbolizer', 'LineSymbolizer']
90
91    actions = (
92        { 'id'            : 'view',
93          'name'          : 'View',
94          'action'        : 'string:${object_url}/primagislayer_view',
95          'permissions'   : (permissions.View,),
96          'category'      : 'object'
97          },
98        { 'id'            : 'ows',
99          'name'          : 'OWS properties',
100          'action'        : 'string:${object_url}/primagis_ows',
101          'permissions'   : (permissions.ModifyPortalContent,),
102          'condition'     : 'python:object.isWMS() or object.isWFS()',
103          'category'      : 'object'
104          },
105        { 'id'          : 'style',
106          'name'        : 'Style',
107          'action'      : 'string:${object_url}/primagis_style',
108          'permissions' : (permissions.ModifyPortalContent,),
109          'condition'   : 'python:not object.isWMS()',
110          'category'    : 'object'
111          },
112    )
113
114    content_icon = "primagislayer_icon.png"
115    portal_type = meta_type = 'PrimaGISLayer'
116    archetype_name = 'PrimaGIS Layer'
117    _at_rename_after_creation = True
118   
119    security = ClassSecurityInfo()
120
121    security.declarePrivate('_dataSourceVocabulary')
122    def _dataSourceVocabulary(self):
123        """
124        Returns vocabulary for the data source selection.
125        """
126        sources = DisplayList()
127        maptool = getToolByName(self, 'portal_gis')
128       
129        for dsproxy in maptool.getDataStores():
130            proxy_type = dsproxy.__class__.__name__
131            proxy_name = dsproxy.title_or_id()
132           
133            try:
134                for typename in dsproxy.typenames():
135                    sources.add('%s:%s' % (dsproxy.getId(), typename),
136                                '[%s] %s :: %s' % (proxy_type,
137                                                   proxy_name,
138                                                   typename))
139            except AttributeError:
140                # Not all data stores implement the typenames() method
141                log("%s (%s) does not support typenames()" % (proxy_type, dsproxy.getId()))
142                sources.add(dsproxy.getId(),
143                            '[%s] %s' % (proxy_type, proxy_name))
144        return sources
145
146    security.declareProtected(permissions.ModifyPortalContent, 'setOwsSRS')
147    def setOwsSRS(self, value):
148        """
149        Overridden mutator for setting the OWS SRS property.
150        """
151        self.ows_srs = value
152        self.owsSRS = value
153
154    security.declareProtected(permissions.ModifyPortalContent, 'setOwsFormat')
155    def setOwsFormat(self, value):
156        """
157        Overridden mutator for setting the OWS image format property.
158        """
159        self.ows_format = value
160        self.owsFormat = value
161
162    # Begin ZCO.interfaces.ILayerProxy methods
163    security.declarePublic('getDataStore')
164    def getDataStore(self):
165        """
166        Return ILayerProxy's DataStore
167        """
168        datastore_id, typename = self.parseDataSource()
169        # The LayerOverviewer methods depend on a 'typename' attribute.
170        self.typename = typename
171        # Delegate the request back to the map tool
172        if shasattr(self, 'ows_srs') and shasattr(self, 'ows_format'):
173            directlyProvides(self, IOWSMapRequest)
174        return getToolByName(self, 'portal_gis').getDataStore(datastore_id)
175
176    security.declarePublic('layer')
177    def layer(self):
178        """
179        Return ILayerProxy's subject, an instance of PCL's Layer
180        """
181        datastore_id, typename = self.parseDataSource()
182        if self.getUsePythonFilters():
183            classifier = 'INTERNAL'
184        else:
185            classifier = 'EXTERNAL'
186           
187        if self.getOwsStyle():
188            typename = '%s/%s' % (typename, self.getOwsStyle())
189        layer = styles.Layer(self.getDataStore().datastore(), typename,
190                             classifier=classifier)
191
192        # Add the OWS properties if necessary
193        if self.getOwsSRS():
194            layer.ows_srs = self.getOwsSRS()
195        if self.getOwsFormat():
196            layer.ows_format = self.getOwsFormat()
197        if shasattr(layer, 'ows_srs') and shasattr(layer, 'ows_format'):
198            directlyProvides(layer, IOWSMapRequest)
199        return layer
200    # End ILayerProxy methods
201
202    security.declarePublic('getGeometryType')
203    def getGeometryType(self):
204        """
205        Returns the geometry type that is used by the data source defined for
206        this layer.
207        """
208        try:
209            datastore_id, typename = self.parseDataSource()
210            geom = self.getDataStore().datastore().featuretype(typename).defaultgeometry()
211            datatype = getattr(geom, 'homotype', None)
212            # Map the datatype returned by PCL to the form understood by PrimaGIS
213            if datatype.upper().find('POINT') > -1:
214                return 'POINT'
215            if datatype.upper().find('LINE') > -1:
216                return 'LINE'
217            if datatype.upper().find('POLYGON') > -1:
218                return 'POLYGON'
219            return None
220        except AttributeError:
221            return None
222
223    security.declareProtected(permissions.ModifyPortalContent, 'parseDataSource')
224    def parseDataSource(self):
225        """
226        Returns the typename and datastore path set for this layer.
227        """
228        ds_id = self.getDataSourceIdentifier().split(":")
229        if not ds_id:
230            return None, None
231            #raise ValueError, "No data source set for PrimaGISLayer at %s" % ("/".join(self.getPhysicalPath()))
232       
233        if len(ds_id) > 1:
234            # There is a typename definition
235            datastore_id, typename = ds_id
236        else:
237            typename = None
238            datastore_id = ds_id[0]
239       
240        return datastore_id, typename
241 
242
243    security.declarePrivate('bestbbox')
244    def bestbbox(self):
245        """
246        Overrides ZCO.overview.LayerOverviewer.bestbbox().
247        """
248        return self.getDataStore().datastore().bounds(self.typename).totuple()
249        # the map SRS
250        source = self.getSpatialReferenceCode()
251        # the layer SRS
252        if self.isWMS() or self.isWFS():
253            target = self.getOwsSRS()
254        else:
255            store = self.getDataStore().datastore()
256            if IOWSStore.providedBy(store) or IFeatureStore.providedBy(store):
257                dsource = self.getDataStore().datastore().source(self.typename)
258                srs = dsource.featuretype(self.typename).defaultsrs()
259                target = srs.tostring().replace('+init=epsg', 'EPSG')
260            else:
261                target = store.info().get('srs')
262       
263        bbox = self.getDefaultExtent()
264        min_p = self.reprojectPoint(Point(bbox.minx, bbox.miny),
265                                    srcSRS=source,
266                                    tgtSRS=target)
267        max_p = self.reprojectPoint(Point(bbox.maxx, bbox.maxy),
268                                    srcSRS=source,
269                                    tgtSRS=target)
270       
271        return (min_p.x, min_p.y, max_p.x, max_p.y)
272
273    security.declarePublic('isRaster')
274    def isRaster(self):
275        """Returns True if this layer is a raster layer."""
276        storeclass = self.getDataStore().__class__.__name__
277        return storeclass.startswith('WMS') or storeclass.startswith('DiskRaster')
278
279    security.declarePublic('isWMS')
280    def isWMS(self):
281        """
282        Returns True if this layer is a WMS layer.
283        """
284        return self.getDataStore().__class__.__name__.startswith('WMS')
285
286    security.declarePublic('isWFS')
287    def isWFS(self):
288        """
289        Returns True if this layer is a WFS layer.
290        """
291        return self.getDataStore().__class__.__name__.startswith('WFS')
292
293    security.declareProtected(permissions.ModifyPortalContent, 'getAvailableOWSSRS')
294    def getAvailableOWSSRS(self, override_cache=False):
295        """
296        Returns a list of available spatial reference systems for this OWS
297        layer.
298       
299        Parameters
300        ----------
301        override_cache : boolean
302            Force PrimaGISLayer to refresh the information from the OWS source.
303            PrimaGISLayer will cache the information by default.
304        """
305        if self.isWMS():
306            if override_cache or getattr(self, '_v_wms', None) is None:
307                self._readWMSCapabilities()
308            storeid, typename = self.parseDataSource()
309            return self._v_wms.capabilities.getContentByName(typename).crsOptions
310        else:
311            return ('Automatic SRS value parsing is not supported for WFS layers yet',)
312
313    security.declareProtected(permissions.ModifyPortalContent, 'getAvailableOWSFormats')
314    def getAvailableOWSFormats(self, override_cache=False):
315        """
316        Returns a list of available
317       
318        Parameters
319        ----------
320        override_cache : boolean
321            Force PrimaGISLayer to refresh the information from the OWS source.
322            PrimaGISLayer will cache the information by default.
323        """
324        if self.isWMS():
325            if override_cache or getattr(self, '_v_wms', None) is None:
326                self._readWMSCapabilities()
327            return self._v_wms.capabilities.getOperationByName('GetMap').formatOptions
328   
329    security.declareProtected(permissions.ModifyPortalContent, 'getAvailableOWSStyles')
330    def getAvailableOWSStyles(self, override_cache=False):
331        """XXX"""
332        if self.isWMS():
333            if override_cache or getattr(self, '_v_wms', None) is None:
334                self._readWMSCapabilities()
335            storeid, typename = self.parseDataSource()
336            styles = self._v_wms.capabilities.getContentByName(typename).styles
337            return [{'id':style,'title':styles[style].get('title',style)}
338                    for style in styles]
339        return []
340
341    security.declareProtected(permissions.ModifyPortalContent, 'getWFSFeatureSchema')
342    def getWFSFeatureSchema(self, override_cache=False):
343        """
344        Returns the URL to the WFS feature type schema.
345        """
346        if self.isWFS():
347            if override_cache or getattr(self, '_v_wfs_infoset', None) is None:
348                self._readWFSCapabilities()
349            return self._v_wfs_infoset.getCapabilityInfo().get('description')
350        return None
351
352    security.declarePrivate('_readWMSCapabilities')
353    def _readWMSCapabilities(self):
354        """
355        Reads the WMS Capabilities information.
356        """
357        dsproxy = self.getDataStore()
358        self._v_wms = wms.WebMapService(dsproxy.url, dsproxy.version)
359
360    security.declarePrivate('_readWFSCapabilities')
361    def _readWFSCapabilities(self):
362        """
363        Reads the WFS Capabilities information.
364        """
365        dsproxy = self.getDataStore()
366        reader = wfs.WFSCapabilitiesReader(dsproxy.version)
367        self._v_wfs_infoset = reader.read(dsproxy.url)
368
369
370registerType(PrimaGISLayer, PROJECTNAME)
Note: See TracBrowser for help on using the browser.