I'm trying to add an image to a Google Map using the Google Maps Android API v2:
mMap = googleMap;
LatLng sw = new LatLng(47.01377857060625, 8.305519705172628);
LatLng ne = new LatLng(47.01395211967171, 8.306270482717082);
LatLng nw = new LatLng(47.014014755501165, 8.305559697328135);
LatLng se = new LatLng(47.01370751919609, 8.306236284552142);
LatLngBounds latLngBounds = new LatLngBounds(sw, ne).including(nw).including(se);
GroundOverlayOptions groundOverlayOptions = new GroundOverlayOptions();
BitmapDescriptor bitmapDescriptor = BitmapDescriptorFactory.fromAsset("t2c.png");
groundOverlayOptions.image(bitmapDescriptor);
groundOverlayOptions.positionFromBounds(latLngBounds);
mMap.addGroundOverlay(groundOverlayOptions);
Unfortunately, the LatLng values are not 100% accurate, so the background image doesn't rotate but is skewed according to Google:
If the bounds do not match the original aspect ratio, the image will be skewed.
Using only LatLng sw and ne:
https://developers.google.com/maps/documentation/android-api/groundoverlay#use_latlngbounds_to_position_an_image
I don't know how I should be able to figure out the exact LatLng of south-west and north-east, so I'm interested in a way to define a polygon and squeeze the image somehow into it with the four LatLng as anchors. Using four LatLng currently looks like this. Using LatLng sw, ne, nw and se:
you will never achieve what you want using LatLngBounds because you are trying to rotate your GroundOverlay.
What you need to do is to use bearing (rotation) and width/height of the ground overlay.
Let's say your building in -20 degrees rotated and has width-height of 40-20 (only you know these values.
1- Get the LatLng of the center of the building, which is finding the center of the four coordinates in your code above.
That value will be your "centerLocation"
GroundOverlayOptions groundOverlayOptions = new GroundOverlayOptions();
BitmapDescriptor bitmapDescriptor = BitmapDescriptorFactory.fromAsset("t2c.png");
groundOverlayOptions.image(bitmapDescriptor);
groundOverlayOptions.position(centerLocation,widthInMeters,heightInMeters);
groundOverlayOptions.bearing(rotated);//the value is clockwise and rotation is about anchor point (which should be by default 0.5,0.5 of your image
mMap.addGroundOverlay(groundOverlayOptions);
This should work, obviously you have to calculate values or go step by step trying different values.
Hope it helps
The first answer is what will works for u. I will just write my solution in detail so it could be easy to just copy paste code and get it done...
First Step if u have four bounds, get center of them for which u can use the below the function
private LatLng computeCentroid(List<LatLng> points) {
double latitude = 0;
double longitude = 0;
int n = points.size();
for (LatLng point : points) {
latitude += point.latitude;
longitude += point.longitude;
}
return new LatLng(latitude/n, longitude/n);
}
Second get the width and height in meters. Here are method to get distance between two points in meters.
private double distance(double lat1, double lon1, double lat2, double lon2) {
double theta = lon1 - lon2;
double dist = Math.sin(deg2rad(lat1))
* Math.sin(deg2rad(lat2))
+ Math.cos(deg2rad(lat1))
* Math.cos(deg2rad(lat2))
* Math.cos(deg2rad(theta));
dist = Math.acos(dist);
dist = rad2deg(dist);
dist = dist * 60 * 1.1515;
return dist * 1609.34;
}
private double deg2rad(double deg) {
return (deg * Math.PI / 180.0);
}
private double rad2deg(double rad) {
return (rad * 180.0 / Math.PI);
}
Last just use these values and done
double height=distance(selectedFloorPlan.getGeoData().get(0).getLat(), selectedFloorPlan.getGeoData().get(0).getLng(),selectedFloorPlan.getGeoData().get(1).getLat(), selectedFloorPlan.getGeoData().get(1).getLng());
double width=distance(selectedFloorPlan.getGeoData().get(0).getLat(), selectedFloorPlan.getGeoData().get(0).getLng(),selectedFloorPlan.getGeoData().get(3).getLat(), selectedFloorPlan.getGeoData().get(3).getLng());
GroundOverlayOptions newarkMap = new GroundOverlayOptions()
.image(images.get(0))
.position(centerPoint,(float)width,(float)height)
.bearing(-67.18571490414709f);
imageOverlay = map.addGroundOverlay(newarkMap);
Related
ArrayList<LatLng> arrayList= new ArrayList<LatLng>();
LatLng near = new LatLng(33.549232, 73.125256);
LatLng pass = new LatLng(33.549844, 73.125849);
LatLng qareeb = new LatLng(33.549603, 73.124201);
public void onMapReady(GoogleMap googleMap) {
mMap= googleMap;
for (int i =0; i<arrayList.size();i++){
mMap.addMarker(new MarkerOptions().position(arrayList.get(i)).title("Marker").snippet("and snippet")
.icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_ORANGE)));
LatLng latLng = new LatLng(currentLocation.getLatitude(),currentLocation.getLongitude());
MarkerOptions markerOptions = new MarkerOptions().position(latLng).title("Main Yahan hn");
mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(arrayList.get(i),15));
mMap.animateCamera(CameraUpdateFactory.zoomIn());
mMap.animateCamera(CameraUpdateFactory.zoomTo(15), 2000, null);
/*mMap.animateCamera(CameraUpdateFactory.zoomTo(15.0f));*/
mMap.moveCamera(CameraUpdateFactory.newLatLng(arrayList.get(i)));
googleMap.addMarker(markerOptions);
}
}
Above is my code and I want to create 3 points which compare to my current location that which one is near to the current location.
The question which you are asking and the code which you have posted are not complete. The code simply enables GoogleMaps on the Activity. Instead of coding the entire solution to you, I would explain you the logic and expect you try it out at your end and repost the updated answer here if required.
Steps:
Fetch the current location of the device using FusedLocationProviderClient and store the value in a variable (say currentLocation). (Get the last known location)
Use Location class to find the distance between point1 and currentLocation. (say d1). (distanceBetween)
Use the same process to find the distances between point2 and point3 from the currentLocation.
Now you will have 3 distance variables, d1, d2 and d3. I guess you'll be able to find the shortest of these easily.
You can calculate distance between two location and then compare the distance.
private double distance(double lat1, double lon1, double lat2, double lon2) {
double theta = lon1 - lon2;
double dist = Math.sin(deg2rad(lat1))
* Math.sin(deg2rad(lat2))
+ Math.cos(deg2rad(lat1))
* Math.cos(deg2rad(lat2))
* Math.cos(deg2rad(theta));
dist = Math.acos(dist);
dist = rad2deg(dist);
dist = dist * 60 * 1.1515;
return (dist);
}
private double deg2rad(double deg) {
return (deg * Math.PI / 180.0);
}
private double rad2deg(double rad) {
return (rad * 180.0 / Math.PI);
}
You can use this as below code
LatLng near = new LatLng(33.549232, 73.125256);
LatLng pass = new LatLng(33.549844, 73.125849);
LatLng qareeb = new LatLng(33.549603, 73.124201);
double distance = distance(near.latitude, near.longitude, pass.latitude, pass.longitude);
I am creating an Android app which requires finding a coordinate on the same route after X kilometers.
I have two coordinates x1,y1 & x2,y2 on a road. Now, my requirement is to find coordinate x3,y3 after some 3 kilometers (i.e., coordinate after x2,y2 not between x1,y1 & x2,y2) on the same road.
How can this be achieved ?
If you know the bearing, you can calculate the destination coordinate.
Sample Code:
private LatLng getDestinationPoint(LatLng source, double brng, double dist) {
dist = dist / 6371;
brng = Math.toRadians(brng);
double lat1 = Math.toRadians(source.latitude), lon1 = Math.toRadians(source.longitude);
double lat2 = Math.asin(Math.sin(lat1) * Math.cos(dist) +
Math.cos(lat1) * Math.sin(dist) * Math.cos(brng));
double lon2 = lon1 + Math.atan2(Math.sin(brng) * Math.sin(dist) *
Math.cos(lat1),
Math.cos(dist) - Math.sin(lat1) *
Math.sin(lat2));
if (Double.isNaN(lat2) || Double.isNaN(lon2)) {
return null;
}
return new LatLng(Math.toDegrees(lat2), Math.toDegrees(lon2));
}
Sample usage:
double radiusInKM = 10.0;
double bearing = 90;
LatLng destinationPoint = getDestinationPoint(new LatLng((25.48, -71.26), bearing, radiusInKM);
Or you can use heading between your pointA and pointB instead of bearing:
LatLng destinationPoint = getDestinationPoint(new LatLng(37.4038194,-122.081267), SphericalUtil.computeHeading(new LatLng(37.7577,-122.4376), new LatLng(37.4038194,-122.081267)), radiusInKM);
The SphericalUtil.computeHeading(p1, p2); method is from the Android Google Maps Utility library.
This is based on the Javascript method from this Stackoverflow answer.
If you want the point on same road, you might checkout this PHP answer.
I am working on an Android version of an Ios app I have developed and are running into problems with differences in the Google Maps API on the two platforms. In Ios I can use the method: GMSGeometryOffset. However, it is not present in the Android API. Therefore, I am looking for an alternative to this method so that I can calculate a new location based on the current location, a direction/bearing and a distance/radius.
I am using it to Draw a round circle while avoiding the egg shape. My code so far looks like the following. However, it gives me an egg shape unless the current location is directly on top of equator.
for(double angle = 0; angle < 2*Math.PI; angle = angle+ Math.PI/resolutionCircle)
{
latitude = position.latitude + (radiusCircle * Math.sin(angle));
longitude = position.longitude + (radiusCircle * Math.cos(angle));
inner.add(new LatLng(latitude, longitude));
}
There is a computeOffset method in the Google Maps Android API Utility Library.
It is a static method:
public static LatLng computeOffset(LatLng from,
double distance,
double heading)
It returns the LatLng resulting from moving a distance from an origin in the specified heading (expressed in degrees clockwise from north).
You can refer this documentation for more detail about Google Maps Android API Utility Library.
Google Maps' GitHub page also provide its detail implementation:
public static LatLng computeOffset(LatLng from, double distance, double heading) {
distance /= EARTH_RADIUS;
heading = toRadians(heading);
// http://williams.best.vwh.net/avform.htm#LL
double fromLat = toRadians(from.latitude);
double fromLng = toRadians(from.longitude);
double cosDistance = cos(distance);
double sinDistance = sin(distance);
double sinFromLat = sin(fromLat);
double cosFromLat = cos(fromLat);
double sinLat = cosDistance * sinFromLat + sinDistance * cosFromLat * cos(heading);
double dLng = atan2(
sinDistance * cosFromLat * sin(heading),
cosDistance - sinFromLat * sinLat);
return new LatLng(toDegrees(asin(sinLat)), toDegrees(fromLng + dLng));
}
I'm also try to draw arc (I'm referencing on this and this questions). I'll get from web service following:
Lat and Lng
Radius (in meters)
Start angle (end angle is startA + 60 degrees)
Now I encounter on following problem because I do not have two LatLng, just one, and in new map api v2 there is no radius = Projection.metersToEquatorPixels method for providing to RectF.set(point.x - radius,...)
Do you have code example, links, etc?
Also what about performances of App, because I'll have up to 500 arcs on map?
Starting from a LatLng point you can calculate another LatLng point in a given distance (radius) and a given angle as follows:
private static final double EARTHRADIUS = 6366198;
/**
* Move a LatLng-Point into a given distance and a given angle (0-360,
* 0=North).
*/
public static LatLng moveByDistance(LatLng startGp, double distance,
double angle) {
/*
* Calculate the part going to north and the part going to east.
*/
double arc = Math.toRadians(angle);
double toNorth = distance * Math.cos(arc);
double toEast = distance * Math.sin(arc);
double lonDiff = meterToLongitude(toEast, startGp.latitude);
double latDiff = meterToLatitude(toNorth);
return new LatLng(startGp.latitude + latDiff, startGp.longitude
+ lonDiff);
}
private static double meterToLongitude(double meterToEast, double latitude) {
double latArc = Math.toRadians(latitude);
double radius = Math.cos(latArc) * EARTHRADIUS;
double rad = meterToEast / radius;
double degrees = Math.toDegrees(rad);
return degrees;
}
private static double meterToLatitude(double meterToNorth) {
double rad = meterToNorth / EARTHRADIUS;
double degrees = Math.toDegrees(rad);
return degrees;
}
Is there a way to get the coordinates of the current area, which is shown at the device?
Background is, we want to show "nearby" places, which are stored in our own database. So let's say, the user looks at following clip of a map:
How do we get the longitude/latitude of the screen (or the point in the middle of the screen and a radius which covers everything?). Please keep in mind, center of the map is not usually the current position, since the user can move the center of the card!
Use map.getProjection().getVisibleRegion(). From VisibleRegion you can get LatLngBounds, which is easy to work with. You may also try directly with the region, which might be trapezoid.
I found the solution for Google Map API v2 from few of responses:
stackoverflow#1 and
stackoverflow#2
So, need implements Activity from GoogleMap.OnCameraChangeListener interface
private static final int REQUEST_CODE_GOOGLE_PLAY_SERVECES_ERROR = -1;
private static final double EARTH_RADIOUS = 3958.75; // Earth radius;
private static final int METER_CONVERSION = 1609;
private GoogleMap mGoogleMap;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_layout);
int status = GooglePlayServicesUtil.isGooglePlayServicesAvailable(mContext);
if (status != ConnectionResult.SUCCESS)
{
Dialog dialog = GooglePlayServicesUtil.getErrorDialog(status, activity,
REQUEST_CODE_GOOGLE_PLAY_SERVECES_ERROR);
dialog.show();
mGoogleMap = null;
}
else
{
mGoogleMap = ((SupportMapFragment) getFragmentManager().findFragmentById(
R.id.fragment_shops_layout_maps_fragment)).getMap();
mGoogleMap.setOnCameraChangeListener(this);
}
}
The listener, that working when map scaled. Determin as LatLng the positions of bottom left, bottom right, top left and top right sides of map, that showing on screen. By greatest side of screen and two points we can get radius from center of map.
#Override
public void onCameraChange(CameraPosition cameraPosition)
{
// Listener of zooming;
float zoomLevel = cameraPosition.zoom;
VisibleRegion visibleRegion = mGoogleMap.getProjection().getVisibleRegion();
LatLng nearLeft = visibleRegion.nearLeft;
LatLng nearRight = visibleRegion.nearRight;
LatLng farLeft = visibleRegion.farLeft;
LatLng farRight = visibleRegion.farRight;
double dist_w = distanceFrom(nearLeft.latitude, nearLeft.longitude, nearRight.latitude, nearRight.longitude);
double dist_h = distanceFrom(farLeft.latitude, farLeft.longitude, farRight.latitude, farRight.longitude);
Log.d("DISTANCE: ", "DISTANCE WIDTH: " + dist_w + " DISTANCE HEIGHT: " + dist_h);
}
Return distance between 2 points, stored as 2 pair location at meters;
public double distanceFrom(double lat1, double lng1, double lat2, double lng2)
{
// Return distance between 2 points, stored as 2 pair location;
double dLat = Math.toRadians(lat2 - lat1);
double dLng = Math.toRadians(lng2 - lng1);
double a = Math.sin(dLat / 2) * Math.sin(dLat / 2) + Math.cos(Math.toRadians(lat1))
* Math.cos(Math.toRadians(lat2)) * Math.sin(dLng / 2) * Math.sin(dLng / 2);
double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
double dist = EARTH_RADIOUS * c;
return new Double(dist * METER_CONVERSION).floatValue();
}
If you want get radius of area, that showed on screen just need devided by 2.
I hope will useful !
This calculates the radio in km based on the map width:
public double calculateVisibleRadius() {
float[] distanceWidth = new float[1];
VisibleRegion visibleRegion = map.getProjection().getVisibleRegion();
LatLng farRight = visibleRegion.farRight;
LatLng farLeft = visibleRegion.farLeft;
LatLng nearRight = visibleRegion.nearRight;
LatLng nearLeft = visibleRegion.nearLeft;
//calculate the distance between left <-> right of map on screen
Location.distanceBetween( (farLeft.latitude + nearLeft.latitude) / 2, farLeft.longitude, (farRight.latitude + nearRight.latitude) / 2, farRight.longitude, distanceWidth );
// visible radius is / 2 and /1000 in Km:
return distanceWidth[0] / 2 / 1000 ;
}