Updated to worldwind release 20070817
[worldwind-tracker.git] / gov / nasa / worldwind / layers / CompassLayer.java
blobca162d41fcbec089978cfecab0458fb5a0ce9614
1 /*
2 Copyright (C) 2001, 2006 United States Government
3 as represented by the Administrator of the
4 National Aeronautics and Space Administration.
5 All Rights Reserved.
6 */
7 package gov.nasa.worldwind.layers;
9 import com.sun.opengl.util.texture.*;
10 import gov.nasa.worldwind.exception.WWRuntimeException;
11 import gov.nasa.worldwind.geom.Vec4;
12 import gov.nasa.worldwind.render.*;
13 import gov.nasa.worldwind.util.Logging;
14 import gov.nasa.worldwind.view.*;
16 import javax.media.opengl.GL;
17 import java.awt.*;
18 import java.io.*;
20 /**
21 * @author tag
22 * @version $Id$
24 public class CompassLayer extends AbstractLayer
26 public final static String NORTHWEST = "gov.nasa.worldwind.CompassLayer.NorthWest";
27 public final static String SOUTHWEST = "gov.nasa.worldwind.CompassLayer.SouthWest";
28 public final static String NORTHEAST = "gov.nasa.worldwind.CompassLayer.NorthEast";
29 public final static String SOUTHEAST = "gov.nasa.worldwind.CompassLayer.SouthEast";
31 /**
32 * On window resize, scales the compass icon to occupy a constant relative size of the viewport.
34 public final static String RESIZE_STRETCH = "gov.nasa.worldwind.CompassLayer.ResizeStretch";
35 /**
36 * On window resize, scales the compass icon to occupy a constant relative size of the viewport, but not larger than
37 * the icon's inherent size scaled by the layer's icon scale factor.
39 public final static String RESIZE_SHRINK_ONLY = "gov.nasa.worldwind.CompassLayer.ResizeShrinkOnly";
40 /**
41 * Does not modify the compass icon size when the window changes size.
43 public final static String RESIZE_KEEP_FIXED_SIZE = "gov.nasa.worldwind.CompassLayer.ResizeKeepFixedSize";
45 private String iconFilePath = "images/notched-compass.png"; // TODO: make configurable
46 private double compassToViewportScale = 0.2; // TODO: make configurable
47 private double iconScale = 0.5;
48 private int borderWidth = 20; // TODO: make configurable
49 private String position = NORTHEAST; // TODO: make configurable
50 private String resizeBehavior = RESIZE_SHRINK_ONLY;
51 private Texture iconTexture = null;
52 private Vec4 locationCenter = null;
53 private boolean showTilt = false;
55 // Draw it as ordered with an eye distance of 0 so that it shows up in front of most other things.
56 private OrderedIcon orderedImage = new OrderedIcon();
58 private class OrderedIcon implements OrderedRenderable
60 public double getDistanceFromEye()
62 return 0;
65 public void pick(DrawContext dc, Point pickPoint)
67 // Not implemented
70 public void render(DrawContext dc)
72 CompassLayer.this.draw(dc);
76 public CompassLayer()
78 this.setOpacity(0.8); // TODO: make configurable
81 public CompassLayer(String iconFilePath)
83 this.setIconFilePath(iconFilePath);
84 this.setOpacity(0.8); // TODO: make configurable
87 /**
88 * Returns the layer's current icon file path.
90 * @return the icon file path
92 public String getIconFilePath()
94 return iconFilePath;
97 /**
98 * Sets the compass icon's image location. The layer first searches for this location in the current Java classpath.
99 * If not found then the specified path is assumed to refer to the local file system. found there then the
101 * @param iconFilePath the path to the icon's image file
103 public void setIconFilePath(String iconFilePath)
105 if (iconFilePath == null)
107 String message = Logging.getMessage("nullValue.IconFilePath");
108 Logging.logger().severe(message);
109 throw new IllegalArgumentException(message);
111 this.iconFilePath = iconFilePath;
115 * Returns the layer's compass-to-viewport scale factor.
117 * @return the compass-to-viewport scale factor
119 public double getCompassToViewportScale()
121 return compassToViewportScale;
125 * Sets the scale factor applied to the viewport size to determine the displayed size of the compass icon. This
126 * scale factor is used only when the layer's resize behavior is {@link #RESIZE_STRETCH} or {@link
127 * #RESIZE_SHRINK_ONLY}. The icon's width is adjusted to occupy the proportion of the viewport's width indicated by
128 * this factor. The icon's height is adjusted to maintain the compass image's native aspect ratio.
130 * @param compassToViewportScale the compass to viewport scale factor
132 public void setCompassToViewportScale(double compassToViewportScale)
134 this.compassToViewportScale = compassToViewportScale;
138 * Returns the icon scale factor. See {@link #setIconScale(double)} for a description of the scale factor.
140 * @return the current icon scale
142 public double getIconScale()
144 return iconScale;
148 * Sets the scale factor defining the displayed size of the compass icon relative to the icon's width and height in
149 * its image file. Values greater than 1 magify the image, values less than one minify it. If the layer's resize
150 * behavior is other than {@link #RESIZE_KEEP_FIXED_SIZE}, the icon's displayed sized is further affected by the
151 * value specified by {@link #setCompassToViewportScale(double)} and the current viewport size.
153 * @param iconScale the icon scale factor
155 public void setIconScale(double iconScale)
157 this.iconScale = iconScale;
161 * Returns the compass icon's resize behavior.
163 * @return the icon's resize behavior
165 public String getResizeBehavior()
167 return resizeBehavior;
171 * Sets the behavior the layer uses to size the compass icon when the viewport size changes, typically when the
172 * World Wind window is resized. If the value is {@link #RESIZE_KEEP_FIXED_SIZE}, the icon size is kept to the size
173 * specified in its image file scaled by the layer's current icon scale. If the value is {@link #RESIZE_STRETCH},
174 * the icon is resized to have a constant size relative to the current viewport size. If the viewport shrinks the
175 * icon size decreases; if it expands then the icon file enlarges. The relative size is determined by the current
176 * compass-to-viewport scale and by the icon's image file size scaled by the current icon scale. If the value is
177 * {@link #RESIZE_SHRINK_ONLY} (the default), icon sizing behaves as for {@link #RESIZE_STRETCH} but the icon will
178 * not grow larger than the size specified in its image file scaled by the current icon scale.
180 * @param resizeBehavior the desired resize behavior
182 public void setResizeBehavior(String resizeBehavior)
184 this.resizeBehavior = resizeBehavior;
187 public int getBorderWidth()
189 return borderWidth;
193 * Sets the compass icon offset from the viewport border.
195 * @param borderWidth the number of pixels to offset the compass icon from the borders indicated by {@link
196 * #setPosition(String)}.
198 public void setBorderWidth(int borderWidth)
200 this.borderWidth = borderWidth;
204 * Returns the current relative compass icon position.
206 * @return the current compass position
208 public String getPosition()
210 return position;
214 * Sets the relative viewport location to display the compass icon. Can be one of {@link #NORTHEAST} (the default),
215 * {@link #NORTHWEST}, {@link #SOUTHEAST}, or {@link #SOUTHWEST}. These indicate the corner of the viewport to place
216 * the icon.
218 * @param position the desired compass position
220 public void setPosition(String position)
222 if (position == null)
224 String message = Logging.getMessage("nullValue.CompassPositionIsNull");
225 Logging.logger().severe(message);
226 throw new IllegalArgumentException(message);
228 this.position = position;
231 public Vec4 getLocationCenter()
233 return locationCenter;
236 public void setLocationCenter(Vec4 locationCenter)
238 this.locationCenter = locationCenter;
241 protected void doRender(DrawContext dc)
243 dc.addOrderedRenderable(this.orderedImage);
246 public boolean isShowTilt()
248 return showTilt;
251 public void setShowTilt(boolean showTilt)
253 this.showTilt = showTilt;
256 private void draw(DrawContext dc)
258 if (this.iconFilePath == null)
259 return;
261 GL gl = dc.getGL();
263 boolean attribsPushed = false;
264 boolean modelviewPushed = false;
265 boolean projectionPushed = false;
269 gl.glPushAttrib(GL.GL_DEPTH_BUFFER_BIT
270 | GL.GL_COLOR_BUFFER_BIT
271 | GL.GL_ENABLE_BIT
272 | GL.GL_TEXTURE_BIT
273 | GL.GL_TRANSFORM_BIT
274 | GL.GL_VIEWPORT_BIT
275 | GL.GL_CURRENT_BIT);
276 attribsPushed = true;
278 if (this.iconTexture == null)
279 this.initializeTexture(dc);
281 gl.glEnable(GL.GL_TEXTURE_2D);
282 iconTexture.bind();
284 gl.glColor4d(1d, 1d, 1d, this.getOpacity());
285 gl.glEnable(GL.GL_BLEND);
286 gl.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA);
287 gl.glDisable(GL.GL_DEPTH_TEST);
289 double width = this.getScaledIconWidth();
290 double height = this.getScaledIconHeight();
292 // Load a parallel projection with xy dimensions (viewportWidth, viewportHeight)
293 // into the GL projection matrix.
294 java.awt.Rectangle viewport = dc.getView().getViewport();
295 gl.glMatrixMode(javax.media.opengl.GL.GL_PROJECTION);
296 gl.glPushMatrix();
297 projectionPushed = true;
298 gl.glLoadIdentity();
299 double maxwh = width > height ? width : height;
300 gl.glOrtho(0d, viewport.width, 0d, viewport.height, -0.6 * maxwh, 0.6 * maxwh);
302 gl.glMatrixMode(GL.GL_MODELVIEW);
303 gl.glPushMatrix();
304 modelviewPushed = true;
305 gl.glLoadIdentity();
307 double scale = this.computeScale(viewport);
308 Vec4 locationSW = this.computeLocation(viewport, scale);
309 double heading = this.computeHeading(dc.getView());
310 double pitch = this.computePitch(dc.getView());
312 gl.glTranslated(locationSW.x, locationSW.y, locationSW.z);
313 gl.glScaled(scale, scale, 1);
315 gl.glTranslated(width / 2, height / 2, 0);
316 if (this.showTilt) // formula contributed by Ty Hayden
317 gl.glRotated(70d * (pitch / 90.0), 1d, 0d, 0d);
318 gl.glRotated(heading, 0d, 0d, 1d);
319 gl.glTranslated(-width / 2, -height / 2, 0);
321 TextureCoords texCoords = this.iconTexture.getImageTexCoords();
322 gl.glScaled(width, height, 1d);
323 dc.drawUnitQuad(texCoords);
325 finally
327 if (projectionPushed)
329 gl.glMatrixMode(GL.GL_PROJECTION);
330 gl.glPopMatrix();
332 if (modelviewPushed)
334 gl.glMatrixMode(GL.GL_MODELVIEW);
335 gl.glPopMatrix();
337 if (attribsPushed)
338 gl.glPopAttrib();
342 private double computeScale(java.awt.Rectangle viewport)
344 if (this.resizeBehavior.equals(RESIZE_SHRINK_ONLY))
346 return Math.min(1d, (this.compassToViewportScale) * viewport.width / this.getScaledIconWidth());
348 else if (this.resizeBehavior.equals(RESIZE_STRETCH))
350 return (this.compassToViewportScale) * viewport.width / this.getScaledIconWidth();
352 else if (this.resizeBehavior.equals(RESIZE_KEEP_FIXED_SIZE))
354 return 1d;
356 else
358 return 1d;
362 private double getScaledIconWidth()
364 return this.iconTexture.getWidth() * this.iconScale;
367 private double getScaledIconHeight()
369 return this.iconTexture.getHeight() * this.iconScale;
372 private Vec4 computeLocation(java.awt.Rectangle viewport, double scale)
374 double width = this.getScaledIconWidth();
375 double height = this.getScaledIconHeight();
377 double scaledWidth = scale * width;
378 double scaledHeight = scale * height;
380 double x;
381 double y;
383 if (this.locationCenter != null)
385 x = viewport.getWidth() - scaledWidth / 2 - this.borderWidth;
386 y = viewport.getHeight() - scaledHeight / 2 - this.borderWidth;
388 else if (this.position.equals(NORTHEAST))
390 x = viewport.getWidth() - scaledWidth - this.borderWidth;
391 y = viewport.getHeight() - scaledHeight - this.borderWidth;
393 else if (this.position.equals(SOUTHEAST))
395 x = viewport.getWidth() - scaledWidth - this.borderWidth;
396 y = 0d + this.borderWidth;
398 else if (this.position.equals(NORTHWEST))
400 x = 0d + this.borderWidth;
401 y = viewport.getHeight() - scaledHeight - this.borderWidth;
403 else if (this.position.equals(SOUTHWEST))
405 x = 0d + this.borderWidth;
406 y = 0d + this.borderWidth;
408 else // use North East
410 x = viewport.getWidth() - scaledWidth / 2 - this.borderWidth;
411 y = viewport.getHeight() - scaledHeight / 2 - this.borderWidth;
414 return new Vec4(x, y, 0);
417 private double computeHeading(View view)
419 if (view == null)
420 return 0.0;
422 if (!(view instanceof OrbitView))
423 return 0.0;
425 OrbitView orbitView = (OrbitView) view;
426 return orbitView.getHeading().getDegrees();
429 private double computePitch(View view)
431 if (view == null)
432 return 0.0;
434 if (!(view instanceof OrbitView))
435 return 0.0;
437 OrbitView orbitView = (OrbitView) view;
438 return orbitView.getPitch().getDegrees();
441 private void initializeTexture(DrawContext dc)
443 if (this.iconTexture != null)
444 return;
448 InputStream iconStream = this.getClass().getResourceAsStream("/" + this.iconFilePath);
449 if (iconStream == null)
451 File iconFile = new File(this.iconFilePath);
452 if (iconFile.exists())
454 iconStream = new FileInputStream(iconFile);
458 this.iconTexture = TextureIO.newTexture(iconStream, true, null);
459 this.iconTexture.bind();
461 catch (IOException e)
463 String msg = Logging.getMessage("layers.IOExceptionDuringInitialization");
464 Logging.logger().severe(msg);
465 throw new WWRuntimeException(msg, e);
468 GL gl = dc.getGL();
469 gl.glTexEnvf(GL.GL_TEXTURE_ENV, GL.GL_TEXTURE_ENV_MODE, GL.GL_MODULATE);
470 gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MIN_FILTER, GL.GL_LINEAR_MIPMAP_LINEAR);
471 gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MAG_FILTER, GL.GL_LINEAR);
472 gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_WRAP_S, GL.GL_CLAMP_TO_EDGE);
473 gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_WRAP_T, GL.GL_CLAMP_TO_EDGE);
474 // Enable texture anisotropy, improves "tilted" compass quality.
475 int[] maxAnisotropy = new int[1];
476 gl.glGetIntegerv(GL.GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, maxAnisotropy, 0);
477 gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MAX_ANISOTROPY_EXT, maxAnisotropy[0]);
480 @Override
481 public String toString()
483 return Logging.getMessage("layers.CompassLayer.Name");