2 Copyright (C) 2001, 2006 United States Government
3 as represented by the Administrator of the
4 National Aeronautics and Space Administration.
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
;
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";
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";
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";
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()
65 public void pick(DrawContext dc
, Point pickPoint
)
70 public void render(DrawContext dc
)
72 CompassLayer
.this.draw(dc
);
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
88 * Returns the layer's current icon file path.
90 * @return the icon file path
92 public String
getIconFilePath()
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()
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()
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()
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
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()
251 public void setShowTilt(boolean showTilt
)
253 this.showTilt
= showTilt
;
256 private void draw(DrawContext dc
)
258 if (this.iconFilePath
== null)
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
273 | GL
.GL_TRANSFORM_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
);
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
);
297 projectionPushed
= true;
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
);
304 modelviewPushed
= true;
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
);
327 if (projectionPushed
)
329 gl
.glMatrixMode(GL
.GL_PROJECTION
);
334 gl
.glMatrixMode(GL
.GL_MODELVIEW
);
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
))
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
;
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
)
422 if (!(view
instanceof OrbitView
))
425 OrbitView orbitView
= (OrbitView
) view
;
426 return orbitView
.getHeading().getDegrees();
429 private double computePitch(View view
)
434 if (!(view
instanceof OrbitView
))
437 OrbitView orbitView
= (OrbitView
) view
;
438 return orbitView
.getPitch().getDegrees();
441 private void initializeTexture(DrawContext dc
)
443 if (this.iconTexture
!= null)
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
);
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]);
481 public String
toString()
483 return Logging
.getMessage("layers.CompassLayer.Name");