/** * Returns the minimum distance from the globe's surface necessary to make the globe's extents visible in this World * Window. * * @return the distance in meters needed to view the entire globe */ public double distanceToViewGlobeExtents() { double sinfovy_2 = Math.sin(Math.toRadians(this.fieldOfView * 0.5)); double radius = this.globe.getEquatorialRadius(); return radius / sinfovy_2 - radius; }
@Override public Vec3 geographicToCartesian(Globe globe, double latitude, double longitude, double altitude, Vec3 result) { if (globe == null) { throw new IllegalArgumentException( Logger.logMessage(Logger.ERROR, "ProjectionWgs84", "geographicToCartesian", "missingGlobe")); } if (result == null) { throw new IllegalArgumentException( Logger.logMessage(Logger.ERROR, "ProjectionWgs84", "geographicToCartesian", "missingResult")); } double radLat = Math.toRadians(latitude); double radLon = Math.toRadians(longitude); double cosLat = Math.cos(radLat); double sinLat = Math.sin(radLat); double cosLon = Math.cos(radLon); double sinLon = Math.sin(radLon); double ec2 = globe.getEccentricitySquared(); double rpm = globe.getEquatorialRadius() / Math.sqrt(1.0 - ec2 * sinLat * sinLat); result.x = (altitude + rpm) * cosLat * sinLon; result.y = (altitude + rpm * (1.0 - ec2)) * sinLat; result.z = (altitude + rpm) * cosLat * cosLon; return result; }
/** * Indicates whether this tile should be subdivided based on the current navigation state and a specified detail * factor. * * @param rc the current render context * @param detailFactor the detail factor to consider * * @return true if the tile should be subdivided, otherwise false */ public boolean mustSubdivide(RenderContext rc, double detailFactor) { this.distanceToCamera = this.distanceToCamera(rc); double texelSize = this.texelSizeFactor * rc.globe.getEquatorialRadius(); double pixelSize = rc.pixelSizeAtDistance(this.distanceToCamera); double densityFactor = 1.0; // Adjust the subdivision factory when the display density is low. Values of detailFactor have been calibrated // against high density devices. Low density devices need roughly half the detailFactor. if (rc.resources.getDisplayMetrics().densityDpi <= DisplayMetrics.DENSITY_MEDIUM) { densityFactor = 0.5; } return texelSize > (pixelSize * detailFactor * densityFactor); }
@Override public Vec3 geographicToCartesianNormal(Globe globe, double latitude, double longitude, Vec3 result) { if (globe == null) { throw new IllegalArgumentException( Logger.logMessage(Logger.ERROR, "ProjectionWgs84", "geographicToCartesianNormal", "missingGlobe")); } if (result == null) { throw new IllegalArgumentException( Logger.logMessage(Logger.ERROR, "ProjectionWgs84", "geographicToCartesianNormal", "missingResult")); } double radLat = Math.toRadians(latitude); double radLon = Math.toRadians(longitude); double cosLat = Math.cos(radLat); double sinLat = Math.sin(radLat); double cosLon = Math.cos(radLon); double sinLon = Math.sin(radLon); double eqr2 = globe.getEquatorialRadius() * globe.getEquatorialRadius(); double pol2 = globe.getPolarRadius() * globe.getPolarRadius(); result.x = cosLat * sinLon / eqr2; result.y = (1 - globe.getEccentricitySquared()) * sinLat / pol2; result.z = cosLat * cosLon / eqr2; return result.normalize(); }
public double radiusOfPrimeVeritcal(double geographicLat) { double a = globe.getEquatorialRadius(); double e2 = globe.getEccentricitySquared(); double sinSquared = Math.pow(Math.sin(Math.toRadians(geographicLat)), 2); return a / Math.sqrt(1 - e2 * sinSquared); }
/** * Ensures the equatorial radius matches the semi-major axis used to define the globe. * * @throws Exception */ @Test public void testGetEquatorialRadius() throws Exception { double equatorialRadius = globe.getEquatorialRadius(); assertEquals("equatorial radius", OFFICIAL_WGS84_SEMI_MAJOR_AXIS, equatorialRadius, 0); }
double radiansPerPixel = metersPerPixel / globe.getEquatorialRadius();
@Override public boolean handleMessage(Message msg) { if (this.animationAmount < 1) { // Increment the animation amount. this.animationAmount += this.animationIncrement; for (int idx = 0, len = this.flightPathLayer.count(); idx < len; idx++) { // Identify the departure airport and destination airport associated with each flight path. Path path = (Path) this.flightPathLayer.getRenderable(idx); Airport dept = (Airport) path.getUserProperty("dept"); Airport dest = (Airport) path.getUserProperty("dest"); // Compute the location on the great circle path between the departure and the destination that // corresponds to the animation amount. Position nextPos = dept.pos.interpolateAlongPath(dest.pos, WorldWind.GREAT_CIRCLE, this.animationAmount, new Position()); // Compute the altitude on the flight path that corresponds to the animation amount. We mock altitude // using an inverse parabolic function scaled to reach a max altitude of 10% of the flight distance. double dist = dept.pos.greatCircleDistance(dest.pos) * this.getWorldWindow().getGlobe().getEquatorialRadius(); double altCurve = (1 - this.animationAmount) * this.animationAmount * 4; nextPos.altitude = altCurve * dist * 0.1; // Append the location and altitude to the flight path's list of positions. List<Position> positions = path.getPositions(); positions.add(nextPos); path.setPositions(positions); } // Redraw the WorldWindow to display the changes. this.getWorldWindow().requestRedraw(); // Continue the animation after a delay. this.handler.sendEmptyMessageDelayed(0 /*what*/, 1000); } return false; }
Sector initialSector = new Sector(minLat, minLon, deltaLat, deltaLon); Globe initialGlobe = PowerMockito.mock(Globe.class); PowerMockito.when(initialGlobe.getEquatorialRadius()).thenReturn(notionalGlobeRadius); String initialNotionalServiceAddress = "notionalServiceAddress"; String initialNotionalLayerList = "notionalLayerList";
/** * An instance which is easily visualized for understanding the forwards intersection instance. * * @throws Exception */ @Test public void testSimpleIntersection() throws Exception { ProjectionWgs84 wgs84 = new ProjectionWgs84(); Globe mockedGlobe = PowerMockito.mock(Globe.class); PowerMockito.when(mockedGlobe.getEquatorialRadius()).thenReturn(1.0); PowerMockito.when(mockedGlobe.getPolarRadius()).thenReturn(1.0); Line ray = new Line(new Vec3(0.8, 0.8, 0.0), new Vec3(0.0, -1.0, 0.0)); boolean intersection = wgs84.intersect(mockedGlobe, ray, new Vec3()); assertTrue("simple intersection", intersection); }
/** * Test the four parameter {@code setConfiguration} method updates when the {@link Globe} is changed. */ @Test public void testSetConfiguration_FourParameter_GlobeUpdate() { // Mocked objects facilitating testing double minLat = 10.0; double deltaLat = 1.0; double minLon = -95.0; double deltaLon = 2.0; double notionalGlobeRadius = 3000000.0; Sector initialSector = new Sector(minLat, minLon, deltaLat, deltaLon); Globe initialGlobe = PowerMockito.mock(Globe.class); PowerMockito.when(initialGlobe.getEquatorialRadius()).thenReturn(notionalGlobeRadius); String initialNotionalServiceAddress = "notionalServiceAddress"; String initialNotionalLayerList = "notionalLayerList"; WmsLayerConfig initialWmsLayerConfig = new WmsLayerConfig(initialNotionalServiceAddress, initialNotionalLayerList); double metersPerPixel = 0.5; WmsLayer wmsLayer = new WmsLayer(initialSector, initialGlobe, metersPerPixel, initialWmsLayerConfig); int initialLayers = ((TiledSurfaceImage) wmsLayer.getRenderable(0)).getLevelSet().numLevels(); Globe alternativeGlobe = PowerMockito.mock(Globe.class); PowerMockito.when(alternativeGlobe.getEquatorialRadius()).thenReturn(2 * notionalGlobeRadius); wmsLayer.setConfiguration(initialSector, alternativeGlobe, metersPerPixel, initialWmsLayerConfig); int numberOfLevels = ((TiledSurfaceImage) wmsLayer.getRenderable(0)).getLevelSet().numLevels(); assertFalse("layer levels updated by globe object change", initialLayers == numberOfLevels); }
protected void renderGround(RenderContext rc) { if (rc.terrain.getSector().isEmpty()) { return; // no terrain surface to render on } Pool<DrawableGroundAtmosphere> pool = rc.getDrawablePool(DrawableGroundAtmosphere.class); DrawableGroundAtmosphere drawable = DrawableGroundAtmosphere.obtain(pool); drawable.program = (GroundProgram) rc.getShaderProgram(GroundProgram.KEY); if (drawable.program == null) { drawable.program = (GroundProgram) rc.putShaderProgram(GroundProgram.KEY, new GroundProgram(rc.resources)); } drawable.lightDirection.set(this.activeLightDirection); drawable.globeRadius = rc.globe.getEquatorialRadius(); // Use this layer's night image when the light location is different than the eye location. if (this.nightImageSource != null && this.lightLocation != null) { drawable.nightTexture = rc.getTexture(this.nightImageSource); if (drawable.nightTexture == null) { drawable.nightTexture = rc.retrieveTexture(this.nightImageSource, this.nightImageOptions); } } else { drawable.nightTexture = null; } rc.offerSurfaceDrawable(drawable, Double.POSITIVE_INFINITY /*z-order after all other surface drawables*/); }
@Test public void testDistanceTo() throws Exception { BoundingBox boundingBox = new BoundingBox(); double radius = globe.getEquatorialRadius(); float minElevation = 0; float maxElevation = 1000; Sector sector = Sector.fromDegrees(-0.5, -0.5, 1d, 1d); boundingBox.setToSector(sector, globe, minElevation, maxElevation); Vec3 point = globe.geographicToCartesian(0, 0, 0, new Vec3()); double result = boundingBox.distanceTo(point); assertEquals(boundingBox.center.z - radius, result, 1e-3); }
double a = globe.getEquatorialRadius(); double e2 = globe.getEccentricitySquared(); double sinLat = Math.sin(Math.toRadians(lat));
/** * Test the four parameter {@code setConfiguration} method updates when the {@link Sector} is changed. */ @Test public void testSetConfiguration_FourParameter_SectorUpdate() { // Mocked objects facilitating testing double minLat = 10.0; double deltaLat = 1.0; double minLon = -95.0; double deltaLon = 2.0; double notionalGlobeRadius = 3000000.0; Sector initialSector = new Sector(minLat, minLon, deltaLat, deltaLon); Globe initialGlobe = PowerMockito.mock(Globe.class); PowerMockito.when(initialGlobe.getEquatorialRadius()).thenReturn(notionalGlobeRadius); String initialNotionalServiceAddress = "notionalServiceAddress"; String initialNotionalLayerList = "notionalLayerList"; WmsLayerConfig initialWmsLayerConfig = new WmsLayerConfig(initialNotionalServiceAddress, initialNotionalLayerList); double metersPerPixel = 0.5; WmsLayer wmsLayer = new WmsLayer(initialSector, initialGlobe, metersPerPixel, initialWmsLayerConfig); double alternativeLatMin = -45.0; double alternativeLonMin = 50.0; double alternativeDeltaLat = 5.0; double alternativeDeltaLon = 2.0; Sector alternativeSector = new Sector(alternativeLatMin, alternativeLonMin, alternativeDeltaLat, alternativeDeltaLon); wmsLayer.setConfiguration(alternativeSector, initialGlobe, metersPerPixel, initialWmsLayerConfig); Sector sector = ((TiledSurfaceImage) wmsLayer.getRenderable(0)).getLevelSet().sector; assertEquals("sector updated", alternativeSector, sector); }
/** * An instance which is easily visualized for understanding the backwards intersection instance. * * @throws Exception */ @Test public void testSimpleBackwardsIntersection() throws Exception { ProjectionWgs84 wgs84 = new ProjectionWgs84(); Globe mockedGlobe = PowerMockito.mock(Globe.class); PowerMockito.when(mockedGlobe.getEquatorialRadius()).thenReturn(1.0); PowerMockito.when(mockedGlobe.getPolarRadius()).thenReturn(1.0); Line ray = new Line(new Vec3(0.8, 0.8, 0.0), new Vec3(0.0, 1.0, 0.0)); boolean intersection = wgs84.intersect(mockedGlobe, ray, new Vec3()); assertFalse("simple backwards intersection", intersection); }
protected void renderSky(RenderContext rc) { Pool<DrawableSkyAtmosphere> pool = rc.getDrawablePool(DrawableSkyAtmosphere.class); DrawableSkyAtmosphere drawable = DrawableSkyAtmosphere.obtain(pool); int size = 128; drawable.program = (SkyProgram) rc.getShaderProgram(SkyProgram.KEY); if (drawable.program == null) { drawable.program = (SkyProgram) rc.putShaderProgram(SkyProgram.KEY, new SkyProgram(rc.resources)); } drawable.vertexPoints = rc.getBufferObject(VERTEX_POINTS_KEY); if (drawable.vertexPoints == null) { drawable.vertexPoints = rc.putBufferObject(VERTEX_POINTS_KEY, this.assembleVertexPoints(rc, size, size, (float) drawable.program.getAltitude())); } drawable.triStripElements = rc.getBufferObject(TRI_STRIP_ELEMENTS_KEY); if (drawable.triStripElements == null) { drawable.triStripElements = rc.putBufferObject(TRI_STRIP_ELEMENTS_KEY, this.assembleTriStripElements(size, size)); } drawable.lightDirection.set(this.activeLightDirection); drawable.globeRadius = rc.globe.getEquatorialRadius(); rc.offerSurfaceDrawable(drawable, Double.POSITIVE_INFINITY /*z-order after all other surface drawables*/); }
/** * An instance which demonstrates two intersections with a ray originating within the ellipsoid. * * @throws Exception */ @Test public void testSimpleTwoIntersectionInternal() throws Exception { ProjectionWgs84 wgs84 = new ProjectionWgs84(); Globe mockedGlobe = PowerMockito.mock(Globe.class); PowerMockito.when(mockedGlobe.getEquatorialRadius()).thenReturn(1.0); PowerMockito.when(mockedGlobe.getPolarRadius()).thenReturn(1.0); Line ray = new Line(new Vec3(-0.8, 0, 0.0), new Vec3(1.0, 0.0, 0.0).normalize()); Vec3 result = new Vec3(); double errorThreshold = 1e-9; boolean intersection = wgs84.intersect(mockedGlobe, ray, result); assertTrue("simple internal intersection", intersection); assertEquals("forward calculated intersection x", 1.0, result.x, errorThreshold); assertEquals("forward calculated intersection y", 0.0, result.y, errorThreshold); }
/** * An instance which demonstrates two intersections, but the closest, or first surface intersection position is * desired. * * @throws Exception */ @Test public void testSimpleTwoIntersection() throws Exception { ProjectionWgs84 wgs84 = new ProjectionWgs84(); Globe mockedGlobe = PowerMockito.mock(Globe.class); PowerMockito.when(mockedGlobe.getEquatorialRadius()).thenReturn(1.0); PowerMockito.when(mockedGlobe.getPolarRadius()).thenReturn(1.0); Line ray = new Line(new Vec3(-1.0, 2.0, 0.0), new Vec3(1.0, -1.0, 0.0).normalize()); Vec3 result = new Vec3(); double errorThreshold = 1e-9; boolean intersection = wgs84.intersect(mockedGlobe, ray, result); assertTrue("simple intersection", intersection); assertEquals("nearest calculated intersection x", 0.0, result.x, errorThreshold); assertEquals("nearest calculated intersection y", 1.0, result.y, errorThreshold); }
/** * Test the four parameter {@code setConfiguration} method updates when the meters per pixel is changed. */ @Test public void testSetConfiguration_FourParameter_MetersPerPixelUpdate() { // Mocked objects facilitating testing double minLat = 10.0; double deltaLat = 1.0; double minLon = -95.0; double deltaLon = 2.0; double notionalGlobeRadius = 3000000.0; Sector initialSector = new Sector(minLat, minLon, deltaLat, deltaLon); Globe initialGlobe = PowerMockito.mock(Globe.class); PowerMockito.when(initialGlobe.getEquatorialRadius()).thenReturn(notionalGlobeRadius); String initialNotionalServiceAddress = "notionalServiceAddress"; String initialNotionalLayerList = "notionalLayerList"; WmsLayerConfig initialWmsLayerConfig = new WmsLayerConfig(initialNotionalServiceAddress, initialNotionalLayerList); double metersPerPixel = 0.5; WmsLayer wmsLayer = new WmsLayer(initialSector, initialGlobe, metersPerPixel, initialWmsLayerConfig); double alternativeMetersPerPixel = 10.0; int originalNumberOfLevels = ((TiledSurfaceImage) wmsLayer.getRenderable(0)).getLevelSet().numLevels(); wmsLayer.setConfiguration(initialSector, initialGlobe, alternativeMetersPerPixel, initialWmsLayerConfig); int numberOfLevels = ((TiledSurfaceImage) wmsLayer.getRenderable(0)).getLevelSet().numLevels(); assertFalse("levels updated", originalNumberOfLevels == numberOfLevels); } }