@@ -42,6 +42,8 @@ public class SurfaceText extends AbstractSurfaceObject implements GeographicText
42
42
protected CharSequence text ;
43
43
/** Location at which to draw the text. */
44
44
protected Position location ;
45
+ /** The angle of text rotation from the true north (clockwise). */
46
+ protected Angle heading = Angle .ZERO ;
45
47
/** The height of the text in meters. */
46
48
protected double textSizeInMeters = DEFAULT_TEXT_SIZE_IN_METERS ;
47
49
/** Dragging Support */
@@ -153,6 +155,30 @@ public void setPosition(Position position)
153
155
this .onShapeChanged ();
154
156
}
155
157
158
+ /** {@inheritDoc} */
159
+ public Angle getHeading ()
160
+ {
161
+ return this .heading ;
162
+ }
163
+
164
+ /**
165
+ * {@inheritDoc}
166
+ * <p>
167
+ * The angle of text rotation from the true north (clockwise)
168
+ */
169
+ public void setHeading (Angle heading )
170
+ {
171
+ if (heading == null )
172
+ {
173
+ String message = Logging .getMessage ("nullValue.HeadingIsNull" );
174
+ Logging .logger ().severe (message );
175
+ throw new IllegalArgumentException (message );
176
+ }
177
+
178
+ this .heading = heading ;
179
+ this .onShapeChanged ();
180
+ }
181
+
156
182
/** {@inheritDoc} */
157
183
public Font getFont ()
158
184
{
@@ -394,13 +420,6 @@ protected void drawGeographic(DrawContext dc, SurfaceTileDrawContext sdc)
394
420
protected void drawText (DrawContext dc )
395
421
{
396
422
TextRenderer tr = this .getTextRenderer (dc );
397
-
398
- Point2D point = this .getOffset ().computeOffset (this .textBounds .getWidth (), this .textBounds .getHeight (), null ,
399
- null );
400
-
401
- int x = (int ) point .getX ();
402
- int y = (int ) point .getY ();
403
-
404
423
try
405
424
{
406
425
tr .begin3DRendering ();
@@ -409,9 +428,9 @@ protected void drawText(DrawContext dc)
409
428
CharSequence text = this .getText ();
410
429
411
430
tr .setColor (bgColor );
412
- tr .draw (text , x + 1 , y - 1 );
431
+ tr .draw (text , 1 , - 1 );
413
432
tr .setColor (this .getColor ());
414
- tr .draw (text , x , y );
433
+ tr .draw (text , 0 , 0 );
415
434
}
416
435
finally
417
436
{
@@ -461,6 +480,24 @@ protected void applyDrawTransform(DrawContext dc, SurfaceTileDrawContext sdc)
461
480
462
481
// Apply the scaling factor to draw the text at the correct geographic size
463
482
gl .glScaled (this .scale , this .scale , 1d );
483
+
484
+ double widthInPixels = this .textBounds .getWidth ();
485
+ double heightInPixels = this .textBounds .getHeight ();
486
+
487
+ Point2D textDimensions = getRotatedTextDimensions ();
488
+ double rotatedPixelHeight = textDimensions .getY ();
489
+ double rotatedPixelWidth = textDimensions .getX ();
490
+
491
+ Point2D textOffset = getOffset ().computeOffset (rotatedPixelWidth , rotatedPixelHeight , null , null );
492
+
493
+ // Move to offset position.
494
+ gl .glTranslated (rotatedPixelWidth / 2.0 + textOffset .getX (), rotatedPixelHeight / 2.0 + textOffset .getY (), 0 );
495
+
496
+ // Apply rotation angle from text center.
497
+ gl .glRotated (-this .heading .degrees , 0 , 0 , 1 );
498
+
499
+ // Move to text center.
500
+ gl .glTranslated (-widthInPixels / 2.0 , -heightInPixels / 2.0 , 0 );
464
501
}
465
502
466
503
/**
@@ -523,6 +560,54 @@ protected Color computeBackgroundColor(Color color)
523
560
else
524
561
return new Color (1 , 1 , 1 , 0.7f );
525
562
}
563
+
564
+ private Point2D getRotatedTextDimensions ()
565
+ {
566
+ double widthInPixels = this .textBounds .getWidth ();
567
+ double heightInPixels = this .textBounds .getHeight ();
568
+
569
+ Angle rotation = Angle .normalizedLongitude (this .heading );
570
+ double ct = Math .cos (rotation .radians );
571
+ double st = Math .sin (rotation .radians );
572
+
573
+ double hct = heightInPixels * ct ;
574
+ double wct = widthInPixels * ct ;
575
+ double hst = heightInPixels * st ;
576
+ double wst = widthInPixels * st ;
577
+
578
+ if (rotation .degrees > 0 )
579
+ {
580
+ if (rotation .degrees < 90 )
581
+ {
582
+ // 0 < theta < 90
583
+ heightInPixels = hct + wst ;
584
+ widthInPixels = wct + hst ;
585
+ }
586
+ else
587
+ {
588
+ // 90 <= theta <= 180
589
+ heightInPixels = wst - hct ;
590
+ widthInPixels = hst - wct ;
591
+ }
592
+ }
593
+ else
594
+ {
595
+ if (rotation .degrees > -90 )
596
+ {
597
+ // -90 < theta <= 0
598
+ heightInPixels = hct - wst ;
599
+ widthInPixels = wct - hst ;
600
+ }
601
+ else
602
+ {
603
+ // -180 <= theta <= -90
604
+ heightInPixels = -(hct + wst );
605
+ widthInPixels = -(wct + hst );
606
+ }
607
+ }
608
+
609
+ return new Point2D .Double (widthInPixels , heightInPixels );
610
+ }
526
611
527
612
/**
528
613
* Compute the sector covered by this surface text.
@@ -536,30 +621,32 @@ protected Sector[] computeSector(DrawContext dc)
536
621
// Compute text extent depending on distance from eye
537
622
Globe globe = dc .getGlobe ();
538
623
539
- double widthInPixels = this .textBounds .getWidth ();
540
- double heightInPixels = this .textBounds .getHeight ();
541
-
542
- double heightInMeters = this .textSizeInMeters ;
624
+ Point2D textDimensions = getRotatedTextDimensions ();
625
+ double heightInPixels = textDimensions .getY ();
626
+ double widthInPixels = textDimensions .getX ();
627
+
628
+ double heightFactor = heightInPixels / this .textBounds .getHeight ();
629
+ double heightInMeters = heightFactor * this .textSizeInMeters ;
543
630
double widthInMeters = heightInMeters * (widthInPixels / heightInPixels );
544
-
631
+
545
632
double radius = globe .getRadius ();
546
633
double heightInRadians = heightInMeters / radius ;
547
634
double widthInRadians = widthInMeters / radius ;
548
635
549
- // Compute the offset from the reference position. Convert pixels to meters based on the geographic size
550
- // of the text.
551
- Point2D point = this . getOffset ().computeOffset (widthInPixels , heightInPixels , null , null );
636
+ // Compute the offset from the reference position.
637
+ // Convert pixels to meters based on the geographic size of the text.
638
+ Point2D textOffset = getOffset ().computeOffset (widthInPixels , heightInPixels , null , null );
552
639
553
640
double metersPerPixel = heightInMeters / heightInPixels ;
554
641
555
- double dxRadians = (point .getX () * metersPerPixel ) / radius ;
556
- double dyRadians = (point .getY () * metersPerPixel ) / radius ;
557
-
642
+ double dxRadians = (textOffset .getX () * metersPerPixel ) / radius ;
643
+ double dyRadians = (textOffset .getY () * metersPerPixel ) / radius ;
644
+
558
645
double minLat = this .location .latitude .addRadians (dyRadians ).degrees ;
559
646
double maxLat = this .location .latitude .addRadians (dyRadians + heightInRadians ).degrees ;
560
647
double minLon = this .location .longitude .addRadians (dxRadians ).degrees ;
561
648
double maxLon = this .location .longitude .addRadians (dxRadians + widthInRadians ).degrees ;
562
-
649
+
563
650
this .drawLocation = LatLon .fromDegrees (minLat , minLon );
564
651
565
652
if (maxLon > 180 ) {
0 commit comments