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 }