org.geomajas.plugin.wmsclient.client.service.WmsServiceImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.geomajas.plugin.wmsclient.client.service.WmsServiceImpl.java

Source

/*
 * This is part of Geomajas, a GIS framework, http://www.geomajas.org/.
 *
 * Copyright 2008-2013 Geosparc nv, http://www.geosparc.com/, Belgium.
 *
 * The program is available in open source according to the GNU Affero
 * General Public License. All contributions in this program are covered
 * by the Geomajas Contributors License Agreement. For full licensing
 * details, see LICENSE.txt in the project root.
 */

package org.geomajas.plugin.wmsclient.client.service;

import java.util.ArrayList;
import java.util.List;

import org.geomajas.geometry.Bbox;
import org.geomajas.geometry.Coordinate;
import org.geomajas.gwt.client.command.CommandCallback;
import org.geomajas.gwt.client.command.GwtCommand;
import org.geomajas.gwt.client.command.GwtCommandDispatcher;
import org.geomajas.gwt.client.map.RenderSpace;
import org.geomajas.layer.tile.TileCode;
import org.geomajas.plugin.wmsclient.client.capabilities.WmsGetCapabilitiesInfo;
import org.geomajas.plugin.wmsclient.client.capabilities.v1_1_1.WmsGetCapabilitiesInfo111;
import org.geomajas.plugin.wmsclient.client.capabilities.v1_3_0.WmsGetCapabilitiesInfo130;
import org.geomajas.plugin.wmsclient.client.layer.FeaturesSupportedWmsLayer;
import org.geomajas.plugin.wmsclient.client.layer.WmsLayer;
import org.geomajas.plugin.wmsclient.client.layer.config.WmsLayerConfiguration;
import org.geomajas.plugin.wmsclient.client.layer.config.WmsServiceVendor;
import org.geomajas.plugin.wmsclient.client.layer.feature.FeatureCollection;
import org.geomajas.plugin.wmsclient.server.command.dto.GetFeatureInfoRequest;
import org.geomajas.plugin.wmsclient.server.command.dto.GetFeatureInfoResponse;
import org.geomajas.puregwt.client.map.feature.Feature;
import org.geomajas.puregwt.client.map.feature.FeatureFactory;
import org.geomajas.puregwt.client.map.layer.LegendConfig;

import com.google.gwt.core.client.Callback;
import com.google.gwt.http.client.Request;
import com.google.gwt.http.client.RequestBuilder;
import com.google.gwt.http.client.RequestCallback;
import com.google.gwt.http.client.RequestException;
import com.google.gwt.http.client.Response;
import com.google.gwt.http.client.URL;
import com.google.gwt.i18n.client.NumberFormat;
import com.google.gwt.xml.client.Document;
import com.google.gwt.xml.client.XMLParser;
import com.google.inject.Inject;

/**
 * Default implementation of the {@link WmsService}.
 * 
 * @author Pieter De Graef
 * @author An Buyle (support for static and dynamic legend when printing)
 */
public class WmsServiceImpl implements WmsService {

    private static final NumberFormat NUMBERFORMAT = NumberFormat.getFormat("#0.0#");

    private static final double DEFAULT_PIXEL_TOLERANCE = 0.0; // Default tolerance for the location

    private static final int DEFAULT_MAX_FEATURES = 20; // Default maximum number of feats returned by
    // getFeatureInfo()

    private static final String WMS_LEGEND_OPTIONS_START = "&legend_options=";

    private static final int LEGEND_DPI = 288;

    @Inject
    private WmsTileService tileService;

    @Inject
    private FeatureFactory featureFactory;

    private WmsUrlTransformer urlTransformer;

    // ------------------------------------------------------------------------
    // WmsService implementation:
    // ------------------------------------------------------------------------

    @Override
    public void getCapabilities(String baseUrl, final WmsVersion version,
            final Callback<WmsGetCapabilitiesInfo, String> callback) {
        String url = getCapabilitiesUrl(baseUrl, version);
        RequestBuilder builder = new RequestBuilder(RequestBuilder.GET, url);
        try {
            builder.sendRequest(null, new RequestCallback() {

                public void onError(Request request, Throwable e) {
                    callback.onFailure(e.getMessage());
                }

                public void onResponseReceived(Request request, Response response) {
                    if (200 == response.getStatusCode()) {
                        Document messageDom = XMLParser.parse(response.getText());
                        WmsGetCapabilitiesInfo capabilities;
                        switch (version) {
                        case V1_1_1:
                            capabilities = new WmsGetCapabilitiesInfo111(messageDom.getDocumentElement());
                            break;
                        case V1_3_0:
                            capabilities = new WmsGetCapabilitiesInfo130(messageDom.getDocumentElement());
                            break;
                        default:
                            callback.onFailure("Unsupported version");
                            return;
                        }
                        callback.onSuccess(capabilities);
                    } else {
                        callback.onFailure(response.getText());
                    }
                }
            });
        } catch (RequestException e) {
            // Couldn't connect to server
            callback.onFailure(e.getMessage());
        }
    }

    @Override
    public String getMapUrl(WmsLayerConfiguration wmsConfig, String crs, Bbox worldBounds, int imageWidth,
            int imageHeight) {
        StringBuilder url = getBaseUrlBuilder(wmsConfig);

        // Add the base parameters needed for getMap:
        addBaseParameters(url, wmsConfig, crs, worldBounds, imageWidth, imageHeight);

        // Parameter: request type
        url.append("&request=GetMap");

        return finishUrl(WmsRequest.GETMAP, url);
    }

    @Override
    public void getFeatureInfo(final FeaturesSupportedWmsLayer layer, Coordinate location,
            final Callback<FeatureCollection, String> callback) {
        final String url = getFeatureInfoUrl(layer, location, GetFeatureInfoFormat.GML2, DEFAULT_PIXEL_TOLERANCE,
                DEFAULT_MAX_FEATURES);

        GwtCommand command = new GwtCommand(GetFeatureInfoRequest.COMMAND_NAME);
        command.setCommandRequest(new GetFeatureInfoRequest(url));
        GwtCommandDispatcher.getInstance().execute(command, new CommandCallback<GetFeatureInfoResponse>() {

            public void execute(GetFeatureInfoResponse response) {
                List<Feature> features = new ArrayList<Feature>();
                for (org.geomajas.layer.feature.Feature feature : response.getFeatures()) {
                    features.add(featureFactory.create(feature, layer));
                }

                callback.onSuccess(new FeatureCollection(features, response.getAttributeDescriptors()));
            }
        });
    }

    @Override
    public void getFeatureInfo(FeaturesSupportedWmsLayer layer, Coordinate location, GetFeatureInfoFormat format,
            final Callback<Object, String> callback) {
        String url = getFeatureInfoUrl(layer, location, format, DEFAULT_PIXEL_TOLERANCE, DEFAULT_MAX_FEATURES);
        GwtCommand command = new GwtCommand(GetFeatureInfoRequest.COMMAND_NAME);
        command.setCommandRequest(new GetFeatureInfoRequest(url));
        GwtCommandDispatcher.getInstance().execute(command, new CommandCallback<GetFeatureInfoResponse>() {

            public void execute(GetFeatureInfoResponse response) {
                if (response.getFeatures() != null) {
                    callback.onSuccess(response.getFeatures());
                } else {
                    callback.onSuccess(response.getWmsResponse());
                }
            }
        });
    }

    // ------------------------------------------------------------------------
    // WMS GetLegendGraphic methods:
    // ------------------------------------------------------------------------

    @Override
    public String getLegendGraphicUrl(WmsLayerConfiguration wmsConfig, LegendConfig legendConfig) {
        StringBuilder url = getBaseUrlBuilder(wmsConfig);

        // Parameter: service
        int pos = url.lastIndexOf("?");
        if (pos > 0) {
            url.append("&service=WMS");
        } else {
            url.append("?service=WMS");
        }

        // Parameter: layers
        url.append("&layer=");
        url.append(wmsConfig.getLayers()); // No URL.encode here!

        // Parameter: request type
        url.append("&request=GetLegendGraphic");

        // Parameter: format
        url.append("&format=");
        String format = legendConfig.getImageFormat();
        if (format == null) {
            url.append("image/png");
        } else if (!format.startsWith("image/")) {
            url.append("image/");
            url.append(format.toLowerCase());
        } else {
            url.append(format.toLowerCase());
        }

        // Parameter: width
        url.append("&width=");
        url.append(legendConfig.getIconWidth());

        // Parameter: height
        url.append("&height=");
        url.append(legendConfig.getIconHeight());

        // Parameter: transparent
        url.append("&transparent=true");

        // Check for specific vendor options:
        if (WmsServiceVendor.GEOSERVER_WMS.equals(wmsConfig.getWmsServiceVendor())) {
            url.append(WMS_LEGEND_OPTIONS_START);
            if (null != legendConfig.getFontStyle().getFamily()) {
                url.append("fontName:");
                url.append(legendConfig.getFontStyle().getFamily());
                url.append(";");
            }
            url.append("fontAntiAliasing:true;");

            if (null != legendConfig.getFontStyle().getColor()) {
                url.append("fontColor:");
                url.append(legendConfig.getFontStyle().getColor().replace("#", "0x"));
                url.append(";");
            }
            if (legendConfig.getFontStyle().getSize() > 0) {
                url.append("fontSize:");
                url.append(legendConfig.getFontStyle().getSize());
                url.append(";");
            }
            url.append("bgColor:0xFFFFFF;dpi:" + LEGEND_DPI);
        }

        return finishUrl(WmsRequest.GETLEGENDGRAPHIC, url);
    }

    @Override
    public String getLegendGraphicUrl(WmsLayerConfiguration wmsConfig) {
        return getLegendGraphicUrl(wmsConfig, wmsConfig.getLegendConfig());
    }

    // ------------------------------------------------------------------------
    // Proxy options:
    // ------------------------------------------------------------------------

    @Override
    public void setWmsUrlTransformer(WmsUrlTransformer urlTransformer) {
        this.urlTransformer = urlTransformer;
    }

    @Override
    public WmsUrlTransformer getWmsUrlTransformer() {
        return urlTransformer;
    }

    // ------------------------------------------------------------------------
    // Private methods:
    // ------------------------------------------------------------------------

    private String getFeatureInfoUrl(WmsLayer layer, Coordinate location, GetFeatureInfoFormat format,
            double tolerance, int maxFeatures) {
        StringBuilder url = getBaseUrlBuilder(layer.getConfig());

        // Calculate the denominator for tile height and width adaptation to reflect the specified tolerance in pixels
        int toleranceCorrection = (int) Math.round(tolerance * 2.0);
        if (toleranceCorrection <= 0) {
            toleranceCorrection = 1;
        }
        if (toleranceCorrection > 2.0) {
            toleranceCorrection = 2; // limit because it seems sometimes not to work if > 2
        }

        TileCode tileCode = tileService.getTileCodeForLocation(layer.getViewPort(), layer.getTileConfig(), location,
                layer.getViewPort().getScale());
        Bbox worldBounds = tileService.getWorldBoundsForTile(layer.getViewPort(), layer.getTileConfig(), tileCode);

        Bbox screenBounds = layer.getViewPort().transform(worldBounds, RenderSpace.WORLD, RenderSpace.SCREEN);
        Coordinate screenLocation = layer.getViewPort().transform(location, RenderSpace.WORLD, RenderSpace.SCREEN);

        // Add the base parameters needed for getMap:
        addBaseParameters(url, layer.getConfig(), layer.getCrs(), worldBounds,
                layer.getTileConfig().getTileWidth() / toleranceCorrection,
                layer.getTileConfig().getTileHeight() / toleranceCorrection);

        url.append("&QUERY_LAYERS=");
        url.append(layer.getConfig().getLayers()); // No URL.encode here!
        url.append("&request=GetFeatureInfo");
        switch (layer.getConfig().getVersion()) {
        case V1_3_0:
            url.append("&I=");
            url.append(
                    (int) Math.round((screenLocation.getX() - screenBounds.getX()) / (double) toleranceCorrection));
            url.append("&J=");
            url.append(
                    (int) Math.round((screenLocation.getY() - screenBounds.getY()) / (double) toleranceCorrection));
            break;
        case V1_1_1:
        default:
            url.append("&X=");
            url.append(
                    (int) Math.round((screenLocation.getX() - screenBounds.getX()) / (double) toleranceCorrection));
            url.append("&Y=");
            url.append(
                    (int) Math.round((screenLocation.getY() - screenBounds.getY()) / (double) toleranceCorrection));
        }
        url.append("&FEATURE_COUNT=" + maxFeatures);
        url.append("&INFO_FORMAT=");
        url.append(format.toString());

        return finishUrl(WmsRequest.GETFEATUREINFO, url);
    }

    private StringBuilder getBaseUrlBuilder(WmsLayerConfiguration config) {
        return new StringBuilder(config.getBaseUrl());
    }

    private String finishUrl(WmsRequest request, StringBuilder builder) {
        String url = builder.toString();
        if (urlTransformer != null) {
            url = urlTransformer.transform(request, url);
        }
        return URL.encode(url);
    }

    private StringBuilder addBaseParameters(StringBuilder url, WmsLayerConfiguration config, String crs,
            Bbox worldBounds, int imageWidth, int imageHeight) {
        // Parameter: service
        int pos = url.lastIndexOf("?");
        if (pos > 0) {
            url.append("&service=WMS");
        } else {
            url.append("?service=WMS");
        }

        // Parameter: layers
        url.append("&layers=");
        url.append(config.getLayers()); // No URL.encode here, performed in finishUrl

        // Parameter: width
        url.append("&width=");
        url.append(Integer.toString(imageWidth));

        // Parameter: height
        url.append("&height=");
        url.append(Integer.toString(imageHeight));

        // Parameter: bbox
        url.append("&bbox=");
        if (useInvertedAxis(config.getVersion(), crs)) {
            // Replace
            url.append(floatToStringWithDecimalPoint((worldBounds.getY())));
            url.append(",");
            url.append(floatToStringWithDecimalPoint(worldBounds.getX()));
            url.append(",");
            url.append(floatToStringWithDecimalPoint(worldBounds.getMaxY()));
            url.append(",");
            url.append(floatToStringWithDecimalPoint(worldBounds.getMaxX()));
        } else {
            url.append(floatToStringWithDecimalPoint(worldBounds.getX()));
            url.append(",");
            url.append(floatToStringWithDecimalPoint(worldBounds.getY()));
            url.append(",");
            url.append(floatToStringWithDecimalPoint(worldBounds.getMaxX()));
            url.append(",");
            url.append(floatToStringWithDecimalPoint(worldBounds.getMaxY()));
        }

        // Parameter: format
        url.append("&format=");
        url.append(config.getFormat());

        // Parameter: version
        url.append("&version=");
        url.append(config.getVersion().toString());

        // Parameter: crs/srs
        switch (config.getVersion()) {
        case V1_1_1:
            url.append("&srs=");
            break;
        case V1_3_0:
        default:
            url.append("&crs=");
            break;
        }
        url.append(crs); // No URL.encode here, performed in finishUrl

        // Parameter: styles
        url.append("&styles=");
        url.append(config.getStyles());

        // Parameter: transparent
        if (config.isTransparent()) {
            url.append("&transparent=");
            url.append(config.isTransparent());
        }

        // Return the URL:
        return url;
    }

    private static String floatToStringWithDecimalPoint(double number) {
        return NUMBERFORMAT.format(number).replace(",", ".");
    }

    private String getCapabilitiesUrl(String baseUrl, WmsVersion version) {
        StringBuilder url = new StringBuilder(baseUrl);

        // Parameter: Service
        int pos = url.lastIndexOf("?");
        if (pos > 0) {
            url.append("&service=WMS");
        } else {
            url.append("?service=WMS");
        }

        // Parameter: Version
        url.append("&version=");
        url.append(version.toString());

        // Parameter: request type
        url.append("&request=GetCapabilities");

        return finishUrl(WmsRequest.GETCAPABILITIES, url);
    }

    private boolean useInvertedAxis(WmsVersion version, String crs) {
        if (WmsVersion.V1_3_0.equals(version)
                && ("EPSG:4326".equalsIgnoreCase(crs) || "WGS:84".equalsIgnoreCase(crs))) {
            return true;
        }
        return false;
    }
}