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", "(null)" ) );
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 between ViewPoint
239 * 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 FeatureCollection of all
293 * 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 }