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 }