/*---------------- FILE HEADER ------------------------------------------
This file is part of deegree.
Copyright (C) 2001-2012 by:
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:
lat/lon GmbH
Aennchenstr. 19
53177 Bonn
Germany
E-Mail: info@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.igeo.commands.model;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import net.sf.ehcache.Cache;
import org.deegree.datatypes.QualifiedName;
import org.deegree.datatypes.values.TypedLiteral;
import org.deegree.framework.log.ILogger;
import org.deegree.framework.log.LoggerFactory;
import org.deegree.framework.util.KVP2Map;
import org.deegree.framework.util.StringTools;
import org.deegree.igeo.config.OnlineResourceType;
import org.deegree.igeo.config.WMSDatasourceType;
import org.deegree.igeo.config.ServiceDatasourceType.CapabilitiesURL;
import org.deegree.igeo.dataadapter.DataAccessException;
import org.deegree.igeo.i18n.Messages;
import org.deegree.igeo.mapmodel.AuthenticationInformation;
import org.deegree.igeo.mapmodel.Datasource;
import org.deegree.igeo.mapmodel.LayerGroup;
import org.deegree.igeo.mapmodel.MapModel;
import org.deegree.igeo.mapmodel.WMSDatasource;
import org.deegree.kernel.AbstractCommand;
import org.deegree.model.Identifier;
import org.deegree.ogcwebservices.OWSUtils;
import org.deegree.ogcwebservices.getcapabilities.GetCapabilities;
import org.deegree.ogcwebservices.getcapabilities.MetadataURL;
import org.deegree.ogcwebservices.wms.capabilities.Layer;
import org.deegree.ogcwebservices.wms.capabilities.LegendURL;
import org.deegree.ogcwebservices.wms.capabilities.Style;
import org.deegree.ogcwebservices.wms.capabilities.WMSCapabilities;
import org.deegree.owscommon_new.DomainType;
import org.deegree.owscommon_new.HTTP;
import org.deegree.owscommon_new.Operation;
import org.deegree.owscommon_new.OperationsMetadata;
import org.deegree.owscommon_new.ServiceIdentification;
/**
* <code>AddWMSLayerCommand</code> adds a WMS as a new layer to the map model
*
* @author <a href="mailto:wanhoff@lat-lon.de">Jeronimo Wanhoff</a>
* @author <a href="mailto:buesching@lat-lon.de">Lyn Buesching</a>
* @author last edited by: $Author$
*
* @version $Revision$, $Date$
*
*/
public class AddWMSLayerCommand extends AbstractCommand {
private static final ILogger LOG = LoggerFactory.getLogger( AddWMSLayerCommand.class );
public static final QualifiedName commandName = new QualifiedName( "add layer from WMS command" );
private MapModel mapModel;
private String layerName = UUID.randomUUID().toString();
private String layerTitle = "";
private String layerAbstract = "";
private String nameDS = "";
private double minScaleDenominator = 0;
private double maxScaleDenominator = 9E99;
private URL capabilitiesURL;
private WMSCapabilities wmsCapabilities;
private String baseRequest;
private List<org.deegree.igeo.config.LayerType.MetadataURL> metadataURLs = new ArrayList<org.deegree.igeo.config.LayerType.MetadataURL>();
private org.deegree.igeo.mapmodel.Layer newLayer;
private boolean performed = false;
private boolean allowSwapAxis;
/**
* @param mapModel
* the mapModel to add the new layer
* @param wmsCapabilities
* @param layerTitle
* the title of the layer
* @param allowSwapAxsisOrder
* if <code>true</code> axis order of bounding box will be swapped if a WMS 1.3 GetMap request shall be
* created
*/
private AddWMSLayerCommand( MapModel mapModel, WMSCapabilities wmsCapabilities, String layerTitle,
boolean allowSwapAxis ) {
setMetaInformation( wmsCapabilities );
this.allowSwapAxis = allowSwapAxis;
this.mapModel = mapModel;
try {
String s = OWSUtils.getHTTPGetOperationURL( wmsCapabilities, GetCapabilities.class ).toExternalForm();
s = OWSUtils.validateHTTPGetBaseURL( s );
this.capabilitiesURL = new URL( s + "version=" + wmsCapabilities.getVersion()
+ "&service=WMS&request=GetCapabilities" );
} catch ( MalformedURLException e ) {
throw new DataAccessException( e );
}
this.layerTitle = layerTitle;
}
/**
* @param mapModel
* the mapModel to add the new layer
* @param wmsCapabilities
* @param layerName
* the name of the new layer
* @param layerTitle
* the title of the new layer
* @param layerAbstract
* abstract of the new layer
* @param nameDS
* the name of the datasource
* @param minScaleDenominator
* the minScaleDenominator of the new datasource
* @param maxScaleDenominator
* the maxScaleDenominator of the new datasource
* @param selectedStyleLayers
* the selected layers with style information
* @param baseRequest
* baseRequest of the datasource
* @param allowSwapAxsisOrder
* if <code>true</code> axis order of bounding box will be swapped if a WMS 1.3 GetMap request shall be
* created
*/
public AddWMSLayerCommand( MapModel mapModel, WMSCapabilities wmsCapabilities, String layerName, String layerTitle,
String layerAbstract, String nameDS, double minScaleDenominator,
double maxScaleDenominator, Map<Layer, String> selectedStyleLayers, String baseRequest,
boolean allowSwapAxis ) {
this( mapModel, wmsCapabilities, layerTitle, allowSwapAxis );
this.layerName = layerName;
this.layerAbstract = layerAbstract;
this.nameDS = nameDS;
this.minScaleDenominator = minScaleDenominator;
this.maxScaleDenominator = maxScaleDenominator;
setMetadataURLs( selectedStyleLayers );
this.baseRequest = baseRequest;
}
/**
* @param mapModel
* the mapModel to add the new layer
* @param wmsCapabilities
* the capabilities document of the WMS to add as new layer
* @param selectedLayers
* the selected layers
* @param layerTitle
* the title of the layer
* @param allowSwapAxsisOrder
* if <code>true</code> axis order of bounding box will be swapped if a WMS 1.3 GetMap request shall be
* created
*/
public AddWMSLayerCommand( MapModel mapModel, WMSCapabilities wmsCapabilities, List<Layer> selectedLayers,
String layerTitle, boolean allowSwapAxis ) {
this( mapModel, wmsCapabilities, layerTitle, allowSwapAxis );
setMetadataURLs( selectedLayers );
String layers = null;
for ( Layer layer : selectedLayers ) {
if ( layers != null && layers.length() > 0 ) {
layers = StringTools.concat( 300, layers, ',', layer.getName() );
} else {
layers = layer.getName();
}
}
this.baseRequest = StringTools.concat( 500, "REQUEST=GetMap&TRANSPARENT=TRUE", "&FORMAT=",
getFormat( wmsCapabilities ), "&LAYERS=", layers );
}
/**
* @param mapModel
* the mapModel to add the new layer
* @param wmsCapabilities
* the capabilities document of the WMS to add as new layer
* @param selectedLayerStyles
* the selected layers with style information
* @param layerTitle
* the title of the layer
* @param allowSwapAxsisOrder
* if <code>true</code> axis order of bounding box will be swapped if a WMS 1.3 GetMap request shall be
* created
*/
public AddWMSLayerCommand( MapModel mapModel, WMSCapabilities wmsCapabilities,
Map<Layer, String> selectedLayerStyles, String layerTitle, boolean allowSwapAxis ) {
this( mapModel, wmsCapabilities, layerTitle, allowSwapAxis );
setMetadataURLs( selectedLayerStyles );
this.baseRequest = StringTools.concat( 500, "REQUEST=GetMap&TRANSPARENT=TRUE", "&FORMAT=",
getFormat( wmsCapabilities ), getLayerStyleSection( selectedLayerStyles ) );
}
/**
* @param mapModel
* the mapModel to add the new layer
* @param wmsCapabilities
* the capabilities document of the WMS to add as new layer
* @param selectedLayerStyles
* the selected layers with style information
* @param layerTitle
* the title of the layer
* @param format
* the desired format of the map
* @param transparency
* indicates, if the background should be transparent
* @param allowSwapAxsisOrder
* if <code>true</code> axis order of bounding box will be swapped if a WMS 1.3 GetMap request shall be
* created
*/
public AddWMSLayerCommand( MapModel mapModel, WMSCapabilities wmsCapabilities,
Map<Layer, String> selectedLayerStyles, String layerTitle, String format,
boolean transparency, boolean allowSwapAxis ) {
this( mapModel, wmsCapabilities, layerTitle, allowSwapAxis );
setMetadataURLs( selectedLayerStyles );
this.baseRequest = StringTools.concat( 500, "REQUEST=GetMap&", "&FORMAT=", format, "&TRANSPARENT=",
( "" + transparency ).toUpperCase(),
getLayerStyleSection( selectedLayerStyles ) );
}
/*
* (non-Javadoc)
*
* @see org.deegree.kernel.Command#execute()
*/
public void execute()
throws Exception {
AuthenticationInformation authenticationInformation = null;
Cache cache = null;
String crs = findCommonCRS();
if ( crs == null ) {
throw new DataAccessException( Messages.get( "$DG10089", this.baseRequest ) );
}
List<Datasource> datasources = new ArrayList<Datasource>( 1 );
// create the wms datasource
WMSDatasourceType dsType = new WMSDatasourceType();
dsType.setName( nameDS );
dsType.setMinScaleDenominator( minScaleDenominator );
dsType.setMaxScaleDenominator( maxScaleDenominator );
dsType.setEditable( false );
dsType.setQueryable( true );
dsType.setLazyLoading( true );
dsType.setSupportToolTips( false );
dsType.setBaseRequest( this.baseRequest );
dsType.setNativeCRS( crs );
dsType.setServiceVersion( wmsCapabilities.getVersion() );
dsType.setAllowSwapAxis( allowSwapAxis );
CapabilitiesURL cu = new CapabilitiesURL();
OnlineResourceType ort = new OnlineResourceType();
ort.setHref( this.capabilitiesURL.toExternalForm() );
cu.setOnlineResource( ort );
dsType.setCapabilitiesURL( cu );
datasources.add( new WMSDatasource( dsType, authenticationInformation, cache ) );
Identifier id = new Identifier( this.layerName );
int i = 0;
String tmp = this.layerTitle;
while ( mapModel.exists( id ) ) {
tmp = this.layerTitle + "_" + i;
id = new Identifier( this.layerName + "_" + i++ );
}
// create a new layer
newLayer = new org.deegree.igeo.mapmodel.Layer( mapModel, id, tmp, this.layerAbstract, datasources,
this.metadataURLs );
newLayer.setMinScaleDenominator( minScaleDenominator );
newLayer.setMaxScaleDenominator( maxScaleDenominator );
newLayer.setVisible( true );
newLayer.setEditable( false );
URL legendURL = getLegendURL();
LOG.logDebug( "legend URL: ", legendURL );
try {
newLayer.getCurrentStyle().setLegendURL( legendURL );
} catch ( Exception e ) {
LOG.logWarning( "could not set legend URL", e );
}
// and add the layer to the mapModel
if ( mapModel.getLayerGroups().size() == 0 ) {
LayerGroup layerGroup = new LayerGroup( mapModel, new Identifier(), "LayerGroup", "" );
mapModel.insert( layerGroup, null, null, false );
}
mapModel.insert( newLayer, mapModel.getLayerGroups().get( 0 ), null, false );
performed = true;
fireCommandProcessedEvent();
}
/**
* @return
*/
private String findCommonCRS() {
String crs = mapModel.getCoordinateSystem().getPrefixedName();
Map<String, String> map = KVP2Map.toMap( this.baseRequest );
String[] layers = StringTools.toArray( map.get( "LAYERS" ), ",", true );
List<String[]> layersSRS = new ArrayList<String[]>();
boolean supportsMapSRS = true;
for ( String layerName : layers ) {
Layer layer = wmsCapabilities.getLayer( layerName );
String[] srs = layer.getSrs();
boolean tmp = false;
for ( String string : srs ) {
if ( string.equalsIgnoreCase( crs ) ) {
tmp = true;
break;
}
}
if ( !tmp ) {
supportsMapSRS = false;
}
layersSRS.add( srs );
}
if ( !supportsMapSRS ) {
// find common crs because map model CRS is not supported by all layers
crs = null;
String[] tmp1 = layersSRS.get( 0 );
for ( int i = 0; i < tmp1.length; i++ ) {
int k = 0;
for ( int j = 1; j < layersSRS.size(); j++ ) {
String[] tmp2 = layersSRS.get( j );
for ( int l = 0; l < tmp2.length; l++ ) {
if ( tmp2[l].equalsIgnoreCase( tmp1[i] ) ) {
k++;
break;
}
}
}
// if each layer supports srs tmp1[i] k must be equal to (layersSRS.size() - 1)
if ( k == layersSRS.size() - 1 ) {
crs = tmp1[i];
break;
}
}
}
return crs;
}
private URL getLegendURL() {
Map<String, String> map = KVP2Map.toMap( baseRequest );
String layerName = map.get( "LAYERS" );
LOG.logDebug( "layer: ", layerName );
URL legendURL = null;
// a layer name contains a ',' it is a layer combined from more than one layer, so it will
// not be
// possible to read a legend URL from WMS capabilities
if ( layerName != null && !layerName.contains( "," ) ) {
Layer layer = wmsCapabilities.getLayer( layerName );
if ( layer != null ) {
String s = map.get( "STYLES" );
if ( s == null || s.trim().length() == 0 ) {
s = "default";
}
Style style = layer.getStyleResource( s );
if ( style != null ) {
LegendURL[] legendURLs = style.getLegendURL();
if ( legendURLs != null && legendURLs.length > 0 ) {
legendURL = legendURLs[0].getOnlineResource();
}
}
}
if ( legendURL == null ) {
QualifiedName getLegendGraphic = new QualifiedName( "GetLegendGraphic" );
Operation operation = wmsCapabilities.getOperationMetadata().getOperation( getLegendGraphic );
if ( operation != null ) {
legendURL = ( (HTTP) operation.getDCP().get( 0 ) ).getGetOnlineResources().get( 0 );
try {
String s = legendURL.toURI().toASCIIString();
if ( !s.endsWith( "?" ) ) {
s = s + "?";
}
legendURL = new URL( s
+ "request=GetLegendGraphic&format=image/png&width=40&height=40&version="
+ wmsCapabilities.getVersion() + "&layer=" + layerName + "&style="
+ map.get( "STYLES" ) );
} catch ( Exception e ) {
LOG.logWarning( "could not create legend URL", e );
}
}
}
}
return legendURL;
}
/*
* (non-Javadoc)
*
* @see org.deegree.kernel.Command#getName()
*/
public QualifiedName getName() {
return commandName;
}
/*
* (non-Javadoc)
*
* @see org.deegree.kernel.Command#getResult()
*/
public Object getResult() {
return null;
}
/*
* (non-Javadoc)
*
* @see org.deegree.kernel.Command#isUndoSupported()
*/
public boolean isUndoSupported() {
return true;
}
/*
* (non-Javadoc)
*
* @see org.deegree.kernel.Command#undo()
*/
public void undo()
throws Exception {
if ( performed ) {
mapModel.remove( newLayer );
performed = false;
}
}
/**
* extract the metainformation out of the wms capabilities document
*
* @param wmsCapabilities
* the capabilities of the WMS
*/
private void setMetaInformation( WMSCapabilities wmsCapabilities ) {
this.wmsCapabilities = wmsCapabilities;
ServiceIdentification service = wmsCapabilities.getServiceIdentification();
if ( service != null ) {
if ( this.layerTitle == null || !( this.layerTitle.length() > 0 ) ) {
this.layerTitle = service.getTitle();
}
this.nameDS = service.getTitle();
}
}
/**
* @param wmsCapabilities
* the capabilities of the WMS
* @return the format declared at first position
*/
private String getFormat( WMSCapabilities wmsCapabilities ) {
OperationsMetadata om = wmsCapabilities.getOperationMetadata();
Operation op = om.getOperation( new QualifiedName( "GetMap" ) );
if ( op == null ) {
op = om.getOperation( new QualifiedName( "map" ) );
}
if ( op != null ) {
DomainType parameter = (DomainType) op.getParameter( new QualifiedName( "Format" ) );
List<TypedLiteral> values = parameter.getValues();
return values.get( 0 ).getValue();
}
return null;
}
/**
* @param selectedLayerStyles
* the map containing all selected layers with assigned Styles
* @return the kvp encoded layer/style section for the GetMap request
*/
private String getLayerStyleSection( Map<Layer, String> selectedLayerStyles ) {
String layers = null;
String styles = null;
for ( Layer layer : selectedLayerStyles.keySet() ) {
String styleName = selectedLayerStyles.get( layer );
if ( layers != null && layers.length() > 0 ) {
layers = StringTools.concat( 300, layers, ',', layer.getName() );
styles = StringTools.concat( 300, styles, ',', styleName );
} else {
layers = layer.getName();
styles = styleName;
}
}
return StringTools.concat( 500, "&LAYERS=", layers, "&STYLES=", styles );
}
/**
* @param layers
* the layer to extract the metadataURLs
*/
private void setMetadataURLs( List<Layer> layers ) {
for ( Layer layer : layers ) {
MetadataURL[] urls = layer.getMetadataURL();
for ( int i = 0; i < urls.length; i++ ) {
org.deegree.igeo.config.LayerType.MetadataURL mu = new org.deegree.igeo.config.LayerType.MetadataURL();
OnlineResourceType ort = new OnlineResourceType();
ort.setHref( urls[i].getOnlineResource().toExternalForm() );
mu.setOnlineResource( ort );
this.metadataURLs.add( mu );
}
}
}
/**
* @param selectedLayerStyles
*/
private void setMetadataURLs( Map<Layer, String> selectedLayerStyles ) {
for ( Layer layer : selectedLayerStyles.keySet() ) {
MetadataURL[] urls = layer.getMetadataURL();
for ( int i = 0; i < urls.length; i++ ) {
org.deegree.igeo.config.LayerType.MetadataURL mu = new org.deegree.igeo.config.LayerType.MetadataURL();
OnlineResourceType ort = new OnlineResourceType();
ort.setHref( urls[i].getOnlineResource().toExternalForm() );
mu.setOnlineResource( ort );
this.metadataURLs.add( mu );
}
}
}
}