Package org.locationtech.udig.catalog.wmsc.server

Source Code of org.locationtech.udig.catalog.wmsc.server.WMSTile$GetMapRequest

/* uDig - User Friendly Desktop Internet GIS client
* http://udig.refractions.net
* (C) 2008, Refractions Research Inc.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* (http://www.eclipse.org/legal/epl-v10.html), and the Refractions BSD
* License v1.0 (http://udig.refractions.net/files/bsd3-v10.html).
*/
package org.locationtech.udig.catalog.wmsc.server;

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.util.Random;

import javax.imageio.ImageIO;

import org.locationtech.udig.catalog.internal.wms.WmsPlugin;

import org.eclipse.core.runtime.IProgressMonitor;
import org.geotools.data.ows.AbstractOpenWebService;
import org.geotools.data.ows.AbstractRequest;
import org.geotools.data.ows.HTTPClient;
import org.geotools.data.ows.HTTPResponse;
import org.geotools.data.ows.Response;
import org.geotools.ows.ServiceException;

import com.vividsolutions.jts.geom.Envelope;

/**
* Represents one tile in a tileset from a WMS Server. Has a unique id, bounds, scale and a
* BufferedImage. All other data such as the format, crs, etc are stored as part of the tileset.
*
* @author GDavis
* @since 1.2.0
*/
public class WMSTile implements Tile {

    private final static boolean testing = false; // for testing output

    public static final int INERROR = 1;
    public static final int OK = 0;

    private String id; // id = "<scale>_<minx>_<maxy>_<maxy>_<miny>"
    private String position; // pos = x_y tile position within tilerange grid for this scale
    private static String ID_DIVIDER = "_"; //$NON-NLS-1$
    private WMSTileSet tileset;
    private AbstractOpenWebService< ? , ? > server;
    private BufferedImage image;
    private Object imageLock = new Object();
    private Envelope bounds;
    private double scale;

    /**
     * The state of the tile; if something happens while fetching the image and the image isn't
     * returned properly we set the state to error. If another request is made for this tile we'll
     * try re-requesting the tile.
     */
    private int state = OK;

    /**
     * The time this Tile is allowed to be cached before being forced to refresh from the server
     */
    private String maxCacheAge;

    public WMSTile( AbstractOpenWebService< ? , ? > server, WMSTileSet tileset, Envelope bounds,
            double scale ) {
        this.server = server;
        this.tileset = tileset;
        this.bounds = bounds;
        this.scale = scale;
        this.id = buildId(bounds, scale);
    }

    /**
     * Builds a tile id in the form of: id = "<scale>_<minx>_<maxy>_<maxy>_<miny>"
     *
     * @param bbox
     * @param scale
     * @return tile id string
     */
    public static String buildId( Envelope bbox, double scale ) {
        if (bbox == null) {
            return null;
        }
        return scale + ID_DIVIDER + bbox.getMinX() + ID_DIVIDER + bbox.getMaxY() + ID_DIVIDER
                + bbox.getMaxY() + ID_DIVIDER + bbox.getMinY();
    }

    /*
     * (non-Javadoc)
     * @see org.locationtech.udig.catalog.wmsc.server.Tile#getId()
     */
    public String getId() {
        if (id == null) {
            id = buildId(bounds, scale);
        }
        return id;
    }

    public TileSet getTileSet() {
        return tileset;
    }

    /*
     * (non-Javadoc)
     * @see org.locationtech.udig.catalog.wmsc.server.Tile#getBounds()
     */
    public Envelope getBounds() {
        return bounds;
    }

    /*
     * (non-Javadoc)
     * @see org.locationtech.udig.catalog.wmsc.server.Tile#getScale()
     */
    public double getScale() {
        return scale;
    }

    /**
     * Set the buffered image while locking and set the state of the tile.
     *
     * @see org.locationtech.udig.catalog.wmsc.server.Tile#setBufferedImage(java.awt.image.BufferedImage)
     */
    public void setBufferedImage( BufferedImage im ) {
        Object lock = getTileLock();
        synchronized (lock) {
            setBufferedImageInternal(im);
            if (getBufferedImage() != null) {
                setTileState(WMSTile.OK);
            } else {
                setTileState(WMSTile.INERROR);
            }
        }
    }

    /**
     * See the buffered image without locking.
     */
    private void setBufferedImageInternal( BufferedImage im ) {
        this.image = im;
    }

    /*
     * (non-Javadoc)
     * @see org.locationtech.udig.catalog.wmsc.server.Tile#getBufferedImage()
     */
    public BufferedImage getBufferedImage() {
        return image;
    }

    public Object getTileLock() {
        return imageLock;
    }

    public int compareTo( Tile other ) {
        // id contains scale and bounds so compare with that
        return getId().compareTo(other.getId());
    }

    public boolean equals( Object arg0 ) {
        if (arg0 instanceof Tile) {
            Tile tile = (Tile) arg0;
            // id contains scale and bounds so compare with that
            if (getId().equals(tile.getId())) {
                return true;
            } else {
                return false;
            }
        }
        return super.equals(arg0);
    }

    /*
     * (non-Javadoc)
     * @see org.locationtech.udig.catalog.wmsc.server.Tile#getTileState()
     */
    public int getTileState() {
        return this.state;
    }

    /*
     * (non-Javadoc)
     * @see org.locationtech.udig.catalog.wmsc.server.Tile#setTileState(int)
     */
    public void setTileState( int state ) {
        this.state = state;
    }

    /*
     * (non-Javadoc)
     * @see org.locationtech.udig.catalog.wmsc.server.Tile#setPosition(java.lang.String)
     */
    public void setPosition( String pos ) {
        this.position = pos;
    }

    /*
     * (non-Javadoc)
     * @see org.locationtech.udig.catalog.wmsc.server.Tile#getPosition()
     */
    public String getPosition() {
        return this.position;
    }

    /*
     * (non-Javadoc)
     * @see
     * org.locationtech.udig.catalog.wmsc.server.Tile#loadTile(org.eclipse.core.runtime.IProgressMonitor
     * )
     */
    public boolean loadTile( IProgressMonitor monitor ) {
        // build request
        if (server == null && tileset != null) {
            // try getting server from the tileset
            setServer(tileset.getServer());
        }
        if (server == null || tileset == null) {
            WmsPlugin.log("error, tile not setup with a server and/or tileset", null); //$NON-NLS-1$
            createErrorImage();
            return false;
        }
        // String baseUrl = server.buildBaseTileRequestURL();
        Envelope env = getBounds();
        URL req = null;
        try {
            URI rawURI = server.getInfo().getSource();
            String baseUrl = rawURI.getScheme()
                    + "://" + rawURI.getHost() + ":" + rawURI.getPort() + "" + rawURI.getPath() + "?"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
            req = new URL(
                    baseUrl
                            + "&version=" + server.getCapabilities().getVersion() + "&" + tileset.createQueryString(env)); //$NON-NLS-1$ //$NON-NLS-2$
        } catch (MalformedURLException e2) {
            WmsPlugin.log("error building request URL:", e2); //$NON-NLS-1$
            return false;
        }
        GetMapRequest request = new GetMapRequest(req);

        // get a lock for this tile and only fetch a new image if
        // it does not have one set already when the lock is obtained.
        // TODO: add support for re-fetching expired images too
        Object lock = getTileLock();
        BufferedImage bufImage = null;
        if (testing) {
            System.out.println("getting lock: " + getId()); //$NON-NLS-1$
        }
        synchronized (lock) {
            if (testing) {
                System.out.println("got lock: " + getId()); //$NON-NLS-1$
            }
            if ((getBufferedImage() != null && getTileState() != WMSTile.INERROR)
                    || monitor.isCanceled()) {
                // tile image already set
                monitor.setCanceled(true);
                if (testing) {
                    System.out.println("REQUEST CANCELLED - REMOVING lock: " + getId()); //$NON-NLS-1$
                }
                return true;
            }
            InputStream inputStream = null;
            try {
                HTTPResponse response = issueRequest(request);
                String cacheControl = extractHeaderCacheControl(response);
                setMaxCacheAge(cacheControl);
                inputStream = response.getResponseStream();
                // simulate latency if testing
                if (testing) {
                    Random rand = new Random();
                    long delay = rand.nextInt(5000); // delay 1-5 secs
                    System.out.println("request delaying for: " + delay); //$NON-NLS-1$
                    Thread.sleep(delay); // simulate latency
                }
                WmsPlugin.log("WMSC GetMap: " + req.toString(), null); //$NON-NLS-1$
                bufImage = ImageIO.read(inputStream);
                if (bufImage != null) {
                    setBufferedImageInternal(bufImage);
                    setTileState(WMSTile.OK);
                } else {
                    // create an error buffered image
                    setBufferedImageInternal(createErrorImage());
                    setTileState(WMSTile.INERROR);
                }
            } catch (Exception e1) {
                // create an error buffered image
                setBufferedImageInternal(createErrorImage());
                setTileState(WMSTile.INERROR);
                WmsPlugin.log("error loading tile, placeholder created:", e1); //$NON-NLS-1$
            } catch (Throwable t) {
                // create an error buffered image
                setBufferedImageInternal(createErrorImage());
                setTileState(WMSTile.INERROR);
                WmsPlugin.log("error loading tile, placeholder created:", t); //$NON-NLS-1$
            } finally {
                if (inputStream != null) {
                    try {
                        inputStream.close();
                    } catch (IOException e) {
                        WmsPlugin.log("failed to close input stream!!!", e); //$NON-NLS-1$
                    }
                }
            }
        } // end synchronized block

        if (testing) {
            System.out.println("REMOVING lock: " + getId()); //$NON-NLS-1$
        }

        // if we successfully set the buffered image to something, return true
        if (getBufferedImage() != null) {
            return true;
        }

        // if we get here, something prevented us from setting an image
        return false;
    }

    /**
     * Extract the cache-control header from the servers response object in the form: 'max-age=3600,
     * must-revalidate'
     *
     * @param response
     * @return
     */
    @SuppressWarnings("nls")
    private String extractHeaderCacheControl( HTTPResponse response ) {
        String cacheControl = response.getResponseHeader("Cache-Control");
        if (cacheControl != null && !"".equals(cacheControl)) {
            String[] split = cacheControl.split(",");
            String maxAge = split[0];
            if (maxAge != null && !"".equals(maxAge)) {
                return maxAge.split("=")[1];
            }
        }
        return null;
    }

    public HTTPResponse issueRequest( GetMapRequest request ) throws IOException, ServiceException {
        URL finalURL = request.getFinalURL();
        if (finalURL.getHost() == null) {
            // System.out.prinln("Poor WMS-C configuration - no host provided by "+ finalURL );
            throw new NullPointerException("No host provided by " + finalURL); //$NON-NLS-1$
        }

        final HTTPClient httpClient = server.getHTTPClient();
        final HTTPResponse httpResponse;

        if (request.requiresPost()) {

            final String postContentType = request.getPostContentType();

            ByteArrayOutputStream out = new ByteArrayOutputStream();
            request.performPostOutput(out);
            InputStream in = new ByteArrayInputStream(out.toByteArray());

            try {
                httpResponse = httpClient.post(finalURL, in, postContentType);
            } finally {
                in.close();
            }
        } else {
            httpResponse = httpClient.get(finalURL);
        }

        return httpResponse;
    }

    private BufferedImage createErrorImage() {
        BufferedImage bf = new BufferedImage(tileset.getWidth(), tileset.getHeight(),
                BufferedImage.TYPE_INT_ARGB);
        Graphics2D g = bf.createGraphics();
        g.setColor(Color.RED);
        g.drawLine(0, 0, tileset.getWidth(), tileset.getHeight());
        g.drawLine(0, tileset.getHeight(), tileset.getWidth(), 0);
        return bf;
    }

    public AbstractOpenWebService< ? , ? > getServer() {
        return server;
    }

    public void setServer( AbstractOpenWebService< ? , ? > server ) {
        this.server = server;
    }

    @Override
    public void setMaxCacheAge( String maxCacheAge ) {
        this.maxCacheAge = maxCacheAge;
    }

    @Override
    public String getMaxCacheAge() {
        return this.maxCacheAge;
    }

    /**
     * Class for sending map requests
     *
     * @author GDavis
     * @since 1.1.0
     */
    static class GetMapRequest extends AbstractRequest {
        private URL url;

        public GetMapRequest( URL url ) {
            super(url, null);
            this.url = url;
        }
        public Response createResponse( HTTPResponse response ) throws ServiceException,
                IOException {
            return new Response(response){
            };
        }

        public URL getFinalURL() {
            return this.url;
        }

        @Override
        protected void initRequest() {
        }
        @Override
        protected void initService() {
        }
        @Override
        protected void initVersion() {
        }
    }
}
TOP

Related Classes of org.locationtech.udig.catalog.wmsc.server.WMSTile$GetMapRequest

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.