//$Header: /home/deegree/jail/deegreerepository/deegree/src/org/deegree/portal/portlet/modules/map/actions/portlets/FeatureInfoPortletPerform.java,v 1.24 2006/10/26 08:54:26 poth Exp $
/*---------------- 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.portal.portlet.modules.map.actions.portlets;
import java.awt.Color;
import java.io.File;
import java.io.StringWriter;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.xml.transform.Source;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import org.apache.jetspeed.portal.Portlet;
import org.deegree.framework.log.ILogger;
import org.deegree.framework.log.LoggerFactory;
import org.deegree.framework.util.StringTools;
import org.deegree.framework.xml.XMLFragment;
import org.deegree.framework.xml.XSLTDocument;
import org.deegree.model.spatialschema.Envelope;
import org.deegree.model.spatialschema.GeometryFactory;
import org.deegree.model.spatialschema.Point;
import org.deegree.ogcwebservices.OGCWebServiceException;
import org.deegree.ogcwebservices.OWSUtils;
import org.deegree.ogcwebservices.wms.operation.GetFeatureInfo;
import org.deegree.ogcwebservices.wms.operation.GetMap;
import org.deegree.portal.PortalException;
import org.deegree.portal.context.Layer;
import org.deegree.portal.context.LayerList;
import org.deegree.portal.context.ViewContext;
import org.deegree.portal.portlet.modules.actions.IGeoPortalPortletPerform;
/**
*
*
* @version $Revision: 1.24 $
* @author <a href="mailto:poth@lat-lon.de">Andreas Poth</a>
* @author last edited by: $Author: poth $
*
* @version 1.0. $Revision: 1.24 $, $Date: 2006/10/26 08:54:26 $
*
* @since 2.0
*/
public class FeatureInfoPortletPerform extends IGeoPortalPortletPerform {
private static final ILogger LOG = LoggerFactory.getLogger( FeatureInfoPortletPerform.class );
protected static String PARAM_FILAYERS = "FILAYERS";
protected static String PARAM_LAYERS = "LAYERS";
protected static String PARAM_BBOX = "BBOX";
protected static String PARAM_X = "X";
protected static String PARAM_Y = "Y";
protected static String PARAM_FEATURECOUNT = "FEATURECOUNT";
protected static String SESSION_INITPARAM = "FIINITPARAM";
protected static String INIT_PATHTOXSLT = "pathToXSLT";
private Map initParams = null;
/**
* @param request
* @param portlet
* @param servletContext will be needed to evaluate the absolut path of the
* transform script(s)
*/
public FeatureInfoPortletPerform( HttpServletRequest request, Portlet portlet, ServletContext sc ) {
super( request, portlet, sc );
initParams = (Map)request.getSession().getAttribute( SESSION_INITPARAM );
}
/**
* initializes the portlet by putting the init parameters to the users
* session. Even if this is not absolutly neccessary it simplifies a
* few things ...
*
*/
public void init() {
if ( request.getSession().getAttribute( SESSION_INITPARAM ) == null ) {
Map map = portlet.getPortletConfig().getInitParameters();
request.getSession().setAttribute( SESSION_INITPARAM, map );
}
}
/**
* performs a GetFeatureInfo/GetFeature/DescribeCoverage request depending
* on the layer typs the request targets. If defiend the result will be transformed
* by a XSLT script to get a human readable out put.
*
* @return
*/
protected void doGetFeatureInfo()
throws PortalException, OGCWebServiceException {
String tmp = (String) parameter.get( PARAM_FILAYERS );
if ( tmp == null || tmp.length() == 0 ) {
throw new PortalException( "at least one layer/featuretype/coverage must be set" );
}
String[] layers = StringTools.toArray( tmp, ",", true );
setCurrentFILayer( layers );
// a get feature info request only can be performed if a viewcontext
// has been initialized before by the MapWindowPortletAction
ViewContext vc = getCurrentViewContext( (String)initParams.get( INIT_MAPPORTLETID ) );
if ( vc == null ) {
throw new PortalException( "no valid view context available through users session" );
}
// synchronize list of visible layer and BBOX with the users view context
// because maybe the user had changed the visible layers before performing
// a GetFeatureInfo request
updateContext();
Layer layer = null;
Layer former = null;
List layerList = new ArrayList( 50 );
StringBuffer sb = new StringBuffer( 20000 );
// performe a feature info request every time the hosting
// server changes. This is required because maybe a user has
// selected two or more layers hosted by different OWS
for ( int i = 0; i < layers.length; i++ ) {
former = layer;
layer = vc.getLayerList().getLayer( layers[i] );
if ( i > 0 ) {
if ( layer.getServer().equals( former.getServer() ) ) {
layerList.add( layer );
} else {
sb.append( perform( layerList, vc ) );
layerList.clear();
layerList.add( layer );
}
} else {
layerList.add( layer );
}
}
sb.append( perform( layerList, vc ) );
vc.getGeneral().getExtension().setMode( "FEATUREINFO" );
setCurrentMapContext( vc, (String)initParams.get( INIT_MAPPORTLETID ) );
// the result will be available through the forwarded request as well as
// through the users session (the portal frontend deciceds what behavior
// it should have)
request.setAttribute( "HTML", sb.toString() );
request.getSession().setAttribute( "HTML", sb.toString() );
}
/**
* sets the name of the the layers that are activated for feature
* info requests in the uses WMC
*/
void setCurrentFILayer( String[] fiLayer ) {
List list = Arrays.asList( fiLayer );
list = new ArrayList( list );
ViewContext vc = getCurrentViewContext( (String)initParams.get( INIT_MAPPORTLETID ) );
LayerList layerList = vc.getLayerList();
Layer[] layers = layerList.getLayers();
for ( int i = 0; i < layers.length; i++ ) {
if ( list.contains( layers[i].getName() ) ) {
layers[i].getExtension().setSelectedForQuery( true );
} else {
layers[i].getExtension().setSelectedForQuery( false );
}
}
}
/**
* distributes the performance of the feature info requests depending on
* the requested service to a specialized method
*
* @param layerList list of context layers provided by the same OWS
* @param vc
* @return
*/
protected String perform( List layerList, ViewContext vc )
throws OGCWebServiceException, PortalException {
Layer layer = (Layer) layerList.get( 0 );
if ( layer.getServer().getService().indexOf( "WMS" ) > -1 ) {
return performWMS( layerList, vc );
} else if ( layer.getServer().getService().indexOf( "WFS" ) > -1 ) {
throw new PortalException( "WFS is not supported as feature info target yet!" );
} else if ( layer.getServer().getService().indexOf( "WCS" ) > -1 ) {
throw new PortalException( "WCS is not supported as feature info target yet!" );
} else {
throw new PortalException( "not supported service: " + layer.getServer().getService()
+ " as feature info target!" );
}
}
/**
* performes a GetFeatureInfo request on a WMS and transforms the result using
* a XSLT script defined in the portlets init-parameters
*
* @param layerList list of context layers provided by the same WMS
* @param vc
* @return
*/
private String performWMS( List layerList, ViewContext vc )
throws OGCWebServiceException, PortalException {
GetFeatureInfo gfi = createGetFeatureInfoRequest( layerList, vc );
Layer layer = (Layer) layerList.get( 0 );
URL url = OWSUtils.getHTTPGetOperationURL( layer.getServer().getCapabilities(),
GetFeatureInfo.class );
String href = OWSUtils.validateHTTPGetBaseURL( url.toExternalForm() );
StringBuffer sb = new StringBuffer( 1000 );
sb.append( href ).append( gfi.getRequestParameter() );
// If a user is registered to the portal use his name and password to
// perform GetFeatureInfo request because maybe the connected server
// is hidden behind a owsProxy
// TODO
// read informations too which server user name and password shall be send
// TODO
// replace sending user name and password by a sessionID
if ( !"anon".equals( request.getAttribute( "$U$" ) ) ) {
sb.append( "&user=" ).append( request.getAttribute( "$U$" ) );
request.removeAttribute( "$U$" );
}
if ( request.getAttribute( "$P$" ) != null ) {
sb.append( "&password=" ).append( request.getAttribute( "$P$" ) );
request.removeAttribute( "$P$" );
}
LOG.logDebug( "info request: ", sb );
XMLFragment frag = null;
try {
frag = new XMLFragment( new URL( sb.toString() ) );
} catch ( Exception e ) {
e.printStackTrace();
throw new OGCWebServiceException( "could not perform GetFeatureInfo request "
+ e.getMessage() );
}
LOG.logDebug( "path to XSLT:", initParams );
String path = getPathToXSLTScript( layer, href );
path = sc.getRealPath( "/WEB-INF/" + path );
Source xmlSource = new DOMSource( frag.getRootElement() );
Source xslSource = new StreamSource( new File( path ) );
StringWriter sw = new StringWriter( 20000 );
String s = null;
try {
XSLTDocument.transform( xmlSource, xslSource, new StreamResult( sw ), null, null );
s = sw.getBuffer().toString();
sw.close();
} catch ( Exception e ) {
throw new PortalException( "could not transform GetFeatureInfo result of request: "
+ sb + " - " + StringTools.stackTraceToString( e ) );
}
return s;
}
/**
* returns the path to the xslt script to be used for transforming
* info data
*
* @param layer
* @param server
* @return
* @throws PortalException
*/
private String getPathToXSLTScript( Layer layer, String server )
throws PortalException {
StringBuffer sbb = new StringBuffer( 1000 );
sbb.append( INIT_PATHTOXSLT ).append( ';' ).append( server ).append( ';' );
sbb.append( layer.getServer().getService() );
String path = (String) initParams.get( sbb.toString() );
if ( path == null ) {
sbb.delete( 0, sbb.length() );
sbb.append( INIT_PATHTOXSLT ).append( ';' );
sbb.append( layer.getServer().getService() );
path = (String) initParams.get( sbb.toString() );
}
if ( path == null ) {
path = (String) initParams.get( INIT_PATHTOXSLT );
}
if ( path == null ) {
// for being compliant with an error in init-param that
// has been used in several instances
sbb = new StringBuffer( 1000 );
sbb.append( "pathToXLST" ).append( ';' ).append( server ).append( ';' );
sbb.append( layer.getServer().getService() );
path = (String) initParams.get( sbb.toString() );
if ( path == null ) {
sbb.delete( 0, sbb.length() );
sbb.append( "pathToXLST" ).append( ';' );
sbb.append( layer.getServer().getService() );
path = (String) initParams.get( sbb.toString() );
}
if ( path == null ) {
path = (String) initParams.get( "pathToXLST" );
}
}
if ( path == null ) {
LOG.logDebug( "initParams: ", initParams );
throw new PortalException( "no XSLT script defined for processing GetFeatureInfo "
+ "response of:" + INIT_PATHTOXSLT + ';' + server + ';'
+ layer.getServer().getService() );
}
return path;
}
/**
* creates a GetFeatureInfo request from the requested target layers depending
* on the current view context
*
* @param layerList
* @param vc
* @return
* @throws OGCWebServiceException
*/
private GetFeatureInfo createGetFeatureInfoRequest( List layerList, ViewContext vc ) {
String[] fiLayers = new String[layerList.size()];
GetMap.Layer[] gmLayers = new GetMap.Layer[layerList.size()];
for ( int i = 0; i < gmLayers.length; i++ ) {
Layer layer = (Layer) layerList.get( i );
fiLayers[i] = layer.getName();
gmLayers[i] = new GetMap.Layer( layer.getName(),
layer.getStyleList().getCurrentStyle().getName() );
}
Layer layer = (Layer) layerList.get( 0 );
Point[] pt = vc.getGeneral().getBoundingBox();
String srs = pt[0].getCoordinateSystem().getName();
int width = vc.getGeneral().getWindow().width;
int height = vc.getGeneral().getWindow().height;
Envelope bbox = GeometryFactory.createEnvelope( pt[0].getX(), pt[0].getY(), pt[1].getX(),
pt[1].getY(), pt[0].getCoordinateSystem() );
// create GetMap request being mandatory part of the GetFeatureInfo request
GetMap gm = GetMap.create( layer.getServer().getVersion(), "id", gmLayers, null, null,
layer.getFormatList().getCurrentFormat().getName(), width,
height, srs, bbox, false, Color.WHITE, null, null, null, null,
null );
int x = Integer.parseInt( (String) parameter.get( PARAM_X ) );
int y = Integer.parseInt( (String) parameter.get( PARAM_Y ) );
java.awt.Point point = new java.awt.Point( x, y );
int featureCount = Integer.parseInt( (String) parameter.get( PARAM_FEATURECOUNT ) );
GetFeatureInfo gfi = GetFeatureInfo.create( layer.getServer().getVersion(), "id", fiLayers,
gm, "application/vnd.ogc.gml", featureCount,
point, null, null, null );
return gfi;
}
}
/* ********************************************************************
Changes to this class. What the people have been up to:
$Log: FeatureInfoPortletPerform.java,v $
Revision 1.24 2006/10/26 08:54:26 poth
logging statement added
Revision 1.23 2006/10/17 20:31:17 poth
*** empty log message ***
Revision 1.22 2006/10/12 15:46:19 poth
adaption required to avoid allocating larger amounts of memory for each user session when using several WMC documents
Revision 1.21 2006/08/29 19:54:14 poth
footer corrected
Revision 1.20 2006/08/20 20:53:54 poth
changes rquired as a consequence of bug fix in wmc implementation/handling. Instead of determining the correct URL as given in a services capabilities deegree always has used the base URL which is just guarenteed to be valid for GetCapabilities requests.
Revision 1.19 2006/08/07 10:52:20 poth
not used imports removed
Revision 1.18 2006/07/06 09:59:04 poth
bug fix - settig correct ID of current map context using more than one WMC (SelectWMCPortlet)
Revision 1.17 2006/07/06 08:16:29 poth
*** empty log message ***
Revision 1.16 2006/06/07 12:19:38 poth
*** empty log message ***
Revision 1.15 2006/05/26 11:09:53 poth
bug fix -> correct naming for initparameter
Revision 1.14 2006/05/01 20:15:27 poth
*** empty log message ***
Revision 1.13 2006/04/11 15:12:31 poth
*** empty log message ***
Revision 1.12 2006/04/11 14:04:25 poth
*** empty log message ***
Revision 1.11 2006/04/11 14:03:17 poth
*** empty log message ***
Revision 1.10 2006/04/06 20:25:21 poth
*** empty log message ***
********************************************************************** */