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

Source Code of org.locationtech.udig.catalog.wmsc.server.WMSTileSet

/* 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.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.geotools.data.ows.AbstractOpenWebService;
import org.geotools.data.ows.CRSEnvelope;
import org.geotools.geometry.jts.ReferencedEnvelope;
import org.geotools.referencing.CRS;
import org.geotools.util.ObjectCache;
import org.geotools.util.ObjectCaches;
import org.opengis.referencing.crs.CoordinateReferenceSystem;

import com.vividsolutions.jts.geom.Envelope;

/**
* Class represents a WMSC tile set.  See:
* <p>
* http://wiki.osgeo.org/wiki/WMS_Tiling_Client_Recommendation#GetCapabilities_Responses
</p>
* @author Emily Gouge, Graham Davis (Refractions Research, Inc.)
* @since 1.2.0
*/
public class WMSTileSet implements TileSet {

    /** Not fear factor! The factor applied to determine what scale zoom levels should switch at */
    private static final double SCALE_FACTOR = 0.2;

    /** a unique identifies */
    private int id;
   
    /** the AbstractOpenWebService **/
    private AbstractOpenWebService<?,?> server;

  /** Coordinate Reference System of the Tiles */
    private CoordinateReferenceSystem crs;
    /**
     * SRSName (usually of the format "EPSG:4326")
     */
    private String epsgCode;

    /** Data bounding box **/
    private ReferencedEnvelope bboxSrs;

    /** size of tiles - in pixels (often 512) */
    private int width;

    /** size of tiles - in pixels (often 512) */
    private int height;

    /** image format - MIME type? */
    private String format;

    /** List of layers (separated by comma?) */
    private String layers;

    /** Comma seperated list of resolutions in units per pixel */
    private String resolutions;

    /** Parsed out resolutions - the strict values from resolutions */
    private double[] dresolutions;

    /**
     * Parsed out resolutions - the relaxed values so we do not request more data that can be drawn
     * per pixel. (Often this amounts 1.2 real pixels per on screen pixel - see SCALE_FACTOR
     */
    private double[] mresolutions; // the scale at which we will switch zoom levels

    /** styles */
    private String styles;

    /** map of tiles
     * NOTE:  This is a WEAKHashMap because we don't want to run out of
     * memory storing all the tiles.  The garbage collector should clean
     * up less-used keys and their objects as necessary.
     **/
    ObjectCache tiles = ObjectCaches.create("soft", 50); //Tiles that are on the screen //$NON-NLS-1$ 

    public WMSTileSet() {
        updateID();
    }

    /* (non-Javadoc)
   * @see org.locationtech.udig.catalog.wmsc.server.TileSet#setCoorindateReferenceSystem(java.lang.String)
   */
    public void setCoorindateReferenceSystem( String epsg ) {
        this.epsgCode = epsg;
        try {
            this.crs = CRS.decode(epsg);
        } catch (Exception ex) {
            // WmsPlugin.trace("Cannot decode tile epsg code: " + epsg, ex); //$NON-NLS-1$
        }
        updateID();
    }

    /* (non-Javadoc)
   * @see org.locationtech.udig.catalog.wmsc.server.TileSet#getCoordinateReferenceSystem()
   */
    public CoordinateReferenceSystem getCoordinateReferenceSystem() {
        return this.crs;
    }

    /* (non-Javadoc)
   * @see org.locationtech.udig.catalog.wmsc.server.TileSet#setBoundingBox(org.geotools.data.ows.CRSEnvelope)
   */
    public void setBoundingBox( CRSEnvelope bbox ) {
        CoordinateReferenceSystem crs = null;
        try {
            crs = CRS.decode(bbox.getEPSGCode());
        } catch (Exception ex) {
            System.out.println("Cannot decode tile epsg code: " + bbox.getEPSGCode()); //$NON-NLS-1$
        }
        bboxSrs = new ReferencedEnvelope(bbox.getMinX(), bbox.getMaxX(), bbox.getMinY(), bbox
                .getMaxY(), crs);
        updateID();
    }
    /* (non-Javadoc)
   * @see org.locationtech.udig.catalog.wmsc.server.TileSet#setWidth(int)
   */
    public void setWidth( int width ) {
        this.width = width;
        updateID();
    }

    /* (non-Javadoc)
   * @see org.locationtech.udig.catalog.wmsc.server.TileSet#setStyles(java.lang.String)
   */
    public void setStyles( String styles ) {
        this.styles = styles;
        updateID();
    }
    /* (non-Javadoc)
   * @see org.locationtech.udig.catalog.wmsc.server.TileSet#setHeight(int)
   */
    public void setHeight( int height ) {
        this.height = height;
        updateID();
    }

    /* (non-Javadoc)
   * @see org.locationtech.udig.catalog.wmsc.server.TileSet#setFormat(java.lang.String)
   */
    public void setFormat( String format ) {
        this.format = format;
        updateID();
    }
    /* (non-Javadoc)
   * @see org.locationtech.udig.catalog.wmsc.server.TileSet#setLayers(java.lang.String)
   */
    public void setLayers( String layers ) {
        this.layers = layers;
        updateID();
    }

    /* (non-Javadoc)
   * @see org.locationtech.udig.catalog.wmsc.server.TileSet#setResolutions(java.lang.String)
   */
    public void setResolutions( String res ) {
        this.resolutions = res;
        String[] sres = resolutions.split(" "); //$NON-NLS-1$
        double[] dres = new double[sres.length];
        for( int i = 0; i < sres.length; i++ ) {
            dres[i] = Double.parseDouble(sres[i]);
        }
        this.dresolutions = dres;

        // compute resolutions where the zoom will switch
        mresolutions = new double[dresolutions.length - 1];
        for( int i = 0; i < dresolutions.length - 1; i++ ) {
            mresolutions[i] = ((dresolutions[i] - dresolutions[i + 1]) * SCALE_FACTOR)
                    + dresolutions[i + 1];
        }
        updateID();
    }

    /* (non-Javadoc)
   * @see org.locationtech.udig.catalog.wmsc.server.TileSet#getNumLevels()
   */
    public int getNumLevels() {
        return this.dresolutions.length;
    }

    /**
     * Given a scale factor for the resulting image it finds the zoom level that matches the scale
     * factor best.
     *
     * @param scale
     * @return resolution of the zoom level that best matches the scale factor
     */
    private double findAppropriateZoomLevel( double scale ) {

        if (scale > mresolutions[0]) {
            return dresolutions[0];
        }
        for( int i = 1; i < mresolutions.length; i++ ) {
            if (mresolutions[i - 1] >= scale && mresolutions[i] < scale) {
                return dresolutions[i];
            }
        }
        // maximum zoom
        return this.dresolutions[this.dresolutions.length - 1];
    }

    /**
     * Creates a wmsc query string from the given bounds.
     * <p>
     * This string is *very* carefully constructed with the assumption that all the getFormat(),
     * getEPSG(), getLayers() methods return Strings that are valid , consistent and ready to go.
     *
     * @param tile
     * @return
     */
    @SuppressWarnings("nls")
    public String createQueryString( Envelope tile ) {
        String query = "service=WMS&request=getMap&tiled=true&width="+width+"&height="+height+"&format=" + getFormat() + "&srs=" + getEPSGCode()
                + "&layers=" + getLayers() + "&bbox=" + tile.getMinX() + "," + tile.getMinY() + ","
                + tile.getMaxX() + "," + tile.getMaxY() + "&styles=" + getStyles();
        return query;
    }

    /* (non-Javadoc)
   * @see org.locationtech.udig.catalog.wmsc.server.TileSet#getTilesFromViewportScale(com.vividsolutions.jts.geom.Envelope, double)
   */
    public Map<String, Tile> getTilesFromViewportScale( Envelope bounds, double viewportScale ) {
        double scale = findAppropriateZoomLevel(viewportScale);
        return getTilesFromZoom(bounds, scale);
    }
   
    /* (non-Javadoc)
   * @see org.locationtech.udig.catalog.wmsc.server.TileSet#getTilesFromZoom(com.vividsolutions.jts.geom.Envelope, double)
   */
    public Map<String, Tile> getTilesFromZoom( Envelope bounds, double zoom ) {

        double xscale = width * zoom;
        double value = bounds.getMinX() - bboxSrs.getMinX();
       
        double minx = Math.floor(value / xscale) * xscale + bboxSrs.getMinX();
        value = bounds.getMaxX() - bboxSrs.getMinX();
        double maxx = Math.ceil(value / xscale) * xscale + bboxSrs.getMinX();

        double yscale = height * zoom;
        value = bounds.getMinY() - bboxSrs.getMinY();
        double miny = Math.floor(value / yscale) * yscale + bboxSrs.getMinY();
        value = bounds.getMaxY() - bboxSrs.getMinY();
        double maxy = Math.ceil(value / yscale) * yscale + bboxSrs.getMinY();
        Map<String, Tile> viewportTiles = new HashMap<String, Tile>();
     
        int xNum = (int)Math.round((maxx- minx) / xscale);
        int yNum = (int)Math.round((maxy - miny) / yscale);
        for (int x = 0; x < xNum; x++){
            double xmin = roundDouble(x * xscale + minx);
            double xmax = roundDouble((x+1) * xscale + minx);
            for (int y = 0; y < yNum; y ++){
                double ymin = roundDouble(y * yscale + miny);
                double ymax = roundDouble((y+1) * yscale + miny);
                //Envelope e = new Envelope(x*xscale+minx, (x+1) * xscale+minx, y * yscale+miny, (y +1)* yscale + miny);
                Envelope e = new Envelope(xmin, xmax, ymin, ymax);
                if (e.getMaxX() <= bboxSrs.getMinX() || e.getMinX() >= bboxSrs.getMaxX()
                        || e.getMaxY() <= bboxSrs.getMinY() || e.getMinY() >= bboxSrs.getMaxY()) {
                    // outside of bounds ignore
                } else {
                    // tile is within the bounds, create it if necessary and
                    // add it to the map
                    String tileid = WMSTile.buildId(e, zoom);
                    Tile tile;
                    if (tiles.peek(tileid) == null || tiles.get(tileid) == null) {
                        tile = new WMSTile(server, this, e, zoom);
                        tiles.put(tileid, tile);
                        // create the tile position within the tilerange grid for this scale
                        double topleft_x = bboxSrs.getMinX();
                        double topleft_y = bboxSrs.getMaxY();
                        double tileleft_x = e.getMinX();
                        double tileleft_y = e.getMaxY();

                        double spacex = tileleft_x - topleft_x; // x is left to right
                        double spacey = topleft_y - tileleft_y; // y is top to bottom

                        int posx = (int) Math.round(spacex / xscale);
                        int posy = (int) Math.round(spacey / yscale);

                        String position = posx + "_" + posy; //$NON-NLS-1$
                        tile.setPosition(position);
                    } else {
                        tile = (Tile) tiles.get(tileid);
                    }
                    viewportTiles.put(tileid, tile);
                }
            }
        }
        return viewportTiles;
    }   
   
   
    /**
     * This function takes the last two digits (8 bits) of a double and 0's them.
     *
     *
     * @param number
     * @return
     */
    private static double roundDouble(double number){
        Long xBits = Double.doubleToLongBits(number);
        //zeroLowerBits
        int nBits = 8;
        long invMask = (1L << nBits) - 1L;
        long mask =~ invMask;
        xBits &= mask;  
        return Double.longBitsToDouble(xBits);
    }
   
    /**
     *  Break up the bounds for this zoom level into a list of bounds so that no single
     *  bounds has more than 1024 tiles in it.
     * 
   * @see org.locationtech.udig.catalog.wmsc.server.TileSet#getBoundsListForZoom(com.vividsolutions.jts.geom.Envelope, double)
   */
    public List<Envelope> getBoundsListForZoom( Envelope bounds, double zoom ) {

      int maxTilesPerBound = 1024;
      List<Envelope> boundsList = new ArrayList<Envelope>();

        double xscale = width * zoom;
        double value = bounds.getMinX() - bboxSrs.getMinX();
       
        double minx = Math.floor(value / xscale) * xscale + bboxSrs.getMinX();
        value = bounds.getMaxX() - bboxSrs.getMinX();
        double maxx = Math.ceil(value / xscale) * xscale + bboxSrs.getMinX();

        double yscale = height * zoom;
        value = bounds.getMinY() - bboxSrs.getMinY();
        double miny = Math.floor(value / yscale) * yscale + bboxSrs.getMinY();
        value = bounds.getMaxY() - bboxSrs.getMinY();
        double maxy = Math.ceil(value / yscale) * yscale + bboxSrs.getMinY();
        long tilesPerRow = Math.round((maxx-minx) / xscale);
        long tilesPerCol = Math.round((maxy-miny) / yscale);
        long totalTiles = tilesPerCol * tilesPerRow;
     
         // if there are not enough tiles to make 1024 for this zoom and bounds, then
        // return the single bounds
      if ( totalTiles <=  maxTilesPerBound ) {
        boundsList.add(bounds);
        return boundsList;
      }
     
      // create the size of each bounds
      double scaleDownFactor = Math.ceil(totalTiles / maxTilesPerBound);
      double boundsWidth = Math.ceil(tilesPerRow / scaleDownFactor) * xscale;
      double boundsHeight = Math.ceil(tilesPerCol / scaleDownFactor) * yscale;
     
      // create each bounds
      double x = minx;
      while (x <= maxx) {
        double y = miny;
        while (y <= maxy) {
          double x2 = x + boundsWidth;
          if (x2 > maxx) x2 = maxx;
          double y2 = y + boundsHeight;
          if (y2 > maxy) y2 = maxy;
          boundsList.add(new Envelope(x, x2, y, y2));
          y += boundsHeight;
        }
        x += boundsWidth;
      }
       
        return boundsList;
    }
   
    /* (non-Javadoc)
   * @see org.locationtech.udig.catalog.wmsc.server.TileSet#getTileCount((com.vividsolutions.jts.geom.Envelope, double))
   */
    public long getTileCount( Envelope bounds, double zoom ) {
        double xscale = width * zoom;
        double value = bounds.getMinX() - bboxSrs.getMinX();
       
        double minx = Math.floor(value / xscale) * xscale + bboxSrs.getMinX();
        value = bounds.getMaxX() - bboxSrs.getMinX();
        double maxx = Math.ceil(value / xscale) * xscale + bboxSrs.getMinX();

        double yscale = height * zoom;
        value = bounds.getMinY() - bboxSrs.getMinY();
        double miny = Math.floor(value / yscale) * yscale + bboxSrs.getMinY();
        value = bounds.getMaxY() - bboxSrs.getMinY();
        double maxy = Math.ceil(value / yscale) * yscale + bboxSrs.getMinY();
        long tilesPerRow = Math.round((maxx-minx) / xscale);
        long tilesPerCol = Math.round((maxy-miny) / yscale);
        return tilesPerCol * tilesPerRow;
    }

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

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

    /* (non-Javadoc)
   * @see org.locationtech.udig.catalog.wmsc.server.TileSet#getFormat()
   */
    public String getFormat() {
        return this.format;
    }
    /* (non-Javadoc)
   * @see org.locationtech.udig.catalog.wmsc.server.TileSet#getEPSGCode()
   */
    public String getEPSGCode() {
        return this.epsgCode;
    }
    /* (non-Javadoc)
   * @see org.locationtech.udig.catalog.wmsc.server.TileSet#getWidth()
   */
    public int getWidth() {
        return this.width;
    }
    /* (non-Javadoc)
   * @see org.locationtech.udig.catalog.wmsc.server.TileSet#getHeight()
   */
    public int getHeight() {
        return this.height;
    }
    /* (non-Javadoc)
   * @see org.locationtech.udig.catalog.wmsc.server.TileSet#getBounds()
   */
    public ReferencedEnvelope getBounds() {
        return this.bboxSrs;
    }

    /* (non-Javadoc)
   * @see org.locationtech.udig.catalog.wmsc.server.TileSet#getId()
   */
    public int getId() {
        return this.id;
    }
   
    /* (non-Javadoc)
   * @see org.locationtech.udig.catalog.wmsc.server.TileSet#getResolutions()
   */
    /**
     * @returns a copy of the resolutions array
     */
    public double[] getResolutions(){
        double[] d = new double[dresolutions.length];
        System.arraycopy(dresolutions, 0, d, 0, d.length);
        return d;
    }
   
    /**
     * Create a unique identifier for the tileset from the various strings that define the tile set.
     */
    private void updateID(){
        //compute a hashset from the attributes;
        StringBuffer sb = new StringBuffer();
       
        if (this.epsgCode != null)
            sb.append(epsgCode);
       
        if (this.format != null)
            sb.append(this.format);
       
        if (this.layers != null)
            sb.append(layers);
       
        if (this.resolutions != null)
            sb.append(this.resolutions);
        if (this.styles != null)
            sb.append(this.styles);
        if (this.bboxSrs != null){
            sb.append(this.bboxSrs.getMinX());
            sb.append(this.bboxSrs.getMaxX());
            sb.append(this.bboxSrs.getMinY());
            sb.append(this.bboxSrs.getMaxY());
            sb.append(this.bboxSrs.getMinX());
        }
        if (this.bboxSrs != null && this.bboxSrs.getCoordinateReferenceSystem() != null) {
            sb.append(this.bboxSrs.getCoordinateReferenceSystem().toString().hashCode());
        }
       
        sb.append(this.width);
        sb.append(this.height);
       
        this.id = sb.toString().hashCode();
    }
   
    public AbstractOpenWebService<?,?> getServer() {
    return server;
  }

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

Related Classes of org.locationtech.udig.catalog.wmsc.server.WMSTileSet

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.