/** * Performs one line of sight calculation between the reference position and a specified grid * position. * * @param gridPosition the grid position. * @throws InterruptedException if the operation is interrupted. */ protected void performIntersection(Position gridPosition) throws InterruptedException { // Intersect the line between this grid point and the selected position. Intersection[] intersections = this.terrain.intersect(this.referencePosition, gridPosition); if (intersections == null || intersections.length == 0) { // No intersection, so the line goes from the center to the grid point. this.sightLines.add(new Position[] {this.referencePosition, gridPosition}); return; } // Only the first intersection is shown. Vec4 iPoint = intersections[0].getIntersectionPoint(); Vec4 gPoint = terrain.getSurfacePoint( gridPosition.getLatitude(), gridPosition.getLongitude(), gridPosition.getAltitude()); // Check to see whether the intersection is beyond the grid point. if (iPoint.distanceTo3(this.referencePoint) >= gPoint.distanceTo3(this.referencePoint)) { // Intersection is beyond the grid point; the line goes from the center to the grid point. this.addSightLine(this.referencePosition, gridPosition); return; } // Compute the position corresponding to the intersection. Position iPosition = this.terrain.getGlobe().computePositionFromPoint(iPoint); // The sight line goes from the user-selected position to the intersection position. this.addSightLine(this.referencePosition, new Position(iPosition, 0)); // Keep track of the intersection positions. this.addIntersectionPosition(iPosition); this.updateProgress(); }
protected void assembleHeightControlPoints() { if (this.controlPoints.size() < 2) return; // Add one control point for the height between the first and second vertices. // TODO: ensure that this control point is visible Position firstVertex = this.controlPoints.get(0).getPosition(); Position secondVertex = this.controlPoints.get(1).getPosition(); Globe globe = this.wwd.getModel().getGlobe(); // Get cartesian points for the vertices Vec4 firstPoint = globe.computePointFromPosition(firstVertex); Vec4 secondPoint = globe.computePointFromPosition(secondVertex); // Find the midpoint of the line segment that connects the vertices Vec4 halfwayPoint = firstPoint.add3(secondPoint).divide3(2.0); Position halfwayPosition = globe.computePositionFromPoint(halfwayPoint); this.controlPoints.add( new ControlPointMarker( CHANGE_HEIGHT_ACTION, halfwayPosition, halfwayPoint, this.heightControlAttributes, this.controlPoints.size())); }
protected void requestTile(DrawContext dc, Tile tile) { Vec4 centroid = dc.getGlobe().computePointFromPosition(tile.getSector().getCentroid(), 0); if (this.getReferencePoint() != null) tile.setPriority(centroid.distanceTo3(this.getReferencePoint())); RequestTask task = new RequestTask(tile, this); this.getRequestQ().add(task); }
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++; } }
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(); }
// Draw the scale label private void drawLabel(DrawContext dc, String text, Vec4 screenPoint) { TextRenderer textRenderer = OGLTextRenderer.getOrCreateTextRenderer(dc.getTextRendererCache(), this.defaultFont); Rectangle2D nameBound = textRenderer.getBounds(text); int x = (int) (screenPoint.x() - nameBound.getWidth() / 2d); int y = (int) screenPoint.y(); textRenderer.begin3DRendering(); textRenderer.setColor(this.getBackgroundColor(this.color)); textRenderer.draw(text, x + 1, y - 1); textRenderer.setColor(this.color); textRenderer.draw(text, x, y); textRenderer.end3DRendering(); }
/** * Add a vertex to the polygon's outer boundary. * * @param mousePoint the point at which the mouse was clicked. The new vertex will be placed as * near as possible to this point, at the elevation of the polygon. */ protected void addVertex(Point mousePoint) { // Try to find the edge that is closest to a ray passing through the screen point. We're trying // to determine // the user's intent as to which edge a new two control points should be added to. Line ray = this.wwd.getView().computeRayFromScreenPoint(mousePoint.getX(), mousePoint.getY()); Vec4 pickPoint = this.intersectPolygonAltitudeAt(ray); double nearestDistance = Double.MAX_VALUE; int newVertexIndex = 0; // Loop through the control points and determine which edge is closest to the pick point for (int i = 0; i < this.controlPoints.size(); i++) { ControlPointMarker thisMarker = (ControlPointMarker) this.controlPoints.get(i); ControlPointMarker nextMarker = (ControlPointMarker) this.controlPoints.get((i + 1) % this.controlPoints.size()); Vec4 pointOnEdge = AirspaceEditorUtil.nearestPointOnSegment(thisMarker.point, nextMarker.point, pickPoint); if (!AirspaceEditorUtil.isPointBehindLineOrigin(ray, pointOnEdge)) { double d = pointOnEdge.distanceTo3(pickPoint); if (d < nearestDistance) { newVertexIndex = i + 1; nearestDistance = d; } } } Position newPosition = this.wwd.getModel().getGlobe().computePositionFromPoint(pickPoint); // Copy the outer boundary list ArrayList<Position> positionList = new ArrayList<Position>(this.controlPoints.size()); for (LatLon position : this.getPolygon().getOuterBoundary()) { positionList.add((Position) position); } // Add the new vertex positionList.add(newVertexIndex, newPosition); this.getPolygon().setOuterBoundary(positionList); }
/** * Computes the lat/lon of the pickPoint over the world map * * @param dc the current <code>DrawContext</code> * @param locationSW the screen location of the bottom left corner of the map * @param mapSize the world map screen dimension in pixels * @return the picked Position */ protected Position computePickPosition(DrawContext dc, Vec4 locationSW, Dimension mapSize) { Position pickPosition = null; Point pickPoint = dc.getPickPoint(); if (pickPoint != null) { Rectangle viewport = dc.getView().getViewport(); // Check if pickpoint is inside the map if (pickPoint.getX() >= locationSW.getX() && pickPoint.getX() < locationSW.getX() + mapSize.width && viewport.height - pickPoint.getY() >= locationSW.getY() && viewport.height - pickPoint.getY() < locationSW.getY() + mapSize.height) { double lon = (pickPoint.getX() - locationSW.getX()) / mapSize.width * 360 - 180; double lat = (viewport.height - pickPoint.getY() - locationSW.getY()) / mapSize.height * 180 - 90; double pickAltitude = 1000e3; pickPosition = new Position(Angle.fromDegrees(lat), Angle.fromDegrees(lon), pickAltitude); } } return pickPosition; }
/** * Compute points on either side of a line segment. This method requires a point on the line, and * either a next point, previous point, or both. * * @param point Center point about which to compute side points. * @param prev Previous point on the line. May be null if {@code next} is non-null. * @param next Next point on the line. May be null if {@code prev} is non-null. * @param leftPositions Left position will be added to this list. * @param rightPositions Right position will be added to this list. * @param halfWidth Distance from the center line to the left or right lines. * @param globe Current globe. */ protected void generateParallelPoints( Vec4 point, Vec4 prev, Vec4 next, List<Position> leftPositions, List<Position> rightPositions, double halfWidth, Globe globe) { if ((point == null) || (prev == null && next == null)) { String message = Logging.getMessage("nullValue.PointIsNull"); Logging.logger().severe(message); throw new IllegalArgumentException(message); } if (leftPositions == null || rightPositions == null) { String message = Logging.getMessage("nullValue.PositionListIsNull"); Logging.logger().severe(message); throw new IllegalArgumentException(message); } if (globe == null) { String message = Logging.getMessage("nullValue.GlobeIsNull"); Logging.logger().severe(message); throw new IllegalArgumentException(message); } Vec4 offset; Vec4 normal = globe.computeSurfaceNormalAtPoint(point); // Compute vector in the direction backward along the line. Vec4 backward = (prev != null) ? prev.subtract3(point) : point.subtract3(next); // Compute a vector perpendicular to segment BC, and the globe normal vector. Vec4 perpendicular = backward.cross3(normal); double length; // If both next and previous points are supplied then calculate the angle that bisects the angle // current, next, prev. if (next != null && prev != null && !Vec4.areColinear(prev, point, next)) { // Compute vector in the forward direction. Vec4 forward = next.subtract3(point); // Calculate the vector that bisects angle ABC. offset = forward.normalize3().add3(backward.normalize3()); offset = offset.normalize3(); // Compute the scalar triple product of the vector BC, the normal vector, and the offset // vector to // determine if the offset points to the left or the right of the control line. double tripleProduct = perpendicular.dot3(offset); if (tripleProduct < 0) { offset = offset.multiply3(-1); } // Determine the length of the offset vector that will keep the left and right lines parallel // to the control // line. Angle theta = backward.angleBetween3(offset); if (!Angle.ZERO.equals(theta)) length = halfWidth / theta.sin(); else length = halfWidth; } else { offset = perpendicular.normalize3(); length = halfWidth; } offset = offset.multiply3(length); // Determine the left and right points by applying the offset. Vec4 ptRight = point.add3(offset); Vec4 ptLeft = point.subtract3(offset); // Convert cartesian points to geographic. Position posLeft = globe.computePositionFromPoint(ptLeft); Position posRight = globe.computePositionFromPoint(ptRight); leftPositions.add(posLeft); rightPositions.add(posRight); }
/** * 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); }
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); } }
/** * Determine the positions that make up the arrowhead. * * @param dc Current draw context. * @param startPosition Position of the arrow's base. * @param endPosition Position of the arrow head tip. * @return Positions that define the arrowhead. */ protected List<Position> computeArrowheadPositions( DrawContext dc, Position startPosition, Position endPosition) { Globe globe = dc.getGlobe(); // Arrowhead looks like this: // _ // A\ | 1/2 width // ________B\ _| // Pt. 1 / // C/ // | | // Length Vec4 p1 = globe.computePointFromPosition(startPosition); Vec4 pB = globe.computePointFromPosition(endPosition); // Find vector in the direction of the arrow Vec4 vB1 = p1.subtract3(pB); double arrowLengthFraction = this.getArrowLength(); // Find the point at the base of the arrowhead Vec4 arrowBase = pB.add3(vB1.multiply3(arrowLengthFraction)); Vec4 normal = globe.computeSurfaceNormalAtPoint(arrowBase); // Compute the length of the arrowhead double arrowLength = vB1.getLength3() * arrowLengthFraction; double arrowHalfWidth = arrowLength * this.getArrowAngle().tanHalfAngle(); // Compute a vector perpendicular to the segment and the normal vector Vec4 perpendicular = vB1.cross3(normal); perpendicular = perpendicular.normalize3().multiply3(arrowHalfWidth); // Find points A and C Vec4 pA = arrowBase.add3(perpendicular); Vec4 pC = arrowBase.subtract3(perpendicular); return TacticalGraphicUtil.asPositionList(globe, pA, pB, pC); }
/** * Compute the positions of the arrow head of the graphic's legs. * * @param dc Current draw context * @param base Position of the arrow's starting point. * @param tip Position of the arrow head tip. * @param arrowLength Length of the arrowhead as a fraction of the total line length. * @param arrowAngle Angle of the arrow head. * @return Positions required to draw the arrow head. */ protected List<Position> computeArrowheadPositions( DrawContext dc, Position base, Position tip, double arrowLength, Angle arrowAngle) { // Build a triangle to represent the arrowhead. The triangle is built from two vectors, one // parallel to the // segment, and one perpendicular to it. Globe globe = dc.getGlobe(); Vec4 ptA = globe.computePointFromPosition(base); Vec4 ptB = globe.computePointFromPosition(tip); // Compute parallel component Vec4 parallel = ptA.subtract3(ptB); Vec4 surfaceNormal = globe.computeSurfaceNormalAtPoint(ptB); // Compute perpendicular component Vec4 perpendicular = surfaceNormal.cross3(parallel); double finalArrowLength = arrowLength * parallel.getLength3(); double arrowHalfWidth = finalArrowLength * arrowAngle.tanHalfAngle(); perpendicular = perpendicular.normalize3().multiply3(arrowHalfWidth); parallel = parallel.normalize3().multiply3(finalArrowLength); // Compute geometry of direction arrow Vec4 vertex1 = ptB.add3(parallel).add3(perpendicular); Vec4 vertex2 = ptB.add3(parallel).subtract3(perpendicular); return TacticalGraphicUtil.asPositionList(globe, vertex1, vertex2, ptB); }
// Rendering public void draw(DrawContext dc) { GL gl = dc.getGL(); boolean attribsPushed = false; boolean modelviewPushed = false; boolean projectionPushed = false; try { gl.glPushAttrib( GL.GL_DEPTH_BUFFER_BIT | GL.GL_COLOR_BUFFER_BIT | GL.GL_ENABLE_BIT | GL.GL_TEXTURE_BIT | GL.GL_TRANSFORM_BIT | GL.GL_VIEWPORT_BIT | GL.GL_CURRENT_BIT); attribsPushed = true; gl.glDisable(GL.GL_TEXTURE_2D); // no textures gl.glEnable(GL.GL_BLEND); gl.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA); gl.glDisable(GL.GL_DEPTH_TEST); double width = this.size.width; double height = this.size.height; // Load a parallel projection with xy dimensions (viewportWidth, viewportHeight) // into the GL projection matrix. java.awt.Rectangle viewport = dc.getView().getViewport(); gl.glMatrixMode(javax.media.opengl.GL.GL_PROJECTION); gl.glPushMatrix(); projectionPushed = true; gl.glLoadIdentity(); double maxwh = width > height ? width : height; gl.glOrtho(0d, viewport.width, 0d, viewport.height, -0.6 * maxwh, 0.6 * maxwh); gl.glMatrixMode(GL.GL_MODELVIEW); gl.glPushMatrix(); modelviewPushed = true; gl.glLoadIdentity(); // Scale to a width x height space // located at the proper position on screen double scale = this.computeScale(viewport); Vec4 locationSW = this.computeLocation(viewport, scale); gl.glTranslated(locationSW.x(), locationSW.y(), locationSW.z()); gl.glScaled(scale, scale, 1); // Compute scale size in real world Position referencePosition = dc.getViewportCenterPosition(); if (referencePosition != null) { Vec4 groundTarget = dc.getGlobe().computePointFromPosition(referencePosition); Double distance = dc.getView().getEyePoint().distanceTo3(groundTarget); this.pixelSize = dc.getView().computePixelSizeAtDistance(distance); Double scaleSize = this.pixelSize * width * scale; // meter String unitLabel = "m"; if (this.unit.equals(UNIT_METRIC)) { if (scaleSize > 10000) { scaleSize /= 1000; unitLabel = "Km"; } } else if (this.unit.equals(UNIT_IMPERIAL)) { scaleSize *= 3.280839895; // feet unitLabel = "ft"; if (scaleSize > 5280) { scaleSize /= 5280; unitLabel = "mile(s)"; } } // Rounded division size int pot = (int) Math.floor(Math.log10(scaleSize)); if (!Double.isNaN(pot)) { int digit = Integer.parseInt(String.format("%.0f", scaleSize).substring(0, 1)); double divSize = digit * Math.pow(10, pot); if (digit >= 5) divSize = 5 * Math.pow(10, pot); else if (digit >= 2) divSize = 2 * Math.pow(10, pot); double divWidth = width * divSize / scaleSize; // Draw scale if (!dc.isPickingMode()) { // Set color using current layer opacity Color backColor = this.getBackgroundColor(this.color); float[] colorRGB = backColor.getRGBColorComponents(null); gl.glColor4d( colorRGB[0], colorRGB[1], colorRGB[2], (double) backColor.getAlpha() / 255d * this.getOpacity()); gl.glTranslated((width - divWidth) / 2, 0d, 0d); this.drawScale(dc, divWidth, height); colorRGB = this.color.getRGBColorComponents(null); gl.glColor4d(colorRGB[0], colorRGB[1], colorRGB[2], this.getOpacity()); gl.glTranslated(-1d / scale, 1d / scale, 0d); this.drawScale(dc, divWidth, height); // Draw label String label = String.format("%.0f ", divSize) + unitLabel; gl.glLoadIdentity(); gl.glDisable(GL.GL_CULL_FACE); drawLabel( dc, label, locationSW.add3( new Vec4(divWidth * scale / 2 + (width - divWidth) / 2, height * scale, 0))); } else { // Picking this.pickSupport.clearPickList(); this.pickSupport.beginPicking(dc); // Draw unique color across the map Color color = dc.getUniquePickColor(); int colorCode = color.getRGB(); // Add our object(s) to the pickable list this.pickSupport.addPickableObject(colorCode, this, referencePosition, false); gl.glColor3ub((byte) color.getRed(), (byte) color.getGreen(), (byte) color.getBlue()); gl.glTranslated((width - divWidth) / 2, 0d, 0d); this.drawRectangle(dc, divWidth, height); // Done picking this.pickSupport.endPicking(dc); this.pickSupport.resolvePick(dc, dc.getPickPoint(), this); } } } } finally { if (projectionPushed) { gl.glMatrixMode(GL.GL_PROJECTION); gl.glPopMatrix(); } if (modelviewPushed) { gl.glMatrixMode(GL.GL_MODELVIEW); gl.glPopMatrix(); } if (attribsPushed) gl.glPopAttrib(); } }