/*---------------- FILE HEADER ------------------------------------------
This file is part of deegree.
Copyright (C) 2001-2006 by:
EXSE, Department of Geography, University of Bonn
http://www.giub.uni-bonn.de/deegree/
lat/lon GmbH
http://www.lat-lon.de
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Contact:
Andreas Poth
lat/lon GmbH
Aennchenstr. 19
53177 Bonn
Germany
E-Mail: poth@lat-lon.de
Prof. Dr. Klaus Greve
Department of Geography
University of Bonn
Meckenheimer Allee 166
53115 Bonn
Germany
E-Mail: greve@giub.uni-bonn.de
---------------------------------------------------------------------------*/
package org.deegree.ogcwebservices.wms;
import java.awt.Color;
import java.io.ByteArrayInputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.StringReader;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.CancellationException;
import org.deegree.datatypes.QualifiedName;
import org.deegree.datatypes.Types;
import org.deegree.datatypes.values.Values;
import org.deegree.framework.concurrent.ExecutionFinishedEvent;
import org.deegree.framework.concurrent.ExecutionFinishedListener;
import org.deegree.framework.concurrent.Executor;
import org.deegree.framework.log.ILogger;
import org.deegree.framework.log.LoggerFactory;
import org.deegree.framework.util.CharsetUtils;
import org.deegree.framework.util.IDGenerator;
import org.deegree.framework.util.MapUtils;
import org.deegree.framework.util.NetWorker;
import org.deegree.framework.xml.XMLFragment;
import org.deegree.framework.xml.XMLTools;
import org.deegree.framework.xml.XSLTDocument;
import org.deegree.graphics.transformation.GeoTransform;
import org.deegree.graphics.transformation.WorldToScreenTransform;
import org.deegree.i18n.Messages;
import org.deegree.model.crs.CRSFactory;
import org.deegree.model.crs.CoordinateSystem;
import org.deegree.model.crs.GeoTransformer;
import org.deegree.model.crs.IGeoTransformer;
import org.deegree.model.feature.Feature;
import org.deegree.model.feature.FeatureCollection;
import org.deegree.model.feature.GMLFeatureCollectionDocument;
import org.deegree.model.feature.schema.FeatureType;
import org.deegree.model.feature.schema.PropertyType;
import org.deegree.model.filterencoding.ComplexFilter;
import org.deegree.model.filterencoding.FeatureFilter;
import org.deegree.model.filterencoding.FeatureId;
import org.deegree.model.filterencoding.Filter;
import org.deegree.model.spatialschema.Envelope;
import org.deegree.model.spatialschema.GMLGeometryAdapter;
import org.deegree.model.spatialschema.Geometry;
import org.deegree.model.spatialschema.GeometryFactory;
import org.deegree.ogcbase.InvalidSRSException;
import org.deegree.ogcwebservices.OGCWebService;
import org.deegree.ogcwebservices.OGCWebServiceException;
import org.deegree.ogcwebservices.OGCWebServiceRequest;
import org.deegree.ogcwebservices.wcs.describecoverage.DescribeCoverage;
import org.deegree.ogcwebservices.wcs.getcoverage.ResultCoverage;
import org.deegree.ogcwebservices.wfs.WFService;
import org.deegree.ogcwebservices.wfs.capabilities.WFSCapabilities;
import org.deegree.ogcwebservices.wfs.capabilities.WFSFeatureType;
import org.deegree.ogcwebservices.wfs.operation.FeatureResult;
import org.deegree.ogcwebservices.wfs.operation.GetFeature;
import org.deegree.ogcwebservices.wfs.operation.Query;
import org.deegree.ogcwebservices.wms.capabilities.Layer;
import org.deegree.ogcwebservices.wms.configuration.AbstractDataSource;
import org.deegree.ogcwebservices.wms.configuration.LocalWFSDataSource;
import org.deegree.ogcwebservices.wms.configuration.RemoteWMSDataSource;
import org.deegree.ogcwebservices.wms.configuration.WMSConfigurationType;
import org.deegree.ogcwebservices.wms.operation.GetFeatureInfo;
import org.deegree.ogcwebservices.wms.operation.GetFeatureInfoResult;
import org.deegree.ogcwebservices.wms.operation.GetMap;
import org.deegree.ogcwebservices.wms.operation.WMSProtocolFactory;
import org.w3c.dom.Document;
/**
*
*
*
* @version $Revision: 1.70 $
* @author <a href="mailto:poth@lat-lon.de">Andreas Poth</a>
* @author last edited by: $Author: schmitz $
*
* @version 1.0. $Revision: 1.70 $, $Date: 2006/11/29 14:10:24 $
*
*/
class DefaultGetFeatureInfoHandler implements GetFeatureInfoHandler,
ExecutionFinishedListener<Object[]> {
protected static final ILogger LOG = LoggerFactory.getLogger( DefaultGetFeatureInfoHandler.class );
private static final double DEFAULT_PIXEL_SIZE = 0.00028;
protected GetFeatureInfo request = null;
protected GetMap getMapRequest = null;
protected WMSConfigurationType configuration = null;
// collects the reponse for each layer
private Object[] featCol = null;
// scale of the map
protected double scale = 0;
// holds the number of request services that have responsed
private int count = 0;
// CRS of the request
protected CoordinateSystem reqCRS = null;
/**
* Creates a new GetMapHandler object.
* @param capabilities
* @param request request to perform
* @throws OGCWebServiceException
*/
public DefaultGetFeatureInfoHandler( WMSConfigurationType capabilities, GetFeatureInfo request )
throws OGCWebServiceException {
LOG.entering();
this.request = request;
this.configuration = capabilities;
getMapRequest = request.getGetMapRequestCopy();
try {
reqCRS = CRSFactory.create( getMapRequest.getSrs() );
if ( reqCRS == null ) {
throw new InvalidSRSException( Messages.getMessage( "WMS_UNKNOWN_CRS",
getMapRequest.getSrs() ) );
}
} catch ( Exception e ) {
throw new InvalidSRSException( Messages.getMessage( "WMS_UNKNOWN_CRS",
getMapRequest.getSrs() ) );
}
try {
Envelope bbox = getMapRequest.getBoundingBox();
scale = MapUtils.calcScale( getMapRequest.getWidth(), getMapRequest.getHeight(), bbox,
reqCRS, DEFAULT_PIXEL_SIZE );
} catch ( Exception e ) {
LOG.logDebug( e.getLocalizedMessage(), e );
throw new OGCWebServiceException( Messages.getMessage( "WMS_SCALECALC", e ) );
}
LOG.exiting();
}
/**
* increases the counter variable that holds the number of services that
* has sent a response. All data are available if the counter value equals
* the number of requested layers.
*/
protected synchronized void increaseCounter() {
count++;
}
/**
* performs a GetFeatureInfo request and retruns the result encapsulated within
* a <tt>WMSFeatureInfoResponse</tt> object. <p>
* The method throws an WebServiceException that only shall be thrown if an
* fatal error occurs that makes it imposible to return a result.
* If something wents wrong performing the request (none fatal error) The
* exception shall be encapsulated within the response object to be returned
* to the client as requested (GetFeatureInfo-Request EXCEPTION-Parameter).
*
* <p>
* All sublayers of the queried layer will be added automatically. Non-queryable
* sublayers are then ignored in the response.
* </p>
*
* @return response to the GetFeatureInfo response
*/
public GetFeatureInfoResult performGetFeatureInfo()
throws OGCWebServiceException {
LOG.entering();
String[] qlayers = request.getQueryLayers();
List<Layer> allLayers = new ArrayList<Layer>();
// here, the explicitly queried layers are checked for being queryable and known
for ( int i = 0; i < qlayers.length; i++ ) {
Layer layer = configuration.getLayer( qlayers[i] );
if ( layer == null ) {
throw new LayerNotDefinedException( Messages.getMessage( "WMS_UNKNOWNLAYER",
qlayers[i] ) );
}
if ( !layer.isQueryable() ) {
throw new LayerNotQueryableException(
Messages.getMessage(
"WMS_LAYER_NOT_QUERYABLE",
qlayers[i] ) );
}
if ( !layer.isSrsSupported( getMapRequest.getSrs() ) ) {
throw new InvalidSRSException( Messages.getMessage( "WMS_UNKNOWN_CRS",
getMapRequest.getSrs(),
qlayers[i] ) );
}
allLayers.add( layer );
// sublayers are added WITHOUT being checked for being queryable
// This is desirable for example in the following scenario:
// Suppose one queryable layer contains a lot of other layers,
// that are mostly queryable. Then you can query all of those layers
// at once by just querying the enclosing layer (the unqueryable
// sublayers are ignored).
allLayers.addAll( Arrays.asList( layer.getLayer() ) );
}
Layer[] layerList = allLayers.toArray( new Layer[allLayers.size()] );
// there must be one feature collection for each requested layer
int cnt = countNumberOfQueryableDataSources( layerList );
featCol = new Object[cnt];
// invokes the data supplyer for each layer in an independ thread
int kk = 0;
for ( int i = 0; i < layerList.length; i++ ) {
if ( validate( layerList[i] ) ) {
AbstractDataSource datasource[] = layerList[i].getDataSource();
for ( int j = 0; j < datasource.length; j++ ) {
if ( datasource[j].isQueryable() && isValidArea( datasource[j].getValidArea() ) ) {
ServiceInvoker si = new ServiceInvoker( layerList[i], datasource[j], kk++ );
ServiceInvokerTask task = new ServiceInvokerTask( si );
Executor.getInstance().performAsynchronously( task, this );
}
}
} else {
// set feature collection to null if no data are available for the requested
// area and/or scale. This will cause this index position will be ignored
// when creating the final result
featCol[kk++] = null;
increaseCounter();
}
}
// waits until the requested layers are available as <tt>DisplayElements</tt>
// or the time limit has been reached.
// TODO
// substitue by an event based approach
try {
waitForFinish();
} catch ( Exception e ) {
return createExceptionResponse( e );
}
GetFeatureInfoResult res = createFeatureInfoResponse();
return res;
}
/**
* returns the number of datasources assigned to the queried layers
* that are queryable
*
* @param layerList
* @return the number
*/
private int countNumberOfQueryableDataSources( Layer[] layerList ) {
int cnt = 0;
for ( int i = 0; i < layerList.length; i++ ) {
AbstractDataSource[] ds = layerList[i].getDataSource();
for ( int j = 0; j < ds.length; j++ ) {
if ( ds[j].isQueryable() && isValidArea( ds[j].getValidArea() ) ) {
cnt++;
}
}
}
return cnt;
}
/**
* returns true if the requested boundingbox intersects with the valid
* area of a datasource
* @param validArea
*/
private boolean isValidArea( Geometry validArea ) {
if ( validArea != null ) {
try {
Envelope env = request.getGetMapRequestCopy().getBoundingBox();
Geometry geom = GeometryFactory.createSurface( env, reqCRS );
if ( !reqCRS.getName().equals( validArea.getCoordinateSystem().getName() ) ) {
// if requested CRS is not identical to the CRS of the valid area
// a transformation must be performed before intersection can
// be checked
IGeoTransformer gt = new GeoTransformer( validArea.getCoordinateSystem() );
geom = gt.transform( geom );
}
return geom.intersects( validArea );
} catch ( Exception e ) {
// should never happen
LOG.logError( Messages.getMessage( "WMS_VALIDATE_DATASOURCE" ), e );
}
}
return true;
}
/**
* validates if the requested layer matches the conditions of the request
* if not a <tt>WebServiceException</tt> will be thrown. If the layer matches
* the request, but isn't able to deviever data for the requested area and/or
* scale false will be returned. If the layer matches the request and contains
* data for the requested area and/or scale true will be returned.
*
* @param layer layer as defined at the capabilities/configuration
*/
private boolean validate( Layer layer )
throws OGCWebServiceException {
LOG.entering();
// why the layer can be null here is not known, but just in case:
String name = ( layer == null ) ? "" : layer.getName();
// check for valid coordinated reference system
String[] srs = layer.getSrs();
boolean tmp = false;
for ( int i = 0; i < srs.length; i++ ) {
if ( srs[i].equalsIgnoreCase( getMapRequest.getSrs() ) ) {
tmp = true;
break;
}
}
if ( !tmp ) {
throw new InvalidSRSException( Messages.getMessage( "WMS_INVALIDSRS", name,
getMapRequest.getSrs() ) );
}
// check bounding box
try {
Envelope bbox = getMapRequest.getBoundingBox();
Envelope layerBbox = layer.getLatLonBoundingBox();
if ( !getMapRequest.getSrs().equalsIgnoreCase( "EPSG:4326" ) ) {
// transform the bounding box of the request to EPSG:4326
IGeoTransformer gt = new GeoTransformer( CRSFactory.create( "EPSG:4326" ) );
bbox = gt.transform( bbox, reqCRS );
}
if ( !bbox.intersects( layerBbox ) ) {
return false;
}
} catch ( Exception e ) {
throw new OGCWebServiceException( Messages.getMessage( "WMS_BBOXCOMPARSION" ) );
}
LOG.exiting();
return true;
}
/**
* creates a <tt>GetFeatureInfoResult</tt> containing an <tt>OGCWebServiceException</tt>
*
* @param e exception to encapsulate into the response
*/
private GetFeatureInfoResult createExceptionResponse( Exception e ) {
OGCWebServiceException exce = null;
// default --> application/vnd.ogc.se_xml
exce = new OGCWebServiceException( getClass().getName(), e.getMessage() );
GetFeatureInfoResult res = WMSProtocolFactory.createGetFeatureInfoResponse( request, exce,
null );
return res;
}
/**
* waits until the requested layers are available as <tt>DisplayElements</tt>
* or the time limit has been reached. If the waiting is terminated by reaching
* the time limit an <tt>WebServiceException</tt> will be thrown to indicated
* that the request couldn't be performed correctly.
*
* @throws WebServiceException if the time limit has been reached
*/
private void waitForFinish()
throws OGCWebServiceException, Exception {
LOG.entering();
// subtract 1 second for architecture overhead and image creation
long timeLimit = 1000 * ( configuration.getDeegreeParams().getRequestTimeLimit() - 1 );
//long timeLimit = 1000 * 100;
long runTime = 0;
while ( count < featCol.length ) {
try {
Thread.sleep( 100 );
} catch ( Exception e ) {
LOG.logError( "WMS_WAITING_LOOP", e );
throw e;
}
runTime += 100;
// finish loop after if request performing hasn't been completed
// after the time limit is reached
if ( runTime > timeLimit ) {
throw new OGCWebServiceException( Messages.getMessage( "WMS_TIMEOUT" ) );
}
}
LOG.exiting();
}
/**
* will be called each time a datasource has been read
*
* @param returnValue
*/
public synchronized void executionFinished( ExecutionFinishedEvent<Object[]> returnValue ) {
Object[] o = null;
try {
o = returnValue.getResult();
} catch ( Throwable t ) {
LOG.logError( "WMS_GETFEATURE_EXCEPTION", t );
}
featCol[( (Integer) o[0] ).intValue()] = o[1];
increaseCounter();
}
/**
* generates the desired output from the GMLs
* @return the result object
* @throws OGCWebServiceException
*/
private GetFeatureInfoResult createFeatureInfoResponse()
throws OGCWebServiceException {
LOG.entering();
Envelope bbox = getMapRequest.getBoundingBox();
StringBuffer sb = new StringBuffer( 20000 );
sb.append( "<ll:FeatureCollection " );
URL schemaLoc = configuration.getDeegreeParams().getFeatureSchemaLocation();
if ( schemaLoc != null ) {
sb.append( "xsi:schemaLocation='" );
sb.append( configuration.getDeegreeParams().getFeatureSchemaNamespace() );
sb.append( " " );
sb.append( schemaLoc.toExternalForm() );
sb.append( "'" );
}
sb.append( " xmlns:gml='http://www.opengis.net/gml' " );
sb.append( "xmlns:ll='http://www.lat-lon.de' " );
sb.append( "xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' " );
URL url = configuration.getDeegreeParams().getSchemaLocation();
if ( url != null ) {
sb.append( "xsi:schemaLocation='" );
sb.append( "http://www.lat-lon.de " + NetWorker.url2String( url ) + "'" );
}
sb.append( "><gml:boundedBy>" );
sb.append( "<gml:Box srsName='" + getMapRequest.getSrs() + "'>" );
sb.append( "<gml:coordinates>" + bbox.getMin().getX() + "," );
sb.append( bbox.getMin().getY() + " " + bbox.getMax().getX() + "," );
sb.append( bbox.getMax().getY() + "</gml:coordinates>" );
sb.append( "</gml:Box></gml:boundedBy>" );
int cnt = 0;
for ( int i = 0; i < featCol.length; i++ ) {
if ( featCol[i] instanceof OGCWebServiceException ) {
throw (OGCWebServiceException) featCol[i];
}
FeatureCollection fc = (FeatureCollection) featCol[i];
cnt = appendFeatureCollection( fc, sb, cnt );
//if ( cnt >= request.getFeatureCount() ) break;
}
sb.append( "</ll:FeatureCollection>" );
GetFeatureInfoResult response = WMSProtocolFactory.createGetFeatureInfoResponse(
request,
null,
sb.toString() );
LOG.exiting();
return response;
}
/**
*
* @param col
* @param sb
* @param cnt
* @return a counter, probably the same that is given as argument
*/
private int appendFeatureCollection( FeatureCollection col, StringBuffer sb, int cnt ) {
LOG.entering();
Feature[] feat = col.toArray();
if ( feat != null ) {
for ( int j = 0; j < feat.length; j++ ) {
FeatureType ft = feat[j].getFeatureType();
PropertyType[] ftp = ft.getProperties();
cnt++;
sb.append( "<gml:featureMember>" );
sb.append( "<ll:" + ft.getName().getLocalName() );
sb.append( " fid='" + feat[j].getId().replace( ' ', '_' ) + "'>" );
for ( int i = 0; i < ftp.length; i++ ) {
if ( ftp[i].getType() != Types.GEOMETRY && ftp[i].getType() != Types.POINT
&& ftp[i].getType() != Types.CURVE && ftp[i].getType() != Types.SURFACE
&& ftp[i].getType() != Types.MULTIPOINT
&& ftp[i].getType() != Types.MULTICURVE
&& ftp[i].getType() != Types.MULTISURFACE ) {
sb.append( "<ll:" + ftp[i].getName().getLocalName() + ">" );
Object prop = feat[j].getDefaultProperty( ftp[i].getName() ).getValue();
if ( prop instanceof FeatureCollection ) {
FeatureCollection fc = (FeatureCollection) prop;
appendFeatureCollection( fc, sb, cnt );
} else {
sb.append( "<![CDATA[" ).append( prop ).append( "]]>" );
}
sb.append( "</ll:" + ftp[i].getName().getLocalName() + ">" );
}
}
sb.append( "</ll:" + ft.getName().getLocalName() + ">" );
sb.append( "</gml:featureMember>" );
if ( cnt >= request.getFeatureCount() )
break;
}
}
LOG.exiting();
return cnt;
}
////////////////////////////////////////////////////////////////////////////
// inner classes //
////////////////////////////////////////////////////////////////////////////
/**
* Inner class for accessing the data of one layer and creating
* a GML document from it. The class extends <tt>Thread</tt> and implements
* the run method, so that a parallel data accessing from several layers is
* possible.
*
* @version $Revision: 1.70 $
* @author <a href="mailto:poth@lat-lon.de">Andreas Poth</a>
*/
public class ServiceInvoker {
private Layer layer = null;
private int index = 0;
private AbstractDataSource datasource = null;
/**
* Creates a new ServiceInvoker object.
*
* @param layer
* @param datasource
* @param index index of the requested layer
*/
ServiceInvoker( Layer layer, AbstractDataSource datasource, int index ) {
this.layer = layer;
this.index = index;
this.datasource = datasource;
}
/**
* central method for access the data assigned to a datasource
* @return result of feature info query
*/
public Object run() {
Object response = null;
if ( datasource != null ) {
OGCWebServiceRequest request = null;
try {
int type = datasource.getType();
switch ( type ) {
case AbstractDataSource.LOCALWFS:
case AbstractDataSource.REMOTEWFS: {
request = createGetFeatureRequest( (LocalWFSDataSource) datasource );
break;
}
case AbstractDataSource.LOCALWCS:
case AbstractDataSource.REMOTEWCS: {
request = createDescribeCoverageRequest( datasource );
break;
}
case AbstractDataSource.REMOTEWMS: {
request = createGetFeatureInfo( datasource );
break;
}
}
} catch ( Exception e ) {
OGCWebServiceException exce = new OGCWebServiceException(
"ServiceInvoker: "
+ layer.getName(),
Messages.getMessage( "WMS_CREATE_QUERY" ) );
response = new Object[] { new Integer( index ), exce };
LOG.logError(
Messages.getMessage( "WMS_CREATE_QUERY" ) + ": " + e.getMessage(),
e );
throw new RuntimeException( e );
}
try {
Executor executor = Executor.getInstance();
DoServiceTask task = new DoServiceTask( datasource.getOGCWebService(), request );
Object o = executor.performSynchronously(
task,
datasource.getRequestTimeLimit() * 1000 );
response = handleResponse( o );
} catch ( CancellationException e ) {
// exception can't be re-thrown because responsible GetMapHandler
// must collect all responses of all datasources
String s = Messages.getMessage( "WMS_TIMEOUTDATASOURCE",
new Integer( datasource.getRequestTimeLimit() ) );
LOG.logError( s, e );
if ( datasource.isFailOnException() ) {
OGCWebServiceException exce = new OGCWebServiceException(
getClass().getName(),
s );
response = new Object[] { new Integer( index ), exce };
} else {
response = new Object[] { new Integer( index ), null };
}
} catch ( Throwable t ) {
// exception can't be re-thrown because responsible GetMapHandler
// must collect all responses of all datasources
String s = Messages.getMessage( "WMS_ERRORDOSERVICE", t.getMessage() );
LOG.logError( s, t );
if ( datasource.isFailOnException() ) {
OGCWebServiceException exce = new OGCWebServiceException(
getClass().getName(),
s );
response = new Object[] { new Integer( index ), exce };
} else {
response = new Object[] { new Integer( index ), null };
}
}
}
return response;
}
/**
* creates a getFeature request considering the getMap request and the
* filterconditions defined in the submitted <tt>DataSource</tt> object.
* The request will be encapsualted within a <tt>OGCWebServiceEvent</tt>.
* @param ds
* @return GetFeature request object
*/
private GetFeature createGetFeatureRequest( LocalWFSDataSource ds )
throws Exception {
LOG.entering();
Envelope targetArea = calcTargetArea( ds );
// no filter condition has been defined
StringBuffer sb = new StringBuffer( 2000 );
sb.append( "<?xml version='1.0' encoding='" + CharsetUtils.getSystemCharset() + "'?>" );
sb.append( "<GetFeature xmlns='http://www.opengis.net/wfs' " );
sb.append( "xmlns:ogc='http://www.opengis.net/ogc' " );
sb.append( "xmlns:gml='http://www.opengis.net/gml' " );
sb.append( "xmlns:" ).append( ds.getName().getPrefix() ).append( '=' );
sb.append( "'" ).append( ds.getName().getNamespace() ).append( "' " );
sb.append( "service='WFS' version='1.1.0' " );
sb.append( "outputFormat='FEATURECOLLECTION'>" );
sb.append( "<Query typeName='" + ds.getName().getAsString() + "'><ogc:Filter>" );
Query query = ds.getQuery();
if ( query == null ) {
// BBOX operation for speeding up the search at simple datasources
// like shapes
sb.append( "<ogc:BBOX><PropertyName>" );
sb.append( ds.getGeometryProperty().getAsString() );
sb.append( "</PropertyName>" );
sb.append( GMLGeometryAdapter.exportAsBox( targetArea ) );
sb.append( "</ogc:BBOX>" );
sb.append( "</ogc:Filter></Query></GetFeature>" );
} else {
Filter filter = query.getFilter();
sb.append( "<ogc:And>" );
// BBOX operation for speeding up the search at simple datasources
// like shapes
sb.append( "<ogc:BBOX><PropertyName>" + ds.getGeometryProperty().getAsString() );
sb.append( "</PropertyName>" );
sb.append( GMLGeometryAdapter.exportAsBox( targetArea ) );
sb.append( "</ogc:BBOX>" );
if ( filter instanceof ComplexFilter ) {
org.deegree.model.filterencoding.Operation op = ( (ComplexFilter) filter ).getOperation();
sb.append( op.toXML() );
} else {
ArrayList featureIds = ( (FeatureFilter) filter ).getFeatureIds();
for ( int i = 0; i < featureIds.size(); i++ ) {
FeatureId fid = (FeatureId) featureIds.get( i );
sb.append( fid.toXML() );
}
}
sb.append( "</ogc:And></ogc:Filter></Query></GetFeature>" );
}
if ( LOG.getLevel() == ILogger.LOG_DEBUG ) {
LOG.logDebug( "GetFeature-request:\n" + sb );
}
// create dom representation of the request
StringReader sr = new StringReader( sb.toString() );
Document doc = XMLTools.parse( sr );
// create OGCWebServiceEvent object
IDGenerator idg = IDGenerator.getInstance();
GetFeature gfr = GetFeature.create( "" + idg.generateUniqueID(),
doc.getDocumentElement() );
LOG.exiting();
return gfr;
}
/**
* calculates the target area for the getfeatureinfo request from the
* maps bounding box, the its size and the image coordinates of interest. An
* area is calculated instead of using a point because to consider
* uncertainties determining the point of interest
* @param ds <tt>DataSource</tt> of the layer that is requested for feature
* infos (each layer may be offered in its own crs)
*/
private Envelope calcTargetArea( AbstractDataSource ds )
throws OGCWebServiceException {
LOG.entering();
int width = request.getGetMapRequestCopy().getWidth();
int height = request.getGetMapRequestCopy().getHeight();
int x = request.getClickPoint().x;
int y = request.getClickPoint().y;
Envelope bbox = request.getGetMapRequestCopy().getBoundingBox();
// transform request bounding box to the coordinate reference
// system the WFS holds the data if requesting CRS and WFS-Data
// crs are different
// WFService se = (WFService)ds.getOGCWebService();
// WFSCapabilities capa = (WFSCapabilities)se.getWFSCapabilities();
//
// org.deegree.ogcwebservices.wfs.capabilities.FeatureType ft =
// capa.getFeatureTypeList().getFeatureType( ds.getName() );
WFService se = (WFService) ds.getOGCWebService();
WFSCapabilities capa = se.getCapabilities();
QualifiedName gn = ds.getName();
WFSFeatureType ft = capa.getFeatureTypeList().getFeatureType( gn );
if ( ft == null ) {
throw new OGCWebServiceException( Messages.getMessage( "WMS_UNKNOWNFT",
ds.getName() ) );
}
String crs = ft.getDefaultSRS().toASCIIString();
Envelope tBbox = null;
try {
GeoTransform gt = new WorldToScreenTransform( bbox.getMin().getX(),
bbox.getMin().getY(),
bbox.getMax().getX(),
bbox.getMax().getY(), 0, 0,
width - 1, height - 1 );
double[] target = new double[4];
int rad = configuration.getDeegreeParams().getFeatureInfoRadius();
target[0] = gt.getSourceX( x - rad );
target[1] = gt.getSourceY( y + rad );
target[2] = gt.getSourceX( x + rad );
target[3] = gt.getSourceY( y - rad );
tBbox = GeometryFactory.createEnvelope( target[0], target[1], target[2], target[3],
null );
if ( !( crs.equalsIgnoreCase( request.getGetMapRequestCopy().getSrs() ) ) ) {
IGeoTransformer transformer = new GeoTransformer( CRSFactory.create( crs ) );
tBbox = transformer.transform( tBbox, reqCRS );
}
} catch ( Exception e ) {
throw new OGCWebServiceException( e.toString() );
}
LOG.exiting();
return tBbox;
}
/**
* creates a describe Coverage request
* The request will be encapsualted within a <tt>OGCWebServiceEvent</tt>.
* @param ds
* @return DescribeCoverage request object
*/
private DescribeCoverage createDescribeCoverageRequest( AbstractDataSource ds ) {
throw new UnsupportedOperationException(
"createDescribeCoverageRequest is not implemented yet" );
}
/**
* creates a GetFeatureInfo request for requesting a cascaded remote WMS
* The request will be encapsualted within a <tt>OGCWebServiceEvent</tt>.
* @param ds
* @return GetFeatureInfo request object
*/
private GetFeatureInfo createGetFeatureInfo( AbstractDataSource ds ) {
LOG.entering();
// create embbeded map request
GetMap gmr = ( (RemoteWMSDataSource) ds ).getGetMapRequest();
String format = getMapRequest.getFormat();
if ( gmr != null && !"%default%".equals( gmr.getFormat() ) ) {
format = gmr.getFormat();
}
org.deegree.ogcwebservices.wms.operation.GetMap.Layer[] lys = null;
lys = new org.deegree.ogcwebservices.wms.operation.GetMap.Layer[1];
lys[0] = GetMap.createLayer( layer.getName(), "$DEFAULT" );
if ( gmr != null && gmr.getLayers() != null ) {
lys = gmr.getLayers();
}
Color bgColor = getMapRequest.getBGColor();
if ( gmr != null && gmr.getBGColor() != null ) {
bgColor = gmr.getBGColor();
}
Values time = getMapRequest.getTime();
if ( gmr != null && gmr.getTime() != null ) {
time = gmr.getTime();
}
Map<String, String> vendorSpecificParameter = getMapRequest.getVendorSpecificParameters();
if ( gmr != null && gmr.getVendorSpecificParameters() != null
&& gmr.getVendorSpecificParameters().size() > 0 ) {
vendorSpecificParameter = gmr.getVendorSpecificParameters();
}
String version = "1.1.0";
if ( gmr != null && gmr.getVersion() != null ) {
version = gmr.getFormat();
}
Values elevation = getMapRequest.getElevation();
if ( gmr != null && gmr.getElevation() != null ) {
elevation = gmr.getElevation();
}
Map<String, Values> sampleDim = null;
if ( gmr != null && gmr.getSampleDimension() != null ) {
sampleDim = gmr.getSampleDimension();
}
IDGenerator idg = IDGenerator.getInstance();
gmr = GetMap.create( version, "" + idg.generateUniqueID(), lys, elevation, sampleDim,
format, getMapRequest.getWidth(), getMapRequest.getHeight(),
getMapRequest.getSrs(), getMapRequest.getBoundingBox(),
getMapRequest.getTransparency(), bgColor,
getMapRequest.getExceptions(), time, null, null,
vendorSpecificParameter );
// create GetFeatureInfo request for cascaded/remote WMS
String[] queryLayers = new String[] { ds.getName().getAsString() };
GetFeatureInfo req = GetFeatureInfo.create( "1.1.0", this.toString(), queryLayers, gmr,
"application/vnd.ogc.gml",
request.getFeatureCount(),
request.getClickPoint(),
request.getExceptions(), null,
request.getVendorSpecificParameters() );
LOG.exiting();
return req;
}
/**
* The method implements the <tt>OGCWebServiceClient</tt> interface. So a
* deegree OWS implementation accessed by this class is able to return the
* result of a request by calling the write-method.
* @param result to a GetXXX request
* @return the response object
* @throws Exception
*/
private Object handleResponse( Object result )
throws Exception {
Object[] response = null;
if ( result instanceof FeatureResult ) {
response = handleGetFeatureResponse( (FeatureResult) result );
} else if ( result instanceof ResultCoverage ) {
response = handleDescribeCoverageResponse( (ResultCoverage) result );
} else if ( result instanceof GetFeatureInfoResult ) {
response = handleGetFeatureInfoResult( (GetFeatureInfoResult) result );
} else {
throw new Exception( Messages.getMessage( "WMS_UNKNOWNRESPONSEFORMAT" ) );
}
return response;
}
/**
* handles the response of a WFS and calls a factory to create
* <tt>DisplayElement</tt> and a <tt>Theme</tt> from it
* @param response
* @return the response objects
* @throws Exception
*/
private Object[] handleGetFeatureResponse( FeatureResult response )
throws Exception {
FeatureCollection fc = null;
Object o = response.getResponse();
if ( o instanceof FeatureCollection ) {
fc = (FeatureCollection) o;
} else if ( o.getClass() == byte[].class ) {
Reader reader = new InputStreamReader( new ByteArrayInputStream( (byte[]) o ) );
GMLFeatureCollectionDocument doc = new GMLFeatureCollectionDocument();
doc.load( reader, XMLFragment.DEFAULT_URL );
fc = doc.parse();
} else {
throw new Exception( Messages.getMessage( "WMS_UNKNOWNDATAFORMATFT" ) );
}
Object[] ro = new Object[2];
ro[0] = new Integer( index );
ro[1] = fc;
return ro;
}
/**
*
* @param res
*/
private Object[] handleGetFeatureInfoResult( GetFeatureInfoResult res )
throws Exception {
FeatureCollection fc = null;
StringReader sr = new StringReader( res.getFeatureInfo() );
XMLFragment xml = new XMLFragment( sr, XMLFragment.DEFAULT_URL );
URL url = ( (RemoteWMSDataSource) datasource ).getFeatureInfoTransform();
if ( url != null ) {
// transform incoming GML/XML to a GML application schema
// that is understood by deegree
XSLTDocument xslt = new XSLTDocument();
xslt.load( url );
xml = xslt.transform( xml, null, null, null );
}
GMLFeatureCollectionDocument doc = new GMLFeatureCollectionDocument();
doc.setRootElement( xml.getRootElement() );
fc = doc.parse();
Object[] ro = new Object[2];
ro[0] = new Integer( index );
ro[1] = fc;
return ro;
}
/**
* handles the response of a WCS and calls a factory to create
* <tt>DisplayElement</tt> and a <tt>Theme</tt> from it
* @param response
* @return the reponse objects
* @throws Exception
*/
private Object[] handleDescribeCoverageResponse( ResultCoverage response )
throws Exception {
throw new UnsupportedOperationException(
"handleDescribeCoverageResponse is not implemented yet" );
}
}
private class DoServiceTask implements Callable<Object> {
OGCWebService webService;
OGCWebServiceRequest request;
DoServiceTask( OGCWebService webService, OGCWebServiceRequest request ) {
this.webService = webService;
this.request = request;
}
public Object call()
throws Exception {
return this.webService.doService( request );
}
}
private class ServiceInvokerTask implements Callable<Object[]> {
ServiceInvoker invoker;
ServiceInvokerTask( ServiceInvoker invoker ) {
this.invoker = invoker;
}
public Object[] call()
throws Exception {
return (Object[]) this.invoker.run();
}
}
}
/* ********************************************************************
Changes to this class. What the people have been up to:
$Log: DefaultGetFeatureInfoHandler.java,v $
Revision 1.70 2006/11/29 14:10:24 schmitz
Added more messages. The Default handlers should now be fully externalized.
Revision 1.69 2006/11/29 13:00:36 schmitz
Cleaned up WMS messages.
Revision 1.68 2006/11/27 09:07:52 poth
JNI integration of proj4 has been removed. The CRS functionality now will be done by native deegree code.
Revision 1.67 2006/11/24 09:33:12 schmitz
Fixed a bug concerning layer specific scale hints.
Using the central i18n mechanism.
Changed the localwfs mechanism to just use one WFS and not recreate them.
Revision 1.66 2006/11/14 15:37:06 poth
bug fix - set correct WFS version for GetFeature request
Revision 1.65 2006/10/17 20:31:17 poth
*** empty log message ***
Revision 1.64 2006/10/10 13:46:02 schmitz
Updated the WMS GetMap and GetFeatureInfo requests to handle sublayers of layers as well.
Revision 1.63 2006/09/27 16:46:41 poth
transformation method signature changed
Revision 1.62 2006/09/25 12:47:00 poth
bug fixes - map scale calculation
Revision 1.61 2006/09/15 09:18:29 schmitz
Updated WMS to use SLD or SLD_BODY sld documents as default when also giving
LAYERS and STYLES parameters at the same time.
Revision 1.60 2006/09/11 15:26:15 mschneider
Fixed bug in createGetFeatureRequest(). Geometry property must be exported using QualifiedName#getAsString().
Revision 1.59 2006/09/11 14:31:48 mschneider
Added debugging messages (to find GetFeature-Info related problem).
Revision 1.58 2006/09/08 08:42:01 schmitz
Updated the WMS to be 1.1.1 conformant once again.
Cleaned up the WMS code.
Added cite WMS test data.
Revision 1.57 2006/08/10 13:30:41 mschneider
Changed TimeoutException to CancellationException (due to changes in Executor). See Executor annotations for further info.
Revision 1.56 2006/08/08 09:57:11 mschneider
Added generics for type safety - constrained the return type of tasks.
Revision 1.55 2006/08/07 13:59:46 mschneider
Refactored due to changes in deegree.framework.concurrent package.
Revision 1.54 2006/08/07 10:01:41 poth
throw UnsupportedOperation exception for for handling DescribeCoverage added
Revision 1.53 2006/08/06 19:50:49 poth
bug fix - exception handling
Revision 1.52 2006/07/13 12:24:45 poth
adaptions required according to changes in org.deegree.ogcwebservice.wms.operations.GetMap
Revision 1.51 2006/07/12 14:46:17 poth
comment footer added
********************************************************************** */