/*
* Copyright (c) 2014 Oculus Info Inc.
* http://www.oculusinfo.com/
*
* Released under the MIT License.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is furnished to do
* so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.oculusinfo.binning.impl;
import com.oculusinfo.binning.BinIndex;
import com.oculusinfo.binning.TileIndex;
import com.oculusinfo.binning.TilePyramid;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.io.Serializable;
import java.util.Collection;
/**
* Area-of-Interest tile pyramid
*
* A tile pyramid that represents a predefined area, breaking it up linearly and
* evenly.
*
* @author Jesse McGeachie
*/
public class AOITilePyramid implements TilePyramid, Serializable {
private static final long serialVersionUID = 1L;
double _minX;
double _maxX;
double _minY;
double _maxY;
double _recipDiffX;
double _recipDiffY;
public AOITilePyramid(double minX, double minY, double maxX, double maxY){
_minX = minX;
_maxX = maxX;
_minY = minY;
_maxY = maxY;
_recipDiffX = 1.0/(_maxX-_minX);
_recipDiffY = 1.0/(_maxY-_minY);
}
@Override
public Rectangle2D getBounds() {
return new Rectangle2D.Double( _minX, _minY ,_maxX - _minX, _maxY - _minY);
}
@Override
public String getProjection () {
return "EPSG:4326";
}
@Override
public String getTileScheme () {
return "TMS";
}
@Override
public TileIndex rootToTile (Point2D point, int level) {
return rootToTile(point.getX(), point.getY(), level, 256, 256);
}
@Override
public TileIndex rootToTile (Point2D point, int level, int xBins, int yBins) {
return rootToTile(point.getX(), point.getY(), level, xBins, yBins);
}
@Override
public TileIndex rootToTile (double x, double y, int level) {
return rootToTile(x, y, level, 256, 256);
}
@Override
public TileIndex rootToTile (double x, double y, int level, int xBins, int yBins) {
long numDivs = 1L << level;
int tileX = (int) Math.floor(numDivs*(x-_minX)*_recipDiffX);
int tileY = (int) Math.floor(numDivs*(y-_minY)*_recipDiffY);
// int tileX = (int) Math.floor(numDivs*(x-_minX)/(_maxX - _minX));
// int tileY = (int) Math.floor(numDivs*(y-_minY)/(_maxY - _minY));
return new TileIndex(level, tileX, tileY, xBins, yBins);
}
@Override
public BinIndex rootToBin (Point2D point, TileIndex tile) {
return rootToBin(point.getX(), point.getY(), tile);
}
@Override
public BinIndex rootToBin (double x, double y, TileIndex tile) {
long numDivs = 1L << tile.getLevel();
int binX = (int) Math.floor((numDivs*(x-_minX)*_recipDiffX - tile.getX())*tile.getXBins());
int binY = (int) Math.floor((numDivs*(y-_minY)*_recipDiffY - tile.getY())*tile.getYBins());
// int pow2 = 1 << tile.getLevel();
// double tileXSize = (_maxX-_minX)/pow2;
// double tileYSize = (_maxY-_minY)/pow2;
//
// double xInTile = x-_minX - tile.getX()*tileXSize;
// double yInTile = y-_minY - tile.getY()*tileYSize;
//
// int binX = (int) Math.floor(xInTile*tile.getXBins()/tileXSize);
// int binY = (int) Math.floor(yInTile*tile.getYBins()/tileYSize);
return new BinIndex(binX, tile.getYBins()-1-binY);
}
@Override
public Rectangle2D getTileBounds (TileIndex tile) {
long pow2 = 1L << tile.getLevel();
double tileXSize = (_maxX-_minX)/pow2;
double tileYSize = (_maxY-_minY)/pow2;
return new Rectangle2D.Double(_minX+tileXSize*tile.getX(),
_minY+tileYSize*tile.getY(),
tileXSize, tileYSize);
}
@Override
public Rectangle2D getBinBounds(TileIndex tile, BinIndex bin) {
long pow2 = 1L << tile.getLevel();
double tileXSize = (_maxX-_minX)/pow2;
double tileYSize = (_maxY-_minY)/pow2;
double binXSize = tileXSize/tile.getXBins();
double binYSize = tileYSize/tile.getYBins();
int adjustedBinY = tile.getYBins()-1-bin.getY();
return new Rectangle2D.Double(_minX+tileXSize*tile.getX()+binXSize*bin.getX(),
_minY+tileYSize*tile.getY()+binYSize*adjustedBinY,
binXSize, binYSize);
}
@Override
public double getBinOverlap (TileIndex tile, BinIndex bin, Rectangle2D area) {
Rectangle2D binBounds = getBinBounds(tile, bin);
// We actually work in lat/lon, so this is what we want.
double minx = (area.getMinX()-binBounds.getMinX())/binBounds.getWidth();
double maxx = (area.getMaxX()-binBounds.getMinX())/binBounds.getWidth();
double miny = (area.getMinY()-binBounds.getMinY())/binBounds.getHeight();
double maxy = (area.getMaxY()-binBounds.getMinY())/binBounds.getHeight();
minx = Math.min(Math.max(minx, 0.0), 1.0);
maxx = Math.min(Math.max(maxx, 0.0), 1.0);
miny = Math.min(Math.max(miny, 0.0), 1.0);
maxy = Math.min(Math.max(maxy, 0.0), 1.0);
return Math.abs((maxx-minx) * (maxy-miny));
}
@Override
public Collection<TileIndex> getBestTiles(Rectangle2D bounds) {
// TODO Auto-generated method stub
return null;
}
@Override
public int hashCode () {
return (int) Math.round(_minX * 49980419 + _maxX * 54907427 +
_minY * 62059399 + _maxY * 67432721);
}
@Override
public boolean equals (Object other) {
if (this == other) return true;
if (null == other) return false;
if (!(other instanceof AOITilePyramid)) return false;
AOITilePyramid that = (AOITilePyramid) other;
double epsilon = 1E-12;
if (Math.abs(this._minX - that._minX) > epsilon) return false;
if (Math.abs(this._maxX - that._maxX) > epsilon) return false;
if (Math.abs(this._minY - that._minY) > epsilon) return false;
if (Math.abs(this._maxY - that._maxY) > epsilon) return false;
return true;
}
}