Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
129 changes: 108 additions & 21 deletions src/gov/nasa/worldwind/render/SurfaceText.java
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ public class SurfaceText extends AbstractSurfaceObject implements GeographicText
protected CharSequence text;
/** Location at which to draw the text. */
protected Position location;
/** The angle of text rotation from the true north (clockwise). */
protected Angle heading = Angle.ZERO;
/** The height of the text in meters. */
protected double textSizeInMeters = DEFAULT_TEXT_SIZE_IN_METERS;
/** Dragging Support */
Expand Down Expand Up @@ -153,6 +155,30 @@ public void setPosition(Position position)
this.onShapeChanged();
}

/** {@inheritDoc} */
public Angle getHeading()
{
return this.heading;
}

/**
* {@inheritDoc}
* <p>
* The angle of text rotation from the true north (clockwise)
*/
public void setHeading(Angle heading)
{
if (heading == null)
{
String message = Logging.getMessage("nullValue.HeadingIsNull");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}

this.heading = heading;
this.onShapeChanged();
}

/** {@inheritDoc} */
public Font getFont()
{
Expand Down Expand Up @@ -394,13 +420,6 @@ protected void drawGeographic(DrawContext dc, SurfaceTileDrawContext sdc)
protected void drawText(DrawContext dc)
{
TextRenderer tr = this.getTextRenderer(dc);

Point2D point = this.getOffset().computeOffset(this.textBounds.getWidth(), this.textBounds.getHeight(), null,
null);

int x = (int) point.getX();
int y = (int) point.getY();

try
{
tr.begin3DRendering();
Expand All @@ -409,9 +428,9 @@ protected void drawText(DrawContext dc)
CharSequence text = this.getText();

tr.setColor(bgColor);
tr.draw(text, x + 1, y - 1);
tr.draw(text, 1, -1);
tr.setColor(this.getColor());
tr.draw(text, x, y);
tr.draw(text, 0, 0);
}
finally
{
Expand Down Expand Up @@ -461,6 +480,24 @@ protected void applyDrawTransform(DrawContext dc, SurfaceTileDrawContext sdc)

// Apply the scaling factor to draw the text at the correct geographic size
gl.glScaled(this.scale, this.scale, 1d);

double widthInPixels = this.textBounds.getWidth();
double heightInPixels = this.textBounds.getHeight();

Point2D textDimensions = getRotatedTextDimensions();
double rotatedPixelHeight = textDimensions.getY();
double rotatedPixelWidth = textDimensions.getX();

Point2D textOffset = getOffset().computeOffset(rotatedPixelWidth, rotatedPixelHeight, null, null);

// Move to offset position.
gl.glTranslated(rotatedPixelWidth / 2.0 + textOffset.getX(), rotatedPixelHeight / 2.0 + textOffset.getY(), 0);

// Apply rotation angle from text center.
gl.glRotated(-this.heading.degrees, 0, 0, 1);

// Move to text center.
gl.glTranslated(-widthInPixels / 2.0, -heightInPixels / 2.0, 0);
}

/**
Expand Down Expand Up @@ -523,6 +560,54 @@ protected Color computeBackgroundColor(Color color)
else
return new Color(1, 1, 1, 0.7f);
}

private Point2D getRotatedTextDimensions()
{
double widthInPixels = this.textBounds.getWidth();
double heightInPixels = this.textBounds.getHeight();

Angle rotation = Angle.normalizedLongitude(this.heading);
double ct = Math.cos(rotation.radians);
double st = Math.sin(rotation.radians);

double hct = heightInPixels * ct;
double wct = widthInPixels * ct;
double hst = heightInPixels * st;
double wst = widthInPixels * st;

if (rotation.degrees > 0)
{
if (rotation.degrees < 90)
{
// 0 < theta < 90
heightInPixels = hct + wst;
widthInPixels = wct + hst;
}
else
{
// 90 <= theta <= 180
heightInPixels = wst - hct;
widthInPixels = hst - wct;
}
}
else
{
if (rotation.degrees > -90 )
{
// -90 < theta <= 0
heightInPixels = hct - wst;
widthInPixels = wct - hst;
}
else
{
// -180 <= theta <= -90
heightInPixels = -(hct + wst);
widthInPixels = -(wct + hst);
}
}

return new Point2D.Double(widthInPixels, heightInPixels);
}

/**
* Compute the sector covered by this surface text.
Expand All @@ -536,30 +621,32 @@ protected Sector[] computeSector(DrawContext dc)
// Compute text extent depending on distance from eye
Globe globe = dc.getGlobe();

double widthInPixels = this.textBounds.getWidth();
double heightInPixels = this.textBounds.getHeight();

double heightInMeters = this.textSizeInMeters;
Point2D textDimensions = getRotatedTextDimensions();
double heightInPixels = textDimensions.getY();
double widthInPixels = textDimensions.getX();

double heightFactor = heightInPixels / this.textBounds.getHeight();
double heightInMeters = heightFactor * this.textSizeInMeters;
double widthInMeters = heightInMeters * (widthInPixels / heightInPixels);

double radius = globe.getRadius();
double heightInRadians = heightInMeters / radius;
double widthInRadians = widthInMeters / radius;

// Compute the offset from the reference position. Convert pixels to meters based on the geographic size
// of the text.
Point2D point = this.getOffset().computeOffset(widthInPixels, heightInPixels, null, null);
// Compute the offset from the reference position.
// Convert pixels to meters based on the geographic size of the text.
Point2D textOffset = getOffset().computeOffset(widthInPixels, heightInPixels, null, null);

double metersPerPixel = heightInMeters / heightInPixels;

double dxRadians = (point.getX() * metersPerPixel) / radius;
double dyRadians = (point.getY() * metersPerPixel) / radius;

double dxRadians = (textOffset.getX() * metersPerPixel) / radius;
double dyRadians = (textOffset.getY() * metersPerPixel) / radius;
double minLat = this.location.latitude.addRadians(dyRadians).degrees;
double maxLat = this.location.latitude.addRadians(dyRadians + heightInRadians).degrees;
double minLon = this.location.longitude.addRadians(dxRadians).degrees;
double maxLon = this.location.longitude.addRadians(dxRadians + widthInRadians).degrees;

this.drawLocation = LatLon.fromDegrees(minLat, minLon);

if (maxLon > 180) {
Expand Down
82 changes: 82 additions & 0 deletions testFunctional/gov/nasa/worldwind/render/SurfaceTextTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
/*
* Copyright (C) 2012 United States Government as represented by the Administrator of the
* National Aeronautics and Space Administration.
* All Rights Reserved.
*/

package gov.nasa.worldwind.render;

import gov.nasa.worldwind.Configuration;
import gov.nasa.worldwind.WorldWind;
import gov.nasa.worldwind.avlist.AVKey;
import gov.nasa.worldwind.geom.Angle;
import gov.nasa.worldwind.geom.Position;
import gov.nasa.worldwind.layers.RenderableLayer;
import gov.nasa.worldwindx.examples.ApplicationTemplate;

/**
* Test of {@link SurfaceText} rotation and offset calculations. This test creates
* several SurfaceText objects with different rotation and offset configurations.
* The SurfaceText objects can be visually inspected to confirm that the rotations
* and placements are correctly calculated, and that all of them are located
* within their respective bounding-sectors.
*
* @author pabercrombie
* @version $Id: SurfaceTextTest.java 1171 2013-02-11 21:45:02Z dcollins $
*/
public class SurfaceTextTest extends ApplicationTemplate
{
private static Position center = Position.fromDegrees(38.9345, -120.1670, 50000);

public static class AppFrame extends ApplicationTemplate.AppFrame
{
public AppFrame()
{
super(true, true, false);

RenderableLayer layer = new RenderableLayer();

PointPlacemarkAttributes attributes = new PointPlacemarkAttributes();
attributes.setLabelColor("ffffffff");
attributes.setLineColor("ff0000ff");
attributes.setUsePointAsDefaultImage(true);
attributes.setScale(5d);

int j = 0;
for (double x = -1.0; x <= 0.0; x += 0.5, j++)
{
for (double y = -1.0; y <= 0.0; y += 0.5, j++)
{
for (int i = 0; i <= 12; i++)
{
double latitude = center.latitude.degrees + ((j - 4) / 5.0);
double longitude = center.longitude.degrees + ((i - 6) / 5.0);
Position position = Position.fromDegrees(latitude, longitude, 0);

SurfaceText surfaceText = new SurfaceText("Test Label Description", position);
surfaceText.setDrawBoundingSectors(true);
surfaceText.setHeading(Angle.fromDegrees(i * 30));
surfaceText.setOffset(Offset.fromFraction(x, y));
layer.addRenderable(surfaceText);

PointPlacemark placemark = new PointPlacemark(position);
placemark.setAltitudeMode(WorldWind.CLAMP_TO_GROUND);
placemark.setAttributes(attributes);
layer.addRenderable(placemark);
}
}
}

this.getWwd().getModel().getLayers().add(layer);
}
}

public static void main(String[] args)
{
Configuration.setValue(AVKey.INITIAL_LATITUDE, center.latitude.degrees);
Configuration.setValue(AVKey.INITIAL_LONGITUDE, center.longitude.degrees);
Configuration.setValue(AVKey.INITIAL_ALTITUDE, center.elevation);

ApplicationTemplate.start("Surface Text Test", AppFrame.class);
}
}