private void makePartialDiskTerrainConformant( DrawContext dc, int numCoords, float[] verts, double altitude, boolean terrainConformant, Vec4 referenceCenter) { Globe globe = dc.getGlobe(); Matrix transform = this.computeTransform(dc.getGlobe(), dc.getVerticalExaggeration()); for (int i = 0; i < numCoords; i += 3) { Vec4 vec = new Vec4(verts[i], verts[i + 1], verts[i + 2]); vec = vec.transformBy4(transform); Position p = globe.computePositionFromPoint(vec); double elevation = altitude; if (terrainConformant) elevation += this.computeElevationAt(dc, p.getLatitude(), p.getLongitude()); vec = globe.computePointFromPosition(p.getLatitude(), p.getLongitude(), elevation); verts[i] = (float) (vec.x - referenceCenter.x); verts[i + 1] = (float) (vec.y - referenceCenter.y); verts[i + 2] = (float) (vec.z - referenceCenter.z); } }
private void makePartialCylinderTerrainConformant( DrawContext dc, int slices, int stacks, float[] verts, double[] altitudes, boolean[] terrainConformant, Vec4 referenceCenter) { Globe globe = dc.getGlobe(); Matrix transform = this.computeTransform(dc.getGlobe(), dc.getVerticalExaggeration()); for (int i = 0; i <= slices; i++) { int index = i * (stacks + 1); index = 3 * index; Vec4 vec = new Vec4(verts[index], verts[index + 1], verts[index + 2]); vec = vec.transformBy4(transform); Position p = globe.computePositionFromPoint(vec); for (int j = 0; j <= stacks; j++) { double elevation = altitudes[j]; if (terrainConformant[j]) elevation += this.computeElevationAt(dc, p.getLatitude(), p.getLongitude()); vec = globe.computePointFromPosition(p.getLatitude(), p.getLongitude(), elevation); index = j + i * (stacks + 1); index = 3 * index; verts[index] = (float) (vec.x - referenceCenter.x); verts[index + 1] = (float) (vec.y - referenceCenter.y); verts[index + 2] = (float) (vec.z - referenceCenter.z); } } }
private void makeRadialWallTerrainConformant( DrawContext dc, int pillars, int stacks, float[] verts, double[] altitudes, boolean[] terrainConformant, Vec4 referenceCenter) { Globe globe = dc.getGlobe(); Matrix transform = this.computeTransform(dc.getGlobe(), dc.getVerticalExaggeration()); for (int p = 0; p <= pillars; p++) { int index = p; index = 3 * index; Vec4 vec = new Vec4(verts[index], verts[index + 1], verts[index + 2]); vec = vec.transformBy4(transform); Position pos = globe.computePositionFromPoint(vec); for (int s = 0; s <= stacks; s++) { double elevation = altitudes[s]; if (terrainConformant[s]) elevation += this.computeElevationAt(dc, pos.getLatitude(), pos.getLongitude()); vec = globe.computePointFromPosition(pos.getLatitude(), pos.getLongitude(), elevation); index = p + s * (pillars + 1); index = 3 * index; verts[index] = (float) (vec.x - referenceCenter.x); verts[index + 1] = (float) (vec.y - referenceCenter.y); verts[index + 2] = (float) (vec.z - referenceCenter.z); } } }
private boolean atMaxLevel(DrawContext dc) { Position vpc = dc.getViewportCenterPosition(); if (dc.getView() == null || this.getLevels() == null || vpc == null) return false; if (!this.getLevels().getSector().contains(vpc.getLatitude(), vpc.getLongitude())) return true; Level nextToLast = this.getLevels().getNextToLastLevel(); if (nextToLast == null) return true; Sector centerSector = nextToLast.computeSectorForPosition( vpc.getLatitude(), vpc.getLongitude(), this.getLevels().getTileOrigin()); return this.needToSplit(dc, centerSector); }
/** * Compute the lat/lon position of the view center * * @param dc the current DrawContext * @param view the current View * @return the ground position of the view center or null */ protected Position computeGroundPosition(DrawContext dc, View view) { if (view == null) return null; Position groundPos = view.computePositionFromScreenPoint( view.getViewport().getWidth() / 2, view.getViewport().getHeight() / 2); if (groundPos == null) return null; double elevation = dc.getGlobe().getElevation(groundPos.getLatitude(), groundPos.getLongitude()); return new Position( groundPos.getLatitude(), groundPos.getLongitude(), elevation * dc.getVerticalExaggeration()); }
protected static boolean isTileVisible( DrawContext dc, Tile tile, double minDistanceSquared, double maxDistanceSquared) { if (!tile.getSector().intersects(dc.getVisibleSector())) return false; View view = dc.getView(); Position eyePos = view.getEyePosition(); if (eyePos == null) return false; Angle lat = clampAngle( eyePos.getLatitude(), tile.getSector().getMinLatitude(), tile.getSector().getMaxLatitude()); Angle lon = clampAngle( eyePos.getLongitude(), tile.getSector().getMinLongitude(), tile.getSector().getMaxLongitude()); Vec4 p = dc.getGlobe().computePointFromPosition(lat, lon, 0d); double distSquared = dc.getView().getEyePoint().distanceToSquared3(p); //noinspection RedundantIfStatement if (minDistanceSquared > distSquared || maxDistanceSquared < distSquared) return false; return true; }
protected Vec4 computeAnnotationPosition(Position pos) { Vec4 surfacePoint = this.wwd .getSceneController() .getTerrain() .getSurfacePoint(pos.getLatitude(), pos.getLongitude()); if (surfacePoint == null) { Globe globe = this.wwd.getModel().getGlobe(); surfacePoint = globe.computePointFromPosition( pos.getLatitude(), pos.getLongitude(), globe.getElevation(pos.getLatitude(), pos.getLongitude())); } return this.wwd.getView().project(surfacePoint); }
protected void assembleVertexControlPoints(DrawContext dc) { Terrain terrain = dc.getTerrain(); ExtrudedPolygon polygon = this.getPolygon(); Position refPos = polygon.getReferencePosition(); Vec4 refPoint = terrain.getSurfacePoint(refPos.getLatitude(), refPos.getLongitude(), 0); int altitudeMode = polygon.getAltitudeMode(); double height = polygon.getHeight(); Vec4 vaa = null; double vaaLength = 0; // used to compute independent length of each cap vertex double vaLength = 0; int i = 0; for (LatLon location : polygon.getOuterBoundary()) { Vec4 vert; // Compute the top/cap point. if (altitudeMode == WorldWind.CONSTANT || !(location instanceof Position)) { if (vaa == null) { // Compute the vector lengths of the top and bottom points at the reference position. vaa = refPoint.multiply3(height / refPoint.getLength3()); vaaLength = vaa.getLength3(); vaLength = refPoint.getLength3(); } // Compute the bottom point, which is on the terrain. vert = terrain.getSurfacePoint(location.getLatitude(), location.getLongitude(), 0); double delta = vaLength - vert.dot3(refPoint) / vaLength; vert = vert.add3(vaa.multiply3(1d + delta / vaaLength)); } else if (altitudeMode == WorldWind.RELATIVE_TO_GROUND) { vert = terrain.getSurfacePoint( location.getLatitude(), location.getLongitude(), ((Position) location).getAltitude()); } else // WorldWind.ABSOLUTE { vert = terrain .getGlobe() .computePointFromPosition( location.getLatitude(), location.getLongitude(), ((Position) location).getAltitude() * terrain.getVerticalExaggeration()); } Position vertexPosition = this.wwd.getModel().getGlobe().computePositionFromPoint(vert); this.controlPoints.add( new ControlPointMarker( MOVE_VERTEX_ACTION, vertexPosition, vert, this.vertexControlAttributes, i)); i++; } }
public void onSuccess(Position[] positions) { for (Position p : positions) { Logging.logger() .info( p.getLatitude().degrees + "," + p.getLongitude().degrees + " --> " + p.getElevation()); } }
protected static boolean isNameVisible( DrawContext dc, PlaceNameService service, Position namePosition) { double elevation = dc.getVerticalExaggeration() * namePosition.getElevation(); Vec4 namePoint = dc.getGlobe() .computePointFromPosition( namePosition.getLatitude(), namePosition.getLongitude(), elevation); Vec4 eyeVec = dc.getView().getEyePoint(); double dist = eyeVec.distanceTo3(namePoint); return dist >= service.getMinDisplayDistance() && dist <= service.getMaxDisplayDistance(); }
protected boolean isNavSectorVisible( DrawContext dc, double minDistanceSquared, double maxDistanceSquared) { if (!navSector.intersects(dc.getVisibleSector())) return false; View view = dc.getView(); Position eyePos = view.getEyePosition(); if (eyePos == null) return false; // check for eyePos over globe if (Double.isNaN(eyePos.getLatitude().getDegrees()) || Double.isNaN(eyePos.getLongitude().getDegrees())) return false; Angle lat = clampAngle(eyePos.getLatitude(), navSector.getMinLatitude(), navSector.getMaxLatitude()); Angle lon = clampAngle( eyePos.getLongitude(), navSector.getMinLongitude(), navSector.getMaxLongitude()); Vec4 p = dc.getGlobe().computePointFromPosition(lat, lon, 0d); double distSquared = dc.getView().getEyePoint().distanceToSquared3(p); //noinspection RedundantIfStatement if (minDistanceSquared > distSquared || maxDistanceSquared < distSquared) return false; return true; }
private Vec4 computeReferencePoint(DrawContext dc) { if (dc.getViewportCenterPosition() != null) return dc.getGlobe().computePointFromPosition(dc.getViewportCenterPosition()); java.awt.geom.Rectangle2D viewport = dc.getView().getViewport(); int x = (int) viewport.getWidth() / 2; for (int y = (int) (0.5 * viewport.getHeight()); y >= 0; y--) { Position pos = dc.getView().computePositionFromScreenPoint(x, y); if (pos == null) continue; return dc.getGlobe().computePointFromPosition(pos.getLatitude(), pos.getLongitude(), 0d); } return null; }
private void makeSectionVertices( DrawContext dc, int locationPos, float[] locations, double[] altitude, boolean[] terrainConformant, int subdivisions, Matrix locationTransform, Vec4 referenceCenter, int vertexPos, float[] vertices) { GeometryBuilder gb = this.getGeometryBuilder(); int numPoints = gb.getSubdivisionPointsVertexCount(subdivisions); Globe globe = dc.getGlobe(); int index1 = 3 * locationPos; int index2 = 3 * (locationPos + 1); float[] locationVerts = new float[3 * numPoints]; gb.makeSubdivisionPoints( locations[index1], locations[index1 + 1], locations[index1 + 2], locations[index2], locations[index2 + 1], locations[index2 + 2], subdivisions, locationVerts); for (int i = 0; i < numPoints; i++) { int index = 3 * i; Vec4 vec = new Vec4(locationVerts[index], locationVerts[index + 1], locationVerts[index + 2]); vec = vec.transformBy4(locationTransform); Position pos = globe.computePositionFromPoint(vec); for (int j = 0; j < 2; j++) { vec = this.computePointFromPosition( dc, pos.getLatitude(), pos.getLongitude(), altitude[j], terrainConformant[j]); index = 2 * i + j; index = 3 * (vertexPos + index); vertices[index] = (float) (vec.x - referenceCenter.x); vertices[index + 1] = (float) (vec.y - referenceCenter.y); vertices[index + 2] = (float) (vec.z - referenceCenter.z); } } }
private void makeCap( DrawContext dc, GeometryBuilder.IndexedTriangleArray ita, double altitude, boolean terrainConformant, int orientation, Matrix locationTransform, Vec4 referenceCenter, int indexPos, int[] indices, int vertexPos, float[] vertices, float[] normals) { GeometryBuilder gb = this.getGeometryBuilder(); Globe globe = dc.getGlobe(); int indexCount = ita.getIndexCount(); int vertexCount = ita.getVertexCount(); int[] locationIndices = ita.getIndices(); float[] locationVerts = ita.getVertices(); this.copyIndexArray( indexCount, (orientation == GeometryBuilder.INSIDE), locationIndices, vertexPos, indexPos, indices); for (int i = 0; i < vertexCount; i++) { int index = 3 * i; Vec4 vec = new Vec4(locationVerts[index], locationVerts[index + 1], locationVerts[index + 2]); vec = vec.transformBy4(locationTransform); Position pos = globe.computePositionFromPoint(vec); vec = this.computePointFromPosition( dc, pos.getLatitude(), pos.getLongitude(), altitude, terrainConformant); index = 3 * (vertexPos + i); vertices[index] = (float) (vec.x - referenceCenter.x); vertices[index + 1] = (float) (vec.y - referenceCenter.y); vertices[index + 2] = (float) (vec.z - referenceCenter.z); } gb.makeIndexedTriangleArrayNormals( indexPos, indexCount, indices, vertexPos, vertexCount, vertices, normals); }
protected String formatMeasurements(Position pos) { StringBuilder sb = new StringBuilder(); /* //sb.append(this.unitsFormat.areaNL(this.getLabel(AREA_LABEL), this.getArea())); sb.append(this.unitsFormat.lengthNL(this.getLabel(PERIMETER_LABEL), this.getLength())); */ // sb.append(this.unitsFormat.lengthNL(this.getLabel(WIDTH_LABEL), // this.shape.getEastWestRadius() * 2)); // sb.append(this.unitsFormat.lengthNL(this.getLabel(LENGTH_LABEL), // this.shape.getNorthSouthRadius() * 2)); // sb.append(this.unitsFormat.lengthNL(this.getLabel(HEIGHT_LABEL), // this.shape.getVerticalRadius() * 2)); // sb.append(this.unitsFormat.angleNL(this.getLabel(HEADING_LABEL), this.shape.getHeading())); // if "activeControlPoint" is in fact one of the control points if (!this.arePositionsRedundant(pos, this.polygon.getReferencePosition())) { sb.append(this.unitsFormat.angleNL(this.getLabel(LATITUDE_LABEL), pos.getLatitude())); sb.append(this.unitsFormat.angleNL(this.getLabel(LONGITUDE_LABEL), pos.getLongitude())); sb.append(this.unitsFormat.lengthNL(this.getLabel(ALTITUDE_LABEL), pos.getAltitude())); } // if "activeControlPoint" is the shape itself if (this.polygon.getReferencePosition() != null) { sb.append( this.unitsFormat.angleNL( this.getLabel(CENTER_LATITUDE_LABEL), this.polygon.getReferencePosition().getLatitude())); sb.append( this.unitsFormat.angleNL( this.getLabel(CENTER_LONGITUDE_LABEL), this.polygon.getReferencePosition().getLongitude())); sb.append( this.unitsFormat.lengthNL( this.getLabel(CENTER_ALTITUDE_LABEL), this.polygon.getReferencePosition().getAltitude())); } return sb.toString(); }
protected void setPolygonHeight(Point previousMousePoint, Point mousePoint) { // Find the closest points between the rays through each screen point, and the ray from the // control point // and in the direction of the globe's surface normal. Compute the elevation difference between // these two // points, and use that as the change in polygon height. Position referencePos = this.polygon.getReferencePosition(); if (referencePos == null) return; Vec4 referencePoint = this.wwd.getModel().getGlobe().computePointFromPosition(referencePos); Vec4 surfaceNormal = this.wwd .getModel() .getGlobe() .computeSurfaceNormalAtLocation( referencePos.getLatitude(), referencePos.getLongitude()); Line verticalRay = new Line(referencePoint, surfaceNormal); Line screenRay = this.wwd.getView().computeRayFromScreenPoint(mousePoint.getX(), mousePoint.getY()); Line previousScreenRay = this.wwd .getView() .computeRayFromScreenPoint(previousMousePoint.getX(), previousMousePoint.getY()); Vec4 pointOnLine = AirspaceEditorUtil.nearestPointOnLine(verticalRay, screenRay); Vec4 previousPointOnLine = AirspaceEditorUtil.nearestPointOnLine(verticalRay, previousScreenRay); Position pos = this.wwd.getModel().getGlobe().computePositionFromPoint(pointOnLine); Position previousPos = this.wwd.getModel().getGlobe().computePositionFromPoint(previousPointOnLine); double elevationChange = pos.getElevation() - previousPos.getElevation(); java.util.List<Position> boundary = new ArrayList<Position>(); for (LatLon ll : this.polygon.getOuterBoundary()) { boundary.add(new Position(ll, ((Position) ll).getElevation() + elevationChange)); } this.polygon.setOuterBoundary(boundary); }
protected void drawMany(DrawContext dc, Iterable<? extends WWIcon> icons, Layer layer) { if (dc == null) { String msg = Logging.getMessage("nullValue.DrawContextIsNull"); Logging.logger().severe(msg); throw new IllegalArgumentException(msg); } if (dc.getVisibleSector() == null) return; SectorGeometryList geos = dc.getSurfaceGeometry(); //noinspection RedundantIfStatement if (geos == null) return; if (icons == null) { String msg = Logging.getMessage("nullValue.IconIterator"); Logging.logger().severe(msg); throw new IllegalArgumentException(msg); } Iterator<? extends WWIcon> iterator = icons.iterator(); if (!iterator.hasNext()) return; double horizon = dc.getView().getHorizonDistance(); while (iterator.hasNext()) { WWIcon icon = iterator.next(); if (!isIconValid(icon, true)) { // Record feedback data for this WWIcon if feedback is enabled. if (icon != null) this.recordFeedback(dc, icon, null, null); continue; } if (!icon.isVisible()) { // Record feedback data for this WWIcon if feedback is enabled. this.recordFeedback(dc, icon, null, null); continue; } // Determine Cartesian position from the surface geometry if the icon is near the surface, // otherwise draw it from the globe. Position pos = icon.getPosition(); Vec4 iconPoint = null; if (pos.getElevation() < dc.getGlobe().getMaxElevation() && !this.isAlwaysUseAbsoluteElevation()) { iconPoint = dc.getSurfaceGeometry().getSurfacePoint(icon.getPosition()); } if (iconPoint == null) { Angle lat = pos.getLatitude(); Angle lon = pos.getLongitude(); double elevation = pos.getElevation(); if (!this.isAlwaysUseAbsoluteElevation()) elevation += dc.getGlobe().getElevation(lat, lon); iconPoint = dc.getGlobe().computePointFromPosition(lat, lon, elevation); } double eyeDistance = icon.isAlwaysOnTop() ? 0 : dc.getView().getEyePoint().distanceTo3(iconPoint); // X-PATCH Marjan // extracted skip conditions into an overrideable method if (!meetsRenderCriteria(dc, icon, iconPoint, eyeDistance)) { this.recordFeedback(dc, icon, iconPoint, null); continue; } // X-END // The icons aren't drawn here, but added to the ordered queue to be drawn back-to-front. dc.addOrderedRenderable(new OrderedIcon(icon, iconPoint, layer, eyeDistance, horizon)); if (icon.isShowToolTip()) this.addToolTip(dc, icon, iconPoint); } }
protected void drawIcon(DrawContext dc) { if (this.getIconFilePath() == null) return; GL gl = dc.getGL(); OGLStackHandler ogsh = new OGLStackHandler(); try { // Initialize texture if necessary Texture iconTexture = dc.getTextureCache().getTexture(this.getIconFilePath()); if (iconTexture == null) { this.initializeTexture(dc); iconTexture = dc.getTextureCache().getTexture(this.getIconFilePath()); if (iconTexture == null) { String msg = Logging.getMessage("generic.ImageReadFailed"); Logging.logger().finer(msg); return; } } gl.glDisable(GL.GL_DEPTH_TEST); double width = this.getScaledIconWidth(); double height = this.getScaledIconHeight(); // Load a parallel projection with xy dimensions (viewportWidth, viewportHeight) // into the GL projection matrix. java.awt.Rectangle viewport = dc.getView().getViewport(); ogsh.pushProjectionIdentity(gl); double maxwh = width > height ? width : height; gl.glOrtho(0d, viewport.width, 0d, viewport.height, -0.6 * maxwh, 0.6 * maxwh); // Translate and scale ogsh.pushModelviewIdentity(gl); double scale = this.computeScale(viewport); Vec4 locationSW = this.computeLocation(viewport, scale); gl.glTranslated(locationSW.x(), locationSW.y(), locationSW.z()); // Scale to 0..1 space gl.glScaled(scale, scale, 1); gl.glScaled(width, height, 1d); if (!dc.isPickingMode()) { gl.glEnable(GL.GL_BLEND); gl.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA); // Draw background color behind the map gl.glColor4ub( (byte) this.backColor.getRed(), (byte) this.backColor.getGreen(), (byte) this.backColor.getBlue(), (byte) (this.backColor.getAlpha() * this.getOpacity())); dc.drawUnitQuad(); // Draw world map icon gl.glColor4d(1d, 1d, 1d, this.getOpacity()); gl.glEnable(GL.GL_TEXTURE_2D); iconTexture.bind(); TextureCoords texCoords = iconTexture.getImageTexCoords(); dc.drawUnitQuad(texCoords); gl.glBindTexture(GL.GL_TEXTURE_2D, 0); gl.glDisable(GL.GL_TEXTURE_2D); // Draw crosshair for current location gl.glLoadIdentity(); gl.glTranslated(locationSW.x(), locationSW.y(), locationSW.z()); // Scale to width x height space gl.glScaled(scale, scale, 1); // Set color float[] colorRGB = this.color.getRGBColorComponents(null); gl.glColor4d(colorRGB[0], colorRGB[1], colorRGB[2], this.getOpacity()); // Draw crosshair Position groundPos = this.computeGroundPosition(dc, dc.getView()); if (groundPos != null) { int x = (int) (width * (groundPos.getLongitude().degrees + 180) / 360); int y = (int) (height * (groundPos.getLatitude().degrees + 90) / 180); int w = 10; // cross branch length // Draw gl.glBegin(GL.GL_LINE_STRIP); gl.glVertex3d(x - w, y, 0); gl.glVertex3d(x + w + 1, y, 0); gl.glEnd(); gl.glBegin(GL.GL_LINE_STRIP); gl.glVertex3d(x, y - w, 0); gl.glVertex3d(x, y + w + 1, 0); gl.glEnd(); } // Draw view footprint in map icon space if (this.showFootprint) { this.footPrintPositions = this.computeViewFootPrint(dc, 32); if (this.footPrintPositions != null) { gl.glBegin(GL.GL_LINE_STRIP); LatLon p1 = this.footPrintPositions.get(0); for (LatLon p2 : this.footPrintPositions) { int x = (int) (width * (p2.getLongitude().degrees + 180) / 360); int y = (int) (height * (p2.getLatitude().degrees + 90) / 180); // Draw if (LatLon.locationsCrossDateline(p1, p2)) { int y1 = (int) (height * (p1.getLatitude().degrees + 90) / 180); gl.glVertex3d(x < width / 2 ? width : 0, (y1 + y) / 2, 0); gl.glEnd(); gl.glBegin(GL.GL_LINE_STRIP); gl.glVertex3d(x < width / 2 ? 0 : width, (y1 + y) / 2, 0); } gl.glVertex3d(x, y, 0); p1 = p2; } gl.glEnd(); } } // Draw 1px border around and inside the map gl.glBegin(GL.GL_LINE_STRIP); gl.glVertex3d(0, 0, 0); gl.glVertex3d(width, 0, 0); gl.glVertex3d(width, height - 1, 0); gl.glVertex3d(0, height - 1, 0); gl.glVertex3d(0, 0, 0); gl.glEnd(); } else { // Picking this.pickSupport.clearPickList(); this.pickSupport.beginPicking(dc); // Where in the world are we picking ? Position pickPosition = computePickPosition( dc, locationSW, new Dimension((int) (width * scale), (int) (height * scale))); Color color = dc.getUniquePickColor(); int colorCode = color.getRGB(); this.pickSupport.addPickableObject(colorCode, this, pickPosition, false); gl.glColor3ub((byte) color.getRed(), (byte) color.getGreen(), (byte) color.getBlue()); dc.drawUnitQuad(); this.pickSupport.endPicking(dc); this.pickSupport.resolvePick(dc, dc.getPickPoint(), this); } } finally { dc.restoreDefaultDepthTesting(); dc.restoreDefaultCurrentColor(); if (dc.isPickingMode()) dc.restoreDefaultBlending(); ogsh.pop(gl); } }
/** * Compute the label's screen position from its geographic position. * * @param dc Current draw context. */ protected void computeGeometry(DrawContext dc) { // Project the label position onto the viewport Position pos = this.getPosition(); if (pos == null) return; this.placePoint = dc.computeTerrainPoint(pos.getLatitude(), pos.getLongitude(), 0); this.screenPlacePoint = dc.getView().project(this.placePoint); this.eyeDistance = this.placePoint.distanceTo3(dc.getView().getEyePoint()); boolean orientationReversed = false; if (this.orientationPosition != null) { // Project the orientation point onto the screen Vec4 orientationPlacePoint = dc.computeTerrainPoint( this.orientationPosition.getLatitude(), this.orientationPosition.getLongitude(), 0); Vec4 orientationScreenPoint = dc.getView().project(orientationPlacePoint); this.rotation = this.computeRotation(this.screenPlacePoint, orientationScreenPoint); // The orientation is reversed if the orientation point falls to the right of the screen // point. Text is // never drawn upside down, so when the orientation is reversed the text flips vertically to // keep the text // right side up. orientationReversed = (orientationScreenPoint.x <= this.screenPlacePoint.x); } this.computeBoundsIfNeeded(dc); Offset offset = this.getOffset(); Point2D offsetPoint = offset.computeOffset(this.bounds.getWidth(), this.bounds.getHeight(), null, null); // If a rotation is applied to the text, then rotate the offset as well. An offset in the x // direction // will move the text along the orientation line, and a offset in the y direction will move the // text // perpendicular to the orientation line. if (this.rotation != null) { double dy = offsetPoint.getY(); // If the orientation is reversed we need to adjust the vertical offset to compensate for the // flipped // text. For example, if the offset normally aligns the top of the text with the place point // then without // this adjustment the bottom of the text would align with the place point when the // orientation is // reversed. if (orientationReversed) { dy = -(dy + this.bounds.getHeight()); } Vec4 pOffset = new Vec4(offsetPoint.getX(), dy); Matrix rot = Matrix.fromRotationZ(this.rotation.multiply(-1)); pOffset = pOffset.transformBy3(rot); offsetPoint = new Point((int) pOffset.getX(), (int) pOffset.getY()); } int x = (int) (this.screenPlacePoint.x + offsetPoint.getX()); int y = (int) (this.screenPlacePoint.y - offsetPoint.getY()); this.screenPoint = new Point(x, y); this.screenExtent = this.computeTextExtent(x, y, this.rotation); }