package com.qozix.mapview.zoom;
import java.util.HashSet;
import android.graphics.Rect;
public class ZoomManager {
private static final double BASE_2 = Math.log( 2 );
private static final double PRECISION = 6;
private static final double DECIMAL = Math.pow( 10, PRECISION );
private static final double OFFSET = 1 / DECIMAL;
private ZoomLevelSet zoomLevels = new ZoomLevelSet();
private HashSet<ZoomListener> zoomListeners = new HashSet<ZoomListener>();
private HashSet<ZoomSetupListener> zoomSetupListeners = new HashSet<ZoomSetupListener>();
private double scale = 1;
private double minScale = 0;
private double maxScale = 1;
private double historicalScale;
private double relativeScale;
private double invertedScale;
private double computedScale;
private int zoom;
private int lastZoom = -1;
private int numZoomLevels;
private ZoomLevel currentZoomLevel;
private ZoomLevel highestZoomLevel;
private ZoomLevel lowestZoomLevel;
private int computedCurrentWidth;
private int computedCurrentHeight;
private int currentScaledWidth;
private int currentScaledHeight;
private int baseMapWidth;
private int baseMapHeight;
private boolean zoomLocked = false;
private Rect viewport = new Rect();
private static double getAtPrecision( double s ) {
return Math.round( s * DECIMAL ) / DECIMAL;
}
public static int computeZoom( double scale, int numZoomLevels ){
int zoom = (int) ( numZoomLevels + Math.floor( Math.log( scale - OFFSET ) / BASE_2 ) );
zoom = Math.max( zoom, 0 );
zoom = Math.min( zoom, numZoomLevels - 1);
return zoom;
}
public static double computeRelativeScale( double scale, int numZoomLevels, int zoom ){
double relativeScale = scale * ( 1 << ( ( numZoomLevels - 1 ) - zoom ) );
return getAtPrecision( relativeScale );
}
public static double computeInvertedScale( int numZoomLevels, int zoom ){
return 1 << ( ( numZoomLevels - zoom ) - 1 );
}
public static double computeOffsetScale( double scale, int numZoomLevels, int zoom ) {
return computeInvertedScale( numZoomLevels, zoom ) + computeRelativeScale( scale, numZoomLevels, zoom ) - 1;
}
public static double computeScaleForZoom( double scale, int zoom ) {
double computedScale = ( scale / Math.pow( 2, -zoom ) ) - 1;
return getAtPrecision( computedScale );
}
public ZoomManager(){
update( true );
}
public double getScale() {
return scale;
}
public void setScale( double s ) {
// constrain between minimum and maximum allowed values
s = Math.min( s, maxScale );
s = Math.max( s, minScale );
// round to PRECISION decimal places
s = getAtPrecision( s );
// is it changed?
boolean changed = ( scale != s );
// set it
scale = s;
// update computed values
update( changed );
}
public void updateViewport( int left, int top, int right, int bottom ) {
viewport.set( left, top, right, bottom );
}
public Rect getViewport() {
return viewport;
}
private void update( boolean changed ){
// if no levels, fast-fail
if(numZoomLevels == 0){
zoom = 0;
relativeScale = invertedScale = scale;
currentZoomLevel = highestZoomLevel = lowestZoomLevel = null;
computedCurrentWidth = computedCurrentHeight = 0;
return;
}
// get references to top and bottom levels
highestZoomLevel = zoomLevels.getLast();
lowestZoomLevel = zoomLevels.getFirst();
// update zoom if unlocked
if(!zoomLocked){
zoom = computeZoom( scale, numZoomLevels );
}
// update current zoom level
currentZoomLevel = zoomLevels.get( zoom );
// update computed scales
relativeScale = computeRelativeScale( scale, numZoomLevels, zoom );
computedScale = computeOffsetScale( scale, numZoomLevels, zoom );
invertedScale = computeInvertedScale( numZoomLevels, zoom );
// update current dimensions
baseMapWidth = currentZoomLevel.getMapWidth();
baseMapHeight = currentZoomLevel.getMapHeight();
computedCurrentWidth = (int) ( baseMapWidth * invertedScale );
computedCurrentHeight = (int) ( baseMapHeight * invertedScale );
currentScaledWidth = (int) ( computedCurrentWidth * scale );
currentScaledHeight = (int) ( computedCurrentHeight * scale );
// broadcast scale change
if( changed ) {
for ( ZoomListener listener : zoomListeners ) {
listener.onZoomScaleChanged( scale );
}
}
// if there's a change in zoom, update appropriate values
if ( zoom != lastZoom ) {
// notify all interested parties
for ( ZoomListener listener : zoomListeners ) {
listener.onZoomLevelChanged( lastZoom, zoom );
}
lastZoom = zoom;
}
}
public void lockZoom(){
zoomLocked = true;
}
public void unlockZoom(){
zoomLocked = false;
}
public void setZoom( int z ) {
int maxZoom = numZoomLevels - 1;
z = Math.max(z, 0);
z = Math.min(z, maxZoom);
double s = 1 / (double) ( 1 << ( maxZoom - z ) );
setScale( s );
}
public void addZoomListener( ZoomListener l ) {
zoomListeners.add( l );
}
public void removeZoomListener( ZoomListener l ) {
zoomListeners.remove( l );
}
public void addzoomSetupListener( ZoomSetupListener l ) {
zoomSetupListeners.add( l );
}
public void removezoomSetupListener( ZoomSetupListener l ) {
zoomSetupListeners.remove( l );
}
public void addZoomLevel( int wide, int tall, String pattern ) {
ZoomLevel zoomLevel = new ZoomLevel( this, wide, tall, pattern );
registerZoomLevel( zoomLevel );
}
public void addZoomLevel( int wide, int tall, String pattern, String downsample ) {
ZoomLevel zoomLevel = new ZoomLevel( this, wide, tall, pattern, downsample );
registerZoomLevel( zoomLevel );
}
public void addZoomLevel( int wide, int tall, String pattern, int tileWidth, int tileHeight ) {
ZoomLevel zoomLevel = new ZoomLevel( this, wide, tall, pattern, tileWidth, tileHeight );
registerZoomLevel( zoomLevel );
}
public void addZoomLevel( int wide, int tall, String pattern, String downsample, int tileWidth, int tileHeight ) {
ZoomLevel zoomLevel = new ZoomLevel( this, wide, tall, pattern, downsample, tileWidth, tileHeight );
registerZoomLevel( zoomLevel );
}
private void registerZoomLevel( ZoomLevel zoomLevel ) {
zoomLevels.addZoomLevel( zoomLevel );
numZoomLevels = zoomLevels.size();
update( true );
for ( ZoomSetupListener listener : zoomSetupListeners ) {
listener.onZoomLevelAdded();
}
}
public void resetZoomLevels(){
zoomLevels.clear();
numZoomLevels = 0;
update( true );
}
public ZoomLevel getCurrentZoomLevel() {
return currentZoomLevel;
}
public ZoomLevel getHighestZoomLevel(){
return highestZoomLevel;
}
public ZoomLevel getLowestZoomLevel(){
return lowestZoomLevel;
}
public int getComputedCurrentWidth(){
return computedCurrentWidth;
}
public int getComputedCurrentHeight(){
return computedCurrentHeight;
}
public int getCurrentScaledWidth(){
return currentScaledWidth;
}
public int getCurrentScaledHeight(){
return currentScaledHeight;
}
public int getZoom() {
return zoom;
}
public int getNumZoomLevels() {
return numZoomLevels;
}
public int getMaxZoom() {
return numZoomLevels - 1;
}
public double getRelativeScale() {
return relativeScale;
}
public double getInvertedScale(){
return invertedScale;
}
public double getComputedScale(){
return computedScale;
}
public int getBaseMapWidth() {
return baseMapWidth;
}
public int getBaseMapHeight() {
return baseMapHeight;
}
public double getHistoricalScale(){
return historicalScale;
}
public void saveHistoricalScale(){
historicalScale = scale;
}
}