001: /*
002: * Geotools2 - OpenSource mapping toolkit
003: * http://geotools.org
004: * (C) 2002, Geotools Project Managment Committee (PMC)
005: *
006: * This library is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU Lesser General Public
008: * License as published by the Free Software Foundation;
009: * version 2.1 of the License.
010: *
011: * This library is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * Lesser General Public License for more details.
015: *
016: */
017: package org.geotools.arcsde.gce;
018:
019: import java.awt.Dimension;
020: import java.awt.Point;
021: import java.awt.Rectangle;
022: import java.util.ArrayList;
023: import java.util.Arrays;
024: import java.util.Collections;
025: import java.util.Comparator;
026: import java.util.logging.Level;
027:
028: import org.geotools.data.DataSourceException;
029: import org.geotools.geometry.GeneralEnvelope;
030: import org.geotools.geometry.jts.ReferencedEnvelope;
031: import org.opengis.referencing.crs.CoordinateReferenceSystem;
032:
033: import com.esri.sde.sdk.client.SDEPoint;
034: import com.esri.sde.sdk.client.SeException;
035: import com.esri.sde.sdk.client.SeExtent;
036: import com.esri.sde.sdk.client.SeRasterAttr;
037:
038: /**
039: * This class represents an ArcSDE Raster Pyramid. Basically, it wraps the SeRasterAttr
040: * object and implements some convenience methods for doing calculations with it.
041: *
042: * @author Saul Farber
043: *
044: */
045: public class ArcSDEPyramid {
046:
047: ArrayList pyramidList;
048:
049: int tileWidth, tileHeight;
050:
051: /**
052: * Creates an in-memory representation of an ArcSDE Raster Pyramid. Basically it wraps the
053: * supplide SeRasterAttr object and implements some convenience logic for extracting information/
054: * doing calculations with it.
055: *
056: * @param rasterAttributes the SeRasterAttr object for the raster of interest.
057: * @param crs
058: * @throws DataSourceException
059: */
060: public ArcSDEPyramid(SeRasterAttr rasterAttributes,
061: CoordinateReferenceSystem crs) throws DataSourceException {
062: try {
063: final int numLevels = rasterAttributes.getMaxLevel() + 1;
064: pyramidList = new ArrayList(numLevels);
065:
066: tileWidth = rasterAttributes.getTileWidth();
067: tileHeight = rasterAttributes.getTileHeight();
068:
069: for (int i = 0; i < numLevels; i++) {
070: if (i == 1 && rasterAttributes.skipLevelOne()) {
071: continue;
072: }
073:
074: ReferencedEnvelope levelExtent = new ReferencedEnvelope(
075: crs);
076: SeExtent slExtent = rasterAttributes
077: .getExtentByLevel(i);
078: levelExtent.expandToInclude(slExtent.getMinX(),
079: slExtent.getMinY());
080: levelExtent.expandToInclude(slExtent.getMaxX(),
081: slExtent.getMaxY());
082:
083: final int imageWidth = rasterAttributes
084: .getImageWidthByLevel(i);
085: final int imageHeight = rasterAttributes
086: .getImageHeightByLevel(i);
087:
088: Dimension size = new Dimension(imageWidth, imageHeight);
089:
090: addPyramidLevel(i,
091: rasterAttributes.getExtentByLevel(i), crs,
092: rasterAttributes.getImageOffsetByLevel(i),
093: rasterAttributes.getTilesPerRowByLevel(i),
094: rasterAttributes.getTilesPerColByLevel(i), size);
095: }
096:
097: } catch (SeException se) {
098: throw new DataSourceException(se);
099: }
100:
101: }
102:
103: public Dimension getTileDimension() {
104: return new Dimension(tileWidth, tileHeight);
105: }
106:
107: public ArcSDEPyramidLevel getPyramidLevel(int level) {
108: return (ArcSDEPyramidLevel) pyramidList.get(level);
109: }
110:
111: public int getNumLevels() {
112: return pyramidList.size();
113: }
114:
115: /**
116: * Given this raster's pyramid info this method picks the optimal pyramid
117: * level for rendering this request.
118: *
119: * @param requestEnvelope
120: * The requested geographical extent
121: * @param pixelDimensions
122: * The request pixel size of the image
123: * @return the integer number of the raster level most appropriate for this
124: * request.
125: */
126: public int pickOptimalRasterLevel(
127: ReferencedEnvelope requestEnvelope,
128: Rectangle pixelDimensions) throws DataSourceException {
129:
130: double reqXRes = requestEnvelope.getWidth()
131: / pixelDimensions.width;
132: double reqYRes = requestEnvelope.getHeight()
133: / pixelDimensions.height;
134:
135: ArcSDEPyramidLevel[] pyramidInfo = (ArcSDEPyramidLevel[]) pyramidList
136: .toArray(new ArcSDEPyramidLevel[pyramidList.size()]);
137:
138: int targetLevel = 0;
139: for (int i = 0; i < pyramidInfo.length; i++) {
140: if (reqXRes >= pyramidInfo[i].getXRes()
141: && reqYRes >= pyramidInfo[i].getYRes()) {
142: targetLevel = i;
143: } else {
144: break;
145: }
146: }
147:
148: return targetLevel;
149: }
150:
151: /**
152: * Given a requested envelope and a chosen raster level, figure out and return the
153: * actual SDE raster tiles, image size and the exact envelope of that image.
154: * @param reqEnv The original requested envelope.
155: * @param rasterLvl The chosen pyramid level at which to best-fit the requsted envelope.
156: * @return
157: */
158: public RasterQueryInfo fitExtentToRasterPixelGrid(
159: ReferencedEnvelope reqEnv, int rasterLvl) {
160: final RasterQueryInfo ret = new RasterQueryInfo();
161: final ArcSDEPyramidLevel pLevel = getPyramidLevel(rasterLvl);
162:
163: double delta = reqEnv.getMinX()
164: - pLevel.getEnvelope().getMinX();
165: final int xMinPixel = (int) Math
166: .floor(delta / pLevel.getXRes());
167:
168: delta = reqEnv.getMaxX() - pLevel.getEnvelope().getMinX();
169: final int xMaxPixel = (int) Math.ceil(delta / pLevel.getXRes());
170:
171: delta = pLevel.getEnvelope().getMaxY() - reqEnv.getMaxY();
172: // Distance in pixels from the top of the whole pyramid image to the top of our AOI.
173: // If we're off the top, this number will be negative.
174: final int yMinPixel = (int) Math
175: .floor(delta / pLevel.getYRes());
176:
177: delta = pLevel.getEnvelope().getMaxY() - reqEnv.getMinY();
178: final int yMaxPixel = (int) Math.ceil(delta / pLevel.getYRes());
179:
180: final int widthPixel = xMaxPixel - xMinPixel;
181: final int heightPixel = yMaxPixel - yMinPixel;
182:
183: final double xMinGeo = pLevel.getEnvelope().getMinX()
184: + pLevel.getXRes() * xMinPixel;
185: final double yMinGeo = pLevel.getEnvelope().getMaxY()
186: - pLevel.getYRes() * (yMinPixel + heightPixel);
187: final double widthGeo = pLevel.getXRes() * widthPixel;
188: final double heightGeo = pLevel.getYRes() * heightPixel;
189:
190: ret.envelope = new ReferencedEnvelope(xMinGeo, xMinGeo
191: + widthGeo, yMinGeo, yMinGeo + heightGeo, reqEnv
192: .getCoordinateReferenceSystem());
193: ret.image = new Rectangle(xMinPixel, yMinPixel, widthPixel,
194: heightPixel);
195:
196: return ret;
197: }
198:
199: /**
200: * Don't use this method. It's only public for unit testing purposes.
201: *
202: * @param level DON'T USE
203: * @param extent DON'T USE
204: * @param crs DON'T USE
205: * @param offset DON'T USE
206: * @param xTiles DON'T USE
207: * @param yTiles DON'T USE
208: * @param imageSize DON'T USE
209: */
210: public void addPyramidLevel(int level, SeExtent extent,
211: CoordinateReferenceSystem crs, SDEPoint offset, int xTiles,
212: int yTiles, Dimension imageSize) {
213:
214: pyramidList.add(level, new ArcSDEPyramidLevel(level, extent,
215: crs, offset, xTiles, yTiles, imageSize));
216:
217: Collections.sort(pyramidList, new Comparator() {
218: public int compare(Object arg0, Object arg1) {
219: ArcSDEPyramidLevel p0, p1;
220: p0 = (ArcSDEPyramidLevel) arg0;
221: p1 = (ArcSDEPyramidLevel) arg1;
222: return (p0.getLevel() - p1.getLevel());
223:
224: }
225: });
226: }
227:
228: /**
229: * Don't use this constructor. It only exists for unit testing purposes.
230: *
231: * @param tileWidth DON'T USE
232: * @param tileHeight DON'T USE
233: * @param numLayers DON'T USE
234: */
235: public ArcSDEPyramid(int tileWidth, int tileHeight, int numLayers) {
236: this .tileWidth = tileWidth;
237: this .tileHeight = tileHeight;
238: pyramidList = new ArrayList(numLayers);
239: }
240:
241: }
242:
243: class RasterQueryInfo {
244:
245: public Rectangle image;
246: public ReferencedEnvelope envelope;
247:
248: }
|