I am trying to generate random points on the map near a given location. I have a circle shape which surrounds the user location with a radius of 100, and I would like to generate random LatLng coordinates within this circle area. I have come up with the following function so far, but the point markers are still appearing outside the circle range.
double lat = location.getLatitude();
double lon = location.getLongitude();
for (int i = 0; i < markers.size(); i++) {
Marker mrk = markers.get(i);
Random random = new Random();
double radiusInDegrees =mCircle.getRadius();
double u = random.nextDouble();
double v = random.nextDouble();
double w = radiusInDegrees * Math.sqrt(u);
double t = 2 * Math.PI * v;
double x = w * Math.cos(t);
double y = w * Math.sin(t);
// Adjust the x-coordinate for the shrinking of the east-west distances
double new_x = x / Math.cos(lat);
double newLongitude = new_x + lon;
double newLatitude = y + lat;
mrk.setPosition(new LatLng(newLatitude,newLongitude));
}
with the help of this
https://gis.stackexchange.com/a/68275
I am able to make a function which generate random LatLng point within certain radius, where radius is in meter.
public LatLng getRandomLocation(LatLng point, int radius) {
List<LatLng> randomPoints = new ArrayList<>();
List<Float> randomDistances = new ArrayList<>();
Location myLocation = new Location("");
myLocation.setLatitude(point.latitude);
myLocation.setLongitude(point.longitude);
//This is to generate 10 random points
for(int i = 0; i<10; i++) {
double x0 = point.latitude;
double y0 = point.longitude;
Random random = new Random();
// Convert radius from meters to degrees
double radiusInDegrees = radius / 111000f;
double u = random.nextDouble();
double v = random.nextDouble();
double w = radiusInDegrees * Math.sqrt(u);
double t = 2 * Math.PI * v;
double x = w * Math.cos(t);
double y = w * Math.sin(t);
// Adjust the x-coordinate for the shrinking of the east-west distances
double new_x = x / Math.cos(y0);
double foundLatitude = new_x + x0;
double foundLongitude = y + y0;
LatLng randomLatLng = new LatLng(foundLatitude, foundLongitude);
randomPoints.add(randomLatLng);
Location l1 = new Location("");
l1.setLatitude(randomLatLng.latitude);
l1.setLongitude(randomLatLng.longitude);
randomDistances.add(l1.distanceTo(myLocation));
}
//Get nearest point to the centre
int indexOfNearestPointToCentre = randomDistances.indexOf(Collections.min(randomDistances));
return randomPoints.get(indexOfNearestPointToCentre);
}
The purpose of for loop is just to ensure to get nearest random point, as I have seen points were getting out of circle as I am increasing the radius. You may remove loop.
This answer should help. It looks like what you have execpt for converting the radiusfrom meters to degrees.
// Convert radius from meters to degrees
double radiusInDegrees = radius / 111000f;
See link here.
Related
I want to integrate this code into mine, but don't know how to get it to work.
I got problem with the return statement and then how to create a mark with randomly generated position?
Can you please tell me how to get the getRandomLocation() method working in creating markers?
public Location getRandomLocation() {
Location location = new Location("");
LatLng latLng = new LatLng(location.getLatitude(), location.getLongitude());
int radius = 10;
double x0 = latLng.latitude;
double y0 = latLng.longitude;
Random random = new Random();
// Convert radius from meters to degrees
double radiusInDegrees = radius / 111000f;
double u = random.nextDouble();
double v = random.nextDouble();
double w = radiusInDegrees * Math.sqrt(u);
double t = 2 * Math.PI * v;
double x = w * Math.cos(t);
double y = w * Math.sin(t);
// Adjust the x-coordinate for the shrinking of the east-west distances
double new_x = x / Math.cos(y0);
double foundLatitude = new_x + x0;
double foundLongitude = y + y0;
LatLng randomLatLng = new LatLng(foundLatitude, foundLongitude);
Location loc = new Location("");
loc.setLatitude(randomLatLng.latitude);
loc.setLongitude(randomLatLng.longitude);
//dont know what to return
return ;
}
public final void addMarker(GoogleMap mMap) {
//dont know how to get working the getRandomLocation())
mMap.addMarker( new MarkerOptions()
// .position(new LatLng(48.349723, 18.052405))
.position(getRandomLocation())
.title("krokodĂl")
.icon(BitmapDescriptorFactory.fromBitmap(resizeMapIcons("krokodil", 100, 100))));
mMap.addMarker(new MarkerOptions()
// .position(new LatLng(48.310025, 18.038878))
.position(getRandomLocation())
.icon(BitmapDescriptorFactory.fromBitmap(resizeMapIcons("fretka", 100, 100)))
.title("fretk"));
mMap.addMarker (new MarkerOptions()
.title("hroch")
.icon(BitmapDescriptorFactory.fromBitmap(resizeMapIcons("hroch", 100, 100)))
// .title()
//.snippet(mObj.getNumber(Configs.MONSTERS_MONSTER_POINTS) + " points")
// .position(new LatLng(48.318569, 18.055767)));
.position(getRandomLocation()));
}
According to the logic of your code, you need to return randomLatLng.
Change the line:
public Location getRandomLocation() {
to:
public LatLng getRandomLocation() {
and make the return statement something like:
return randomLatLng;
The method MarkerOptions.position() needs an object of type LatLng, that is what your IDE showing you as an error.
I have a GoogleMap in my project. It's set in zoom level 21. I want to draw a line that is 5 meter in length with a specific heading. I used this code:
private LatLng drawHeadingOnMap(LatLng centre, double radius, double heading)
{
double EARTH_RADIUS = 6378100.0;
// Convert to radians.
double lat = Math.toRadians(centre.latitude );
double lon = Math.toRadians(centre.longitude);
// y
double latPoint = lat + (radius / EARTH_RADIUS) * Math.sin(Math.toRadians(heading));
// x
double lonPoint = lon + (radius / EARTH_RADIUS) * Math.cos( Math.sin(Math.toRadians(heading)) / Math.cos(lat));
LatLng point =new LatLng(latPoint * 180.0 / Math.PI, lonPoint * 180.0 / Math.PI);
return point;
}
I run it by:
LatLng ll = drawHeadingOnMap(origin, 5, 90);
LatLng lll = drawHeadingOnMap(origin, 5, 0);
googleMap.addPolyline(new PolylineOptions().add(Mabda).add(ll).color(Color.BLUE).width(3));
googleMap.addPolyline(new PolylineOptions().add(Mabda).add(lll).color(Color.BLUE).width(3));
It draw 0 degree very well!! but others are wrong. for example this pic is shown the above code :
When I want to draw 90 degree, It draw sth like this pic! and after 90 , it get back to 0 degree (When I write drawHeadingOnMap(origin, 5, 180), It draw 0 degree!). How can I fix it? I'm so confused !!!...
Updated:
I tried it for origin= (12,12)...
I got this result:
ll.Latitude = 12.000898320495335
ll.Longitude = 12.00046835742835
lll.latitude = 12.0
lll.longitude = 12.000898320495335
ll is result for moving of (12,12) for 1 meter in direction of 90 degree.
lll is result for moving of (12,12) for 1 meter in direction of 0 degree.
the method is just OK for 0 degree ...
If you have a center point (10, 20), and you want to find the other point (x, y) to its 20 degree with radius 5, you can do the following math:
x = 10 + 5 * Math.sin(Math.toRadians(20));
y = 20 + 5 * Math.cos(Math.toRadians(20));
Not sure why you did Math.cos( Math.sin(Math.toRadians(heading)) / Math.cos(lat)) for your lonPoint.
To understand exact math I suggest reading this link.
If a working implementation is all you need use this function (adopted from Maps SphericalUtil):
/**
* #param loc location to transale (creates a copy)
* #param distance in meters
* #param heading in degrees, where 0 is NORTH, clockwise
* #return new location
*/
public static LatLng translate(LatLng loc, double distance, double heading){
double EARTH_RADIUS = 6378100.0;
heading = Math.toRadians(heading);
distance = distance/EARTH_RADIUS;
// http://williams.best.vwh.net/avform.htm#LL
double fromLat = Math.toRadians(loc.latitude);
double fromLng = Math.toRadians(loc.longitude);
double cosDistance = Math.cos(distance);
double sinDistance = Math.sin(distance);
double sinFromLat = Math.sin(fromLat);
double cosFromLat = Math.cos(fromLat);
double sinLat = cosDistance * sinFromLat + sinDistance * cosFromLat * Math.cos(heading);
double dLng = Math.atan2(
sinDistance * cosFromLat * Math.sin(heading),
cosDistance - sinFromLat * sinLat);
return new LatLng(Math.toDegrees(Math.asin(sinLat)), Math.toDegrees(fromLng + dLng));
}
I wish to get the distance to a line and started using haversine code.
private static final double _eQuatorialEarthRadius = 6378.1370D;
private static final double _d2r = (Math.PI / 180D);
private static double PRECISION = 0.001;
// Haversine Algorithm
// source: http://stackoverflow.com/questions/365826/calculate-distance-between-2-gps-coordinates
private static double HaversineInM(double lat1, double long1, double lat2, double long2) {
return (1000D * HaversineInKM(lat1, long1, lat2, long2));
}
private static double HaversineInKM(double lat1, double long1, double lat2, double long2) {
double dlong = (long2 - long1) * _d2r;
double dlat = (lat2 - lat1) * _d2r;
double a = Math.pow(Math.sin(dlat / 2D), 2D) + Math.cos(lat1 * _d2r) * Math.cos(lat2 * _d2r)
* Math.pow(Math.sin(dlong / 2D), 2D);
double c = 2D * Math.atan2(Math.sqrt(a), Math.sqrt(1D - a));
double d = _eQuatorialEarthRadius * c;
return d;
}
// Distance between a point and a line
public static double pointLineDistanceTest(double[] aalatlng,double[] bblatlng,double[] ttlatlng) {
double [] a = aalatlng;
double [] b = bblatlng;
double [] c = ttlatlng;
double[] nearestNode = nearestPointGreatCircle(a, b, c);
// System.out.println("nearest node: " + Double.toString(nearestNode[0]) + "," + Double.toString(nearestNode[1]));
double result = HaversineInM(c[0], c[1], nearestNode[0], nearestNode[1]);
// System.out.println("result: " + Double.toString(result));
return (result);
}
// source: http://stackoverflow.com/questions/1299567/how-to-calculate-distance-from-a-point-to-a-line-segment-on-a-sphere
private static double[] nearestPointGreatCircle(double[] a, double[] b, double c[])
{
double[] a_ = toCartsian(a);
double[] b_ = toCartsian(b);
double[] c_ = toCartsian(c);
double[] G = vectorProduct(a_, b_);
double[] F = vectorProduct(c_, G);
double[] t = vectorProduct(G, F);
return fromCartsian(multiplyByScalar(normalize(t), _eQuatorialEarthRadius));
}
#SuppressWarnings("unused")
private static double[] nearestPointSegment (double[] a, double[] b, double[] c)
{
double[] t= nearestPointGreatCircle(a,b,c);
if (onSegment(a,b,t))
return t;
return (HaversineInKM(a[0], a[1], c[0], c[1]) < HaversineInKM(b[0], b[1], c[0], c[1])) ? a : b;
}
private static boolean onSegment (double[] a, double[] b, double[] t)
{
// should be return distance(a,t)+distance(b,t)==distance(a,b),
// but due to rounding errors, we use:
return Math.abs(HaversineInKM(a[0], a[1], b[0], b[1])-HaversineInKM(a[0], a[1], t[0], t[1])-HaversineInKM(b[0], b[1], t[0], t[1])) < PRECISION;
}
// source: http://stackoverflow.com/questions/1185408/converting-from-longitude-latitude-to-cartesian-coordinates
private static double[] toCartsian(double[] coord) {
double[] result = new double[3];
result[0] = _eQuatorialEarthRadius * Math.cos(Math.toRadians(coord[0])) * Math.cos(Math.toRadians(coord[1]));
result[1] = _eQuatorialEarthRadius * Math.cos(Math.toRadians(coord[0])) * Math.sin(Math.toRadians(coord[1]));
result[2] = _eQuatorialEarthRadius * Math.sin(Math.toRadians(coord[0]));
return result;
}
private static double[] fromCartsian(double[] coord){
double[] result = new double[2];
result[0] = Math.toDegrees(Math.asin(coord[2] / _eQuatorialEarthRadius));
result[1] = Math.toDegrees(Math.atan2(coord[1], coord[0]));
return result;
}
// Basic functions
private static double[] vectorProduct (double[] a, double[] b){
double[] result = new double[3];
result[0] = a[1] * b[2] - a[2] * b[1];
result[1] = a[2] * b[0] - a[0] * b[2];
result[2] = a[0] * b[1] - a[1] * b[0];
return result;
}
private static double[] normalize(double[] t) {
double length = Math.sqrt((t[0] * t[0]) + (t[1] * t[1]) + (t[2] * t[2]));
double[] result = new double[3];
result[0] = t[0]/length;
result[1] = t[1]/length;
result[2] = t[2]/length;
return result;
}
private static double[] multiplyByScalar(double[] normalize, double k) {
double[] result = new double[3];
result[0] = normalize[0]*k;
result[1] = normalize[1]*k;
result[2] = normalize[2]*k;
return result;
}
and had many errors so wrote this to calculate using bearings to get the angle then using distance (point a, target) to calculate the target to (point a, point b) line. Point A and B are the line and I want the distance from target to line. No great circle.
sin angle (difference in bearings) *point a to target distance= Length of the side of a right angle triangle from the right angle on AB line to target.
public double pointlinedistancetest(){
//set latlng location point a
Location apoint=new Location("");
apoint.setLatitude(lata);
apoint.setLongitude(lona);
//set latlng location point b
Location bpoint=new Location("");
bpoint.setLatitude(latb);
bpoint.setLongitude(lonb);
//set latlng location target to get dis to line
Location tpoint=new Location("");
tpoint.setLatitude(lat);
tpoint.setLongitude(lon);
float pbearingf = apoint.bearingTo(bpoint);
float tbearingf= apoint.bearingTo(tpoint);
double tb=tbearingf;
double ab=pbearingf;
//get angle degree difference
float angle= Math.min((pbearingf-tbearingf)<0?pbearingf-tbearingf+360:pbearingf-tbearingf, (tbearingf-pbearingf)<0?tbearingf-pbearingf+360:tbearingf-pbearingf);
// float angle= Math.min((tbearingf-pbearingf)<0?tbearingf-pbearingf+360:tbearingf-pbearingf, (pbearingf-tbearingf)<0?pbearingf-tbearingf+360:pbearingf-tbearingf);
// min((a1-a2)<0?a1-a2+360:a1-a2, (a2-a1)<0?a2-a1+360:a2-a1)
double aabearing=angle;
float atot=apoint.distanceTo(tpoint);
double atotdis=atot;
//right angle triangle formula
dis=(Math.sin(aabearing))*atotdis;
return (dis);
}
Now both are still showing massive errors of up to 30%. Does this code look correct and is there any better way of doing this or are my errors else where. My GPS is showing 4 metre accuracy and my formulas(code) is showing errors of 10 to 20 metres and does not seem to be remotely the same.
You can use Google Geometry library to find length of line and its bearing .
From documentation
computeDistanceBetween(from:LatLng, to:LatLng, radius?:number) Returns
the distance between two LatLngs as a number in meters. The radius
defaults to the Earth's radius in meters(6378137).
computeHeading(from:LatLng, to:LatLng) Returns the heading from one
LatLng to another LatLng. Headings are expressed in degrees clockwise
from North within the range [-180,180) as a number.
computeOffset(from:LatLng, distance:number, heading:number,
radius?:number) Returns the LatLng resulting from moving a distance
from an origin in the specified heading (expressed in degrees
clockwise from north) as LatLng.
First find distance between start and end of line
var spherical = google.maps.geometry.spherical;
var length = google.maps.geometry.spherical.computeDistanceBetween(A,B);
Then find bearing of line
var heading = google.maps.geometry.spherical.computeHeading(A,B);
From image 120 degrees
Then find distance and bearing between start of line A and point C
var length2 = google.maps.geometry.spherical.computeDistanceBetween(A,C);
var heading2 = google.maps.geometry.spherical.computeHeading(A,C);
From image 50 degrees
var angleBAC = heading1-heading2
Angle BAC = 120 - 50 degrees
= 70 degrees
As you now know angle BAC and distance from A to C you can use triginometry to find length CD
sineBAC = opp/Hypotenuese
var CD = Math.sin(angleBAC) * length2;
From Image
opp = sine 70 degrees X length AC
From image 0.9397 X 5 = 4.6984
I have a DB with Geopoints.
I need to do a query to get all geopoints in the radius of X meters of me.
How can i do this?
I think the best way is get the minimal lat/long possible point, and the max lat/long point and get all of them for which: geopoint > minPoint AND geopoint < MaxPoint
Other ideas?
You can use this class to get de distance between to points:
How to use:
double distInKm = GeoMath.getDistance(12.345, -8.788, 12.33, -8.77);
Or, if point1 and point2 are GeoPoint:
double distInKm = GeoMath.getDistance(point1, point2);
You can also calculate a Geopoint that is a distance of you and along a bearing.
This computes a point that is 5 km northward from point1:
GeoPoint northPointAt5 = GeoMath.getGeoPointAlongBearing(point1, 0, 5);
You can calculate the other points at 90 degrees, 180 degrees and 270 degrees to calculate minPoint AND MaxPoint.
GeoMath class:
public class GeoMath {
public static final int EARTH_MEAN_RADIUS = 6371; // earth's mean radius in Km
public static double getDistance(double startLatitude, double startLongitude,
double endLatitude, double endLongitude){
return distHaversine(startLatitude,startLongitude,endLatitude,endLongitude);
}
public static double getDistance(GeoPoint point1, GeoPoint point2){
return distHaversine(point1.getLatitudeE6()/1E6, point1.getLongitudeE6()/1E6,
point2.getLatitudeE6()/1E6, point2.getLongitudeE6()/1E6);
}
private static double getSpanInRadians(double max, double min){
return Math.toRadians(max - min);
}
//Distance in Km between point1 (lat1,lon1) and point2 (lat2,lon2) Haversine formula
private static double distHaversine(double lat1, double lon1, double lat2, double lon2) {
double dLat = getSpanInRadians(lat2,lat1);
double dLon = getSpanInRadians(lon2,lon1);
lat1 = Math.toRadians(lat1);
lat2 = Math.toRadians(lat2);
double a = Math.sin(dLat/2) * Math.sin(dLat/2) +
Math.cos(lat1) * Math.cos(lat2) * Math.sin(dLon/2) * Math.sin(dLon/2);
double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
double dist = EARTH_MEAN_RADIUS * c;
return Math.round(dist * 1000)/1000; //3 decimal places
}
// Get GeoPoint at distance along a bearing
// bearing in degrees
// distance in Km
public static GeoPoint getGeoPointAlongBearing(GeoPoint location, double bearing, double distance){
double PI = Math.PI;
double NM = 1.852; //1 nm = 1.852 Km -> nm = Km/NM
GeoPoint geoPointAlongBearing;
double locationLatRad = Math.toRadians(location.getLatitudeE6()/1E6);
double locationLongRad = Math.toRadians(location.getLongitudeE6()/1E6)*(-1.0d);
double distanceRad = distance/NM * PI/(180*60);
double bearingRad = Math.toRadians(bearing);
double latAlongBearingRad = Math.asin(Math.sin(locationLatRad) *
Math.cos(distanceRad) +
Math.cos(locationLatRad) *
Math.sin(distanceRad) *
Math.cos(bearingRad));
double lonAlongBearingRad = mod(locationLongRad -
Math.asin(Math.sin(bearingRad) *
Math.sin(distanceRad) /
Math.cos(latAlongBearingRad)) + PI, 2 * PI) - PI;
double latAlongBearing = rad2lat(latAlongBearingRad);
double lonAlongBearing = rad2lng(lonAlongBearingRad) * (-1);
geoPointAlongBearing = new GeoPoint((int)(latAlongBearing*1E6),(int)(lonAlongBearing*1E6));
return geoPointAlongBearing;
}
private static double mod(double y, double x) {
return y - x * Math.floor(y/x);
}
}
With your query I think that you will find all points inside a square centered at your location and with a side length X.
After that, yuo can get all the points that are inside a circle centered at your location with radius X.
What about this pseudo code:
//create your location
Location yourLocation=new Location("myLoc");
double latitude = geoPointYourLocation.getLatitudeE6() / 1E6;
double longitude = geoPointYourLocation.getLongitudeE6() / 1E6;
yourLocation.setLatitude(latitude);
yourLocation.setLongitude(longitude);
//Browse geopoints from DB, convert to GeoPoint and check if it is at a distance less than X
for (geoPointTemp in in query geopoint of your DDBB inside square) {
//create the location of the geopoint
Location locTemp=new Location("locTemp");
double latitude = geoPointTemp.getLatitudeE6() / 1E6;
double longitude = geoPointTemp.getLongitudeE6() / 1E6;
locTemp.setLatitude(latitude);
locTemp.setLongitude(longitude);
//calculate the distance between you and de temporary location
double distance=yourLocation.distanceTo(locTemp);
if(distance<X){
//do something
}
With Mysql, you can use built-in spacial functions such as GLength, linestring ....
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;
}