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 }