org.geosdi.geoplatform.services.GPWMSServiceImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.geosdi.geoplatform.services.GPWMSServiceImpl.java

Source

/**
 * geo-platform
 * Rich webgis framework
 * http://geo-platform.org
 * ====================================================================
 * <p>
 * Copyright (C) 2008-2019 geoSDI Group (CNR IMAA - Potenza - ITALY).
 * <p>
 * This program is free software: you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version. This program 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 General Public License
 * for more details. You should have received a copy of the GNU General
 * Public License along with this program. If not, see http://www.gnu.org/licenses/
 * <p>
 * ====================================================================
 * <p>
 * Linking this library statically or dynamically with other modules is
 * making a combined work based on this library. Thus, the terms and
 * conditions of the GNU General Public License cover the whole combination.
 * <p>
 * As a special exception, the copyright holders of this library give you permission
 * to link this library with independent modules to produce an executable, regardless
 * of the license terms of these independent modules, and to copy and distribute
 * the resulting executable under terms of your choice, provided that you also meet,
 * for each linked independent module, the terms and conditions of the license of
 * that module. An independent module is a module which is not derived from or
 * based on this library. If you modify this library, you may extend this exception
 * to your version of the library, but you are not obligated to do so. If you do not
 * wish to do so, delete this exception statement from your version.
 */
package org.geosdi.geoplatform.services;

import com.google.common.collect.Lists;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.methods.GetMethod;
import org.geojson.Feature;
import org.geojson.FeatureCollection;
import org.geosdi.geoplatform.connector.server.request.GPWMSGetMapBaseRequest;
import org.geosdi.geoplatform.connector.server.request.WMSGetMapBaseRequest;
import org.geosdi.geoplatform.connector.server.v111.GPWMSGetFeatureInfoV111Request;
import org.geosdi.geoplatform.connector.server.v111.IGPWMSConnectorStoreV111;
import org.geosdi.geoplatform.core.dao.GPServerDAO;
import org.geosdi.geoplatform.core.model.GPBBox;
import org.geosdi.geoplatform.core.model.GPLayerInfo;
import org.geosdi.geoplatform.core.model.GeoPlatformServer;
import org.geosdi.geoplatform.exception.IllegalParameterFault;
import org.geosdi.geoplatform.exception.ResourceNotFoundFault;
import org.geosdi.geoplatform.exception.ServerInternalFault;
import org.geosdi.geoplatform.hibernate.validator.support.GPI18NValidator;
import org.geosdi.geoplatform.hibernate.validator.support.request.GPI18NRequestValidator;
import org.geosdi.geoplatform.request.RequestByID;
import org.geosdi.geoplatform.response.RasterLayerDTO;
import org.geosdi.geoplatform.response.ServerDTO;
import org.geosdi.geoplatform.services.httpclient.GeoSDIHttpClient;
import org.geosdi.geoplatform.services.request.GPWMSGetFeatureInfoElement;
import org.geosdi.geoplatform.services.request.GPWMSGetFeatureInfoRequest;
import org.geosdi.geoplatform.services.request.WMSHeaderParam;
import org.geosdi.geoplatform.services.response.GPLayerTypeResponse;
import org.geosdi.geoplatform.services.response.WMSGetFeatureInfoResponse;
import org.geosdi.geoplatform.wms.v111.WMSDescribeLayerResponse;
import org.geotools.data.ows.CRSEnvelope;
import org.geotools.data.ows.Layer;
import org.geotools.data.ows.StyleImpl;
import org.geotools.data.ows.WMSCapabilities;
import org.geotools.data.wms.WebMapServer;
import org.geotools.ows.ServiceException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.MessageSource;
import org.xml.sax.InputSource;
import org.xml.sax.XMLReader;

import javax.annotation.Resource;
import javax.jws.WebMethod;
import javax.ws.rs.core.Response;
import javax.xml.bind.JAXBContext;
import javax.xml.parsers.SAXParserFactory;
import javax.xml.transform.sax.SAXSource;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.*;
import java.util.stream.Collectors;

import static com.google.common.base.Preconditions.checkArgument;
import static java.lang.Boolean.FALSE;
import static java.util.Locale.ENGLISH;
import static java.util.Locale.forLanguageTag;
import static org.geosdi.geoplatform.connector.pool.builder.v111.GPWMSConnectorBuilderPoolV111.wmsConnectorBuilderPoolV111;
import static org.geosdi.geoplatform.connector.server.config.GPPooledConnectorConfigBuilder.PooledConnectorConfigBuilder.pooledConnectorConfigBuilder;
import static org.geosdi.geoplatform.connector.server.request.WMSFeatureInfoFormat.GML;

public class GPWMSServiceImpl implements GPWMSService {

    private static final Logger logger = LoggerFactory.getLogger(GPWMSServiceImpl.class);
    //
    private final static String EPSG_4326 = "EPSG:4326";
    private final static String EPSG_3857 = "EPSG:3857";
    private final static String EPSG_GOOGLE = "EPSG:900913";
    private static final String GEB = "earthbuilder.google.com";
    // DAO
    @Resource(name = "serverDAO")
    private GPServerDAO serverDao;
    @Resource(name = "wmsMessageSource")
    private MessageSource wmsMessageSource;
    @Resource(name = "wmsRequestValidator")
    private GPI18NValidator<GPI18NRequestValidator, String> wmsRequestValidator;

    /**
     * @param serverUrl
     * @param request
     * @param token
     * @param authkey
     * @return {@link ServerDTO}
     * @throws ResourceNotFoundFault
     */
    @Override
    public ServerDTO getCapabilities(String serverUrl, RequestByID request, String token, String authkey)
            throws ResourceNotFoundFault {
        ServerDTO serverDTO;
        if (request.getId() != null) {
            GeoPlatformServer server = serverDao.find(request.getId());
            if (server == null) {
                throw new ResourceNotFoundFault("Server has been deleted", request.getId());
            }
            serverDTO = new ServerDTO(server);
        } else {
            serverDTO = new ServerDTO();
            serverDTO.setServerUrl(serverUrl);
        }
        WMSCapabilities wmsCapabilities = this.getWMSCapabilities(serverUrl, token, authkey);
        List<RasterLayerDTO> layers = this.convertToLayerList(wmsCapabilities.getLayer(), serverUrl);
        serverDTO.setLayerList(layers);
        return serverDTO;
    }

    /**
     * @param serverUrl
     * @param request
     * @param token
     * @param authkey
     * @param headers
     * @return {@link ServerDTO}
     * @throws ResourceNotFoundFault
     */
    @Override
    public ServerDTO getCapabilitiesAuth(String serverUrl, RequestByID request, String token, String authkey,
            List<WMSHeaderParam> headers) throws ResourceNotFoundFault {
        ServerDTO serverDTO;
        if (request.getId() != null) {
            GeoPlatformServer server = serverDao.find(request.getId());
            if (server == null) {
                throw new ResourceNotFoundFault("Server has been deleted", request.getId());
            }
            serverDTO = new ServerDTO(server);
        } else {
            serverDTO = new ServerDTO();
            serverDTO.setServerUrl(serverUrl);
        }
        WMSCapabilities wmsCapabilities = this.getWMSCapabilitiesAuth(serverUrl, token, authkey, headers);
        List<RasterLayerDTO> layers = this.convertToLayerList(wmsCapabilities.getLayer(), serverUrl);
        serverDTO.setLayerList(layers);
        return serverDTO;
    }

    @Override
    public ServerDTO getShortServer(String serverUrl) throws ResourceNotFoundFault {
        GeoPlatformServer server = serverDao.findByServerUrl(serverUrl);
        if (server == null) {
            throw new ResourceNotFoundFault("Server not found " + serverUrl);
        }
        return new ServerDTO(server);
    }

    /**
     * @param serverURL
     * @param layerName
     * @return {@link GPLayerTypeResponse}
     * @throws Exception
     */
    @Override
    public GPLayerTypeResponse getLayerType(String serverURL, String layerName) throws Exception {
        checkArgument((serverURL != null) && !(serverURL.trim().isEmpty()),
                "The Parameter serverURL must not be null or an empty string.");
        checkArgument((layerName != null) && !(layerName.trim().isEmpty()),
                "The Parameter layerName must not be null or an empty string.");
        logger.debug(
                "###########################TRYING TO RETRIEVE LAYER_TYPE with serverURL : {} - layerName : {]\n",
                serverURL, layerName);
        int index = serverURL.indexOf("?");
        if (index != -1) {
            serverURL = serverURL.substring(0, index);
        }
        String decribeLayerUrl = serverURL.concat("?service=WMS&request=DescribeLayer&version=1.1.1&layers=")
                .concat(layerName);
        logger.info("#########################DESCRIBE_LAYER_URL : {}\n", decribeLayerUrl);
        try {
            HttpClient httpClient = new HttpClient();
            GetMethod getMethod = new GetMethod(decribeLayerUrl);
            httpClient.executeMethod(getMethod);
            InputStream inputStream = getMethod.getResponseBodyAsStream();
            SAXParserFactory spf = SAXParserFactory.newInstance();
            spf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", FALSE);
            spf.setFeature("http://xml.org/sax/features/validation", FALSE);

            XMLReader xmlReader = spf.newSAXParser().getXMLReader();
            InputSource inputSource = new InputSource(new InputStreamReader(inputStream));
            SAXSource source = new SAXSource(xmlReader, inputSource);
            JAXBContext jaxbContext = JAXBContext.newInstance(WMSDescribeLayerResponse.class);
            WMSDescribeLayerResponse describeLayerResponse = (WMSDescribeLayerResponse) jaxbContext
                    .createUnmarshaller().unmarshal(source);
            return new GPLayerTypeResponse(describeLayerResponse);
        } catch (Exception ex) {
            ex.printStackTrace();
            throw new ServerInternalFault(ex.getMessage());
        }
    }

    /**
     * @param request
     * @return {@link Response}
     * @throws Exception
     */
    @WebMethod(exclude = true)
    @Override
    public Response wmsGetFeatureInfo(GPWMSGetFeatureInfoRequest request) throws Exception {
        if (request == null) {
            throw new IllegalParameterFault(this.wmsMessageSource.getMessage("gp_wms_request.valid",
                    new Object[] { "GPWMSGetFeatureInfoRequest" }, ENGLISH));
        }
        logger.trace("##########################Validating Request -------------------> {}\n", request);
        String message = this.wmsRequestValidator.validate(request, forLanguageTag(request.getLang()));
        if (message != null)
            throw new IllegalParameterFault(message);
        WMSGetFeatureInfoResponse wmsGetFeatureInfoResponse = new WMSGetFeatureInfoResponse();
        for (GPWMSGetFeatureInfoElement wmsGetFeatureInfoElement : request.getWmsFeatureInfoElements()) {
            try {
                IGPWMSConnectorStoreV111 wmsServerConnector = wmsConnectorBuilderPoolV111()
                        .withServerUrl(new URL(wmsGetFeatureInfoElement.getWmsServerURL()))
                        .withPooledConnectorConfig(pooledConnectorConfigBuilder().withMaxTotalConnections(60)
                                .withDefaultMaxPerRoute(30).withMaxRedirect(15).build())
                        .build();
                GPWMSGetFeatureInfoV111Request<Object> wmsGetFeatureInfoV111Request = wmsServerConnector
                        .createGetFeatureInfoRequest();
                GPWMSGetMapBaseRequest wmsGetMapBaseRequest = new WMSGetMapBaseRequest(
                        request.getBoundingBox().toWMSBoundingBox(), wmsGetFeatureInfoElement.getLayers(),
                        request.getCrs(), request.getWidth(), request.getHeight());
                FeatureCollection featureCollection = (FeatureCollection) wmsGetFeatureInfoV111Request
                        .withWMSGetMapRequest(wmsGetMapBaseRequest).withFeatureCount(20)
                        .withQueryLayers(wmsGetFeatureInfoElement.getLayers().stream().toArray(s -> new String[s]))
                        .withX(request.getPoint().getX()).withY(request.getPoint().getY()).withInfoFormat(GML)
                        .getResponse();
                logger.debug("########################FOUND : {}\n", featureCollection);
                wmsGetFeatureInfoResponse.addFeature(featureCollection);
            } catch (Exception ex) {
                FeatureCollection featureCollection = new FeatureCollection();
                Feature feature = new Feature();
                feature.setId("Error for : ".concat(wmsGetFeatureInfoElement.getWmsServerURL()));
                Map<String, Object> properties = new LinkedHashMap<>();
                properties.put("LAYERS_IN_ERROR",
                        wmsGetFeatureInfoElement.getLayers().stream().collect(Collectors.joining(",")));
                properties.put("ERROR_MESSAGE", ex.getMessage());
                featureCollection.add(feature);
                wmsGetFeatureInfoResponse.addFeature(featureCollection);
                ex.printStackTrace();
            }
        }
        return Response.ok(wmsGetFeatureInfoResponse).build();
    }

    private boolean isURLServerAlreadyExists(String serverUrl) {
        return serverDao.findByServerUrl(serverUrl) == null ? false : true;
    }

    private WMSCapabilities getWMSCapabilities(String serverUrl, String token, String authkey)
            throws ResourceNotFoundFault {
        URL serverURL;
        WebMapServer wms;
        WMSCapabilities cap = null;

        String urlServerEdited = this.editServerUrl(serverUrl, token, authkey);
        logger.debug("####################URL Server edited: {}\n", urlServerEdited);

        try {
            serverURL = new URL(urlServerEdited);
            wms = new WebMapServer(serverURL);
            cap = wms.getCapabilities();
        } catch (MalformedURLException e) {
            logger.error("MalformedURLException: " + e);
            throw new ResourceNotFoundFault("Malformed URL");
        } catch (ServiceException e) {
            logger.error("ServiceException: " + e);
            throw new ResourceNotFoundFault("Invalid URL");
        } catch (IOException e) {
            logger.error("IOException: " + e);
            throw new ResourceNotFoundFault("Inaccessible URL");
        } catch (Exception e) {
            logger.error("Exception: " + e);
            throw new ResourceNotFoundFault("Incorrect URL");
        }
        return cap;
    }

    /**
     * @param serverUrl
     * @param token
     * @param authkey
     * @param headers
     * @return {@link WMSCapabilities}
     * @throws ResourceNotFoundFault
     */
    private WMSCapabilities getWMSCapabilitiesAuth(String serverUrl, String token, String authkey,
            List<WMSHeaderParam> headers) throws ResourceNotFoundFault {
        URL serverURL;
        WebMapServer wms;
        WMSCapabilities cap = null;

        String urlServerEdited = this.editServerUrl(serverUrl, token, authkey);
        logger.debug("####################URL Server edited: {}\n", urlServerEdited);
        logger.info("Forward headers:  " + headers);

        try {
            serverURL = new URL(urlServerEdited);
            GeoSDIHttpClient authClient = new GeoSDIHttpClient();
            authClient.setHeaders(headers);
            wms = new WebMapServer(serverURL, authClient);
            cap = wms.getCapabilities();
        } catch (MalformedURLException e) {
            e.printStackTrace();
            throw new ResourceNotFoundFault("Malformed URL");
        } catch (ServiceException e) {
            e.printStackTrace();
            throw new ResourceNotFoundFault("Invalid URL");
        } catch (IOException e) {
            e.printStackTrace();
            throw new ResourceNotFoundFault("Inaccessible URL");
        } catch (Exception e) {
            e.printStackTrace();
            throw new ResourceNotFoundFault("Incorrect URL");
        }
        return cap;
    }

    private String editServerUrl(String urlServer, String token, String authkey) {
        StringBuilder stringBuilder = new StringBuilder(urlServer);
        if (!urlServer.contains("?")) {
            stringBuilder.append("?request=GetCapabilities");
        }

        if (urlServer.contains(GEB)) {
            stringBuilder.append("&access_token=").append(token);
        }
        if (authkey != null) {
            stringBuilder.append("&authkey=").append(authkey);
        }
        return stringBuilder.toString();
    }

    private List<RasterLayerDTO> convertToLayerList(Layer layer, String urlServer) {
        List<RasterLayerDTO> shortLayers = Lists.<RasterLayerDTO>newArrayList();

        RasterLayerDTO raster = this.getRasterAndSubRaster(layer, layer, urlServer);
        shortLayers.add(raster);

        return shortLayers;
    }

    private RasterLayerDTO getRasterAndSubRaster(Layer ancestorLayer, Layer layer, String urlServer) {
        RasterLayerDTO raster = this.convertLayerToRaster(ancestorLayer, layer, urlServer);

        List<Layer> subLayerList = layer.getLayerChildren();
        List<RasterLayerDTO> subRasterList = Lists
                .<RasterLayerDTO>newArrayListWithExpectedSize(subLayerList.size());
        raster.setSubLayerList(subRasterList);

        // ADD subRaster
        for (Layer layerIth : subLayerList) {
            RasterLayerDTO rasterIth = this.getRasterAndSubRaster(ancestorLayer, layerIth, urlServer);
            subRasterList.add(rasterIth);
        }

        return raster;
    }

    private RasterLayerDTO convertLayerToRaster(Layer ancestorLayer, Layer layer, String urlServer) {
        RasterLayerDTO raster = new RasterLayerDTO();

        raster.setUrlServer(this.getUrlServer(urlServer));
        raster.setName(layer.getName());
        raster.setAbstractText(layer.get_abstract());
        if (layer.getTitle() == null || layer.getTitle().trim().equals("")) {
            raster.setTitle(layer.getName());
        } else {
            raster.setTitle(layer.getTitle());
        }
        Map<String, CRSEnvelope> additionalBounds = layer.getBoundingBoxes();

        logger.debug("ADDITIONAL BOUNDS ###############################" + additionalBounds.toString());

        if (!additionalBounds.isEmpty()) {
            if (additionalBounds.containsKey(EPSG_GOOGLE) || additionalBounds.containsKey(EPSG_3857)) {
                CRSEnvelope env = additionalBounds.get(EPSG_GOOGLE);
                if (env == null) {
                    env = additionalBounds.get(EPSG_3857);
                }
                raster.setBbox(this.createBbox(env));
                raster.setSrs(env.getEPSGCode());
            } else {
                raster.setBbox(this.createBbox(layer.getLatLonBoundingBox()));
                raster.setSrs(EPSG_4326);
            }

        } else {
            additionalBounds = ancestorLayer.getBoundingBoxes();
            if (additionalBounds.containsKey(EPSG_GOOGLE) || additionalBounds.containsKey(EPSG_3857)) {
                CRSEnvelope env = additionalBounds.get(EPSG_GOOGLE);
                if (env == null) {
                    env = additionalBounds.get(EPSG_3857);
                }
                raster.setBbox(this.createBbox(env));
                raster.setSrs(env.getEPSGCode());
                logger.info("GOOGLE");
            } else {
                raster.setBbox(this.createBbox(ancestorLayer.getLatLonBoundingBox()));
                raster.setSrs(EPSG_4326);
                logger.info("4326");
            }

        }

        logger.debug("Raster Name: " + raster.getName());
        logger.debug("Raster BBOX: " + raster.getBbox());
        logger.debug("Raster SRS: " + raster.getSrs());

        if (urlServer.contains(GEB)) {
            if (layer.getLatLonBoundingBox() != null) {
                raster.setBbox(this.createBbox(layer.getLatLonBoundingBox()));
            }
            raster.setSrs(EPSG_4326);
        }

        // Set LayerInfo of Raster Ith
        GPLayerInfo layerInfo = new GPLayerInfo();
        layerInfo.setQueryable(layer.isQueryable());
        if (layer.getKeywords() != null) {
            List<String> keywordList = Arrays.asList(layer.getKeywords());
            if (keywordList.size() > 0) {
                layerInfo.setKeywords(keywordList);
            }
        }
        raster.setLayerInfo(layerInfo);

        // Set Styles of Raster Ith
        List<StyleImpl> stylesImpl = layer.getStyles();
        logger.debug("\n*** Layer \"{}\" has {} SubLayers and {} StyleImpl ***", layer.getTitle(),
                layer.getLayerChildren().size(), stylesImpl.size());

        raster.setStyleList(this.createStyleList(stylesImpl));

        return raster;
    }

    private List<ServerDTO> convertToServerList(List<GeoPlatformServer> serverList) {
        List<ServerDTO> shortServers = new ArrayList<ServerDTO>(serverList.size());
        ServerDTO serverDTOIth = null;
        for (GeoPlatformServer server : serverList) {
            serverDTOIth = new ServerDTO(server);
            shortServers.add(serverDTOIth);
        }
        return shortServers;
    }

    private List<String> createStyleList(List<StyleImpl> stylesImpl) {
        List<String> styleList = new ArrayList<String>(stylesImpl.size());
        for (StyleImpl style : stylesImpl) {
            styleList.add(style.getName());
        }
        return styleList;
    }

    private GPBBox createBbox(CRSEnvelope env) {
        return ((env != null) ? new GPBBox(env.getMinX(), env.getMinY(), env.getMaxX(), env.getMaxY())
                : new GPBBox(-179, -89, 179, 89));
    }

    private String getUrlServer(String urlServer) {
        int index = -1;
        if (urlServer.contains(".map")) {
            index = urlServer.indexOf(".map") + 4;
        } else if (urlServer.contains("mapserv.exe") || urlServer.contains("mapserver")
                || urlServer.contains("mapserv") || urlServer.contains("usertoken") || urlServer.contains("map")) {
            index = urlServer.indexOf("&");
        } else {
            index = urlServer.indexOf("?");
            // index += 1;
        }
        if (index != -1) {
            String newUrl = urlServer.substring(0, index);
            return newUrl;
        }
        return urlServer;
    }

    private double longToSphericalMercatorX(double x) {
        return (x / 180.0) * 20037508.34;
    }

    private double latToSphericalMercatorY(double y) {
        if (y > 85.05112) {
            y = 85.05112;
        }

        if (y < -85.05112) {
            y = -85.05112;
        }

        y = (Math.PI / 180.0) * y;
        double tmp = Math.PI / 4.0 + y / 2.0;
        return 20037508.34 * Math.log(Math.tan(tmp)) / Math.PI;
    }
}