org.kalypso.ogc.gml.wms.deegree.DeegreeWMSUtilities.java Source code

Java tutorial

Introduction

Here is the source code for org.kalypso.ogc.gml.wms.deegree.DeegreeWMSUtilities.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.deegree;

import java.net.URL;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.lang3.ArrayUtils;
import org.deegree.datatypes.QualifiedName;
import org.deegree.model.spatialschema.Envelope;
import org.deegree.model.spatialschema.Position;
import org.deegree.ogcwebservices.wms.capabilities.Layer;
import org.deegree.ogcwebservices.wms.capabilities.LayerBoundingBox;
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.GetMap;
import org.deegree.owscommon_new.DCP;
import org.deegree.owscommon_new.HTTP;
import org.deegree.owscommon_new.Operation;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.resource.ImageDescriptor;
import org.kalypso.contribs.eclipse.core.runtime.StatusUtilities;
import org.kalypso.contribs.java.util.Arrays;
import org.kalypso.transformation.transformer.GeoTransformerFactory;
import org.kalypso.transformation.transformer.IGeoTransformer;
import org.kalypso.ui.KalypsoGisPlugin;
import org.kalypso.ui.internal.i18n.Messages;
import org.kalypsodeegree.graphics.transformation.GeoTransformUtils;
import org.kalypsodeegree.model.geometry.GM_Envelope;
import org.kalypsodeegree_impl.model.geometry.GeometryFactory;

/**
 * This class provides functions for dealing with the WMS client from degree.
 * 
 * @author Holger Albert
 */
public final class DeegreeWMSUtilities {
    private DeegreeWMSUtilities() {
        throw new UnsupportedOperationException();
    }

    /**
     * This function creates the get map request.
     * 
     * @param capabilities
     *          The wms capabilities.
     * @param negotiatedSRS
     *          The negotiated srs.
     * @param themeName
     *          The theme name.
     * @param layers
     *          The layers.
     * @param styles
     *          The styles.
     * @param width
     *          The requested width.
     * @param height
     *          The requested height.
     * @param requestedEnvLocalSRS
     *          The requested envelope in the local coordinate system.
     * @param localSRS
     *          The local coordinate system.
     * @param sldBody
     *          This is the content of a SLD, if we want the server to render the image with a specific style.
     * @return The get map request.
     */
    public static GetMap createGetMapRequest(final WMSCapabilities capabilities, final String negotiatedSRS,
            final String themeName, final String[] layers, final String[] styles, final int width, final int height,
            final GM_Envelope requestedEnvLocalSRS, final String localSRS, final String sldBody)
            throws CoreException {
        try {
            final Map<String, String> wmsParameter = prepareRequestParameters(capabilities, "GetMap"); //$NON-NLS-1$

            final String layerParam = Arrays.toString(layers, ","); //$NON-NLS-1$
            wmsParameter.put("LAYERS", layerParam); //$NON-NLS-1$
            if (styles != null) {
                final String styleParam = Arrays.toString(styles, ","); //$NON-NLS-1$
                wmsParameter.put("STYLES", styleParam); //$NON-NLS-1$
            }

            // some WMS-themes use style name="" and when deegree makes "STYLES=default" out of this, this does not work
            // I think style name="" is also not valid (can we be flexible ?)
            // ask me ( v.doemming@tuhh.de )

            // REMARK: empty style name is valid according to WMS spec i.e. STYLES=,, means three styles for three layers, all
            // names are empty i.e. the GetMap implementation is not correct :-(

            wmsParameter.put("FORMAT", "image/png"); //$NON-NLS-1$ //$NON-NLS-2$
            wmsParameter.put("TRANSPARENT", "TRUE"); //$NON-NLS-1$ //$NON-NLS-2$
            wmsParameter.put("WIDTH", "" + width); //$NON-NLS-1$ //$NON-NLS-2$
            wmsParameter.put("HEIGHT", "" + height); //$NON-NLS-1$ //$NON-NLS-2$
            wmsParameter.put("SRS", negotiatedSRS); //$NON-NLS-1$

            final IGeoTransformer gt = GeoTransformerFactory.getGeoTransformer(negotiatedSRS);
            final GM_Envelope requestedEnvLocalCRS = GeometryFactory.createGM_Envelope(
                    requestedEnvLocalSRS.getMinX(), requestedEnvLocalSRS.getMinY(), requestedEnvLocalSRS.getMaxX(),
                    requestedEnvLocalSRS.getMaxY(), localSRS);
            final GM_Envelope targetEnvRemoteSRS = gt.transform(requestedEnvLocalCRS);

            if (targetEnvRemoteSRS.getMax().getX() - targetEnvRemoteSRS.getMin().getX() <= 0)
                throw new Exception("invalid bbox"); //$NON-NLS-1$

            if (targetEnvRemoteSRS.getMax().getY() - targetEnvRemoteSRS.getMin().getY() <= 0)
                throw new Exception("invalid bbox"); //$NON-NLS-1$

            final String targetEnvRemoteSRSstring = DeegreeWMSUtilities.env2bboxString(targetEnvRemoteSRS);
            wmsParameter.put("BBOX", targetEnvRemoteSRSstring); //$NON-NLS-1$

            /* Add the ID parameter to them. */
            wmsParameter.put("ID", "KalypsoWMSRequest" + themeName + new Date().getTime()); //$NON-NLS-1$ //$NON-NLS-2$

            /* Create the GetMap request. */
            final GetMap request = GetMap.create(wmsParameter);
            if (sldBody == null || sldBody.length() == 0)
                return request;

            /* HACK: Recreate the request, using vendor specific parameters. */
            /* HACK: SLD_BODY is not handled correctly in deegree2. */
            final Map<String, String> vendorSpecificParameter = new HashMap<>();
            vendorSpecificParameter.put("SLD_BODY", sldBody); //$NON-NLS-1$

            return GetMap.create(request.getVersion(), request.getId(), request.getLayers(), request.getElevation(),
                    request.getSampleDimension(), request.getFormat(), request.getWidth(), request.getHeight(),
                    request.getSrs(), request.getBoundingBox(), request.getTransparency(), request.getBGColor(),
                    request.getExceptions(), request.getTime(), request.getSLD_URL(),
                    request.getStyledLayerDescriptor(), vendorSpecificParameter);
        } catch (final Exception ex) {
            /* Create the error status. */
            final IStatus status = StatusUtilities.statusFromThrowable(ex,
                    Messages.getString("org.kalypso.ogc.gml.wms.deegree.DeegreeWMSUtilities.35")); //$NON-NLS-1$

            throw new CoreException(status);
        }
    }

    /**
     * This function prepares the request parameter.
     * 
     * @param capabilities
     *          The wms capabilities.
     * @param name
     *          The name of the operation.
     * @return The request parameter.
     */
    private static HashMap<String, String> prepareRequestParameters(final WMSCapabilities capabilities,
            final String operationName) throws CoreException {
        final HashMap<String, String> wmsParameter = new HashMap<>();

        /* HACK: in order to keep any existing query parts, add existing query parts from base url. */
        final Operation operation = checkOperation(capabilities, operationName);
        final List<DCP> types = operation.getDCP();

        for (final DCP type : types) {
            if (type instanceof HTTP) {
                final HTTP httpProtocol = (HTTP) type;
                final List<URL> getOnlineResources = httpProtocol.getGetOnlineResources();
                for (final URL url : getOnlineResources) {
                    if (url != null) {
                        final String query = url.getQuery();
                        if (query != null && query.length() > 0) {
                            /* The base URL may already contain a query part, we do not want to delete it Quotation from WMS-Spec: */
                            /* "An OGC Web Service shall be prepared to encounter parameters that are not part of this specification." */
                            final String[] requestParts = query.split("&"); //$NON-NLS-1$
                            for (final String requestPart : requestParts) {
                                final String[] queryParts = requestPart.split("="); //$NON-NLS-1$
                                if (queryParts.length != 2)
                                    continue;

                                wmsParameter.put(queryParts[0], queryParts[1]);
                            }
                        }

                        /* The first valid url is enough. */
                        break;
                    }

                    /* The first http-protocol is enough. */
                    break;
                }
            }
        }

        wmsParameter.put("SERVICE", "WMS"); //$NON-NLS-1$ //$NON-NLS-2$
        wmsParameter.put("VERSION", capabilities.getVersion()); //$NON-NLS-1$
        wmsParameter.put("REQUEST", operationName); //$NON-NLS-1$
        wmsParameter.put("EXCEPTIONS", "application/vnd.ogc.se_xml"); //$NON-NLS-1$ //$NON-NLS-2$

        return wmsParameter;
    }

    /**
     * Tries to find the operation.
     * 
     * @param capabilities
     *          The wms capabilities.
     * @param name
     *          The name of the operation.
     * @return The operation.
     * @throws CoreException
     *           If this service does not supports this operation.
     */
    private static Operation checkOperation(final WMSCapabilities capabilities, final String name)
            throws CoreException {
        final Operation operation = capabilities.getOperationMetadata().getOperation(new QualifiedName(name));
        if (operation == null)
            throw new CoreException(new Status(IStatus.ERROR, KalypsoGisPlugin.PLUGIN_ID,
                    Messages.getString("org.kalypso.ogc.gml.wms.deegree.DeegreeWMSUtilities.44") + name)); //$NON-NLS-1$

        return operation;
    }

    /**
     * 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.
     */
    public static String[] negotiateCRS(final String localCRS, final WMSCapabilities capabilities,
            final String[] layerNames) {
        final Layer topLayer = capabilities.getLayer();
        final String crs = matchCrs(topLayer, layerNames, localCRS);
        if (crs != null)
            return new String[] { localCRS };

        /* Get crs from top layer. */
        final String[] topLayerSRS = topLayer.getSrs();

        return topLayerSRS;
    }

    /**
     * This method tries to match the local coordinate system to a given layer selection.
     * 
     * @param topLayer
     *          The top layer of the layer structur of a web map service.
     * @param layerSelection
     *          Layers to be matched.
     * @param localCRS
     *          The local coordinate system.
     * @return Null, if one element of the layers to be matched is not available in the local coordinate system, otherwise
     *         it returns the local crs.
     */
    private static String matchCrs(final Layer topLayer, final String[] layerSelection, final String localCRS) {
        final Set<Layer> collector = new HashSet<>();

        collect(collector, topLayer, layerSelection);

        for (final Layer layer : collector) {
            final String[] layerSRS = layer.getSrs();
            if (!ArrayUtils.contains(layerSRS, localCRS))
                return null;
        }

        return localCRS;
    }

    /**
     * This method collects all layers (or the specified layers) from the top layer of a WMSCapabilites document. If the
     * parameter layerSeletion is empty or null the method collects all layers, otherwise returns all layers with the same
     * name as in the layerSelection.
     * 
     * @param collector
     *          The set that collects the layers found.
     * @param layer
     *          the top layer of the wms capabilites document.
     * @param layerSelection
     *          An array of layer names to search for.
     */
    private static void collect(final Set<Layer> collector, final Layer layer, final String[] layerSelection) {
        final Layer[] layerTree = layer.getLayer();
        for (final Layer newLayer : layerTree) {
            if (newLayer.getLayer().length > 0) // it is a layer container
                collect(collector, newLayer, layerSelection);
            else if (layerSelection != null) {
                if (ArrayUtils.contains(layerSelection, newLayer.getName()))
                    collector.add(newLayer);
            } else
                collector.add(newLayer);
        }
    }

    /**
     * This method gets the max bounding box of a wms layer.
     * 
     * @param layers
     *          The layers in the map in an array.
     * @param srs
     *          The coordinate system the returned envelope will be in.
     * @return The max bounding box of a wms layer.
     */
    public static GM_Envelope getMaxExtent(final String[] layers, final WMSCapabilities capabilites,
            final String srs) throws Exception {
        final IGeoTransformer geoTransformer = GeoTransformerFactory.getGeoTransformer(srs);

        final Layer topLayer = capabilites.getLayer();
        final HashSet<Layer> layerCollector = new HashSet<>();

        collect(layerCollector, topLayer, layers);

        GM_Envelope resultEnvelope = null;
        for (final Layer layer : layerCollector) {
            final GM_Envelope layerEnv = findEnvelope(layer, srs);
            if (layerEnv != null) {
                final GM_Envelope envTransformed = geoTransformer.transform(layerEnv);
                resultEnvelope = resultEnvelope == null ? envTransformed : resultEnvelope.getMerged(envTransformed);
            }
        }

        if (resultEnvelope != null)
            return resultEnvelope;

        final GM_Envelope topEnvelope = findEnvelope(topLayer, srs);
        return geoTransformer.transform(topEnvelope);
    }

    private static GM_Envelope findEnvelope(final Layer layer, final String srs) {
        final LayerBoundingBox[] boundingBoxes = layer.getBoundingBoxes();
        for (final LayerBoundingBox bbox : boundingBoxes) {
            if (srs.equals(bbox.getSRS())) {
                final Position min = bbox.getMin();
                final Position max = bbox.getMax();
                return GeometryFactory.createGM_Envelope(min.getX(), min.getY(), max.getX(), max.getY(), srs);
            }
        }

        final Envelope latLonBoundingBox = layer.getLatLonBoundingBox();
        if (latLonBoundingBox == null)
            return null;

        final Position min = latLonBoundingBox.getMin();
        final Position max = latLonBoundingBox.getMax();

        return GeometryFactory.createGM_Envelope(min.getX(), min.getY(), max.getX(), max.getY(),
                GeoTransformUtils.EPSG_LATLON);
    }

    /**
     * This method collects all layers from a capabilites document.
     * 
     * @param capabilites
     *          WMS capabilites document.
     * @param set
     *          The Set, where the layers are collected in.
     */
    public static void getAllLayers(final WMSCapabilities capabilites, final Set<Layer> set) {
        try {
            final Layer topLayer = capabilites.getLayer();
            collect(set, topLayer, null);
        } catch (final Exception ex) {
            KalypsoGisPlugin.getDefault().getLog().log(StatusUtilities.statusFromThrowable(ex));
        }
    }

    /**
     * This function converts an envelope to a string representation.
     * 
     * @param envelope
     *          The envelope.
     * @return The string representation of the envelope.
     */
    public static String env2bboxString(final GM_Envelope env) {
        return env.getMin().getX() + "," + env.getMin().getY() + "," + env.getMax().getX() + "," //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$
                + env.getMax().getY();
    }

    public static ImageDescriptor getLegendImage(final Layer layer, final String style) {
        /* 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 ImageDescriptor.createFromURL(onlineResource);
    }

    private static LegendURL findLegendURL(final Layer layer, final String styleName) {
        if (styleName == null)
            return null;

        final Style style = layer.getStyleResource(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];
    }
}