001    /*----------------------------------------------------------------------------
002     This file is part of deegree, http://deegree.org/
003     Copyright (C) 2001-2009 by:
004       Department of Geography, University of Bonn
005     and
006       lat/lon GmbH
007    
008     This library is free software; you can redistribute it and/or modify it under
009     the terms of the GNU Lesser General Public License as published by the Free
010     Software Foundation; either version 2.1 of the License, or (at your option)
011     any later version.
012     This library is distributed in the hope that it will be useful, but WITHOUT
013     ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
014     FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
015     details.
016     You should have received a copy of the GNU Lesser General Public License
017     along with this library; if not, write to the Free Software Foundation, Inc.,
018     59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
019    
020     Contact information:
021    
022     lat/lon GmbH
023     Aennchenstr. 19, 53177 Bonn
024     Germany
025     http://lat-lon.de/
026    
027     Department of Geography, University of Bonn
028     Prof. Dr. Klaus Greve
029     Postfach 1147, 53001 Bonn
030     Germany
031     http://www.geographie.uni-bonn.de/deegree/
032    
033     e-mail: info@deegree.org
034    ----------------------------------------------------------------------------*/
035    package org.deegree.ogcwebservices.wpvs;
036    
037    import java.util.ArrayList;
038    import java.util.List;
039    
040    import javax.media.j3d.Locale;
041    import javax.media.j3d.PickShape;
042    import javax.media.j3d.SceneGraphPath;
043    import javax.media.j3d.Shape3D;
044    import javax.media.j3d.VirtualUniverse;
045    
046    import org.deegree.datatypes.Types;
047    import org.deegree.framework.util.IDGenerator;
048    import org.deegree.i18n.Messages;
049    import org.deegree.model.crs.CoordinateSystem;
050    import org.deegree.model.feature.Feature;
051    import org.deegree.model.feature.FeatureCollection;
052    import org.deegree.model.feature.schema.FeatureType;
053    import org.deegree.model.feature.schema.PropertyType;
054    import org.deegree.model.filterencoding.ComplexFilter;
055    import org.deegree.model.filterencoding.OperationDefines;
056    import org.deegree.model.filterencoding.PropertyName;
057    import org.deegree.model.filterencoding.SpatialOperation;
058    import org.deegree.model.spatialschema.Geometry;
059    import org.deegree.model.spatialschema.GeometryException;
060    import org.deegree.model.spatialschema.Position;
061    import org.deegree.model.spatialschema.Surface;
062    import org.deegree.ogcwebservices.OGCWebService;
063    import org.deegree.ogcwebservices.OGCWebServiceException;
064    import org.deegree.ogcwebservices.wfs.operation.FeatureResult;
065    import org.deegree.ogcwebservices.wfs.operation.GetFeature;
066    import org.deegree.ogcwebservices.wfs.operation.Query;
067    import org.deegree.ogcwebservices.wfs.operation.GetFeature.RESULT_TYPE;
068    import org.deegree.ogcwebservices.wms.LayerNotDefinedException;
069    import org.deegree.ogcwebservices.wms.LayerNotQueryableException;
070    import org.deegree.ogcwebservices.wpvs.capabilities.Dataset;
071    import org.deegree.ogcwebservices.wpvs.configuration.AbstractDataSource;
072    import org.deegree.ogcwebservices.wpvs.configuration.LocalWFSDataSource;
073    import org.deegree.ogcwebservices.wpvs.configuration.RemoteWFSDataSource;
074    import org.deegree.ogcwebservices.wpvs.configuration.WPVSConfiguration;
075    import org.deegree.ogcwebservices.wpvs.operation.ConeRequest;
076    import org.deegree.ogcwebservices.wpvs.operation.Get3DFeatureInfo;
077    import org.deegree.ogcwebservices.wpvs.operation.Get3DFeatureInfoResponse;
078    import org.deegree.ogcwebservices.wpvs.operation.LineRequest;
079    import org.deegree.ogcwebservices.wpvs.operation.RequestGeometry;
080    
081    import com.sun.j3d.utils.geometry.GeometryInfo;
082    
083    /**
084     * This class handles a WPVS Get3DFeatureInfo-Request.
085     *
086     *
087     * @version $Revision: $
088     * @author <a href="mailto:cordes@lat-lon.de">Lyn Buesching</a>
089     * @author last edited by: $Author: $
090     *
091     * @version 1.0. $Revision: $, $Date: $
092     *
093     */
094    public class DefaultGet3DFeatureInfoHandler extends Get3DFeatureInfoHandler {
095    
096        private WPVSConfiguration configuration;
097    
098        private int featureCount;
099    
100        // collects all Datasets to query
101        private ArrayList<Dataset> allDatasets;
102    
103        // collects the responses for each dataset
104        private ArrayList<Feature> featCol;
105    
106        // stores the geometry of the request
107        private RequestGeometry geom;
108    
109        private Get3DFeatureInfo request;
110    
111        /**
112         * Create a new instance
113         *
114         * @param owner
115         *            the calling service instance.
116         */
117        public DefaultGet3DFeatureInfoHandler( WPVService owner ) {
118            super( owner );
119            this.configuration = owner.getConfiguration();
120        }
121    
122        @Override
123        /**
124         * Handles the Get3DfeatureInfo request given by <code>Get3DFeatureInfoRequest</code>
125         *
126         * @param request
127         *            the Get3DFeatureInfo-request
128         * @return an instance of Get3DFeatureInfoResponse
129         */
130        public Get3DFeatureInfoResponse handleRequest( Get3DFeatureInfo request )
131                                throws OGCWebServiceException {
132    
133            this.request = request;
134            // Anzahl der zurueckzugebenden Features
135            featureCount = request.getFeatureCount();
136    
137            // stores all datasets to query (even the child-datasets)
138            allDatasets = new ArrayList<Dataset>();
139    
140            // validate QueryDatasets
141            List<String> qDatasets = new ArrayList<String>( request.getQueryDatasets().size() );
142            qDatasets = request.getQueryDatasets();
143            for ( String s : qDatasets ) {
144                Dataset dataset = configuration.findDataset( s );
145                // test if dataset is configured
146                if ( dataset == null ) {
147                    throw new LayerNotDefinedException( Messages.getMessage( "WPVS_UNDEFINED_DATASET", dataset.getName() ) );
148                }
149                // test if queryable
150                if ( !dataset.getQueryable() ) {
151                    throw new LayerNotQueryableException( Messages.getMessage( "WPVS_NO_QUERYABLE_DATASET",
152                                                                               dataset.getName() ) );
153                }
154                // test if dataset is given in GetViewRequestCopy
155                List<String> ds = request.getGetViewRequestCopy().getDatasets();
156                boolean given = false;
157    
158                for ( String dsString : ds ) {
159                    if ( dsString.equals( dataset.getName() ) ) {
160                        given = true;
161                    }
162                }
163                if ( !given ) {
164                    throw new OGCWebServiceException( Messages.getMessage( "WPVS_INVISIBLE_DATASET", dataset.getName() ) );
165                }
166                // test for a valid CRS
167                CoordinateSystem[] csArray = dataset.getCrs();
168                boolean crs = false;
169                for ( int i = 0; i < csArray.length; i++ ) {
170                    if ( csArray[i].equals( request.getGetViewRequestCopy().getCrs() ) ) {
171                        crs = true;
172                    }
173                }
174                if ( !crs ) {
175                    throw new OGCWebServiceException( Messages.getMessage( "WPVS_INVALID_CRS", dataset.getName(),
176                                                                           request.getGetViewRequestCopy().getCrs() ) );
177                }
178    
179                allDatasets.add( dataset );
180                addChildDataset( dataset.getDatasets() );
181            }
182    
183            // initialize the request geometry
184            try {
185                if ( request.getApexAngle() != 0 ) {
186                    geom = new ConeRequest( request );
187                } else {
188                    geom = new LineRequest( request );
189                    // }
190                    // }
191    
192                }
193                // sets the geometry for WFS GetFeature-Request
194                geom.setWfsReqGeom();
195                // sets the 3d-geometry for final test of intersection with a feature
196                geom.setPickshape();
197    
198            } catch ( GeometryException e ) {
199                throw new OGCWebServiceException( Messages.getMessage( "WPVS_IMPOSSIBLE_CREATE_GEOMETRY" ) );
200            }
201    
202            // query WFS
203            featCol = new ArrayList<Feature>();
204            handleGetFeatureRequest( allDatasets );
205            Get3DFeatureInfoResponse response = createGet3DFeatureInfoResponse();
206            return response;
207        }
208    
209        /**
210         * Adds the subdatasets to the collection of all datasets to query.
211         *
212         * @param ds
213         *            an array of subdatasets to add to the Collection of all Datasets
214         *
215         */
216        private void addChildDataset( Dataset[] ds ) {
217            for ( int i = 0; i < ds.length; i++ ) {
218    
219                // test for a valid CRS
220                CoordinateSystem[] csArray = ds[i].getCrs();
221                boolean crs = false;
222                for ( int j = 0; j < csArray.length; j++ ) {
223                    if ( csArray[j].equals( request.getGetViewRequestCopy().getCrs() ) ) {
224                        crs = true;
225                    }
226                }
227                // test if dataset is queryable
228                if ( ds[i].getQueryable() && crs == true ) {
229                    allDatasets.add( ds[i] );
230                }
231                if ( ds[i].getDatasets().length != 0 ) {
232                    addChildDataset( ds[i].getDatasets() );
233                }
234            }
235        }
236    
237        /**
238         * Tests the parameter feat for an intersection with the Ray of view and calculates the distance
239         * between ViewPoint and feature.
240         *
241         * @param feat
242         *            the Feature to test for intersection
243         * @return distance between ViewPoint and feature
244         */
245        private double distance( Feature feat ) {
246            PickShape pick = geom.getPickshape();
247    
248            double[] dist = new double[1];
249            Geometry[] geoms = feat.getGeometryPropertyValues();
250            for ( Geometry geom : geoms ) {
251                float[] coords = toCoords( geom ); // interior rings???
252                if ( coords != null ) {
253                    GeometryInfo geomInfo = new GeometryInfo( GeometryInfo.POLYGON_ARRAY );
254                    geomInfo.setCoordinates( coords );
255                    geomInfo.setStripCounts( new int[] { coords.length / 3 } );
256                    Shape3D shape = new Shape3D( geomInfo.getGeometryArray() );
257                    Locale l = new Locale( new VirtualUniverse() );
258                    SceneGraphPath sgp = new SceneGraphPath( l, shape );
259                    boolean erg = shape.intersect( sgp, pick, dist );
260                    if ( !erg ) {
261                        dist[0] = Double.NaN;
262                    }
263                }
264            }
265            return dist[0];
266        }
267    
268        /**
269         * Converts the coordinates of a geometry to an array of floats.
270         *
271         * @param geom
272         *            the Geometry to convert
273         * @return the coords
274         */
275        private float[] toCoords( Geometry geom ) {
276            float[] coords = null;
277            if ( geom instanceof Surface ) {
278                Surface p = (Surface) geom;
279                Position[] positions = p.getSurfaceBoundary().getExteriorRing().getPositions();
280                coords = new float[3 * ( positions.length - 1 )];
281                for ( int i = 0; i < positions.length - 1; i++ ) {
282                    int ix = 3 * i;
283                    coords[ix] = (float) positions[i].getX();
284                    coords[ix + 1] = (float) positions[i].getY();
285                    coords[ix + 2] = (float) positions[i].getZ();
286                }
287            }
288            return coords;
289        }
290    
291        /**
292         * Handles the WFS GetFeature-Request for all datasets. Collects the Responses in the
293         * FeatureCollection of all valide Results.
294         *
295         * @param datasets
296         *            list of all valide datasets to query
297         * @throws OGCWebServiceException
298         */
299        private void handleGetFeatureRequest( List<Dataset> datasets )
300                                throws OGCWebServiceException {
301            Feature tmpFeat = null;
302            double tmpDist = 0;
303            for ( Dataset dataset : datasets ) {
304                AbstractDataSource[] datasource = dataset.getDataSources();
305                for ( int i = 0; i < datasource.length; i++ ) {
306    
307                    GetFeature WFSrequest = null;
308                    if ( datasource[i].getServiceType() == AbstractDataSource.LOCAL_WFS ) {
309                        WFSrequest = createGetFeatureRequest( (LocalWFSDataSource) datasource[i] );
310                    } else if ( datasource[i].getServiceType() == AbstractDataSource.REMOTE_WFS ) {
311                        WFSrequest = createGetFeatureRequest( (RemoteWFSDataSource) datasource[i] );
312                    } else {
313                        throw new OGCWebServiceException( Messages.getMessage( "WPVS_INVALID_DATASOURCE",
314                                                                               datasource[i].getName() ) );
315                    }
316                    OGCWebService webservice = datasource[i].getOGCWebService();
317                    FeatureResult fr = (FeatureResult) webservice.doService( WFSrequest );
318                    Object testFC = fr.getResponse();
319                    if ( testFC != null && testFC instanceof FeatureCollection ) {
320                        FeatureCollection fc = (FeatureCollection) testFC;
321                        Feature[] features = fc.toArray();
322                        // all Features must be checked for intersection with the geometry of the
323                        // request
324                        for ( Feature feat : features ) {
325                            double dist = distance( feat );
326                            if ( !Double.isNaN( dist ) ) {
327                                // filter the nearest object, if wanted (FEATURE_COUNT=1)
328                                if ( featureCount != 1 && featCol.size() < featureCount ) {
329                                    featCol.add( feat );
330                                } else if ( featureCount == 1 ) {
331                                    if ( tmpFeat == null ) {
332                                        tmpFeat = feat;
333                                        tmpDist = dist;
334                                    } else {
335                                        if ( tmpDist > dist ) {
336                                            tmpFeat = feat;
337                                            tmpDist = dist;
338                                        }
339                                    }
340                                }
341                            }
342                        }
343                    }
344    
345                }
346            }
347            // adds the nearest Object as result to the FeatureCollection
348            if ( tmpFeat != null ) {
349                featCol.add( tmpFeat );
350            }
351        }
352    
353        /**
354         * Creates the WFS GetFeature-Request for a WFS-DataSource.
355         *
356         * @param ds
357         *            the LocalWFSDataSource to request
358         * @return the GetFeature request bean.
359         */
360        private GetFeature createGetFeatureRequest( LocalWFSDataSource ds ) {
361            Geometry queryGeom = geom.getWfsReqGeom();
362            SpatialOperation spatialOp = new SpatialOperation( OperationDefines.INTERSECTS,
363                                                               new PropertyName( ds.getGeometryProperty() ), queryGeom );
364            ComplexFilter comp = new ComplexFilter( spatialOp );
365            Query query = Query.create( ds.getName(), comp );
366            IDGenerator idg = IDGenerator.getInstance();
367            GetFeature gf = GetFeature.create( "1.1.0", String.valueOf( idg.generateUniqueID() ), RESULT_TYPE.RESULTS,
368                                               null, null, 9999999, 0, -1, -1, new Query[] { query } );
369    
370            return gf;
371        }
372    
373        /**
374         * Creates the Response of a WPVS Get3DFeatureInfo-Request.
375         *
376         * @return the Response
377         */
378        private Get3DFeatureInfoResponse createGet3DFeatureInfoResponse() {
379            StringBuffer sb = new StringBuffer( 2000 );
380            sb.append( "<ll:FeatureCollection numberOfFeatures='" ).append( featCol.size() ).append(
381                                                                                                     "' xmlns:gml='http://www.opengis.net/gml'" ).append(
382                                                                                                                                                          " xmlns:ll='http://www.lat-lon.de'>" );
383            for ( Feature feat : featCol ) {
384                FeatureType ft = feat.getFeatureType();
385                PropertyType[] pt = ft.getProperties();
386                sb.append( "<gml:featureMember>" ).append( "<ll:" + ft.getName().getLocalName() ).append( " fid='" ).append(
387                                                                                                                             feat.getId() ).append(
388                                                                                                                                                    "'>" );
389                for ( int i = 0; i < pt.length; i++ ) {
390                    if ( pt[i].getType() != Types.GEOMETRY && pt[i].getType() != Types.POINT
391                         && pt[i].getType() != Types.CURVE && pt[i].getType() != Types.SURFACE
392                         && pt[i].getType() != Types.MULTIPOINT && pt[i].getType() != Types.MULTICURVE
393                         && pt[i].getType() != Types.MULTISURFACE ) {
394    
395                        sb.append( "<ll:" ).append( pt[i].getName().getLocalName() ).append( ">" ).append(
396                                                                                                           feat.getDefaultProperty(
397                                                                                                                                    pt[i].getName() ).getValue() ).append(
398                                                                                                                                                                           "</ll:"
399                                                                                                                                                                                                   + pt[i].getName().getLocalName()
400                                                                                                                                                                                                   + ">" );
401                    }
402                }
403                sb.append( "</ll:" ).append( ft.getName().getLocalName() ).append( ">" ).append( "</gml:featureMember>" );
404    
405            }
406            sb.append( "</ll:FeatureCollection>" );
407    
408            Get3DFeatureInfoResponse response = new Get3DFeatureInfoResponse( sb.toString() );
409    
410            return response;
411        }
412    
413    }