[android] #3758 - Projection / Visible Region implementation.

[android] #3758 - add VisibleRegion + unit tests, removed boundingbox, cleanup jni, cleanup test app, renamed CoordinateSpan to LatLng.
This commit is contained in:
Tobrun
2016-02-03 17:26:09 +01:00
parent bc1f4b1fc0
commit aa05cba0df
24 changed files with 460 additions and 686 deletions

View File

@@ -99,7 +99,7 @@ public class InfoWindow {
// Calculate default Android x,y coordinate
mCoordinates = mapboxMap.toScreenLocation(position);
mCoordinates = mapboxMap.getProjection().toScreenLocation(position);
float x = mCoordinates.x - (view.getMeasuredWidth() / 2) + offsetX;
float y = mCoordinates.y - view.getMeasuredHeight() + offsetY;
@@ -231,7 +231,7 @@ public class InfoWindow {
Marker marker = mBoundMarker.get();
View view = mView.get();
if (mapboxMap != null && marker != null && view != null) {
mCoordinates = mapboxMap.toScreenLocation(marker.getPosition());
mCoordinates = mapboxMap.getProjection().toScreenLocation(marker.getPosition());
view.setX(mCoordinates.x + mViewWidthOffset);
view.setY(mCoordinates.y + mMarkerHeightOffset);
}

View File

@@ -1,297 +0,0 @@
package com.mapbox.mapboxsdk.geometry;
import android.os.Parcel;
import android.os.Parcelable;
import java.io.Serializable;
import java.util.List;
/**
* A rectangular geographical area defined in latitude and longitude units.
*/
public final class BoundingBox implements Parcelable {
private final double mLatNorth;
private final double mLatSouth;
private final double mLonEast;
private final double mLonWest;
private final boolean mIsValid;
/**
* Construct a new bounding box based on its corners, given in NESW
* order.
*
* @param northLatitude Northern Latitude
* @param eastLongitude Eastern Longitude
* @param southLatitude Southern Latitude
* @param westLongitude Western Longitude
*/
public BoundingBox(final double northLatitude, final double eastLongitude, final double southLatitude, final double westLongitude) {
this.mLatNorth = northLatitude;
this.mLonEast = eastLongitude;
this.mLatSouth = southLatitude;
this.mLonWest = westLongitude;
this.mIsValid = ((this.mLonWest < this.mLonEast) && (this.mLatNorth > this.mLatSouth));
}
/**
* Construct a new bounding box based on its corners, given in NESW order.
*
* @param northEast Coordinate
* @param southWest Coordinate
*/
public BoundingBox(final LatLng northEast, final LatLng southWest) {
this(northEast.getLatitude(), northEast.getLongitude(), southWest.getLatitude(), southWest.getLongitude());
}
/**
* Create a bounding box from another bounding box
*
* @param other the other bounding box
*/
public BoundingBox(final BoundingBox other) {
this.mLatNorth = other.getLatNorth();
this.mLonEast = other.getLonEast();
this.mLatSouth = other.getLatSouth();
this.mLonWest = other.getLonWest();
this.mIsValid = other.isValid();
}
/**
* Calculates the centerpoint of this bounding box by simple interpolation and returns
* it as a point. This is a non-geodesic calculation which is not the geographic center.
*
* @return LatLng center of this BoundingBox
*/
public LatLng getCenter() {
return new LatLng((this.mLatNorth + this.mLatSouth) / 2,
(this.mLonEast + this.mLonWest) / 2);
}
public double getLatNorth() {
return this.mLatNorth;
}
public double getLatSouth() {
return this.mLatSouth;
}
public double getLonEast() {
return this.mLonEast;
}
public double getLonWest() {
return this.mLonWest;
}
public boolean isValid() {
return this.mIsValid;
}
/**
* Get the area spanned by this bounding box
*
* @return CoordinateSpan area
*/
public CoordinateSpan getSpan() {
return new CoordinateSpan(getLatitudeSpan(), getLongitudeSpan());
}
/**
* Get the absolute distance, in degrees, between the north and
* south boundaries of this bounding box
*
* @return Span distance
*/
public double getLatitudeSpan() {
return Math.abs(this.mLatNorth - this.mLatSouth);
}
/**
* Get the absolute distance, in degrees, between the west and
* east boundaries of this bounding box
*
* @return Span distance
*/
public double getLongitudeSpan() {
return Math.abs(this.mLonEast - this.mLonWest);
}
/**
* Validate if bounding box is empty, determined if absolute distance is
*
* @return boolean indicating if span is empty
*/
public boolean isEmpty() {
return getLongitudeSpan() == 0.0 || getLatitudeSpan() == 0.0;
}
@Override
public String toString() {
return "N:" + this.mLatNorth + "; E:" + this.mLonEast + "; S:" + this.mLatSouth + "; W:" + this.mLonWest;
}
/**
* Constructs a bounding box that contains all of a list of LatLng
* objects. Empty lists will yield invalid bounding boxes.
*
* @param latLngs List of LatLng objects
* @return BoundingBox
*/
public static BoundingBox fromLatLngs(final List<? extends ILatLng> latLngs) {
double minLat = 90,
minLon = 180,
maxLat = -90,
maxLon = -180;
for (final ILatLng gp : latLngs) {
final double latitude = gp.getLatitude();
final double longitude = gp.getLongitude();
minLat = Math.min(minLat, latitude);
minLon = Math.min(minLon, longitude);
maxLat = Math.max(maxLat, latitude);
maxLon = Math.max(maxLon, longitude);
}
return new BoundingBox(maxLat, maxLon, minLat, minLon);
}
/**
* Determines whether this bounding box matches another one via coordinates.
*
* @param o another object
* @return a boolean indicating whether the bounding boxes are equal
*/
@Override
public boolean equals(final Object o) {
if (this == o) return true;
if (o instanceof BoundingBox) {
BoundingBox other = (BoundingBox) o;
return mLatNorth == other.getLatNorth()
&& mLatSouth == other.getLatSouth()
&& mLonEast == other.getLonEast()
&& mLonWest == other.getLonWest();
}
return false;
}
/**
* Determines whether this bounding box contains a point and the point
* does not touch its boundary.
*
* @param pGeoPoint the point which may be contained
* @return true, if the point is contained within the box.
*/
public boolean contains(final ILatLng pGeoPoint) {
final double latitude = pGeoPoint.getLatitude();
final double longitude = pGeoPoint.getLongitude();
return ((latitude < this.mLatNorth)
&& (latitude > this.mLatSouth))
&& ((longitude < this.mLonEast)
&& (longitude > this.mLonWest));
}
/**
* Returns a new BoundingBox that stretches to contain both this and another BoundingBox.
*
* @param box BoundingBox to add
* @return BoundingBox
*/
public BoundingBox union(BoundingBox box) {
return union(box.getLatNorth(), box.getLonEast(), box.getLatSouth(), box.getLonWest());
}
/**
* Returns a new BoundingBox that stretches to include another bounding box,
* given by corner points.
*
* @param lonNorth Northern Longitude
* @param latEast Eastern Latitude
* @param lonSouth Southern Longitude
* @param latWest Western Longitude
* @return BoundingBox
*/
public BoundingBox union(final double lonNorth, final double latEast, final double lonSouth, final double latWest) {
return new BoundingBox((this.mLatNorth < lonNorth) ? lonNorth : this.mLatNorth,
(this.mLonEast < latEast) ? latEast : this.mLonEast,
(this.mLatSouth > lonSouth) ? lonSouth : this.mLatSouth,
(this.mLonWest > latWest) ? latWest : this.mLonWest);
}
/**
* Returns a new BoundingBox that is the intersection of this with another box
*
* @param box BoundingBox to intersect with
* @return BoundingBox
*/
public BoundingBox intersect(BoundingBox box) {
double minLatWest = Math.max(getLonWest(), box.getLonWest());
double maxLatEast = Math.min(getLonEast(), box.getLonEast());
if (maxLatEast > minLatWest) {
double minLonSouth = Math.max(getLatSouth(), box.getLatSouth());
double maxLonNorth = Math.min(getLatNorth(), box.getLatNorth());
if (maxLonNorth > minLonSouth) {
return new BoundingBox(maxLonNorth, maxLatEast, minLonSouth, minLatWest);
}
}
return null;
}
/**
* Returns a new BoundingBox that is the intersection of this with another box
*
* @param northLongitude Northern Longitude
* @param eastLatitude Eastern Latitude
* @param southLongitude Southern Longitude
* @param westLatitude Western Latitude
* @return BoundingBox
*/
public BoundingBox intersect(double northLongitude, double eastLatitude, double southLongitude, double westLatitude) {
return intersect(new BoundingBox(northLongitude, eastLatitude, southLongitude, westLatitude));
}
public static final Parcelable.Creator<BoundingBox> CREATOR =
new Parcelable.Creator<BoundingBox>() {
@Override
public BoundingBox createFromParcel(final Parcel in) {
return readFromParcel(in);
}
@Override
public BoundingBox[] newArray(final int size) {
return new BoundingBox[size];
}
};
@Override
public int hashCode() {
return (int) ((mLatNorth + 90)
+ ((mLatSouth + 90) * 1000)
+ ((mLonEast + 180) * 1000000)
+ ((mLonEast + 180) * 1000000000));
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(final Parcel out, final int arg1) {
out.writeDouble(this.mLatNorth);
out.writeDouble(this.mLonEast);
out.writeDouble(this.mLatSouth);
out.writeDouble(this.mLonWest);
}
private static BoundingBox readFromParcel(final Parcel in) {
final double lonNorth = in.readDouble();
final double latEast = in.readDouble();
final double lonSouth = in.readDouble();
final double latWest = in.readDouble();
return new BoundingBox(lonNorth, latEast, lonSouth, latWest);
}
}

View File

@@ -3,10 +3,7 @@ package com.mapbox.mapboxsdk.geometry;
import android.os.Parcel;
import android.os.Parcelable;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import com.mapbox.mapboxsdk.annotations.Icon;
import com.mapbox.mapboxsdk.annotations.Marker;
import com.mapbox.mapboxsdk.exceptions.InvalidLatLngBoundsException;
import java.util.ArrayList;
@@ -68,10 +65,10 @@ public class LatLngBounds implements Parcelable {
/**
* Get the area spanned by this LatLngBounds
*
* @return CoordinateSpan area
* @return LatLngSpan area
*/
public CoordinateSpan getSpan() {
return new CoordinateSpan(getLatitudeSpan(), getLongitudeSpan());
public LatLngSpan getSpan() {
return new LatLngSpan(getLatitudeSpan(), getLongitudeSpan());
}
/**

View File

@@ -7,17 +7,17 @@ import android.support.annotation.NonNull;
/**
* A geographical span defined by its latitude and longitude span.
*/
public class CoordinateSpan implements Parcelable {
public class LatLngSpan implements Parcelable {
private double mLatitudeSpan;
private double mLongitudeSpan;
public CoordinateSpan(@NonNull Parcel in){
public LatLngSpan(@NonNull Parcel in){
mLatitudeSpan = in.readDouble();
mLongitudeSpan = in.readDouble();
}
public CoordinateSpan(double latitudeSpan, double longitudeSpan) {
public LatLngSpan(double latitudeSpan, double longitudeSpan) {
mLatitudeSpan = latitudeSpan;
mLongitudeSpan = longitudeSpan;
}
@@ -41,24 +41,24 @@ public class CoordinateSpan implements Parcelable {
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o instanceof CoordinateSpan) {
CoordinateSpan other = (CoordinateSpan) o;
if (o instanceof LatLngSpan) {
LatLngSpan other = (LatLngSpan) o;
return mLongitudeSpan == other.getLongitudeSpan()
&& mLatitudeSpan == other.getLatitudeSpan();
}
return false;
}
public static final Parcelable.Creator<CoordinateSpan> CREATOR =
new Parcelable.Creator<CoordinateSpan>() {
public static final Parcelable.Creator<LatLngSpan> CREATOR =
new Parcelable.Creator<LatLngSpan>() {
@Override
public CoordinateSpan createFromParcel(Parcel in) {
return new CoordinateSpan(in);
public LatLngSpan createFromParcel(Parcel in) {
return new LatLngSpan(in);
}
@Override
public CoordinateSpan[] newArray(int size) {
return new CoordinateSpan[size];
public LatLngSpan[] newArray(int size) {
return new LatLngSpan[size];
}
};

View File

@@ -0,0 +1,120 @@
package com.mapbox.mapboxsdk.geometry;
import android.os.Parcel;
import android.os.Parcelable;
/**
* Contains the four points defining the four-sided polygon that is visible in a map's camera.
* This polygon can be a trapezoid instead of a rectangle, because a camera can have tilt.
* If the camera is directly over the center of the camera, the shape is rectangular,
* but if the camera is tilted, the shape will appear to be a trapezoid whose
* smallest side is closest to the point of view.
*/
public class VisibleRegion implements Parcelable {
/**
* LatLng object that defines the far left corner of the camera.
*/
public final LatLng farLeft;
/**
* LatLng object that defines the far right corner of the camera.
*/
public final LatLng farRight;
/**
* LatLng object that defines the bottom left corner of the camera.
*/
public final LatLng nearLeft;
/**
* LatLng object that defines the bottom right corner of the camera.
*/
public final LatLng nearRight;
/**
* The smallest bounding box that includes the visible region defined in this class.
*/
public final LatLngBounds latLngBounds;
private VisibleRegion(Parcel in) {
this.farLeft = in.readParcelable(LatLng.class.getClassLoader());
this.farRight = in.readParcelable(LatLng.class.getClassLoader());
this.nearLeft = in.readParcelable(LatLng.class.getClassLoader());
this.nearRight = in.readParcelable(LatLng.class.getClassLoader());
this.latLngBounds = in.readParcelable(LatLngBounds.class.getClassLoader());
}
/**
* Creates a new VisibleRegion given the four corners of the camera.
*
* @param farLeft A LatLng object containing the latitude and longitude of the near left corner of the region.
* @param farRight A LatLng object containing the latitude and longitude of the near left corner of the region.
* @param nearLeft A LatLng object containing the latitude and longitude of the near left corner of the region.
* @param nearRight A LatLng object containing the latitude and longitude of the near left corner of the region.
* @param latLngBounds The smallest bounding box that includes the visible region defined in this class.
*/
public VisibleRegion(LatLng farLeft, LatLng farRight, LatLng nearLeft, LatLng nearRight, LatLngBounds latLngBounds) {
this.farLeft = farLeft;
this.farRight = farRight;
this.nearLeft = nearLeft;
this.nearRight = nearRight;
this.latLngBounds = latLngBounds;
}
/**
* Compares this VisibleRegion to another object.
* If the other object is actually a pointer to this object,
* or if all four corners and the bounds of the two objects are the same,
* this method returns true. Otherwise, this method returns false.
* @param o The Object to compare with.
* @return true if both objects are the same object.
*/
@Override
public boolean equals(Object o) {
if (!(o instanceof VisibleRegion)) {
return false;
}
if (o == this) {
return true;
}
VisibleRegion visibleRegion = (VisibleRegion) o;
return farLeft.equals(visibleRegion.farLeft)
&& farRight.equals(visibleRegion.farRight)
&& nearLeft.equals(visibleRegion.nearLeft)
&& nearRight.equals(visibleRegion.nearRight)
&& latLngBounds.equals(visibleRegion.latLngBounds);
}
@Override
public String toString() {
return "[farLeft [" + farLeft + "], farRight [" + farRight + "], nearLeft [" + nearLeft + "], nearRight [" + nearRight + "], latLngBounds ["+latLngBounds+"]]";
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel out, int flags) {
out.writeParcelable(farLeft, flags);
out.writeParcelable(farRight, flags);
out.writeParcelable(nearLeft, flags);
out.writeParcelable(nearRight, flags);
out.writeParcelable(latLngBounds, flags);
}
public static final Parcelable.Creator<VisibleRegion> CREATOR
= new Parcelable.Creator<VisibleRegion>() {
public VisibleRegion createFromParcel(Parcel in) {
return new VisibleRegion(in);
}
public VisibleRegion[] newArray(int size) {
return new VisibleRegion[size];
}
};
}

View File

@@ -240,7 +240,7 @@ public class CameraUpdateFactory {
centerPixel.y = viewportHeight - centerPixel.y;
LatLng center = mapboxMap.fromScreenLocation(centerPixel);
LatLng center = mapboxMap.getProjection().fromScreenLocation(centerPixel);
return new CameraPosition.Builder()
.target(center)
@@ -370,7 +370,7 @@ public class CameraUpdateFactory {
} else {
return new CameraPosition.Builder(cameraPosition)
.zoom(transformZoom(cameraPosition.zoom))
.target(mapboxMap.fromScreenLocation(new PointF(getX(), getY())))
.target(mapboxMap.getProjection().fromScreenLocation(new PointF(getX(), getY())))
.build();
}
}

View File

@@ -73,8 +73,8 @@ import com.mapbox.mapboxsdk.constants.MyLocationTracking;
import com.mapbox.mapboxsdk.constants.Style;
import com.mapbox.mapboxsdk.exceptions.IconBitmapChangedException;
import com.mapbox.mapboxsdk.exceptions.InvalidAccessTokenException;
import com.mapbox.mapboxsdk.geometry.BoundingBox;
import com.mapbox.mapboxsdk.geometry.LatLng;
import com.mapbox.mapboxsdk.geometry.LatLngBounds;
import com.mapbox.mapboxsdk.layers.CustomLayer;
import com.mapbox.mapboxsdk.utils.ApiAccess;
@@ -1254,7 +1254,7 @@ public class MapView extends FrameLayout {
return new ArrayList<>(mAnnotations);
}
private List<Marker> getMarkersInBounds(@NonNull BoundingBox bbox) {
private List<Marker> getMarkersInBounds(@NonNull LatLngBounds bbox) {
if (bbox == null) {
Log.w(TAG, "bbox was null, so just returning null");
return null;
@@ -1699,15 +1699,13 @@ public class MapView extends FrameLayout {
RectF tapRect = new RectF(tapPoint.x - toleranceSides, tapPoint.y + toleranceTop,
tapPoint.x + toleranceSides, tapPoint.y - toleranceBottom);
List<LatLng> corners = Arrays.asList(
fromScreenLocation(new PointF(tapRect.left, tapRect.bottom)),
fromScreenLocation(new PointF(tapRect.left, tapRect.top)),
fromScreenLocation(new PointF(tapRect.right, tapRect.top)),
fromScreenLocation(new PointF(tapRect.right, tapRect.bottom))
);
LatLngBounds.Builder builder = new LatLngBounds.Builder();
builder.include(fromScreenLocation(new PointF(tapRect.left, tapRect.bottom)));
builder.include(fromScreenLocation(new PointF(tapRect.left, tapRect.top)));
builder.include(fromScreenLocation(new PointF(tapRect.right, tapRect.top)));
builder.include(fromScreenLocation(new PointF(tapRect.right, tapRect.bottom)));
BoundingBox tapBounds = BoundingBox.fromLatLngs(corners);
List<Marker> nearbyMarkers = getMarkersInBounds(tapBounds);
List<Marker> nearbyMarkers = getMarkersInBounds(builder.build());
long newSelectedMarkerId = -1;
if (nearbyMarkers != null && nearbyMarkers.size() > 0) {

View File

@@ -2,7 +2,7 @@ package com.mapbox.mapboxsdk.maps;
import android.Manifest;
import android.content.Context;
import android.graphics.PointF;
import android.location.Location;
import android.os.Bundle;
import android.support.annotation.FloatRange;
@@ -39,6 +39,7 @@ public class MapboxMap {
private MapView mMapView;
private UiSettings mUiSettings;
private Projection mProjection;
private CameraPosition mCameraPosition;
private boolean mInvalidCameraPosition;
private String mStyleUrl;
@@ -63,6 +64,7 @@ public class MapboxMap {
mMapView = mapView;
mMapView.addOnMapChangedListener(new MapChangeCameraPositionListener());
mUiSettings = new UiSettings(mapView);
mProjection = new Projection(mapView);
mSelectedMarkers = new ArrayList<>();
mInfoWindows = new ArrayList<>();
}
@@ -80,6 +82,17 @@ public class MapboxMap {
return mUiSettings;
}
//
// Projection
//
/**
* Get the Projection object that you can use to convert between screen coordinates and latitude/longitude coordinates.
*/
public Projection getProjection() {
return mProjection;
}
//
// Camera API
//
@@ -438,49 +451,6 @@ public class MapboxMap {
return mMapView.getAccessToken();
}
//
// Projection
//
/**
* Converts a point in this view's coordinate system to a map coordinate.
*
* @param point A point in this view's coordinate system.
* @return The converted map coordinate.
*/
@UiThread
@NonNull
public LatLng fromScreenLocation(@NonNull PointF point) {
return mMapView.fromScreenLocation(point);
}
/**
* Converts a map coordinate to a point in this view's coordinate system.
*
* @param location A map coordinate.
* @return The converted point in this view's coordinate system.
*/
@UiThread
@NonNull
public PointF toScreenLocation(@NonNull LatLng location) {
return mMapView.toScreenLocation(location);
}
/**
* <p>
* Returns the distance spanned by one pixel at the specified latitude and current zoom level.
* </p>
* The distance between pixels decreases as the latitude approaches the poles.
* This relationship parallels the relationship between longitudinal coordinates at different latitudes.
*
* @param latitude The latitude for which to return the value.
* @return The distance measured in meters.
*/
@UiThread
public double getMetersPerPixelAtLatitude(@FloatRange(from = -180, to = 180) double latitude) {
return mMapView.getMetersPerPixelAtLatitude(latitude);
}
//
// Annotations
//

View File

@@ -10,8 +10,8 @@ import android.view.Surface;
import com.mapbox.mapboxsdk.annotations.Marker;
import com.mapbox.mapboxsdk.annotations.Polygon;
import com.mapbox.mapboxsdk.annotations.Polyline;
import com.mapbox.mapboxsdk.geometry.BoundingBox;
import com.mapbox.mapboxsdk.geometry.LatLng;
import com.mapbox.mapboxsdk.geometry.LatLngBounds;
import com.mapbox.mapboxsdk.geometry.ProjectedMeters;
import com.mapbox.mapboxsdk.layers.CustomLayer;
@@ -403,7 +403,7 @@ final class NativeMapView {
nativeRemoveAnnotations(mNativeMapViewPtr, ids);
}
public long[] getAnnotationsInBounds(BoundingBox bbox) {
public long[] getAnnotationsInBounds(LatLngBounds bbox) {
return nativeGetAnnotationsInBounds(mNativeMapViewPtr, bbox);
}
@@ -633,7 +633,7 @@ final class NativeMapView {
private native void nativeRemoveAnnotations(long nativeMapViewPtr, long[] id);
private native long[] nativeGetAnnotationsInBounds(long mNativeMapViewPtr, BoundingBox bbox);
private native long[] nativeGetAnnotationsInBounds(long mNativeMapViewPtr, LatLngBounds bbox);
private native void nativeAddAnnotationIcon(long nativeMapViewPtr, String symbol,
int width, int height, float scale, byte[] pixels);

View File

@@ -0,0 +1,72 @@
package com.mapbox.mapboxsdk.maps;
import android.graphics.PointF;
import android.support.annotation.NonNull;
import com.mapbox.mapboxsdk.geometry.LatLng;
import com.mapbox.mapboxsdk.geometry.LatLngBounds;
import com.mapbox.mapboxsdk.geometry.VisibleRegion;
/**
* A projection is used to translate between on screen location and geographic coordinates on
* the surface of the Earth (LatLng). Screen location is in screen pixels (not display pixels)
* with respect to the top left corner of the map (and not necessarily of the whole screen).
*/
public class Projection {
private MapView mMapView;
Projection(@NonNull MapView mapView) {
this.mMapView = mapView;
}
/**
* Returns the geographic location that corresponds to a screen location.
* The screen location is specified in screen pixels (not display pixels) relative to the
* top left of the map (not the top left of the whole screen).
*
* @param point A Point on the screen in screen pixels.
* @return The LatLng corresponding to the point on the screen, or null if the ray through
* the given screen point does not intersect the ground plane.
*/
public LatLng fromScreenLocation(PointF point) {
return mMapView.fromScreenLocation(point);
}
/**
* Gets a projection of the viewing frustum for converting between screen coordinates and
* geo-latitude/longitude coordinates.
*
* @return The projection of the viewing frustum in its current state.
*/
public VisibleRegion getVisibleRegion() {
LatLngBounds.Builder builder = new LatLngBounds.Builder();
int viewportWidth = mMapView.getWidth();
int viewportHeight = mMapView.getHeight();
LatLng topLeft = fromScreenLocation(new PointF(0, 0));
LatLng topRight = fromScreenLocation(new PointF(viewportWidth, 0));
LatLng bottomRight = fromScreenLocation(new PointF(viewportWidth, viewportHeight));
LatLng bottomLeft = fromScreenLocation(new PointF(0, viewportHeight));
builder.include(topLeft)
.include(topRight)
.include(bottomRight)
.include(bottomLeft);
return new VisibleRegion(topLeft,topRight,bottomLeft,bottomRight,builder.build());
}
/**
* Returns a screen location that corresponds to a geographical coordinate (LatLng).
* The screen location is in screen pixels (not display pixels) relative to the top left
* of the map (not of the whole screen).
*
* @param location A LatLng on the map to convert to a screen location.
* @return A Point representing the screen location in screen pixels.
*/
public PointF toScreenLocation(LatLng location) {
return mMapView.toScreenLocation(location);
}
}