org.kalypso.ogc.gml.wms.provider.images.AbstractDeegreeImageProvider.java Source code

Java tutorial

Introduction

Here is the source code for org.kalypso.ogc.gml.wms.provider.images.AbstractDeegreeImageProvider.java

Source

/*----------------    FILE HEADER KALYPSO ------------------------------------------
 *
 *  This file is part of kalypso.
 *  Copyright (C) 2004 by:
 *
 *  Technical University Hamburg-Harburg (TUHH)
 *  Institute of River and coastal engineering
 *  Denickestrae 22
 *  21073 Hamburg, Germany
 *  http://www.tuhh.de/wb
 *
 *  and
 *
 *  Bjoernsen Consulting Engineers (BCE)
 *  Maria Trost 3
 *  56070 Koblenz, Germany
 *  http://www.bjoernsen.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:
 *
 *  E-Mail:
 *  belger@bjoernsen.de
 *  schlienger@bjoernsen.de
 *  v.doemming@tuhh.de
 *
 *  ---------------------------------------------------------------------------*/
package org.kalypso.ogc.gml.wms.provider.images;

import java.awt.Image;
import java.awt.Point;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.deegree.datatypes.QualifiedName;
import org.deegree.model.metadata.iso19115.OnlineResource;
import org.deegree.ogcwebservices.OGCWebServiceException;
import org.deegree.ogcwebservices.OGCWebServiceRequest;
import org.deegree.ogcwebservices.wms.RemoteWMService;
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.ogcwebservices.wms.operation.GetFeatureInfo;
import org.deegree.ogcwebservices.wms.operation.GetFeatureInfoResult;
import org.deegree.ogcwebservices.wms.operation.GetMap;
import org.deegree.ogcwebservices.wms.operation.GetMapResult;
import org.deegree.owscommon_new.DCP;
import org.deegree.owscommon_new.HTTP;
import org.deegree.owscommon_new.Operation;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.MultiStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.resource.ImageDescriptor;
import org.kalypso.contribs.eclipse.core.runtime.StatusUtilities;
import org.kalypso.contribs.eclipse.core.variables.VariableUtils;
import org.kalypso.ogc.core.exceptions.ExceptionCode;
import org.kalypso.ogc.core.exceptions.OWSException;
import org.kalypso.ogc.core.utils.OWSUtilities;
import org.kalypso.ogc.gml.wms.deegree.DeegreeWMSUtilities;
import org.kalypso.ogc.gml.wms.loader.ICapabilitiesLoader;
import org.kalypso.ogc.gml.wms.utils.KalypsoWMSUtilities;
import org.kalypso.ui.KalypsoGisPlugin;
import org.kalypso.ui.internal.i18n.Messages;
import org.kalypsodeegree.model.geometry.GM_Envelope;

/**
 * @author Holger Albert
 */
public abstract class AbstractDeegreeImageProvider implements IKalypsoImageProvider {
    private static final ImageCache m_legendCache = new ImageCache();

    /**
     * This variable stores the name of the theme.
     */
    private String m_themeName;

    /**
     * The LAYERS property of the source.
     */
    private String[] m_layers;

    /**
     * The STYLES property of the source.
     */
    private String[] m_styles;

    /**
     * The service.
     */
    private String m_service;

    /**
     * This variable stores the client coordinate system.
     */
    private String m_localSRS;

    /**
     * This is the content of a SLD, if we want the server to render the image with a specific style.
     */
    private String m_sldBody;

    /**
     * This variable stores the WMS service or is null.
     */
    private RemoteWMService m_wms;

    /**
     * The negotiated coordinate system.
     */
    private String m_negotiatedSRS;

    private URL m_getMapUrl;

    private String m_lastRequest;

    private GetMap m_lastGetMap;

    private WMSCapabilities m_capabilities;

    private String m_providerID;

    @Override
    public void init(final String providerID, final String themeName, final String[] layers, final String[] styles,
            final String service, final String localSRS, final String sldBody) {
        m_providerID = providerID;
        m_themeName = themeName;
        m_layers = layers;
        m_styles = styles;
        m_service = service;
        m_localSRS = localSRS;
        m_sldBody = sldBody;

        m_wms = null;
        m_negotiatedSRS = null;
        m_getMapUrl = null;
        m_lastRequest = null;
        m_lastGetMap = null;
    }

    @Override
    public synchronized IStatus checkInitialize(final IProgressMonitor monitor) {
        if (m_service == null)
            return new Status(IStatus.ERROR, KalypsoGisPlugin.getId(),
                    Messages.getString("org.kalypso.ogc.gml.wms.provider.images.AbstractDeegreeImageProvider.1")); //$NON-NLS-1$

        try {
            final WMSCapabilities wmsCaps = loadCapabilities();

            /* Initialize the remote WMS, if it is not already done. */
            return initializeRemoteWMS(wmsCaps);
        } catch (final CoreException e) {
            e.printStackTrace();
            return new Status(IStatus.ERROR, KalypsoGisPlugin.PLUGIN_ID,
                    Messages.getString("AbstractDeegreeImageProvider.0"), e); //$NON-NLS-1$
        }
    }

    private WMSCapabilities loadCapabilities() throws CoreException {
        if (m_capabilities != null)
            return m_capabilities;

        /* Create the service URL. */
        final URL serviceURL = parseServiceUrl(m_service);

        /* Init the loader. */
        final ICapabilitiesLoader loader = createCapabilitiesLoader();
        m_capabilities = loader.load(serviceURL, new NullProgressMonitor());
        return m_capabilities;
    }

    @Override
    public Image getImage(final int width, final int height, final GM_Envelope bbox) throws CoreException {
        if (m_wms == null)
            return null;

        return loadImage(width, height, bbox);
    }

    @Override
    public synchronized ImageDescriptor getLegendGraphic(final String layer, final String style) {
        if (m_capabilities == null)
            return null;

        /* Get the URL of the legend. */
        final LegendURL legendURL = findLegendURL(layer, style);
        if (legendURL == null)
            return null;

        /* Get the real URL. */
        final URL onlineResource = legendURL.getOnlineResource();

        return m_legendCache.getImage(onlineResource);
    }

    /**
     * This function initializes the WMS and loads the Capabilities.
     */
    private IStatus initializeRemoteWMS(final WMSCapabilities wmsCaps) {
        if (m_wms != null)
            return Status.OK_STATUS;

        /* Ask for the srs. */
        m_negotiatedSRS = negotiateCRS(m_localSRS, wmsCaps, m_layers);

        /* Find some URLs. */
        m_getMapUrl = findGetMapURL(wmsCaps);

        /* Initialize the WMS. */
        m_wms = createRemoteService(wmsCaps);

        return Status.OK_STATUS;
    }

    private URL findGetMapURL(final WMSCapabilities wmsCaps) {
        final Operation operation = wmsCaps.getOperationMetadata().getOperation(new QualifiedName("GetMap")); //$NON-NLS-1$

        final List<DCP> dcps = operation.getDCP();
        for (final DCP dcp : dcps) {
            if (dcp instanceof HTTP) {
                final HTTP http = (HTTP) dcp;
                final List<OnlineResource> links = http.getLinks();
                if (links.size() > 0)
                    return links.get(0).getLinkage().getHref();
            }
        }

        return null;
    }

    /**
     * This function creates the remote service and returns it.
     * 
     * @param capabilities
     *          The capabilites for the remote service.
     * @return The remote service.
     */
    protected abstract RemoteWMService createRemoteService(WMSCapabilities capabilities);

    /**
     * This function parses a String into an URL to the WMS service.
     * 
     * @param service
     *          The String representation of the URL to the WMS service.
     * @return The URL to the WMS service.
     */
    private URL parseServiceUrl(final String service) throws CoreException {
        try {
            final String resolvedService = VariableUtils.resolveVariablesQuietly(service);
            return new URL(resolvedService);
        } catch (final MalformedURLException e) {
            throw new CoreException(StatusUtilities.statusFromThrowable(e,
                    Messages.getString("org.kalypso.ogc.gml.wms.provider.images.AbstractDeegreeImageProvider.3", //$NON-NLS-1$
                            service, e.getLocalizedMessage())));
        }
    }

    /**
     * This method tries to find a common spatial reference system (srs) for a given set of layers. If all layers
     * coorespond to the local crs the local crs is returned, otherwise the srs of the top layer is returned and the
     * client must choose one to transform it to the local coordinate system
     * 
     * @param localCRS
     *          The local spatial reference system.
     * @param capabilities
     *          The capabilites document of the web map service.
     * @param layerNames
     *          The layers that have to be matched to the local srs.
     * @return An array of possible coordiante systems.
     */
    private String negotiateCRS(final String localSRS, final WMSCapabilities wmsCapabilities,
            final String[] layers) {
        /* Match the local with the remote coordinate system. */
        final String[] crs = DeegreeWMSUtilities.negotiateCRS(localSRS, wmsCapabilities, layers);
        if (crs.length > 0)
            return crs[0];

        return localSRS;
    }

    @Override
    public synchronized GM_Envelope getFullExtent() {
        try {
            if (m_wms == null || m_layers == null)
                return null;

            return DeegreeWMSUtilities.getMaxExtent(m_layers, (WMSCapabilities) m_wms.getCapabilities(),
                    m_negotiatedSRS);
        } catch (final Exception ex) {
            KalypsoGisPlugin.getDefault().getLog().log(StatusUtilities.statusFromThrowable(ex,
                    Messages.getString("org.kalypso.ogc.gml.wms.provider.images.AbstractDeegreeImageProvider.5"))); //$NON-NLS-1$
        }

        return null;
    }

    private String getThemeName() {
        return m_themeName;
    }

    protected synchronized String getService() {
        return m_service;
    }

    private String getLocalSRS() {
        return m_localSRS;
    }

    private RemoteWMService getWms() {
        return m_wms;
    }

    private String getNegotiatedSRS() {
        return m_negotiatedSRS;
    }

    private final Image loadImage(final int width, final int height, final GM_Envelope bbox) throws CoreException {
        try {
            /* Reset the request. */
            m_lastRequest = null;
            m_lastGetMap = null;

            /* Check if nothing to request. */
            if (bbox == null)
                return null;

            /* If the some of the parameters has the wrong values, return null. */
            if (width == 0 || height == 0 || bbox.getWidth() == 0 || bbox.getHeight() == 0)
                return null;

            /* Work locally against a copy of the reference, because it may change any time... */
            final RemoteWMService remoteWMS = getWms();
            if (remoteWMS == null)
                return null;

            /* Create the GetMap request. */
            final GetMap request = DeegreeWMSUtilities.createGetMapRequest(
                    (WMSCapabilities) remoteWMS.getCapabilities(), getNegotiatedSRS(), getThemeName(), m_layers,
                    m_styles, width, height, bbox, getLocalSRS(), m_sldBody);

            /* HACK: Some wms servers seems to be wrongly configured. */
            String getMapUrl = m_getMapUrl.toExternalForm();
            final int indexOf = getMapUrl.indexOf("?"); //$NON-NLS-1$
            if (indexOf > -1)
                getMapUrl = getMapUrl.substring(0, indexOf);

            /* HACK: The deegree class GetMap returns two different types of requests (regarding the wms version). */
            /* HACK: One with & at the beginning and one without. */
            String requestParameters = request.toString();
            if (requestParameters.startsWith("&")) //$NON-NLS-1$
                requestParameters = requestParameters.replaceFirst("&", ""); //$NON-NLS-1$ //$NON-NLS-2$

            /* Store the request, before actually asking the WMS for a response. */
            m_lastRequest = URLDecoder.decode(String.format("%s?%s", getMapUrl, requestParameters), "UTF-8"); //$NON-NLS-1$ //$NON-NLS-2$
            m_lastGetMap = request;

            /* Do the request and wait, until the result is there. */
            final Object result = remoteWMS.doService(request);
            if (result == null)
                return null;

            /* Wrong result, no image is returned. */
            if (!(result instanceof GetMapResult))
                return null;

            /* Cast. */
            final GetMapResult mapResponse = (GetMapResult) result;

            /* Get the image. */
            final Image resultImage = (Image) mapResponse.getMap();
            if (resultImage == null) {
                /* Handle service-exception: convert to status and set it. */
                final OGCWebServiceRequest mapRequest = mapResponse.getRequest();
                final OGCWebServiceException exception = mapResponse.getException();

                final MultiStatus status = new MultiStatus(KalypsoGisPlugin.getId(), 0,
                        Messages.getString("org.kalypso.ogc.gml.wms.loader.images.WMSImageProvider.1"), null); //$NON-NLS-1$
                status.add(new Status(IStatus.ERROR, KalypsoGisPlugin.getId(),
                        Messages.getString("org.kalypso.ogc.gml.wms.loader.images.WMSImageProvider.2") + mapRequest //$NON-NLS-1$
                                + "'")); //$NON-NLS-1$
                status.add(new Status(IStatus.ERROR, KalypsoGisPlugin.getId(),
                        Messages.getString("org.kalypso.ogc.gml.wms.loader.images.WMSImageProvider.4"))); //$NON-NLS-1$
                status.add(StatusUtilities.createMultiStatusFromMessage(IStatus.ERROR, KalypsoGisPlugin.getId(), 0,
                        exception.toString(), "\n", null)); //$NON-NLS-1$

                throw new CoreException(status);
            }

            return resultImage;
        } catch (final Throwable t) {
            throw new CoreException(StatusUtilities.statusFromThrowable(t));
        }
    }

    private LegendURL findLegendURL(final String layerName, final String styleName) {
        final Layer layer = m_capabilities.getLayer(layerName);
        if (layer == null)
            return null;

        final Style style = findLegendStyle(layer, styleName);
        if (style == null)
            return null;

        /* Get the URLs for the legend. */
        final LegendURL[] legendURLs = style.getLegendURL();
        if (legendURLs.length == 0)
            return null;

        // TODO: Only the first legend URL will be used for now.
        return legendURLs[0];
    }

    private Style findLegendStyle(final Layer layer, final String styleName) {
        if (styleName == null) {
            /* Happens if this layer is not visible -> show legend for first style */
            final Style[] styles = layer.getStyles();
            if (styles.length == 0)
                return null;

            return styles[0];
        }

        return layer.getStyleResource(styleName);
    }

    /**
     * This function returns the last request or null.
     * 
     * @return The last request or null.
     */
    public synchronized String getLastRequest() {
        return m_lastRequest;
    }

    public synchronized void setLayers(final String[] layers, final String[] styles) {
        Assert.isTrue(layers.length == styles.length);

        m_layers = layers;
        m_styles = styles;

        /* Reset cached properties */
        m_wms = null;
        m_negotiatedSRS = null;
        m_getMapUrl = null;
        m_lastRequest = null;
        m_lastGetMap = null;
    }

    @Override
    public WMSCapabilities getCapabilities() {
        return m_capabilities;
    }

    @Override
    public boolean isLayerVisible(final String name) {
        return ArrayUtils.contains(m_layers, name);
    }

    @Override
    public void setLayerVisible(final String[] names, final boolean visible) {
        if (m_capabilities == null)
            return;

        /* Prepare which-list */
        final Map<String, String> layersAndStyles = new LinkedHashMap<>();
        for (int i = 0; i < m_layers.length; i++)
            layersAndStyles.put(m_layers[i], m_styles[i]);

        /* Change visibility of that one layer */
        for (final String name : names) {
            if (visible)
                layersAndStyles.put(name, null);
            else
                layersAndStyles.remove(name);
        }

        final WMSLayerConfigurator configurator = new WMSLayerConfigurator(m_capabilities, layersAndStyles);

        final String[] layers = configurator.getLayers();
        final String[] styles = configurator.getStyles();

        setLayers(layers, styles);
    }

    @Override
    public synchronized String getStyle(final Layer layer) {
        final String name = layer.getName();
        for (int i = 0; i < m_layers.length; i++) {
            if (m_layers[i].equals(name))
                return m_styles[i];
        }

        return null;
    }

    @Override
    public String getSource() {
        final String layers = StringUtils.join(m_layers, ","); //$NON-NLS-1$
        final String styles = StringUtils.join(m_styles, ","); //$NON-NLS-1$

        return String.format("%s=%s#%s=%s#%s=%s#%s=%s", IKalypsoImageProvider.KEY_URL, m_service, //$NON-NLS-1$
                IKalypsoImageProvider.KEY_PROVIDER, m_providerID, IKalypsoImageProvider.KEY_LAYERS, layers,
                IKalypsoImageProvider.KEY_STYLES, styles);
    }

    public String getFeatureInfo(final double x, final double y) throws OGCWebServiceException, OWSException {
        /* One request must be done. */
        if (m_lastGetMap == null)
            return null;

        /* Copy. */
        final GetMap lastGetMap = m_lastGetMap;

        /* Work locally against a copy of the reference, because it may change any time... */
        final RemoteWMService remoteWMS = getWms();
        if (remoteWMS == null)
            return null;

        /* Collect the layers. */
        final List<String> layers = new ArrayList<>();
        final org.deegree.ogcwebservices.wms.operation.GetMap.Layer[] allLayers = lastGetMap.getLayers();
        for (final org.deegree.ogcwebservices.wms.operation.GetMap.Layer layer : allLayers) {
            if (isQueryable(layer, m_capabilities))
                layers.add(layer.getName());
        }

        /* There are no queryable layers. */
        if (layers.size() == 0)
            throw new OWSException(Messages.getString("AbstractDeegreeImageProvider.5"), OWSUtilities.OWS_VERSION, //$NON-NLS-1$
                    "en", ExceptionCode.NO_APPLICABLE_CODE, null); //$NON-NLS-1$

        /* Create the GetFeatureInfo request. */
        final GetFeatureInfo featureInfoRequest = GetFeatureInfo.create(lastGetMap.getVersion(), "GetFeatureInfo", //$NON-NLS-1$
                layers.toArray(new String[] {}), lastGetMap, "text/html", 1, new Point((int) x, (int) y), //$NON-NLS-1$
                lastGetMap.getExceptions(), lastGetMap.getStyledLayerDescriptor(),
                lastGetMap.getVendorSpecificParameters());

        /* Do the request and wait, until the result is there. */
        final Object result = remoteWMS.doService(featureInfoRequest);
        if (result == null)
            return null;

        /* Wrong result. */
        if (!(result instanceof GetFeatureInfoResult))
            return null;

        /* Cast. */
        final GetFeatureInfoResult featureInfoResponse = (GetFeatureInfoResult) result;

        /* Get the response. */
        final String featureInfo = featureInfoResponse.getFeatureInfo();

        /* Check, if there was an error. */
        KalypsoWMSUtilities.checkForOwsException(featureInfo);

        return featureInfo;
    }

    private boolean isQueryable(final org.deegree.ogcwebservices.wms.operation.GetMap.Layer layer,
            final WMSCapabilities capabilities) {
        final Layer capabilitiesLayer = capabilities.getLayer(layer.getName());
        if (capabilitiesLayer == null)
            return false;

        return capabilitiesLayer.isQueryable();
    }
}