I want to know how much meters is a certain pixel distance, at a given zoom level.
Reason: I want to know the radius, in meters, of a circle in the mapView, which fits perfectly in the mapView -> radiusPixels = mapView.getWidth()/2;
I found the method mapView.getProjection().metersToEquatorPixels(radiusMeters), which does the opposite of that what I need. But there's no inverse for this method or anything else useful.
My (probably naive) approach to solve it is as follows:
private double getFittingRadiusInMeters() {
return getMeters(mapView.getWidth() / 2);
}
private double getMeters(int pixels) {
Projection proj = mapView.getProjection();
Point mapCenterPixels = new Point(mapView.getWidth() / 2, mapView.getHeight() / 2);
//create 2 geopoints which are at pixels distance
GeoPoint centerGeoPoint = proj.fromPixels(mapCenterPixels.x, mapCenterPixels.y);
GeoPoint otherGeoPoint = proj.fromPixels(mapCenterPixels.x + pixels, mapCenterPixels.y);
Location loc = new Location("");
loc.setLatitude(centerGeoPoint.getLatitudeE6() / 1E6);
loc.setLongitude(centerGeoPoint.getLongitudeE6() / 1E6);
Location loc2 = new Location("");
loc2.setLatitude(otherGeoPoint.getLatitudeE6() / 1E6);
loc2.setLongitude(otherGeoPoint.getLongitudeE6() / 1E6);
return loc.distanceTo(loc2);
}
But it doesn't work well. I always get circles which are far smaller than the mapView - the radius is too small.
I know the distanceTo method says "approximate" but the radius differ significantly from the expected size. Should not be an effect of the approximation.
Thanks.
There is a small mistake in your approach.
You are calculating the value for screen half with at screen center level. The distance found is only valid to draw a circle at the same Latitude value (the Longitude may change without problem).
Because earth is shperic, the distance for the same number of pixels calculated at different Latitude levels, produces different results. Moving from Equador level to position closer to Pole level, same number of pixels result a in smaller distance in meters.
However, this will only be noticable if you call getFittingRadiusInMeters() with map positioned in a very distant Latitude from where you draw the circle.
Otherwise, it should work fine.
Solution
The method getMeters() should receive as parameter a GeoPoint (or at least the Latitude) that should be used to calculate the distance.
Regards.
Related
I have a collection of items and some of them may have the same coordinates.
As a result they are displayed as 1 marker in Google Maps since the markers are painted one on top of each other.
To address this I tried to "move" those markers by a few meters so that the markers do not collide.
I would like to move them to a 5 meters from where their location is.
I did the following following another SO answer:
double newLat = item.getLatitude() + (Math.random() - .005) / 15000;
double newLon = item.getLongitude() + (Math.random() - .005) / 15000;
The problem is that this moves the items a bit but it seems that some are moved by 4m others by 3m and I would like if possible to ensure that I will be between 4-6 meters (min/max)
How can I change my formula to do this?
I think that the best option could be using the SphericalUtil. computeOffset method from the Google Maps Android API Utility Library:
computeOffset
public static LatLng computeOffset(LatLng from,
double distance,
double heading)
Returns the LatLng resulting from moving a distance from an origin in the specified heading (expressed in degrees clockwise from north).
Parameters:
from - The LatLng from which to start.
distance - The distance to travel.
heading - The heading in degrees clockwise from north.
In your case you can set the distance to be 5 meters and randomise the heading parameter to be something between 0 and 360 degrees:
Random r = new Random();
int randomHeading = r.nextInt(360);
LatLng newLatLng = SphericalUtil.computeOffset(oldLatLng, 5, randomHeading);
I want to perform a zoom on a region in google map
map.animateCamera(CameraUpdateFactory.newLatLngBounds(new LatLngBounds(southwest, northeast), padding)
the problem is that i dont have: southwest and northeast points
I just have the center of the map (LatLnt) and the distance in meters between southwest and northeast,
I'd like to know how to calculate southwest and northeast points from map_center and the distance?
any help is welcome
thank you in advance.
This answer assumes that you are using a square map. In other words that the angle from the starting point is 45 degrees (also assuming that the map is flat which means the distances are small). I am also assuming that you are not at either of the poles (yeah I know duh!).
Ok with that in mind it is simple geometry. Since you know the start point and the length from the Southwest corner to the Northeast corner I use half this distance as that is the distance from the starting point to each of these points. I am then finding the x and y component (which is the same with a square map) of this "half distance". To do this I just use the formula latlongOffset equals half of the "half distance" times the square root of 2.
the code is as follows (you provide a LatLng startPoint, Double someDistance, and int padding):
LatLng southwest, northeast;
Double swLat, neLat, swLng, neLng;
Double halfSomeDistance = someDistance / 2.0;
Double latlongOffset = halfSomeDistance * Math.sqrt(2.0) / 2.0;
swLat = startPoint.latitude - latlongOffset;
swLng = startPoint.longitude - latlongOffset;
neLat = startPoint.latitude + latlongOffset;
neLng = startPoint.longitude + latlongOffset;
southwest = new LatLng(swLat, swLng);
northeast = new LatLng(neLat, neLng);
map.animateCamera(CameraUpdateFactory.newLatLngBounds(new LatLngBounds(southwest, northeast), padding));
If you are not using a square map you will need to use right triangle geometry to get the latOffset and lngOffset as they will not be the same.
I hope this helps.
I'm making an Android application which uses Google Maps API, and I want to scale a MapView to X_pixels:X_meters.
For example, 5 pixels of the MapView in my screen, 20 meters on reality.
Is that possible?
Thx
Use the following code:
int nPixles = 5; //number of pixels
GeoPoint g0 = mapview.getProjection().fromPixels(0, mapview.getHeight()/2);
GeoPoint g1 = mapview.getProjection().fromPixels(nPixles, mapview.getHeight()/2);
float[] results = new float[1];
Location.distanceBetween(g0.getLatitudeE6()/1E6, g0.getLongitudeE6()/1E6, g1.getLatitudeE6(), g1.getLongitudeE6()/1E6, results);
float distanceInMeters = results[0];
This calculates distance in meters for latitude level at screen center. Because earth is spheric distance vary from bottom to the top of screen. This is mostly noticed with low zoom levels.
Regards.
I have an application of augmented reality in which I have stored information such us metro, gas stations, places of interest, etc. with the corresponding latitude and longitude.
Now, according to the orientation of the device, I would show a marker for each site in the camera view of the device. Similar to Layar and Wikitude.
It takes three days searching without stopping and have not found anyone to explain how to solve this problem.
Since information on this topic is very sparse, and I recently solved this problem on the iPhone, I thought I would share my method for anyone that can make it work with Android (there's nothing really specific to iPhone in this answer except for the Math functions sin, cos, and fmod, which can be found in java.lang.Math). These are the steps I took:
Obtain your own lat/lon and your current compass heading (lat1, lon1 and heading). On the iPhone, CLLocation returns these in degrees, but for these calculations they MUST be in radians (i.e. multiply by PI/180)
Obtain lat/lon of Points of Interest (POI) in radians (lat2 and lon2).
Calculate the distance between lat1/lon1 and lat2/lon2 using formula found here: http://www.movable-type.co.uk/scripts/latlong.html
Calculate angle to lat2/lon2 in relation to north. This is also described in the link above but I had a little bit of trouble getting this to work, here is C code for this:
double latDelta = (lat2 - lat1);
double lonDelta = (lon2 - lon1);
double y = sin(lonDelta) * cos(lat2);
double x = cos(lat1) * sin(lat2) - sin(lat1) * cos(lat2)* cos(lonDelta);
double angle = atan2(y, x); //not finished here yet
double headingDeg = compass.currentHeading;
double angleDeg = angle * 180/PI;
double heading = headingDeg*PI/180;
angle = fmod(angleDeg + 360, 360) * PI/180; //normalize to 0 to 360 (instead of -180 to 180), then convert back to radians
angleDeg = angle * 180/PI;
Using standard trigonometry, I calculate x and y. Remember, these coordinates are in 3D space, so we are not finished here yet because you still have to map them to 2D:
x = sin(angle-heading) * distance;
z = cos(angle-heading) * distance; //typically, z faces into the screen, but in our 2D map, it is a y-coordinate, as if you are looking from the bottom down on the world, like Google Maps
Finally, using the projection formula, you can calculate screen x ( I didn't do y because it was not necessary for my project, but you would need to get accelerator data and figure out if the device is perpendicular to the ground). The projection formula is found here (scroll to the very bottom): http://membres.multimania.fr/amycoders/tutorials/3dbasics.html
double screenX = (x * 256) / z
Now you can use this x coordinate to move an image or a marker on your screen. Remember a few points:
Everything must be in radians
The angle from you to the POI relative to North is angleBeteweenPoints - currentHeading
(For some reason I can't properly format the code on this computer, so if anyone wants to edit this answer, feel free).
I have a MapView that I'm displaying a "useful radius" (think accuracy of coordinate) in. Using MapView's Projection's metersToEquatorPixels, which is admittedly just for equatorial distance) isn't giving me an accurate enough distance (in pixels). How would you compute this if you wanted to display a circle around your coordinate, given radius?
So, Google Maps uses a Mercator projection. This means that the further you get from the equator the more distorted the distances become. According to this discussion, the proper way to convert for distances is something like:
public static int metersToRadius(float meters, MapView map, double latitude) {
return (int) (map.getProjection().metersToEquatorPixels(meters) * (1/ Math.cos(Math.toRadians(latitude))));
}
mMap.addCircle(
new CircleOptions().center(
new LatLng(
bounds.getCenter().latitude,
bounds.getCenter().longitude
)
)
.radius(50000)
.strokeWidth(0f)
.fillColor(0x550000FF)
);
projection.toPixels(GeoP, pt);
float radius = projection.metersToEquatorPixels(50);
Try this and see... i used it on my MapRadius and it seems to be working