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
.render
;
9 import com
.sun
.opengl
.util
.texture
.TextureData
;
10 import gov
.nasa
.worldwind
.*;
11 import gov
.nasa
.worldwind
.globes
.Globe
;
12 import gov
.nasa
.worldwind
.geom
.*;
13 import gov
.nasa
.worldwind
.layers
.TextureTile
;
14 import gov
.nasa
.worldwind
.util
.Logging
;
16 import javax
.media
.opengl
.GL
;
18 import java
.awt
.image
.*;
23 * @version $Id: SurfaceShape.java 2570 2007-08-16 22:31:33Z tgaskins $
25 public abstract class SurfaceShape
implements Renderable
, Disposable
, Movable
27 public static Dimension TEXTURE_SIZE_1024
= new Dimension(1024, 1024);
28 public static Dimension TEXTURE_SIZE_512
= new Dimension(512, 512);
29 public static Dimension TEXTURE_SIZE_256
= new Dimension(256, 256);
30 public static Dimension TEXTURE_SIZE_128
= new Dimension(128, 128);
31 public static Dimension TEXTURE_SIZE_64
= new Dimension(64, 64);
32 public static Dimension TEXTURE_SIZE_32
= new Dimension(32, 32);
33 public static Dimension TEXTURE_SIZE_16
= new Dimension(16, 16);
34 public static Dimension TEXTURE_SIZE_8
= new Dimension(8, 8);
36 private static final Color DEFAULT_COLOR
= new Color(1f
, 1f
, 0f
, 0.4f
);
37 private static final Color DEFAULT_BORDER_COLOR
= new Color(1f
, 1f
, 0f
, 0.7f
);
38 private static final Dimension DEFAULT_TEXTURE_SIZE
= TEXTURE_SIZE_64
;
39 private static final double DEFAULT_NUM_EDGE_INTERVALS_PER_DEGREE
= 1;
41 private ArrayList
<TextureTile
> tiles
= new ArrayList
<TextureTile
>();
42 private Dimension textureSize
= DEFAULT_TEXTURE_SIZE
;
43 protected Globe globe
;
45 private Color borderColor
;
46 private Stroke stroke
= new BasicStroke();
47 private boolean drawBorder
= true;
48 private boolean drawInterior
= true;
49 private boolean antiAlias
= false;
50 private double numEdgeIntervalsPerDegree
= DEFAULT_NUM_EDGE_INTERVALS_PER_DEGREE
;
51 protected ArrayList
<LatLon
> positions
= new ArrayList
<LatLon
>();
53 protected abstract BufferedImage
drawShape(Globe globe
, Sector sector
, BufferedImage image
);
55 public SurfaceShape(Iterable
<LatLon
> positions
, Color color
, Color borderColor
, Dimension textureSize
)
57 if (positions
== null)
59 String message
= Logging
.getMessage("nullValue.PositionsListIsNull");
60 Logging
.logger().severe(message
);
61 throw new IllegalArgumentException(message
);
64 if (textureSize
!= null)
65 this.textureSize
= textureSize
;
67 // Set draw attributes
68 this.paint
= color
!= null ? color
: DEFAULT_COLOR
;
69 this.borderColor
= borderColor
!= null ? borderColor
: DEFAULT_BORDER_COLOR
;
71 // Copy positions list.
72 this.replacePositions(positions
);
78 private void replacePositions(Iterable
<LatLon
> newPositions
)
80 this.positions
.clear();
81 for (LatLon position
: newPositions
)
83 this.positions
.add(position
);
87 protected void createTextureTiles()
90 if (!LatLon
.positionsCrossDateLine(this.getPositions()))
92 this.tiles
.add(new TextureTile(Sector
.boundingSector(this.getPositions()), null));
96 Sector
[] sectors
= this.computeSplitSectors(this.getPositions());
97 this.tiles
.add(new TextureTile(sectors
[0], null));
98 this.tiles
.add(new TextureTile(sectors
[1], null));
103 * Returns two 'mirror' sectors each on one side of the longitude boundary - for boundary crossing shapes
105 * @param positions the shape positions
106 * @return an array of two sectors representing the shape
108 private Sector
[] computeSplitSectors(Iterable
<LatLon
> positions
)
110 Sector
[] sectors
= new Sector
[2];
111 // Find out longitude extremes for each sides
112 double maxWest
= Angle
.NEG180
.getDegrees();
113 double minEast
= Angle
.POS180
.getDegrees();
114 // Find out absolute latitude extremes
115 double minSouth
= Angle
.POS90
.getDegrees();
116 double maxNorth
= Angle
.NEG90
.getDegrees();
118 LatLon lastPos
= null;
119 for (LatLon pos
: positions
)
121 double lat
= pos
.getLatitude().getDegrees();
127 double lon
= pos
.getLongitude().getDegrees();
128 if (lon
<= 0 && lon
> maxWest
)
130 if (lon
>= 0 && lon
< minEast
)
135 double lastLon
= lastPos
.getLongitude().getDegrees();
136 if (Math
.signum(lon
) != Math
.signum(lastLon
))
137 if (Math
.abs(lon
- lastLon
) < 180)
139 // Crossing the zero longitude line too
146 // Mirror the two sectors - same longitude span
147 maxWest
= minEast
< -maxWest ?
-minEast
: maxWest
;
148 minEast
= minEast
> -maxWest ?
-maxWest
: minEast
;
150 sectors
[0] = Sector
.fromDegrees(minSouth
, maxNorth
, minEast
, 180d
); // East side
151 sectors
[1] = Sector
.fromDegrees(minSouth
, maxNorth
, -180d
, maxWest
); // West side
156 public void dispose()
161 public ArrayList
<Sector
> getSectors()
163 ArrayList
<Sector
> sectors
= new ArrayList
<Sector
>();
164 for (TextureTile tile
: this.tiles
)
166 sectors
.add(tile
.getSector());
171 public Iterable
<LatLon
> getPositions()
173 return this.positions
;
176 public void setPositions(Iterable
<LatLon
> positions
)
178 this.replacePositions(positions
);
179 this.clearTextureData();
182 public Paint
getPaint()
187 public void setPaint(Paint paint
)
190 this.clearTextureData();
193 public Color
getBorderColor()
198 public void setBorderColor(Color borderColor
)
200 this.borderColor
= borderColor
;
201 this.clearTextureData();
204 public Dimension
getTextureSize()
209 public void setTextureSize(Dimension textureSize
)
211 this.textureSize
= textureSize
;
212 this.clearTextureData();
215 public Stroke
getStroke()
220 public void setStroke(Stroke stroke
)
222 this.stroke
= stroke
;
223 this.clearTextureData();
226 public boolean isDrawBorder()
231 public void setDrawBorder(boolean drawBorder
)
233 this.drawBorder
= drawBorder
;
234 this.clearTextureData();
237 public boolean isDrawInterior()
242 public void setDrawInterior(boolean drawInterior
)
244 this.drawInterior
= drawInterior
;
245 this.clearTextureData();
248 public boolean isAntiAlias()
253 public void setAntiAlias(boolean antiAlias
)
255 this.antiAlias
= antiAlias
;
256 this.clearTextureData();
259 public double getNumEdgeIntervalsPerDegree()
261 return numEdgeIntervalsPerDegree
;
264 public void setNumEdgeIntervalsPerDegree(double numEdgeIntervals
)
266 this.numEdgeIntervalsPerDegree
= numEdgeIntervals
;
267 this.clearTextureData();
270 private boolean intersects(Sector sector
)
272 for (TextureTile tile
: this.tiles
)
274 if (tile
.getSector().intersects(sector
))
280 public void render(DrawContext dc
)
282 this.globe
= dc
.getGlobe(); // retain the globe used, for potential subsequent move
284 if (this.tiles
.size() == 0)
285 this.createTextureTiles();
287 if (!this.intersects(dc
.getVisibleSector()))
290 if (!this.tiles
.get(0).isTextureInMemory(dc
.getTextureCache()))
291 makeTextureData(dc
, this.textureSize
);
295 gl
.glPushAttrib(GL
.GL_COLOR_BUFFER_BIT
| GL
.GL_POLYGON_BIT
);
299 if (!dc
.isPickingMode())
301 gl
.glEnable(GL
.GL_BLEND
);
302 gl
.glBlendFunc(GL
.GL_SRC_ALPHA
, GL
.GL_ONE_MINUS_SRC_ALPHA
);
305 gl
.glPolygonMode(GL
.GL_FRONT
, GL
.GL_FILL
);
306 gl
.glEnable(GL
.GL_CULL_FACE
);
307 gl
.glCullFace(GL
.GL_BACK
);
309 dc
.getGeographicSurfaceTileRenderer().renderTiles(dc
, this.tiles
);
317 private void makeTextureData(DrawContext dc
, Dimension size
)
319 for (TextureTile tile
: this.tiles
)
321 BufferedImage image
= new BufferedImage((int) size
.getWidth(), (int) size
.getHeight(),
322 BufferedImage
.TYPE_4BYTE_ABGR
);
324 TextureData td
= new TextureData(GL
.GL_RGBA
, GL
.GL_RGBA
, false,
325 this.drawShape(dc
.getGlobe(), tile
.getSector(), image
));
326 td
.setMustFlipVertically(false);
327 tile
.setTextureData(td
);
331 private void clearTextureData()
336 public Position
getReferencePosition()
338 LatLon centroid
= this.tiles
.get(0).getSector().getCentroid();
339 return new Position(centroid
, 0);
342 public void move(Position delta
)
346 String msg
= Logging
.getMessage("nullValue.PositionIsNull");
347 Logging
.logger().severe(msg
);
348 throw new IllegalArgumentException(msg
);
351 this.moveTo(this.getReferencePosition().add(delta
));
355 * Move the shape over the sphereoid surface without maintaining its original azimuth -- its orientation relative to
358 * @param position the new position to move the shapes reference position to.
360 public void shiftTo(Position position
)
362 if (this.globe
== null)
365 if (position
== null)
367 String msg
= Logging
.getMessage("nullValue.PositionIsNull");
368 Logging
.logger().severe(msg
);
369 throw new IllegalArgumentException(msg
);
372 Vec4 p1
= globe
.computePointFromPosition(this.getReferencePosition().getLatitude(),
373 this.getReferencePosition().getLongitude(), 0);
374 Vec4 p2
= globe
.computePointFromPosition(position
.getLatitude(), position
.getLongitude(), 0);
375 Vec4 delta
= p2
.subtract3(p1
);
377 for (int i
= 0; i
< this.positions
.size(); i
++)
379 LatLon ll
= this.positions
.get(i
);
380 Vec4 p
= globe
.computePointFromPosition(ll
.getLatitude(), ll
.getLongitude(), 0);
382 Position pos
= globe
.computePositionFromPoint(p
);
384 this.positions
.set(i
, new LatLon(pos
.getLatitude(), pos
.getLongitude()));
387 this.createTextureTiles();
391 * Move the shape over the sphereoid surface while maintaining its original azimuth -- its orientation relative to
394 * @param position the new position to move the shapes reference position to.
396 public void moveTo(Position position
)
398 if (LatLon
.positionsCrossDateLine(this.positions
))
400 // TODO: Replace this hack by figuring out how to *accurately* move date-line crossing shapes using the
401 // distance/azimuth method used below for shapes that do not cross the dateline.
406 LatLon oldRef
= this.getReferencePosition().getLatLon();
407 LatLon newRef
= position
.getLatLon();
409 for (int i
= 0; i
< this.positions
.size(); i
++)
411 LatLon p
= this.positions
.get(i
);
412 double distance
= LatLon
.sphericalDistance(oldRef
, p
).radians
;
413 double azimuth
= LatLon
.azimuth(oldRef
, p
).radians
;
414 LatLon pp
= LatLon
.endPosition(newRef
, azimuth
, distance
);
415 this.positions
.set(i
, pp
);
418 this.createTextureTiles();
421 public static SurfaceShape
createEllipse(Globe globe
, LatLon center
, double majorAxisLength
,
422 double minorAxisLength
, Angle orientation
, int intervals
, Color interiorColor
, Color borderColor
,
423 Dimension textureSize
)
425 if (orientation
== null)
426 orientation
= Angle
.ZERO
;
428 if (majorAxisLength
<= 0)
430 String message
= Logging
.getMessage("Geom.MajorAxisInvalid");
431 Logging
.logger().severe(message
);
432 throw new IllegalArgumentException(message
);
435 if (minorAxisLength
<= 0)
437 String message
= Logging
.getMessage("Geom.MajorAxisInvalid");
438 Logging
.logger().severe(message
);
439 throw new IllegalArgumentException(message
);
442 int numPositions
= 1 + Math
.max(intervals
, 4);
443 final ArrayList
<LatLon
> positions
= new ArrayList
<LatLon
>();
445 double radius
= globe
.getRadiusAt(center
.getLatitude(), center
.getLongitude());
446 double da
= 2 * Math
.PI
/ (numPositions
- 1);
447 for (int i
= 0; i
< numPositions
; i
++)
449 // azimuth runs positive clockwise from north and through 360ยก.
450 double angle
= (i
!= numPositions
- 1) ? i
* da
: 0;
451 double azimuth
= Math
.PI
/ 2 - (angle
+ orientation
.radians
);
452 double xLength
= majorAxisLength
* Math
.cos(angle
);
453 double yLength
= minorAxisLength
* Math
.sin(angle
);
454 double distance
= Math
.sqrt(xLength
* xLength
+ yLength
* yLength
);
455 LatLon p
= LatLon
.endPosition(center
, azimuth
, distance
/ radius
);
459 return new SurfacePolygon(positions
, interiorColor
, borderColor
, textureSize
);