Geo Coordinates (Long,Lat) to Meters (x,y) - android

I am working coordinates system as shown below:
x and y are in meters. I am interested in only positive x,y. I want to convert (x,y) to (lat,lon) and vise versa.
I thought it is simple and I dont see a problem with my solutions below. But I am not getting very correct results.
My Solution:
As we see in the image below, I considered the Latitude and Longitude as angles of 2 circles:
1. (x,y) to (lat,lon)
I applied the Arc Length formula (here), shown below, on both; x and y
Hence, my functions are:
private static double calculateLat(float x) {
int earthRadius = 6371000;
return REF_LOC.getLatitude() + x*360/(2*PI*earthRadius);
}
private static double calculateLong(float y) {
int earthRadius = 6371000;
return REF_LOC.getLongitude() + y*360/(2*PI*earthRadius);
}
REF_LOC is the reference Geo Location for which (x,y) are (0,0). It can be any point on earth.
2. (lat,lon) to (x,y)
For this I am simply using this:
int calculateX(double longitude){
Location.distanceBetween(REF_LOC.getLatitude(), REF_LOC.getLongitude(),
REF_LOC.getLatitude(), lonDeg, results);
return results[0];
}
int calculateY(double latitude){
Location.distanceBetween(REF_LOC.getLatitude(), REF_LOC.getLongitude(),
latDeg, REF_LOC.getLongitude(), results);
return results[0];
}
But I am getting inconsistent results. First I use solution 1 and convert some value (x,y) to (lat,long). But when I use the same (lat,long) back to (x,y) using solution 2, I get about 2 meters difference in x and 10 meters in y. Can anyone help me identify the problem please?

Alluding to my comment on spherical vs elliptical calculations, another way to look at difference in distance calculations for spherical vs elliptical distance is to use the 2 available utilities:
// For REF_LOC = (70, 20)
// Compute a lat/lng due east of a REF_LOC and compute distance using both
// available methods.
LatLng P = SphericalUtil.computeOffsetOrigin(REF_LOC, 20000, 90.0);
// And then compute a distance
d = SphericalUtil.computeDistanceBetween(REF_LOC, P);
Location.distanceBetween(REF_LOC.latitude, REF_LOC.longitude, P.latitude, P.longitude, results);
// d = 20000.000000000036
// results[0] = 20081.818
// and for a REF_LOC = (0, 20)
// d = 20000.000000000127
// results[0] = 20022.377
What's also interesting is the SphericalUtil.computeOffsetOrigin() produces error
in latitude increasing from equator to pole making it non-symmetrical. Yet the resulting distances are essentially exact.
I'd recommend using SphericalUtil.computeOffsetOrigin to compute the X/Y breaking
it into the latitude and longitude offsets as you are doing.
Finally demonstrating the SphericalUtil solution:
// Start with some arbitrary x/y relative to REF_LOC
double Rx = 125.0;
double Ry = 73.0;
// Compute a lat/lon from REF_LOC to the point
LatLng Rll = new LatLng(SphericalUtil.computeOffsetOrigin(REF_LOC, Ry, 180).latitude,
SphericalUtil.computeOffsetOrigin(REF_LOC, Rx, 270).longitude);
// And recompute the x/y components of the lat/lon
double Rxx = SphericalUtil.computeDistanceBetween(REF_LOC, new LatLng(REF_LOC.latitude, Rll.longitude));
double Ryy = SphericalUtil.computeDistanceBetween(REF_LOC, new LatLng(Rll.latitude, REF_LOC.longitude));
Resulting in:
Rx/Ry = (125.0, 73.0)
Rxx/Ryy = (125.00000004545973, 73.00000000137051)
Acceptable error I assume.
So the parting question is - what does the x/y really represent?
Reference the source for both utilities for more information:
Location
SphericalUtil

Related

Drawing a circle on google maps static link

i'm trying to draw a circle on a static map by feeding getCircleAsPolyline with Location data and then encode with PolyUtil.encode, according to this SO answer https://stackoverflow.com/a/38100481/1520234.
unfortunately the output is not a circle but something strange like this:
can anybody kindly explain me why this happen and if it's possible to get a real circle and if so how?
thanks
EDIT
i apologize for not being very clear, i forgot to mention that actually my getCircleAsPolyline is slightly different from the one i linked since i used SphericalUtil.computeOffset to calculate the coordinates; anyway below is my getCircleAsPolyline function:
private static ArrayList<LatLng> getCircleAsPolyline(Location center, float radius) {
ArrayList<LatLng> circlePath = new ArrayList<>();
double step = 5.0;
double centerLatitude = center.getLatitude();
double centerLongitude = center.getLongitude();
for (double angle = 0.0; angle < 360.0; angle += step) {
LatLng circlePoint = SphericalUtil.computeOffset(
new LatLng(centerLatitude, centerLongitude),
radius,
(90.0 - angle + 360.0) % 360.0
);
circlePath.add(circlePoint);
}
return circlePath;
}
but result is the one showed in picture
For those who'll come here having the same problem, well I simply got it avoiding to encode the polyline: infact as long as I drop PolyUtil.encode and append points as | lat, long | I've been able to draw a perfect circle.

How can i check if location is in my radius?

i trying to get if location is in my radius.
i.e I have my current location "LatLng" object and i have one more "LatLng" object and i want to check if the two object are in rang of 1km?
How can i implement that?
In Location.distanceBetween() function provide you distance in meters and float value ..
distanceBetween(double startLatitude, double startLongitude, double
endLatitude, double endLongitude, float[] results) Computes the
approximate distance in meters between two locations, and optionally
the initial and final bearings of the shortest path between them.
use this it working i've already checked it .....
float[] dist = new float[1];
Location.distanceBetween(firstLoaction.latitude,firstLoaction.longitude,anotherLocation.latitude,anotherLocation.longitude,dist);
if(dist[0]/1000 > 1){
//here your code or alert box for outside 1Km radius area
}
NOTE:- For getting the location distance always use Location.distanceBetween() which is provide by ANDROID .
double distanceInKiloMeters = (currentLocation.distanceTo(someLocation)) / 1000; // as distance is in meter
if(distanceInKiloMeters <= 1) {
// It is in range of 1 km
}
else {
// not in range of 1 km
}
you can try converting LatLng to Location object for both first and then using distanceTo method to find the distance between those two and check if it is 1km or not
distanceto methode to get distance from locCenter and point and just substitute this distance from the radius if <0 so the point out of range , else the point in border or inside the range.. good luck

Google Maps Android API: Draw polygon based on pixels known from GroundOverlay's PNG

I'm adding a PNG file as an own floorplan on top of Google Maps with the Google Maps Android API with the following code:
GroundOverlayOptions groundOverlayOptions = new GroundOverlayOptions();
BitmapDescriptor bitmapDescriptor = BitmapDescriptorFactory.fromAsset("building-d.png");
groundOverlayOptions.image(bitmapDescriptor);
groundOverlayOptions.anchor(0, 1);
LatLng buildingSW = new LatLng(47.014815, 8.305098);
LatLng buildingNE = new LatLng(47.015148, 8.305440);
LatLng buildingNW = new LatLng(47.015168, 8.305144);
LatLng buildingSE = new LatLng(47.014792, 8.305385);
Location swLoc = locationFromLatLng(buildingSW);
Location seLoc = locationFromLatLng(buildingSE);
Location nwLoc = locationFromLatLng(buildingNW);
Location neLoc = locationFromLatLng(buildingNE);
float angle = swLoc.bearingTo(nwLoc);
groundOverlayOptions.bearing(angle);
float width = swLoc.distanceTo(seLoc);
groundOverlayOptions.position(buildingSW, width);
mMap.addGroundOverlay(groundOverlayOptions);
Now I know that in the PNG there is a room at pixel coordinates 422/301, 708/301, 422/10 and 708/10 (those are the corners). I'd like to draw a polygon over the GroundOverlay covering that room. How should I do that? Do I need to convert my pixel-coordinates to LatLng and if so, how?
And by the way: Do I really have to use PNGs for GroundOverlays and is there no other supported vector-format like eps, pdf, ...?
Having seen your comment to the other answer, let me complete with some code:
Having set the "origin" in latlng 47.014816, 8.305098, you have to convert those coordinates to mercator and you can do something similar to the below:
public boolean initializeByTwoCouplesOfCooordsAndScale(double[] coordAreal, double[] coordBreal, double[] coordAvirtual, double[] coordBvirtual, double scalingFactor) {
if (coordAreal[0] == coordBreal[0] && coordAvirtual[1] == coordBvirtual[1] && coordAreal[1] == coordBreal[1] && coordAvirtual[0] == coordBvirtual[0]) {
System.err.println("Coordinates must not be the same!");
return false;
}
// aPoint is considered the "origin" point (0,0)
aPoint = coordAreal;
bPoint = coordAvirtual;
// now calculate the angle of the Real world coordinate for the points
double deltaRy = coordBreal[1] - coordAreal[1];
double deltaRx = coordBreal[0] - coordAreal[0];
double aR = Math.atan2(deltaRy, deltaRx);
// Now calculate the angle of the virtual world coordinates
double deltaVy = coordBvirtual[1] - coordAvirtual[1];
double deltaVx = coordBvirtual[0] - coordAvirtual[0];
double aV = Math.atan2(deltaVy, deltaVx);
// Set the transformation angle as the difference between the real and the virtual angles.
mPhi= (aR - aV);
// Set the scaling factor as the provided one
mScale = (scalingFactor);//scaling factor is in function below
// Calculate the scaling factor error correction using the distances of the two systems.
return true;
}
public static double getScalingFactor(double latitude) {
return 1 / (Math.cos(Math.toRadians(latitude)));
}
So you can call the method:
initializeByTwoCouplesOfCooordsAndScale(new double[]{MERCATOR_LNG,MERCATOR_LAT},//real coordinates for point A REMEMBER: LNG,LAT = x,y!
new double[]{0d,0d}, //Virual coordinates for point A
new double[]{MERCATOR_POINT_B_LNG, MERCATOR_POINT_B_LAT},//real point B
new double[]{X_METERS,Y_METERS},//coordinates in meters of point B in virtual map
getScalingFactor(47.014816));
then you can transform with this function:
public double[] transform(double[] coord) {
double[] transCoord = new double[2];
double xscaled = (coord[0] - bPoint[0]) * mScale; // XXX bPoint is the position of origin point in the "VIRTUAL" world. [0] is the x coordinate
double yscaled = (coord[1] - bPoint[1]) * mScale;
transCoord[0] = (xscaled * Math.cos(mPhi)) - (yscaled * Math.sin(mPhi)) + aPoint[0]; //aPoint is the point with real coordinates of origin!
transCoord[1] = (xscaled * Math.sin(mPhi)) + (yscaled * Math.cos(mPhi)) + aPoint[1];
return transCoord;
}
you can find online a way to convert latlng to mercator, it just a bunch of math ;)
You should work in this way:
Your indoor map positions should be relative to a specific point (BOTTOM-LEFT is 0,0 let's say), then all the other positions will be relative to that point in meters, so you will endup in values under 100meters usually.
Having this you have to "move, rotate and scale" the indoor map with respect to the world.
Just take a map on a desktop which is not LAT/LNG and find the coordinates for the same indoor points you have (usually we get real and indoor position for bottom-left and top-right positions) so you can find where it should be in the world.
Take a look also at the scaling factor (depending on the latitude, the map must be scaled)
https://en.wikipedia.org/wiki/Mercator_projection#Scale_factor
We calculate that value by doing something like 1/cos(latitudeINradians)
public static double getScalingFactor(double latitude) {
return 1 / (Math.cos(Math.toRadians(latitude)));
}
Let me know if you can find a way, otherwise i will search and try to strip our code

GPS accuracy circle (in meters) intersects a line on map

I'm drawing a point which reflects my current location. I've got some shapes on the map as well. However, due to changing GPS accuracy I need to show that I've crossed the line on map even if a LatLng point is still before it but the accuracy is ~20(m).
What I have:
- line's start and end points
- circle's center
- circle's radius (in meters)
I'm able to calculate the distance between line and circle's center point but that gives me the value like: 0.00987506668990474 which gives me really nothing because this value does not reflect the distance in meters and I can't really convert that to meters and compare with accuracy radius.
I'm not even sure if I'm on good target to get that information. Maybe there's some another method to get the information if there's an intersection or not.
thanks for any hints
update:
using the distance * 110km I'm getting much better results.
Happy face = intersection not detected - distance < radius, Sad face = intersection detected - distance > radius.
Yellow zone edge is mine line segment. As you can see it works when the intersection is on the top(/bottom), but not on the left(/right) side.
That's my calculation algorithm calculating distance from line segment to point. Forgive the mess... I'm still struggling with that, so it's not optimized yet:
public static double pointLineSegmentDistance(final List<Double> point, final List<List<Double>> line)
{
List<Double> v = line.get(0);
List<Double> w = line.get(1);
double d = pointPointSquaredDistance(v, w);
double t;
List<Double> calculateThis;
if (d > 0)
{
boolean test = (t = ((point.get(0) - v.get(0)) * (w.get(0) - v.get(0)) + (point.get(1) - v.get(1)) * (w.get(1) - v.get(1))) / d) < 0;
if (test)
{
calculateThis = v;
}
else
{
if (t > 1)
{
calculateThis = w;
}
else
{
calculateThis = new ArrayList<Double>();
calculateThis.add(v.get(0) + t * (w.get(0) - v.get(0)));
calculateThis.add(v.get(1) + t * (w.get(1) - v.get(1)));
}
}
}
else
{
calculateThis = v;
}
return Math.sqrt(pointPointSquaredDistance(point, calculateThis));
}
public static double pointPointSquaredDistance(final List<Double> v, final List<Double> w)
{
double dx = v.get(0) - w.get(0);
double dy = v.get(1) - w.get(1);
return dx * dx + dy * dy;
}
final List point - contains 2 elements - (0) latitude (1) longitude
List> line - contains 2 lists - (0) line segment start point (0/0)lat/(0/1)long (1) line segment end point (1/0)lat/(1/1)long
Finally I've solved my problem. It comes the solution is really very simple. Much simpler than I thought. There's maputil library available here:
https://github.com/googlemaps/android-maps-utils
That library privides a function named "isLocationOnPath":
boolean isLocationOnPath(LatLng point, List<LatLng> polyline, boolean geodesic, double tolerance)
Point - is a center or my point.
Polyline - for my needs is an edge of a polygon,
Geodesic - true (of course)
Tolerance - (in meters) is my accuracy taken from GPS accuracy - basicaly is a circle radius.
Details about the method:
http://googlemaps.github.io/android-maps-utils/javadoc/com/google/maps/android/PolyUtil.html#isLocationOnPath-LatLng-java.util.List-boolean-
I hope that helps.
You have two possibilities:
1) The cheap way:
You calcualte the distances between line start/end point and circle center with the android api (distanceBetween()). The result will be in meters, and correctby means of point to point distance calculation. Take the minimum of both start / end point to center circle distances.
If the line segment is very long related to the circle radius, the normal distance of the line to center, using this kind of caluclation is wrong
2) The better way, but more complex:
You transform the line start and end and the circle center to cartesian x,y space with unit = meter and calculate the distance to the line
segment using cartesian math. (see distance to line segment calculation)
First check option1 because it is just one line of code.
Your result of 0.00987506668990474 this looks like a distance in degrees, instead of meters. multiply it with 111km gives a value of about 1km. (at equator)

Moving GPS coordinate

I am fairly new to Android programming, but I am getting pretty good at it (I think: ))
What I am doing is building a situated stories app. It is an app that places audio files at certain GPS markers and enables the user to listen to them at specific locations.
The next step is moving audio files. What I want to do is set a marker at a specific position in a city. (done). Next I want to check the location of a second marker that moves in a circle around it.
What I have so far is this:
public void checkCircularPosition(){
/*
* could be a solution?
*
radius = 250; //offset in meters
gpsLatCenter = 5.1164; //?how can i make this accurate in meters?
gpsLonCenter = 52.0963; //??how can i make this accurate in meters?
degree = 0; //should be variable over time (full circle in 1Hr, 3600sec --> 360/3600 = 0,1 deg/s)
radian;
radian = (degree/180)*Math.PI;
gpsCircleLat = gpsLatCenter+Math.cos(radian)*radius;
gpsCircleLon = gpsLonCenter-Math.sin(radian)*radius;
*/
}
Now, I have checked this code in adobe flash, which made a movie clip move around in a circle. So I know the calculations are somewhat right. But, I have no way of calculating the latitude and longitude of the resulting coordinates.
EDIT!!
i found the solution with the help posted below. still a lot of work to figure out how to use the results. anyway, i posted the resulting function below.
to make this work, you need _radius wich is 6371 (earth's radius), a bearing, a distance, and a start location.
thanks a lot guys!
public static void destinationPoint(double brng, double dist) {
dist = dist/_radius; // convert dist to angular distance in radians
brng = Math.toRadians(brng); //
double lat1 = Math.toRadians(_lat);
double lon1 = Math.toRadians(_lon);
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));
lon2 = (lon2+3*Math.PI) % (2*Math.PI) - Math.PI; // normalise to -180..+180ยบ
Log.i(APPTAG, ""+Math.toDegrees(lat2));
Log.i(APPTAG, ""+Math.toDegrees(lon2));
Location movLoc = new Location("");
movLoc.setLatitude(Math.toDegrees(lat2));
movLoc.setLongitude(Math.toDegrees(lon2));
Log.i(APPTAG, ""+movLoc);
}
You should check the section Destination point given distance and bearing from start point at this website: http://www.movable-type.co.uk/scripts/latlong.html
That website has the proper formula for using your start point (gpsLatCenter/gpsLonCenter) and bearing (degree in you code) to compute the final lat/lon (gpsCircleLat/gpsCircleLon).

Categories

Resources