Java tutorial
/***************************** BEGIN LICENSE BLOCK *************************** The contents of this file are subject to the Mozilla Public License Version 1.1 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.mozilla.org/MPL/MPL-1.1.html Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations under the License. The Original Code is the "Space Time Toolkit". The Initial Developer of the Original Code is the VAST team at the University of Alabama in Huntsville (UAH). <http://vast.uah.edu> Portions created by the Initial Developer are Copyright (C) 2007 the Initial Developer. All Rights Reserved. Please Contact Mike Botts <mike.botts@uah.edu> for more information. Contributor(s): Alexandre Robin <robin@nsstc.uah.edu> ******************************* END LICENSE BLOCK ***************************/ package org.vast.stt.provider.tiling; import java.util.Iterator; import java.util.LinkedList; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.vast.cdm.common.DataType; import org.vast.data.DataArray; import org.vast.data.DataGroup; import org.vast.data.DataValue; import org.vast.stt.data.BlockList; import org.vast.stt.data.BlockListItem; import org.vast.stt.data.DataException; import org.vast.stt.data.DataNode; import org.vast.stt.dynamics.SceneBboxUpdater; import org.vast.stt.dynamics.SpatialExtentUpdater; import org.vast.stt.event.EventType; import org.vast.stt.event.STTEvent; import org.vast.stt.provider.AbstractProvider; import org.vast.stt.provider.tiling.QuadTree; import org.vast.util.SpatialExtent; public abstract class TiledMapProvider extends AbstractProvider { protected Log log = LogFactory.getLog(TiledMapProvider.class); protected final static double DTR = Math.PI / 180.; protected final static double RTD = 180. / Math.PI; protected double fixedAltitude = 0.0; protected QuadTree quadTree; protected BlockList[] blockLists = new BlockList[2]; // 0 for imagery, 1 for grid protected DataArray gridData; protected boolean useAlpha = false; protected boolean useDEM = false; protected SpatialExtent maxBbox; protected TiledMapSelector tileSelector; protected LinkedList<QuadTreeItem> itemsToLoad; protected LinkedList<QuadTreeItem> itemsToHide; protected LinkedList<QuadTreeItem> itemsToDiscard; protected int tileWidth, tileHeight; protected TileThread[] threadPool; protected class TileThread extends Thread { public void run() { while (!itemsToLoad.isEmpty()) { // find best item QuadTreeItem bestItem = itemsToLoad.getFirst(); Iterator<QuadTreeItem> it = itemsToLoad.iterator(); while (it.hasNext()) { QuadTreeItem nextItem = it.next(); if (nextItem.getScore() > bestItem.getScore()) bestItem = nextItem; } // remove from queue //System.out.println("Loading " + bestItem); itemsToLoad.remove(bestItem); if (canceled) return; // load and update lists getNewTile(bestItem); //BlockListItem[] itemBlocks = (BlockListItem[])bestItem.getData(); //System.out.println(itemBlocks[0] + " loaded"); if (canceled) return; tileSelector.appendToBlockLists(bestItem); tileSelector.removeDescendantsFromBlockLists(bestItem); tileSelector.removeHiddenAncestorsFromBlockLists(bestItem); //System.out.println(itemBlocks[0] + " added to block list"); // send event for redraw dispatchEvent(new STTEvent(this, EventType.PROVIDER_DATA_CHANGED), true); } /*QuadTreeItem bestItem = null; while (true) { synchronized(itemsToLoad) { try { while (itemsToLoad.isEmpty()) itemsToLoad.wait(); if (itemsToLoad.isEmpty()) return; // get item with best score bestItem = itemsToLoad.getFirst(); Iterator<QuadTreeItem> it = itemsToLoad.iterator(); while (it.hasNext()) { QuadTreeItem nextItem = it.next(); if (nextItem.getScore() > bestItem.getScore()) bestItem = nextItem; } itemsToLoad.remove(bestItem); } catch (InterruptedException e) { break; } } getNewTile(bestItem); // update lists tileSelector.appendToBlockLists(bestItem); tileSelector.removeDescendantsFromBlockLists(bestItem); tileSelector.removeHiddenAncestorsFromBlockLists(bestItem); // send event for redraw dispatchEvent(new STTEvent(this, EventType.PROVIDER_DATA_CHANGED)); }*/ } }; protected abstract void getNewTile(QuadTreeItem item); protected abstract SpatialExtent transformBbox(SpatialExtent extent); public TiledMapProvider() { quadTree = new QuadTree(); itemsToLoad = new LinkedList<QuadTreeItem>(); itemsToHide = new LinkedList<QuadTreeItem>(); itemsToDiscard = new LinkedList<QuadTreeItem>(); } public TiledMapProvider(int tileWidth, int tileHeight, int maxLevel) { this(); tileSelector = new TiledMapSelector(0, maxLevel, this); this.tileWidth = tileWidth; this.tileHeight = tileHeight; } protected void initThreadPool(int numThreads) { threadPool = new TileThread[numThreads]; for (int t = 0; t < numThreads; t++) { threadPool[t] = new TileThread(); //threadPool[t].start(); } } @Override public void init() throws DataException { // create block list for textures DataGroup pixelData = new DataGroup(3); pixelData.setName("pixel"); pixelData.addComponent("red", new DataValue(DataType.BYTE)); pixelData.addComponent("green", new DataValue(DataType.BYTE)); pixelData.addComponent("blue", new DataValue(DataType.BYTE)); if (useAlpha) pixelData.addComponent("alpha", new DataValue(DataType.BYTE)); DataArray rowData = new DataArray(256); rowData.addComponent(pixelData); rowData.setName("row"); DataArray imageData = new DataArray(256); imageData.addComponent(rowData); imageData.setName("image"); blockLists[0] = dataNode.createList(imageData); //System.out.println(imageData); // create block list for grid DataGroup pointData = new DataGroup(2); pointData.setName("point"); pointData.addComponent("lat", new DataValue(DataType.FLOAT)); pointData.addComponent("lon", new DataValue(DataType.FLOAT)); if (useDEM) pointData.addComponent("alt", new DataValue(DataType.FLOAT)); rowData = new DataArray(10); rowData.addComponent(pointData); rowData.setName("row"); gridData = new DataArray(10); gridData.addComponent(rowData); gridData.setName("grid"); blockLists[1] = dataNode.createList(gridData); //System.out.println(gridData); // set tile sizes in updater SpatialExtentUpdater updater = this.getSpatialExtent().getUpdater(); if (updater != null) { if (updater instanceof SceneBboxUpdater) ((SceneBboxUpdater) updater).setTilesize(tileWidth, tileHeight); updater.update(); } dataNode.setNodeStructureReady(true); } @Override public void updateData() throws DataException { // init DataNode if not done yet if (!dataNode.isNodeStructureReady()) init(); // convert extent to tile system projection SpatialExtent newExtent = transformBbox(spatialExtent); // query tree for matching and unused items itemsToLoad.clear(); itemsToHide.clear(); itemsToDiscard.clear(); tileSelector.setROI(newExtent); tileSelector.setCurrentLevel(0); double tileRatio = spatialExtent.getXTiles() * spatialExtent.getYTiles(); tileSelector.setSizeRatio(tileRatio * 0.35); tileSelector.setMaxDistance(Math.sqrt(tileRatio)); tileSelector.setHidePartiallyVisibleParents(useAlpha); quadTree.accept(tileSelector); // remove hidden items from block lists while (!itemsToHide.isEmpty()) { QuadTreeItem item = itemsToHide.poll(); BlockListItem[] itemBlocks = (BlockListItem[]) item.getData(); if (itemBlocks != null) for (int b = 0; b < itemBlocks.length; b++) blockLists[b].remove(itemBlocks[b]); } // send event for redraw if (!canceled) dispatchEvent(new STTEvent(this, EventType.PROVIDER_DATA_CHANGED), true); // discard items if (itemsToDiscard.size() > 0) { LinkedList<Object> blocksToDiscard = new LinkedList<Object>(); while (!itemsToDiscard.isEmpty()) { QuadTreeItem item = itemsToDiscard.poll(); // add blocks to be deleted BlockListItem[] itemBlocks = (BlockListItem[]) item.getData(); if (itemBlocks != null && blockLists[0].contains(itemBlocks[0])) log.debug(">>>>" + itemBlocks[0] + " STILL IN LIST !!"); if (itemBlocks != null)// && !blockLists[0].contains(itemBlocks[0])) for (int b = 0; b < itemBlocks.length; b++) blocksToDiscard.add(itemBlocks[b]); item.setData(null); // need to make sure we don't have children with data // since they can still have associated textures and DL // if not, we can even remove quad tree item //if (!item.hasChildren()) // item.removeFromParent(); } //System.out.println(blocksToDiscard.size() + " items to be deleted"); if (blocksToDiscard.size() > 0) dispatchEvent(new STTEvent(blocksToDiscard.toArray(), EventType.PROVIDER_DATA_REMOVED), false); } // run synchronously for now if (!canceled) threadPool[0].run(); // print debug info if (log.isDebugEnabled()) { //blockLists[0].checkConsistency(); log.debug("Block lists size = " + blockLists[0].getSize() + ", " + blockLists[1].getSize()); //QuadTreeItemCounter counter = new QuadTreeItemCounter(); //quadTree.accept(counter); //System.out.println("quadtree size = " + counter.numItems + " (" // + counter.numItemsWithData + ")"); } } @Override public void cancelUpdate() { super.cancelUpdate(); synchronized (itemsToLoad) { itemsToLoad.clear(); } } protected double yToLat(double y) { double lat = 0; if (Math.abs(y) < Math.PI - (1e-4)) //lat = 2 * Math.atan(Math.exp(y)) - Math.PI/2; lat = Math.PI / 2 - 2 * Math.atan(Math.exp(-y)); else lat = Math.signum(y) * Math.PI / 2; return lat; } protected double latToY(double lat) { double sinLat = Math.sin(lat); double y = 0.5 * Math.log((1 + sinLat) / (1 - sinLat)); y = Math.max(y, -Math.PI); y = Math.min(y, Math.PI); return y; } @Override public DataNode getDataNode() { if (!dataNode.isNodeStructureReady()) startUpdate(true); return dataNode; } public boolean isSpatialSubsetSupported() { return true; } public boolean isTimeSubsetSupported() { return false; } public boolean isUseDEM() { return useDEM; } public void setUseDEM(boolean useDEM) { this.useDEM = useDEM; } }