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