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));
}
Related
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);
Let's say i have set a walking route on google maps API from point A to point B.
I start at point A (duh). How can i, or is it possible, to update current location on map given the distance walked? (I'm getting distance walked from Google fit SDK).
Example:
From point A to point B is 1.5Km (A route marked on google maps)
I have ran 450m
How do i update map current position based on that?
PS: I can't use GPS as the app i'm making does not show your real current location. It's a false location
ok it is possible but you need to have the bearing of your direction.
for this you need three things -
bearing of your movement from source to destination.
the source latlong.
the distance travelled.
if you have those 3 things then this is how you do it-
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));
}
use my code from above function and put the arguments to function and in return you will get the destination latlng .
let me know if it was helpful to you.
thank you
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'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;
}
Basically the title says it all. Rather than plotting every single entry in my database on a map as I do at the moment, I want to query the database and only plot the entries whose co-ordinates fall within the circle drawn around the users current location, however I can't quite figure out how to do it. At the moment, I have written code that plots the users current location on the map along with the locations of all the entries stored on my database and also the circle around the current location (See picture below). Based on my picture below, I only want the three markers within the circle to be plotted.
Does anyone know if there are any ways of checking if the latlng co-ordinates stored in my database fall within the area of the circle? Or if not, could anyone suggest any alternatives that use a similar idea.
An alternative I was thinking of was using a square / rectangle rather than a circle so that I could simply compare the co-ordinates of my entries with bounds of the square / rectangle, however I don't really know how feasible that is seen as those shapes aren't supported by the Google Maps API. I also came across the LatLngBounds class which could be useful but I can't find any sample code using it. How I might do this either?
I believe the circle has a fixed Radius and a center point.
So, go with this method to get the distance between the center and some LatLng and set a condition
distance <= radius
public static String getDistance(LatLng ll_source, LatLng ll_destination,
int unit) {
int Radius = 6371;// radius of earth in Km
double lat1 = ll_source.latitude;
double lat2 = ll_destination.latitude;
double lon1 = ll_source.longitude;
double lon2 = ll_destination.longitude;
double dLat = Math.toRadians(lat2 - lat1);
double dLon = Math.toRadians(lon2 - lon1);
double a = Math.sin(dLat / 2) * Math.sin(dLat / 2)
+ Math.cos(Math.toRadians(lat1))
* Math.cos(Math.toRadians(lat2)) * Math.sin(dLon / 2)
* Math.sin(dLon / 2);
double c = 2 * Math.asin(Math.sqrt(a));
double valueResult = Radius * c;
double km = valueResult / 1;
DecimalFormat newFormat = new DecimalFormat("####");
Integer kmInDec = Integer.valueOf(newFormat.format(km));
double meter = valueResult % 1000;
Integer meterInDec = Integer.valueOf(newFormat.format(meter));
DecimalFormat df = new DecimalFormat("#.#");
return df.format(valueResult);
}
Ok, I've figured out a solution using the LatLngBounds class I referred to in my question. What it does is:
Creates a new rectangular perimeter around the user's current latitude and longitude co-ordinates (this example is roughly one square kilometer).
It then checks if the perimeter contains the co-ordinates stored in the database.
If it does, the marker is plotted on the map.
public void getNearbyMarkers(){
LatLngBounds perimeter = new LatLngBounds(new LatLng(currentLat - 0.004,
currentLon - 0.004), new LatLng(currentLat + 0.004, currentLon + 0.004));
if (perimeter.contains(LatlongFromDatabase)) {
//Plot Marker on Map
} else {
Toast.makeText(getApplicationContext(), "Co-ordinates not in perimeter!", 8).show();
}
}