The API design object is not so useful, I ran into the same problem and rolled back.
public class SphericalMercatorProjection { public static PointD latLngToWorldXY(LatLng latLng, double zoomLevel) { final double worldWidthPixels = toWorldWidthPixels(zoomLevel); final double x = latLng.longitude / 360 + .5; final double siny = Math.sin(Math.toRadians(latLng.latitude)); final double y = 0.5 * Math.log((1 + siny) / (1 - siny)) / -(2 * Math.PI) + .5; return new PointD(x * worldWidthPixels, y * worldWidthPixels); } public static LatLng worldXYToLatLng(PointD point, double zoomLevel) { final double worldWidthPixels = toWorldWidthPixels(zoomLevel); final double x = point.x / worldWidthPixels - 0.5; final double lng = x * 360; double y = .5 - (point.y / worldWidthPixels); final double lat = 90 - Math.toDegrees(Math.atan(Math.exp(-y * 2 * Math.PI)) * 2); return new LatLng(lat, lng); } private static double toWorldWidthPixels(double zoomLevel) { return 256 * Math.pow(2, zoomLevel); } }
Using this code, you can transform the map around a new target (i.e., not the center). I decided to use a reference point system, where (0,5, 0,5) is the default and represents the middle of the screen. (0,0) top left and (1, 1) bottom left.
private LatLng getOffsetLocation(LatLng location, double zoom) { Point size = getSize(); PointD anchorOffset = new PointD(size.x * (0.5 - anchor.x), size.y * (0.5 - anchor.y)); PointD screenCenterWorldXY = SphericalMercatorProjection.latLngToWorldXY(location, zoom); PointD newScreenCenterWorldXY = new PointD(screenCenterWorldXY.x + anchorOffset.x, screenCenterWorldXY.y + anchorOffset.y); newScreenCenterWorldXY.rotate(screenCenterWorldXY, cameraPosition.bearing); return SphericalMercatorProjection.worldXYToLatLng(newScreenCenterWorldXY, zoom); }
Basically, you use the projection to get the XY coordinates in the world, and then sweep this point and return it back to LatLng. Then you can transfer it to your card. PointD is a simple type that contains x, y as doubles, and also makes a turn.
public class PointD { public double x; public double y; public PointD(double x, double y) { this.x = x; this.y = y; } public void rotate(PointD origin, float angleDeg) { double rotationRadians = Math.toRadians(angleDeg); this.x -= origin.x; this.y -= origin.y; double rotatedX = this.x * Math.cos(rotationRadians) - this.y * Math.sin(rotationRadians); double rotatedY = this.x * Math.sin(rotationRadians) + this.y * Math.cos(rotationRadians); this.x = rotatedX; this.y = rotatedY; this.x += origin.x; this.y += origin.y; } }
Please note that if you are updating the map support and location at the same time, it is important that you use the new bearing in getOffsetLocation.