/*
* Geotoolkit - An Open Source Java GIS Toolkit
* http://www.geotoolkit.org
*
* (C) 2013, Geomatys
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License.
*
* This library 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
* Lesser General Public License for more details.
*/
package org.geotoolkit.display3d.scene.quadtree;
import org.apache.sis.geometry.GeneralEnvelope;
import org.opengis.geometry.Envelope;
import javax.vecmath.Point3i;
import java.awt.*;
/**
* Utilities methods for QuadTree management
*
* @author Thomas Rouby (Geomatys))
*/
public final class QuadTreeUtils {
/**
* Get the QuadTree Grid Size for a specific tree depth
*
* @param treeDepth
* @return
*/
public static Dimension getGridSize(int treeDepth) {
final int numTile = (int)Math.pow(2, treeDepth);
return new Dimension(numTile,numTile);
}
/**
* Get the number of pixel for the grid at a specific tree depth
*
* @param treeDepth
* @param pixelPerTile
* @return
*/
public static long getNumPixel(int treeDepth, long pixelPerTile) {
final int gridSize = (int)Math.pow(2, treeDepth);
return gridSize*pixelPerTile;
}
/**
* Get the scale (crs / pixels) for the grid at a specific tree depth
*
* @param treeDepth
* @param pixelPerTile
* @param crsSpan
* @return
*/
public static double getScale(int treeDepth, long pixelPerTile, double crsSpan) {
final long numPixel = getNumPixel(treeDepth, pixelPerTile);
return crsSpan/numPixel;
}
/**
* Search the nearest tree depth for a specific scale
*
* @param scale
* @param pixelPerTile
* @param crsSpan
* @return
*/
public static int getTreeDepth(double scale, long pixelPerTile, double crsSpan){
int treeDepth = -1;
double quadTreeScale;
do {
quadTreeScale = getScale(++treeDepth, pixelPerTile, crsSpan);
} while (scale < quadTreeScale);
return treeDepth;
}
/**
* Search the top QuadTree node of the QuadTree
*
* @param node
* @return
*/
public static QuadTreeNode findRootNode(QuadTreeNode node) {
QuadTreeNode searchNode = node;
while (searchNode.getQuadParent() != null) {
searchNode = searchNode.getQuadParent();
}
return searchNode;
}
public static Point3i findPosition(Point[] id) {
final int treeDepth = id.length;
final Dimension gridSize = getGridSize(treeDepth);
int x=0, y=0;
int factX = gridSize.width/2;
int factY = gridSize.height/2;
for (int i=0; i<treeDepth; i++){
final Point pid = id[i];
if (pid.x == 1) {
x += factX;
}
if (pid.y == 1) {
y += factY;
}
factX = factX/2;
factY = factY/2;
}
return new Point3i(x, y, treeDepth);
}
public static Point[] findId(Point3i pos) {
return findId(pos.z, pos.x, pos.y);
}
public static Point[] findId(int treeDepth, int numX, int numY){
final Dimension gridSize = getGridSize(treeDepth);
if (numX < 0 || numX >= gridSize.width || numY < 0 || numY >= gridSize.height) return null;
final Point[] id = new Point[treeDepth];
int tmpX = 0, tmpY=0;
int factX = gridSize.width/2;
int factY = gridSize.height/2;
for (int i=0; i<treeDepth; i++) {
int x=0,y=0;
if (tmpX+factX <= numX) {
tmpX+=factX;
x=1;
}
if (tmpY+factY <= numY) {
tmpY+=factY;
y=1;
}
id[i] = new Point(x,y);
factX = factX/2;
factY = factY/2;
}
return id;
}
/**
* Search the id of the quad tree node
*
* @param treeDepth
* @param envelope
* @param lon
* @param lat
* @return
*/
public static Point[] findId(int treeDepth, Envelope envelope, double lon, double lat) {
if (lon < envelope.getMinimum(0) || lon > envelope.getMaximum(0) || lat < envelope.getMinimum(1) || lat > envelope.getMaximum(1)) return null;
final Point[] id = new Point[treeDepth];
final GeneralEnvelope tmpEnv = new GeneralEnvelope(envelope);
for (int i=0; i<treeDepth; i++) {
int x,y;
if (lon < tmpEnv.getMedian(0)) {
x = 0;
tmpEnv.setRange(0, tmpEnv.getMinimum(0), tmpEnv.getMedian(0));
} else {
x = 1;
tmpEnv.setRange(0, tmpEnv.getMedian(0), tmpEnv.getMaximum(0));
}
if (lat < tmpEnv.getMedian(1)) {
y = 1;
tmpEnv.setRange(1, tmpEnv.getMinimum(1), tmpEnv.getMedian(1));
} else {
y = 0;
tmpEnv.setRange(1, tmpEnv.getMedian(1), tmpEnv.getMaximum(1));
}
id[i] = new Point(x,y);
}
return id;
}
}