protected BufferObject assembleVertexPoints(RenderContext rc, int numLat, int numLon, float altitude) { int count = numLat * numLon; float[] altitudes = new float[count]; Arrays.fill(altitudes, altitude); float[] points = new float[count * 3]; rc.globe.geographicToCartesianGrid(this.fullSphereSector, numLat, numLon, altitudes, 1.0f, null, points, 0, 0); int size = points.length * 4; FloatBuffer buffer = ByteBuffer.allocateDirect(size).order(ByteOrder.nativeOrder()).asFloatBuffer(); buffer.put(points).rewind(); return new BufferObject(GLES20.GL_ARRAY_BUFFER, size, buffer); }
protected BoundingBox getExtent(RenderContext rc) { if (this.heightLimits == null) { this.heightLimits = new float[2]; } if (this.extent == null) { this.extent = new BoundingBox(); } long elevationTimestamp = rc.globe.getElevationModel().getTimestamp(); if (elevationTimestamp != this.heightLimitsTimestamp) { // initialize the heights for elevation model scan this.heightLimits[0] = Float.MAX_VALUE; this.heightLimits[1] = -Float.MAX_VALUE; rc.globe.getElevationModel().getHeightLimits(this.sector, this.heightLimits); // check for valid height limits if (this.heightLimits[0] > this.heightLimits[1]) { Arrays.fill(this.heightLimits, 0f); } } double verticalExaggeration = rc.verticalExaggeration; if (verticalExaggeration != this.extentExaggeration || elevationTimestamp != this.heightLimitsTimestamp) { float minHeight = (float) (this.heightLimits[0] * verticalExaggeration); float maxHeight = (float) (this.heightLimits[1] * verticalExaggeration); this.extent.setToSector(this.sector, rc.globe, minHeight, maxHeight); } this.heightLimitsTimestamp = elevationTimestamp; this.extentExaggeration = verticalExaggeration; return this.extent; }
@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; }
@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(); }
/** * Converts a screen point to the geographic coordinates on the globe. * * @param screenX X coordinate * @param screenY Y coordinate * @param result Pre-allocated Position receives the geographic coordinates * * @return true if the screen point could be converted; false if the screen point is not on the globe */ public boolean screenPointToGroundPosition(float screenX, float screenY, Position result) { if (this.wwd.rayThroughScreenPoint(screenX, screenY, ray)) { Globe globe = wwd.getGlobe(); if (globe.intersect(ray, this.pickPoint)) { globe.cartesianToGeographic(pickPoint.x, this.pickPoint.y, this.pickPoint.z, result); return true; } } return false; }
double sz = line.origin.z; double eqr = globe.getEquatorialRadius(); double eqr2 = eqr * eqr; // nominal radius squared double m = eqr / globe.getPolarRadius(); // ratio of the x semi-axis length to the y semi-axis length double m2 = m * m; double a = vx * vx + m2 * vy * vy + vz * vz;
int tileHeight = tile.level.tileHeight; long elevationTimestamp = rc.globe.getElevationModel().getTimestamp(); if (elevationTimestamp != tile.getHeightTimestamp()) { rc.globe.getElevationModel().getHeightGrid(tile.sector, tileWidth, tileHeight, heights); tile.setHeights(heights); rc.globe.geographicToCartesian(tile.sector.centroidLatitude(), tile.sector.centroidLongitude(), 0, origin); rc.globe.geographicToCartesianGrid(tile.sector, tileWidth, tileHeight, heights, (float) verticalExaggeration, origin, points, rowStride + 3, rowStride); rc.globe.geographicToCartesianBorder(tile.sector, tileWidth + 2, tileHeight + 2, borderHeight, origin, points); tile.setOrigin(origin); tile.setPoints(points);
/** * 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; }
@Before public void setUp() { // Mock all the static methods in Logger PowerMockito.mockStatic(Logger.class); // Create the globe object used by the test this.globe = new Globe(WorldWind.WGS84_ELLIPSOID, new ProjectionWgs84()); // Create the terrain object used by the test this.terrain = new BasicTerrain(); // Add a terrain tile used to the mocked terrain LevelSet levelSet = new LevelSet(new Sector().setFullSphere(), 1.0, 1, 5, 5); // tiles with 5x5 vertices TerrainTile tile = new TerrainTile(new Sector(0, 0, 1, 1), levelSet.firstLevel(), 90, 180); ((BasicTerrain) this.terrain).addTile(tile); // Populate the terrain tile's geometry int tileWidth = tile.level.tileWidth; int tileHeight = tile.level.tileHeight; int rowStride = (tileWidth + 2) * 3; float[] points = new float[(tileWidth + 2) * (tileHeight + 2) * 3]; Vec3 tileOrigin = this.globe.geographicToCartesian(0.5, 0.5, 0.0, new Vec3()); this.globe.geographicToCartesianGrid(tile.sector, tileWidth, tileHeight, null, 1.0f, tileOrigin, points, rowStride + 3, rowStride); this.globe.geographicToCartesianBorder(tile.sector, tileWidth + 2, tileHeight + 2, 0.0f, tileOrigin, points); tile.setOrigin(tileOrigin); tile.setPoints(points); }
protected void applyLimits(Camera camera) { double distanceToExtents = this.wwd.distanceToViewGlobeExtents(); double minAltitude = 100; double maxAltitude = distanceToExtents; camera.altitude = WWMath.clamp(camera.altitude, minAltitude, maxAltitude); // Limit the tilt to between nadir and the horizon (roughly) double r = wwd.getGlobe().getRadiusAt(camera.latitude, camera.latitude); double maxTilt = Math.toDegrees(Math.asin(r / (r + camera.altitude))); double minTilt = 0; camera.tilt = WWMath.clamp(camera.tilt, minTilt, maxTilt); } }
protected LookAt cameraToLookAt(Globe globe, Camera camera, LookAt result) { this.cameraToViewingMatrix(globe, camera, this.modelview); this.modelview.extractEyePoint(this.forwardRay.origin); this.modelview.extractForwardVector(this.forwardRay.direction); if (!globe.intersect(this.forwardRay, this.originPoint)) { double horizon = globe.horizonDistance(camera.altitude); this.forwardRay.pointAt(horizon, this.originPoint); } globe.cartesianToGeographic(this.originPoint.x, this.originPoint.y, this.originPoint.z, this.originPos); globe.cartesianToLocalTransform(this.originPoint.x, this.originPoint.y, this.originPoint.z, this.origin); this.modelview.multiplyByMatrix(this.origin); result.latitude = this.originPos.latitude; result.longitude = this.originPos.longitude; result.altitude = this.originPos.altitude; result.range = -this.modelview.m[11]; result.heading = this.modelview.extractHeading(camera.roll); // disambiguate heading and roll result.tilt = this.modelview.extractTilt(); result.roll = camera.roll; // roll passes straight through return result; }
case WorldWind.ABSOLUTE: if (this.globe != null) { return this.globe.geographicToCartesian(latitude, longitude, altitude * this.verticalExaggeration, result); } else if (this.globe != null) { return this.globe.geographicToCartesian(latitude, longitude, 0, result); this.globe.geographicToCartesianNormal(latitude, longitude, this.scratchVector); result.x += this.scratchVector.x * altitude; result.y += this.scratchVector.y * altitude; } else if (this.globe != null) { return this.globe.geographicToCartesian(latitude, longitude, altitude, result);
@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); }
globe.geographicToCartesianGrid(sector, numLat, numLon, heights, 1.0f, null, points, 0, 0); Matrix4 matrix = globe.geographicToCartesianTransform(centroidLat, centroidLon, 0, new Matrix4()); double m[] = matrix.m;
this.rc.verticalExaggeration = this.verticalExaggeration; this.rc.fieldOfView = this.fieldOfView; this.rc.horizonDistance = this.globe.horizonDistance(this.navigator.getAltitude()); this.rc.camera = this.navigator.getAsCamera(this.globe, this.rc.camera); this.rc.cameraPoint = this.globe.geographicToCartesian(this.rc.camera.latitude, this.rc.camera.longitude, this.rc.camera.altitude, this.rc.cameraPoint); this.rc.renderResourceCache = this.renderResourceCache; this.rc.renderResourceCache.setResources(this.getContext().getResources());
protected Camera lookAtToCamera(Globe globe, LookAt lookAt, Camera result) { this.lookAtToViewingTransform(globe, lookAt, this.modelview); this.modelview.extractEyePoint(this.originPoint); globe.cartesianToGeographic(this.originPoint.x, this.originPoint.y, this.originPoint.z, this.originPos); globe.cartesianToLocalTransform(this.originPoint.x, this.originPoint.y, this.originPoint.z, this.origin); this.modelview.multiplyByMatrix(this.origin); result.latitude = this.originPos.latitude; result.longitude = this.originPos.longitude; result.altitude = this.originPos.altitude; result.heading = this.modelview.extractHeading(lookAt.roll); // disambiguate heading and roll result.tilt = this.modelview.extractTilt(); result.roll = lookAt.roll; // roll passes straight through return result; }
/** * Transforms a geographic position to Android screen coordinates. The resultant screen point is in Android screen * pixels relative to this View. * <p/> * This stores the converted point in the result argument, and returns a boolean value indicating whether or not the * converted is successful. This returns false if the Cartesian point is clipped by either of the WorldWindow's * near clipping plane or far clipping plane. * * @param latitude the position's latitude in degrees * @param longitude the position's longitude in degrees * @param altitude the position's altitude in meters * @param result a pre-allocated {@link PointF} in which to return the screen point * * @return true if the transformation is successful, otherwise false * * @throws IllegalArgumentException If the result is null */ public boolean geographicToScreenPoint(double latitude, double longitude, double altitude, PointF result) { if (result == null) { throw new IllegalArgumentException( Logger.logMessage(Logger.ERROR, "WorldWindow", "geographicToScreenPoint", "missingResult")); } // Convert the position from geographic coordinates to Cartesian coordinates. this.globe.geographicToCartesian(latitude, longitude, altitude, this.scratchPoint); // Convert the position from Cartesian coordinates to screen coordinates. return this.cartesianToScreenPoint(this.scratchPoint.x, this.scratchPoint.y, this.scratchPoint.z, result); }
protected void determineLightDirection(RenderContext rc) { // TODO Make light/sun direction an optional property of the WorldWindow and attach it to the RenderContext each frame // TODO RenderContext property defaults to the eye lat/lon like we have below if (this.lightLocation != null) { rc.globe.geographicToCartesianNormal(this.lightLocation.latitude, this.lightLocation.longitude, this.activeLightDirection); } else { rc.globe.geographicToCartesianNormal(rc.camera.latitude, rc.camera.longitude, this.activeLightDirection); } }
/** * Ensures the polar radius matches the value derived from the globe definition. * * @throws Exception */ @Test public void testGetPolarRadius() throws Exception { double polarRadius = globe.getPolarRadius(); // WGS84 official value: 6356752.3142 // Actual computed value: 6356752.314245179 assertEquals("polar radius", OFFICIAL_WGS84_SEMI_MINOR_AXIS, polarRadius, 1.0e-4); }
protected void renderTerrainPickedObject(RenderContext rc) { if (rc.terrain.getSector().isEmpty()) { return; // no terrain to pick } // Acquire a unique picked object ID for terrain. int pickedObjectId = rc.nextPickedObjectId(); // Enqueue a drawable for processing on the OpenGL thread that displays terrain in the unique pick color. Pool<DrawableSurfaceColor> pool = rc.getDrawablePool(DrawableSurfaceColor.class); DrawableSurfaceColor drawable = DrawableSurfaceColor.obtain(pool); drawable.color = PickedObject.identifierToUniqueColor(pickedObjectId, drawable.color); drawable.program = (BasicShaderProgram) rc.getShaderProgram(BasicShaderProgram.KEY); if (drawable.program == null) { drawable.program = (BasicShaderProgram) rc.putShaderProgram(BasicShaderProgram.KEY, new BasicShaderProgram(rc.resources)); } rc.offerSurfaceDrawable(drawable, Double.NEGATIVE_INFINITY /*z-order before all other surface drawables*/); // If the pick ray intersects the terrain, enqueue a picked object that associates the terrain drawable with its // picked object ID and the intersection position. if (rc.pickRay != null && rc.terrain.intersect(rc.pickRay, this.pickPoint)) { rc.globe.cartesianToGeographic(this.pickPoint.x, this.pickPoint.y, this.pickPoint.z, this.pickPos); this.pickPos.altitude = 0; // report the actual altitude, which may not lie on the terrain's surface rc.offerPickedObject(PickedObject.fromTerrain(pickedObjectId, this.pickPos)); } }