001 //$HeadURL: https://svn.wald.intevation.org/svn/deegree/base/branches/2.4_testing/src/org/deegree/portal/portlet/modules/map/actions/portlets/FeatureInfoPortletPerform.java $
002 /*----------------------------------------------------------------------------
003 This file is part of deegree, http://deegree.org/
004 Copyright (C) 2001-2009 by:
005 Department of Geography, University of Bonn
006 and
007 lat/lon GmbH
008
009 This library is free software; you can redistribute it and/or modify it under
010 the terms of the GNU Lesser General Public License as published by the Free
011 Software Foundation; either version 2.1 of the License, or (at your option)
012 any later version.
013 This library is distributed in the hope that it will be useful, but WITHOUT
014 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
015 FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
016 details.
017 You should have received a copy of the GNU Lesser General Public License
018 along with this library; if not, write to the Free Software Foundation, Inc.,
019 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
020
021 Contact information:
022
023 lat/lon GmbH
024 Aennchenstr. 19, 53177 Bonn
025 Germany
026 http://lat-lon.de/
027
028 Department of Geography, University of Bonn
029 Prof. Dr. Klaus Greve
030 Postfach 1147, 53001 Bonn
031 Germany
032 http://www.geographie.uni-bonn.de/deegree/
033
034 e-mail: info@deegree.org
035 ----------------------------------------------------------------------------*/
036 package org.deegree.portal.portlet.modules.map.actions.portlets;
037
038 import java.awt.Color;
039 import java.io.File;
040 import java.io.StringWriter;
041 import java.net.URL;
042 import java.util.ArrayList;
043 import java.util.Arrays;
044 import java.util.HashMap;
045 import java.util.List;
046 import java.util.Map;
047
048 import javax.servlet.ServletContext;
049 import javax.servlet.http.HttpServletRequest;
050 import javax.xml.transform.Source;
051 import javax.xml.transform.dom.DOMSource;
052 import javax.xml.transform.stream.StreamResult;
053 import javax.xml.transform.stream.StreamSource;
054
055 import org.apache.jetspeed.portal.Portlet;
056 import org.deegree.framework.log.ILogger;
057 import org.deegree.framework.log.LoggerFactory;
058 import org.deegree.framework.util.StringTools;
059 import org.deegree.framework.xml.XMLFragment;
060 import org.deegree.framework.xml.XSLTDocument;
061 import org.deegree.model.spatialschema.Envelope;
062 import org.deegree.model.spatialschema.GeometryFactory;
063 import org.deegree.model.spatialschema.Point;
064 import org.deegree.ogcwebservices.OGCWebServiceException;
065 import org.deegree.ogcwebservices.OWSUtils;
066 import org.deegree.ogcwebservices.wms.operation.GetFeatureInfo;
067 import org.deegree.ogcwebservices.wms.operation.GetMap;
068 import org.deegree.portal.PortalException;
069 import org.deegree.portal.context.Layer;
070 import org.deegree.portal.context.LayerList;
071 import org.deegree.portal.context.ViewContext;
072 import org.deegree.portal.portlet.modules.actions.IGeoPortalPortletPerform;
073
074 /**
075 *
076 *
077 * @author <a href="mailto:poth@lat-lon.de">Andreas Poth</a>
078 * @author last edited by: $Author: mschneider $
079 *
080 * @version $Revision: 18195 $, $Date: 2009-06-18 17:55:39 +0200 (Do, 18. Jun 2009) $
081 */
082 public class FeatureInfoPortletPerform extends IGeoPortalPortletPerform {
083
084 private static final ILogger LOG = LoggerFactory.getLogger( FeatureInfoPortletPerform.class );
085
086 protected static String PARAM_FILAYERS = "FILAYERS";
087
088 protected static String PARAM_LAYERS = "LAYERS";
089
090 protected static String PARAM_X = "X";
091
092 protected static String PARAM_Y = "Y";
093
094 protected static String PARAM_FEATURECOUNT = "FEATURECOUNT";
095
096 private static String SESSION_INITPARAM = "FIINITPARAM";
097
098 protected static String INIT_PATHTOXSLT = "pathToXSLT";
099
100 private Map<?, ?> initParams = null;
101
102 /**
103 * @param request
104 * @param portlet
105 * @param sc
106 * will be needed to evaluate the absolut path of the transform script(s)
107 */
108 public FeatureInfoPortletPerform( HttpServletRequest request, Portlet portlet, ServletContext sc ) {
109 super( request, portlet, sc );
110 initParams = (Map<?,?>) request.getSession().getAttribute( SESSION_INITPARAM );
111 }
112
113 /**
114 * initializes the portlet by putting the init parameters to the users session. Even if this is
115 * not absolutly neccessary it simplifies a few things ...
116 *
117 */
118 public void init() {
119 if ( request.getSession().getAttribute( SESSION_INITPARAM ) == null ) {
120 Map<?, ?> map = portlet.getPortletConfig().getInitParameters();
121 request.getSession().setAttribute( SESSION_INITPARAM, map );
122 }
123 }
124
125 /**
126 * performs a GetFeatureInfo/GetFeature/DescribeCoverage request depending on the layer typs the
127 * request targets. If defiend the result will be transformed by a XSLT script to get a human
128 * readable out put.
129 *
130 * @throws PortalException
131 * @throws OGCWebServiceException
132 */
133 protected void doGetFeatureInfo()
134 throws PortalException, OGCWebServiceException {
135
136 StringBuffer sb = new StringBuffer( 20000 );
137
138 // a get feature info request only can be performed if a viewcontext
139 // has been initialized before by the MapWindowPortletAction
140 ViewContext vc = getCurrentViewContext( (String) initParams.get( INIT_MAPPORTLETID ) );
141 if ( vc == null ) {
142 throw new PortalException( "no valid view context available through users session" );
143 }
144
145 if ( parameter.get( "FILAYERS_ALL" ) == null ) {
146
147 String tmp = parameter.get( PARAM_FILAYERS );
148 if ( tmp == null || tmp.length() == 0 ) {
149 throw new PortalException( "at least one layer/featuretype/coverage must be set" );
150 }
151
152 String[] layers = StringTools.toArray( tmp, ",", true );
153
154 setCurrentFILayer( layers );
155
156 // synchronize list of visible layer and BBOX with the users view context
157 // because maybe the user had changed the visible layers before performing
158 // a GetFeatureInfo request
159 updateContext();
160
161 Layer layer = null;
162 Layer former = null;
163 List<Layer> layerList = new ArrayList<Layer>( 50 );
164
165 // performe a feature info request every time the hosting
166 // server changes. This is required because maybe a user has
167 // selected two or more layers hosted by different OWS
168 for ( int i = 0; i < layers.length; i++ ) {
169 former = layer;
170 layer = vc.getLayerList().getLayer( layers[i], null );
171 if ( i > 0 ) {
172 if ( layer.getServer().equals( former.getServer() ) ) {
173 layerList.add( layer );
174 } else {
175 sb.append( perform( layerList, vc ) );
176 layerList.clear();
177 layerList.add( layer );
178 }
179 } else {
180 layerList.add( layer );
181 }
182 }
183 sb.append( perform( layerList, vc ) );
184 } else {
185 handleFeatureInfoForAllLayers( vc, sb );
186 }
187
188 vc.getGeneral().getExtension().setMode( "FEATUREINFO" );
189
190 setCurrentMapContext( vc, (String) initParams.get( INIT_MAPPORTLETID ) );
191
192 // the result will be available through the forwarded request as well as
193 // through the users session (the portal frontend deciceds what behavior
194 // it should have)
195 request.setAttribute( "HTML", sb.toString() );
196 request.getSession().setAttribute( "HTML", sb.toString() );
197
198 }
199
200 /**
201 *
202 * @param vc
203 * @param sb
204 * @throws OGCWebServiceException
205 * @throws PortalException
206 */
207 private void handleFeatureInfoForAllLayers( ViewContext vc, StringBuffer sb )
208 throws OGCWebServiceException, PortalException {
209 String[] layers = findCurrentFILayers();
210 // synchronize list of visible layer and BBOX with the users view context
211 // because maybe the user had changed the visible layers before performing
212 // a GetFeatureInfo request
213 updateContext();
214
215 Layer layer = null;
216 Layer former = null;
217 List<Layer> layerList = new ArrayList<Layer>( 50 );
218
219 // performe a feature info request every time the hosting
220 // server changes. This is required because maybe a user has
221 // selected two or more layers hosted by different OWS
222 for ( int i = 0; i < layers.length; i++ ) {
223 former = layer;
224 layer = vc.getLayerList().getLayer( layers[i], null );
225 if ( i > 0 ) {
226 if ( layer.getServer().equals( former.getServer() ) ) {
227 layerList.add( layer );
228 } else {
229 String result = perform( layerList, vc );
230 if ( result != null ) {
231 sb.append( perform( layerList, vc ) );
232 }
233 layerList.clear();
234 layerList.add( layer );
235 }
236 } else {
237 layerList.add( layer );
238 }
239 }
240 String result = perform( layerList, vc );
241 if ( result != null ) {
242 sb.append( result );
243 }
244 }
245
246 /**
247 * sets the name of the the layers that are activated for feature info requests in the uses WMC
248 *
249 * @param fiLayer
250 */
251 void setCurrentFILayer( String[] fiLayer ) {
252
253 List<String> list = Arrays.asList( fiLayer );
254 list = new ArrayList<String>( list );
255
256 ViewContext vc = getCurrentViewContext( (String) initParams.get( INIT_MAPPORTLETID ) );
257 LayerList layerList = vc.getLayerList();
258 Layer[] layers = layerList.getLayers();
259 for ( int i = 0; i < layers.length; i++ ) {
260 if ( list.contains( layers[i].getName() ) ) {
261 layers[i].getExtension().setSelectedForQuery( true );
262 } else {
263 layers[i].getExtension().setSelectedForQuery( false );
264 }
265 }
266
267 }
268
269 /**
270 * distributes the performance of the feature info requests depending on the requested service
271 * to a specialized method
272 *
273 * @param layerList
274 * list of context layers provided by the same OWS
275 * @param vc
276 * @return formated feature info result
277 * @throws OGCWebServiceException
278 * @throws PortalException
279 */
280 protected String perform( List<Layer> layerList, ViewContext vc )
281 throws OGCWebServiceException, PortalException {
282
283 if ( layerList.size() > 0 ) {
284 Layer layer = layerList.get( 0 );
285 if ( layer.getServer().getService().indexOf( "WMS" ) > -1 ) {
286 return performWMS( layerList, vc );
287 } else if ( layer.getServer().getService().indexOf( "WFS" ) > -1 ) {
288 throw new PortalException( "WFS is not supported as feature info target yet!" );
289 } else if ( layer.getServer().getService().indexOf( "WCS" ) > -1 ) {
290 throw new PortalException( "WCS is not supported as feature info target yet!" );
291 } else {
292 throw new PortalException( "not supported service: " + layer.getServer().getService()
293 + " as feature info target!" );
294 }
295 }
296 return "";
297
298 }
299
300 /**
301 * performes a GetFeatureInfo request on a WMS and transforms the result using a XSLT script
302 * defined in the portlets init-parameters
303 *
304 * @param layerList
305 * list of context layers provided by the same WMS
306 * @param vc
307 * @return formated get feature info result
308 */
309 private String performWMS( List<Layer> layerList, ViewContext vc )
310 throws OGCWebServiceException, PortalException {
311
312 GetFeatureInfo gfi = createGetFeatureInfoRequest( layerList, vc );
313 Layer layer = layerList.get( 0 );
314
315 URL url = OWSUtils.getHTTPGetOperationURL( layer.getServer().getCapabilities(), GetFeatureInfo.class );
316 String href = OWSUtils.validateHTTPGetBaseURL( url.toExternalForm() );
317 StringBuffer sb = new StringBuffer( 1000 );
318 sb.append( href ).append( gfi.getRequestParameter() );
319 // If a user is registered to the portal use his name and password to
320 // perform GetFeatureInfo request because maybe the connected server
321 // is hidden behind a owsProxy
322 // TODO
323 // read informations too which server user name and password shall be send
324 // TODO
325 // replace sending user name and password by a sessionID
326 if ( !"anon".equals( request.getAttribute( "$U$" ) ) ) {
327 sb.append( "&user=" ).append( request.getAttribute( "$U$" ) );
328 request.removeAttribute( "$U$" );
329 }
330 if ( request.getAttribute( "$P$" ) != null ) {
331 sb.append( "&password=" ).append( request.getAttribute( "$P$" ) );
332 request.removeAttribute( "$P$" );
333 }
334 LOG.logDebug( "info request: ", sb );
335
336 XMLFragment frag = null;
337 try {
338 frag = new XMLFragment( new URL( sb.toString() ) );
339 if ( LOG.getLevel() == ILogger.LOG_DEBUG ) {
340 LOG.logDebug( "GetFeatureInfo result: \n", frag.getAsPrettyString() );
341 }
342 } catch ( Exception e ) {
343 e.printStackTrace();
344 throw new OGCWebServiceException( "could not perform GetFeatureInfo request " + e.getMessage() );
345 }
346
347 LOG.logDebug( "get xslt for:", href );
348 String path = getPathToXSLTScript( layer, href );
349 LOG.logDebug( "selected path to xslt: ", path );
350
351 path = sc.getRealPath( "/WEB-INF/" + path );
352
353 Source xmlSource = new DOMSource( frag.getRootElement() );
354
355 Source xslSource = new StreamSource( new File( path ) );
356 StringWriter sw = new StringWriter( 20000 );
357
358 Map<String, String> param = new HashMap<String, String>();
359 param.put( "SERVER", layer.getServer().getTitle() );
360 String s = null;
361 try {
362 XSLTDocument.transform( xmlSource, xslSource, new StreamResult( sw ), null, param );
363 s = sw.getBuffer().toString();
364 sw.close();
365 } catch ( Exception e ) {
366 throw new PortalException( "could not transform GetFeatureInfo result of request: " + sb + " - "
367 + StringTools.stackTraceToString( e ) );
368 }
369
370 return s;
371
372 }
373
374 /**
375 * returns the path to the xslt script to be used for transforming info data
376 *
377 * @param layer
378 * @param server
379 * @return the path to the xslt script to be used for transforming info data
380 * @throws PortalException
381 */
382 private String getPathToXSLTScript( Layer layer, String server )
383 throws PortalException {
384 StringBuffer sbb = new StringBuffer( 1000 );
385 sbb.append( INIT_PATHTOXSLT ).append( ';' ).append( server ).append( ';' );
386 sbb.append( layer.getServer().getService() );
387 String path = (String) initParams.get( sbb.toString() );
388 if ( path == null ) {
389 sbb.delete( 0, sbb.length() );
390 sbb.append( INIT_PATHTOXSLT ).append( ';' );
391 sbb.append( layer.getServer().getService() );
392 path = (String) initParams.get( sbb.toString() );
393 }
394 if ( path == null ) {
395 path = (String) initParams.get( INIT_PATHTOXSLT );
396 }
397 if ( path == null ) {
398 // for being compliant with an error in init-param that
399 // has been used in several instances
400 sbb = new StringBuffer( 1000 );
401 sbb.append( "pathToXLST" ).append( ';' ).append( server ).append( ';' );
402 sbb.append( layer.getServer().getService() );
403 path = (String) initParams.get( sbb.toString() );
404 if ( path == null ) {
405 sbb.delete( 0, sbb.length() );
406 sbb.append( "pathToXLST" ).append( ';' );
407 sbb.append( layer.getServer().getService() );
408 path = (String) initParams.get( sbb.toString() );
409 }
410 if ( path == null ) {
411 path = (String) initParams.get( "pathToXLST" );
412 }
413 }
414
415 if ( path == null ) {
416 LOG.logDebug( "initParams: ", initParams );
417 throw new PortalException( "no XSLT script defined for processing GetFeatureInfo " + "response of:"
418 + INIT_PATHTOXSLT + ';' + server + ';' + layer.getServer().getService() );
419 }
420
421 return path;
422 }
423
424 /**
425 * creates a GetFeatureInfo request from the requested target layers depending on the current
426 * view context
427 *
428 * @param layerList
429 * @param vc
430 * @return GetFeatureInfo request
431 */
432 private GetFeatureInfo createGetFeatureInfoRequest( List<Layer> layerList, ViewContext vc ) {
433
434 String[] fiLayers = new String[layerList.size()];
435 GetMap.Layer[] gmLayers = new GetMap.Layer[layerList.size()];
436 for ( int i = 0; i < gmLayers.length; i++ ) {
437 Layer layer = layerList.get( i );
438 fiLayers[i] = layer.getName();
439 gmLayers[i] = new GetMap.Layer( layer.getName(), layer.getStyleList().getCurrentStyle().getName() );
440 }
441
442 Layer layer = layerList.get( 0 );
443 Point[] pt = vc.getGeneral().getBoundingBox();
444 String srs = pt[0].getCoordinateSystem().getIdentifier();
445 int width = vc.getGeneral().getWindow().width;
446 int height = vc.getGeneral().getWindow().height;
447 Envelope bbox = GeometryFactory.createEnvelope( pt[0].getX(), pt[0].getY(), pt[1].getX(), pt[1].getY(),
448 pt[0].getCoordinateSystem() );
449
450 // create GetMap request being mandatory part of the GetFeatureInfo request
451 GetMap gm = GetMap.create( layer.getServer().getVersion(), "id", gmLayers, null, null,
452 layer.getFormatList().getCurrentFormat().getName(), width, height, srs, bbox, false,
453 Color.WHITE, null, null, null, null, null );
454
455 int x = Integer.parseInt( parameter.get( PARAM_X ) );
456 int y = Integer.parseInt( parameter.get( PARAM_Y ) );
457 java.awt.Point point = new java.awt.Point( x, y );
458 int featureCount = Integer.parseInt( parameter.get( PARAM_FEATURECOUNT ) );
459 GetFeatureInfo gfi = GetFeatureInfo.create( layer.getServer().getVersion(), "id", fiLayers, gm,
460 "application/vnd.ogc.gml", featureCount, point, null, null, null );
461
462 return gfi;
463 }
464
465 /**
466 * sets the name of the the layers that are activated for feature info requests in the uses WMC
467 *
468 * @return layers involved in feature info
469 */
470 String[] findCurrentFILayers() {
471
472 List<String> list = new ArrayList<String>();
473
474 ViewContext vc = getCurrentViewContext( (String) initParams.get( INIT_MAPPORTLETID ) );
475
476 LayerList layerList = vc.getLayerList();
477 Layer[] layers = layerList.getLayers();
478 for ( int i = 0; i < layers.length; i++ ) {
479
480 if ( !layers[i].isHidden() && layers[i].isQueryable() ) {
481 layers[i].getExtension().setSelectedForQuery( true );
482 list.add( layers[i].getName() );
483 }
484 }
485 return list.toArray( new String[list.size()] );
486 }
487
488 }