001    //$HeadURL: svn+ssh://rbezema@svn.wald.intevation.org/deegree/base/tags/2.1/src/org/deegree/ogcwebservices/wpvs/WFSInvoker.java $
002    /*----------------    FILE HEADER  ------------------------------------------
003    
004     This file is part of deegree.
005     Copyright (C) 2001-2006 by:
006     EXSE, Department of Geography, University of Bonn
007     http://www.giub.uni-bonn.de/deegree/
008     lat/lon GmbH
009     http://www.lat-lon.de
010    
011     This library is free software; you can redistribute it and/or
012     modify it under the terms of the GNU Lesser General Public
013     License as published by the Free Software Foundation; either
014     version 2.1 of the License, or (at your option) any later version.
015    
016     This library is distributed in the hope that it will be useful,
017     but WITHOUT ANY WARRANTY; without even the implied warranty of
018     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
019     Lesser General Public License for more details.
020    
021     You should have received a copy of the GNU Lesser General Public
022     License along with this library; if not, write to the Free Software
023     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
024    
025     Contact:
026    
027     Andreas Poth
028     lat/lon GmbH
029     Aennchenstraße 19
030     53177 Bonn
031     Germany
032     E-Mail: poth@lat-lon.de
033    
034     Prof. Dr. Klaus Greve
035     Department of Geography
036     University of Bonn
037     Meckenheimer Allee 166
038     53115 Bonn
039     Germany
040     E-Mail: greve@giub.uni-bonn.de
041     
042     ---------------------------------------------------------------------------*/
043    
044    package org.deegree.ogcwebservices.wpvs;
045    
046    import java.io.StringReader;
047    import java.util.ArrayList;
048    
049    import org.deegree.datatypes.QualifiedName;
050    import org.deegree.datatypes.Types;
051    import org.deegree.framework.log.ILogger;
052    import org.deegree.framework.log.LoggerFactory;
053    import org.deegree.framework.util.CharsetUtils;
054    import org.deegree.framework.util.IDGenerator;
055    import org.deegree.framework.util.StringTools;
056    import org.deegree.framework.xml.XMLTools;
057    import org.deegree.model.feature.Feature;
058    import org.deegree.model.feature.FeatureCollection;
059    import org.deegree.model.feature.FeatureProperty;
060    import org.deegree.model.feature.schema.FeatureType;
061    import org.deegree.model.feature.schema.PropertyType;
062    import org.deegree.model.filterencoding.ComplexFilter;
063    import org.deegree.model.filterencoding.FeatureFilter;
064    import org.deegree.model.filterencoding.FeatureId;
065    import org.deegree.model.filterencoding.Filter;
066    import org.deegree.model.spatialschema.GMLGeometryAdapter;
067    import org.deegree.model.spatialschema.GeometryException;
068    import org.deegree.ogcbase.PropertyPath;
069    import org.deegree.ogcwebservices.OGCWebServiceException;
070    import org.deegree.ogcwebservices.wfs.WFService;
071    import org.deegree.ogcwebservices.wfs.operation.FeatureResult;
072    import org.deegree.ogcwebservices.wfs.operation.GetFeature;
073    import org.deegree.ogcwebservices.wpvs.configuration.AbstractDataSource;
074    import org.deegree.ogcwebservices.wpvs.configuration.LocalWFSDataSource;
075    import org.deegree.ogcwebservices.wpvs.j3d.DefaultSurface;
076    import org.deegree.ogcwebservices.wpvs.j3d.Object3DFactory;
077    import org.deegree.ogcwebservices.wpvs.j3d.PointsToPointListFactory;
078    import org.deegree.ogcwebservices.wpvs.utils.ResolutionStripe;
079    import org.w3c.dom.Document;
080    
081    /**
082     * Invoker for a Web Feature Service.
083     * 
084     * @author <a href="mailto:taddei@lat-lon.de">Ugo Taddei</a>
085     * @author last edited by: $Author: bezema $
086     * 
087     * $Revision: 6259 $, $Date: 2007-03-20 10:15:15 +0100 (Di, 20 Mär 2007) $
088     * 
089     */
090    public class WFSInvoker extends GetViewServiceInvoker {
091    
092        private static final ILogger LOG = LoggerFactory.getLogger( WFSInvoker.class );
093    
094        /* whether the returned data is a 3D object or data for the elevation model */
095        private final boolean isElevationModelRequest;
096    
097        private int id;
098    
099        /**
100         * Creates a new instance of this class.
101         * 
102         * @param owner
103         *            the ResolutionStripe that calls this invoker
104         * @param id
105         * @param isElevationModelRequest
106         */
107        public WFSInvoker( ResolutionStripe owner, int id, boolean isElevationModelRequest ) {
108            super( owner );
109            this.id = id;
110            this.isElevationModelRequest = isElevationModelRequest;
111        }
112    
113        @Override
114        public void invokeService( AbstractDataSource dataSource ) {
115            if ( !( dataSource instanceof LocalWFSDataSource ) ) {
116                LOG.logError( "The given AbstractDataSource is no WFSDataSource instance. It is needed for a WFSInvoker" );
117                throw new RuntimeException( "DataSource should be a WFS-instance for a WFSInvoker" );
118            }
119            WFService service = null;
120            try {
121                service = (WFService) dataSource.getOGCWebService();
122            } catch ( OGCWebServiceException ogcwe ) {
123                LOG.logError( ogcwe.getMessage() );
124                throw new RuntimeException( ogcwe );
125            }
126            Object response = null;
127            try {
128                GetFeature getFeature = createGetFeatureRequest( (LocalWFSDataSource) dataSource );
129                LOG.logDebug( "sending wfs request: " + dataSource.getName() );
130                response = service.doService( getFeature );
131            } catch ( OGCWebServiceException ogcwse ) {
132                if( !Thread.currentThread().isInterrupted() ){
133                    LOG.logError( "Exception while performing WFS-GetFeature: ", ogcwse );
134                } 
135                return;
136            } catch ( GeometryException ge ) {
137                LOG.logError( "Exception while creating WFS-GetFeature request: ", ge );
138                ge.printStackTrace();
139                return;
140            }
141    
142            if ( response != null && response instanceof FeatureResult ) {
143                FeatureCollection result = (FeatureCollection) ( (FeatureResult) response ).getResponse();
144                // System.out.println("resulting featureCollection: " + result );
145                if ( result != null ) {
146    
147                    if ( isElevationModelRequest ) {
148                        // PointListFactory adapter = (PointListFactory) ( (LocalWFSDataSource)
149                        // dataSource ).getFeatureCollectionAdapter();
150                        PointsToPointListFactory ptpFac = new PointsToPointListFactory();
151                        resolutionStripe.setElevationModelFromMeassurePoints( ptpFac.createFromFeatureCollection( result ) );
152                    } else {
153                        Object3DFactory o3DFactory = new Object3DFactory();
154                        for ( int i = 0; i < result.size(); ++i ) {
155                            Feature feature = result.getFeature( i );
156                            // System.out.println( "feature: " + feature );
157                            createSurfaces( o3DFactory, feature );
158                        }
159                    }
160                }
161            } else {
162                LOG.logError( "ERROR while invoking wfs-datasource: " + dataSource.getName()
163                              + " the result was no WFS-response or no FeatureResult instance" );
164            }
165    
166        }
167    
168        /**
169         * This method recursively constructs all the surfaces contained in the given feature. If the
170         * Feature contains a PropertyType of {@link Types#FEATURE} this Feature will also be traversed,
171         * if it contains a {@link Types#GEOMETRY} a {@link DefaultSurface} will be created.
172         * 
173         * @param o3DFactory
174         *            the Factory to create the defaultservice
175         * @param feature
176         *            the feature to traverse.
177         */
178        private void createSurfaces( Object3DFactory o3DFactory, Feature feature ) {
179    
180            FeatureType ft = feature.getFeatureType();
181            PropertyType[] propertyTypes = ft.getProperties();
182            for ( PropertyType pt : propertyTypes ) {
183                if ( pt.getType() == Types.FEATURE ) {
184                    FeatureProperty[] fp = feature.getProperties( pt.getName() );
185                    if ( fp != null ) {
186                        for ( int i = 0; i < fp.length; i++ ) {
187                            createSurfaces( o3DFactory, (Feature) fp[i].getValue() );
188                        }
189                    }
190                } else if ( pt.getType() == Types.GEOMETRY ) {
191                    DefaultSurface ds = o3DFactory.createSurface( feature );
192                    if ( ds != null ) {
193                        resolutionStripe.addFeature( id + "_" + ds.getDefaultSurfaceID(), ds );
194                    }
195                }
196            }
197        }
198    
199        /**
200         * Creates a new <code>GetFeature</code> object from an "XML-String" not nice.
201         * 
202         * @param ds
203         *            the datasource containig service data
204         * @param id
205         *            the request id
206         * @return a new GetFeature request
207         * @throws GeometryException
208         * @throws OGCWebServiceException
209         * @throws Exception
210         */
211        private GetFeature createGetFeatureRequest( LocalWFSDataSource dataSource /*
212                                                                                     * String id,
213                                                                                     * Surface[] boxes
214                                                                                     */)
215                                throws GeometryException, OGCWebServiceException {
216    
217            QualifiedName qn = dataSource.getName();
218    
219            StringBuffer sb = new StringBuffer( 5000 );
220            sb.append( "<?xml version='1.0' encoding='" + CharsetUtils.getSystemCharset() + "'?>" );
221            sb.append( "<wfs:GetFeature xmlns:wfs='http://www.opengis.net/wfs' " );
222            sb.append( "xmlns:ogc='http://www.opengis.net/ogc' " );
223            sb.append( "xmlns:gml='http://www.opengis.net/gml' " );
224            sb.append( "xmlns:" ).append( qn.getPrefix() ).append( '=' );
225            sb.append( "'" ).append( qn.getNamespace() ).append( "' " );
226    
227            if ( dataSource.getServiceType() == AbstractDataSource.LOCAL_WFS ) {
228                sb.append( "outputFormat='FEATURECOLLECTION'>" );
229            } else {
230                sb.append( "outputFormat='text/xml; subtype=gml/3.1.1'>" );
231            }
232    
233            /**
234             * To make things a little clearer compare with this SQL-Statement: SELECT ( !texture )?
235             * geoProperty : * FROM qn.getLocalName() WHERE geoPoperty intersects with
236             * resolutionStripe.getSurface() AND FilterConditions.
237             */
238            PropertyPath geoProperty = dataSource.getGeometryProperty();
239    
240            // FROM
241            sb.append( "<wfs:Query typeName='" ).append( qn.getPrefix() ).append( ":" );
242            sb.append( qn.getLocalName() ).append( "'>" );
243    
244            // SELECT
245            /*
246             * if ( !isElevationModelRequest ) { sb.append( "<wfs:PropertyName>" ); sb.append(
247             * geoProperty.getAsString() ); sb.append( "</wfs:PropertyName>" ); }
248             */
249    
250            StringBuffer sbArea = GMLGeometryAdapter.export( resolutionStripe.getSurface() );
251    
252            // WHERE
253            sb.append( "<ogc:Filter>" );
254            
255            // AND
256            Filter filter = dataSource.getFilter();
257            if ( filter != null ) {
258                if ( filter instanceof ComplexFilter ) {
259                    sb.append( "<ogc:And>" );
260                    sb.append( "<ogc:Intersects>");
261                    sb.append( "<wfs:PropertyName>" );
262                    sb.append( geoProperty.getAsString() );
263                    sb.append( "</wfs:PropertyName>" );
264                    sb.append( sbArea );
265                    sb.append( "</ogc:Intersects>" );
266                    // add filter as defined in the layers datasource description
267                    // to the filter expression
268                    org.deegree.model.filterencoding.Operation op = ( (ComplexFilter) filter ).getOperation();
269                    sb.append( op.toXML() ).append( "</ogc:And>" );
270                } else {
271                    if ( filter instanceof FeatureFilter ) {
272                        ArrayList<FeatureId> featureIds = ( (FeatureFilter) filter ).getFeatureIds();
273                        if ( featureIds.size() != 0 )
274                            sb.append( "<ogc:And>" );
275                        for ( FeatureId fid : featureIds ) {
276                            sb.append( fid.toXML() );
277                        }
278                        if ( featureIds.size() != 0 )
279                            sb.append( "</ogc:And>" );
280                    }
281                }
282            } else {
283                sb.append( "<ogc:Intersects>");
284                sb.append( "<wfs:PropertyName>" );
285                sb.append( geoProperty.getAsString() );
286                sb.append( "</wfs:PropertyName>" );
287                sb.append( sbArea );
288                sb.append( "</ogc:Intersects>" );
289            }
290    
291            sb.append( "</ogc:Filter></wfs:Query></wfs:GetFeature>" );
292    
293            String s = sb.toString();
294    
295            Document doc;
296            try {
297                doc = XMLTools.parse( new StringReader( s ) );
298            } catch ( Exception e ) {
299                e.printStackTrace();
300                String mesg = "Could not parse GetFeature request ";
301                LOG.logError( StringTools.concat( s.length() + 100, mesg, sb.toString() ) );
302                throw new OGCWebServiceException( mesg );
303            }
304    
305            IDGenerator idg = IDGenerator.getInstance();
306            return GetFeature.create( String.valueOf( idg.generateUniqueID() ),
307                                      doc.getDocumentElement() );
308        }
309    
310    }
311    
312    /***************************************************************************************************
313     * <code>
314     * Changes to this class. What the people have been up to: 
315     * 
316     * $Log$
317     * Revision 1.45  2007/03/19 09:58:52  poth
318     * bug fix - considering filter condition
319     *
320     * Revision 1.44  2007/01/31 09:49:05  bezema
321     * added support for the max request time
322     *
323     * Revision 1.43  2007/01/30 14:53:41  bezema
324     * removed unnecesary try catch
325     *
326     * Revision 1.42  2007/01/23 15:08:23  bezema
327     * added a single configuration/capabilities parsing of the datasources
328     *
329     * Revision 1.41  2007/01/15 17:00:30  bezema
330     * added some debug statements
331     * 
332     * 
333     * Revision 1.40 2006/12/15 13:18:49 poth code formatting
334     * 
335     * Revision 1.39 2006/12/14 16:15:34 poth set corrected error message : 
336     * Revision 1.38 2006/12/06 16:10:25 poth bug fix - transperent colors 
337     * Revision 1.37 2006/12/06 15:12:57 bezema renamed the
338     * wpvs.util package into wpvs.utils 
339     * Revision 1.36 2006/12/06 11:24:33 bezema can use the wcs as a
340     * localdatasource for textures and dgm, also added support for the wfs features in gml. Still
341     * trying to figure out how the eyepoint is set by java3d 
342     * Revision 1.35 2006/11/27 15:43:34 bezema
343     * Updated the coordinatesystem handling 
344     * Revision 1.34 2006/11/23 11:46:14 bezema The initial
345     * version of the new wpvs 
346     * Revision 1.32 2006/07/20 08:12:21 taddei use of QualiName for geometry
347     * property 
348     * Revision 1.31 2006/07/05 15:57:47 poth useless parameter removed from method signature
349     * Revision 1.30 2006/07/05 11:22:10 taddei include par to set buildings elev to 0 
350     * Revision 1.29 2006/07/04 09:06:22 taddei todo: excp handling 
351     * Revision 1.28 2006/06/29 16:50:09 poth *** empty log message *** 
352     * Revision 1.27 2006/06/20 10:16:01 taddei clean up and
353     * javadoc Changes to this class. 
354     * Revision 1.26 2006/06/20 07:39:59 taddei added parent dataset and change in ft name 
355     * Revision 1.25 2006/05/12 13:12:45 taddei clean up 
356     * Revision 1.24 2006/05/10 15:01:19 taddei now collecting boxes into a
357     * united geom 
358     * Revision 1.23 2006/05/05 12:41:02 taddei added to to list
359     * 
360     * Revision 1.22 2006/04/18 18:20:26 poth ** empty log message ***
361     * 
362     * Revision 1.21 2006/04/06 20:25:30 poth ** empty log message ***
363     * 
364     * Revision 1.20 2006/04/06 15:07:59 taddei added support for ValidArea
365     * 
366     * Revision 1.19 2006/04/05 09:07:03 taddei added code for computing res of different surfs; and fc
367     * adapter
368     * 
369     * Revision 1.17 2006/03/29 15:08:21 taddei with buildings
370     * 
371     * Revision 1.16 2006/03/10 10:31:40 taddei changes regarding cood sys and scale calculation
372     * 
373     * Revision 1.15 2006/03/09 08:57:58 taddei debug mesgs
374     * 
375     * Revision 1.14 2006/03/07 08:49:20 taddei changes due to pts list factories
376     * 
377     * Revision 1.13 2006/03/02 15:23:52 taddei using now StringTools and StringBuilder
378     * 
379     * Revision 1.12 2006/02/22 17:12:31 taddei implemented correct drawing order
380     * 
381     * Revision 1.11 2006/02/22 13:36:02 taddei refactoring: added service, createOGCWebService; also
382     * better except handling
383     * 
384     * Revision 1.10 2006/02/17 13:38:12 taddei bug fix when counting (resol was using wrong dim) and
385     * fixed � (sz)
386     * 
387     * Revision 1.9 2006/02/14 15:21:41 taddei now working with remote WFS
388     * 
389     * Revision 1.8 2006/02/09 15:47:24 taddei bug fixes, refactoring and javadoc
390     * 
391     * Revision 1.7 2006/01/30 14:58:37 taddei minor internal refactoring
392     * 
393     * Revision 1.6 2006/01/27 10:39:13 taddei query optmization
394     * 
395     * Revision 1.5 2006/01/26 14:42:31 taddei WMS and WFS invokers woring; minor refactoring
396     * 
397     * Revision 1.4 2006/01/18 10:21:07 taddei putting wfs service to work
398     * 
399     * Revision 1.3 2006/01/18 08:59:36 taddei commented out (due to wrong refactoring); fix is coming
400     * 
401     * Revision 1.2 2006/01/18 08:58:00 taddei implementation (WFS)
402     * 
403     * Revision 1.1 2005/12/16 15:19:11 taddei added DeafultViewHandler and the Invokers
404     * 
405     * </code>
406     **************************************************************************************************/