Convert Cartesian (X,Y) to coordinates GPS (Latitude & Longitude) - java

I need to convert coordinates X, Y into Latitude and Longitude. I've read wikipedia map projections, similar stackoverflow forums, applied my custom solutions and still didn't work.
The coordinates I get from my formula are wrong, Longitude is acceptable but Latitude is not. I don't see where my calculous is wrong.
The X,Y Point is taken from a JLayeredPane with the size of a background Map image, once a smaller image is released on this Map image, the point is taken.
public void mouseReleased(MouseEvent e) {
DM.setUpCoordinates(layeredPane.getComponent(index-1).getLocation());
}
After this, I'm trying to correctly calculate the Latitude and Longitude projection. The data I own is:
X,Y coordinates from the Map
Total width and height from the Map
Latitude and Longitude where the map is centered
What I have tryied so far:
Trying Equirectangular projection
public void setUpCoordinates(Point p) {
//Equirectangular projection: optimal for small streets
Long = ((p.getX())/(6371000*Math.cos(MG.getLati())))+MG.getLongi();
Lat = (((p.getY())/6371000)+MG.getLati());
}
I also tryied to implement the Mercator projection from this link with very little to no success at all.
I am aware I'm not using total width and height from the Map on my formulas and this might be the error, but I don't know how to use it!
any help how to convert from (x,y) to (latitude, longitude)?
Thanks,

You need to shift pixel coordinates by center position and also use map scale - longitude and latitude range (I assume that elongation Math.cos(MG.getLati()) coefficient is accounted in longitude scale)
Long = (p.getX() - MapWidth/2)*LongitudeRange/MapWidth +MG.getLongi();
Lat = (p.getY() - MapHeight/2)*LatitudeRange/MapHeight + MG.getLati());

Related

Android Location Bias in autocomplete Rectangular Bounds

I want the autocomplete result to show only 1 km from the user location.
there is a way with RectangularBounds:
setLocationBias(RectangularBounds.newInstance(
new LatLng(-33.880490, 151.184363),
new LatLng(-33.858754, 151.229596)))
There are only 2 points here. How does it make a rectangular?
If the user Latlng is for example 32.0000000, 30.0000000 , How can I do a RectangularBounds of 1 km around him?
For the first question if you look at the docs
RectangularBounds newInstance (LatLng southwest, LatLng northeast)
Is rectangular because it represents two coordinates: southwest and northeast, that's usually refered as a Bounding Box (or shortened to bbox) and represents the area inside that two coordinates.
The second question is less simple and there is more than one way of doing it.
One approach is to find out a method to add a distance to a coordinate, this methods are always approximate. For this LatLng geographic coordinates you could use:
public static LatLng getCoordinate(double lat0, double lng0, long dy, long dx) {
double lat = lat0 + (180 / Math.PI) * (dy / 6378137);
double lng = lng0 + (180 / Math.PI) * (dx / 6378137) / Math.cos(lat0);
return new LatLng(lat, lng);
}
where lat0 and long0 are the original coordinates and dx and dy are a distance in meters (as the Earth radius is set in meters 6378137).
And now you can create an aproximate bounding box with the original point +- 1000 meters. Supposing the point you set as starting point is at latitude -33.869622 and longitude 151.206979 the bbox would be:
RectangularBounds.newInstance(
getCoordinate(-33.869622, 151.206979, -1000, -1000),
getCoordinate(-33.869622, 151.206979, 1000, 1000))
That is the bbox you could use for the api call.
In case you have an instance variable named currentlocation this will be:
RectangularBounds.newInstance(
getCoordinate(this.currentlocation.getLatitude(), this.currentlocation.getLongitude(), -1000, -1000),
getCoordinate(this.currentlocation.getLatitude(), this.currentlocation.getLongitude(), 1000, 1000))
If you still need to be more precise with the 1km distance and discard points that are inside that square (bbox) but out of circle of 1km radius you could filter the results by checking the distance to the original point before returning the result or displaying it.
As a curiosity
Another way I could think of is to change the coordinate's projection to another one to make the calculations easier, if you transform the coordinate to mercator adding 1000 to the x value would add 1 km and adding 1000 to the y would also add 1 km as it uses meters as unit, but then you should transform back to the original projection (working with LatLng) as it's the one that api is using so I don't think it's worth it, but it should work as well.

How to draw a circle on a map according to its real radius in kilometers (Using UnfoldingMaps and Processing in Java)

Earthquake threat circle on the map
I am using UnfoldingMaps to display earthquake information on the map.
I plan to show the threat circle on the map.
A circle is drawn given its radius and center position in pixels. How to get the radius is the problem I met.
Suppose I have the threat circle radius R in kilometers and the center marker A.
I want to create a marker B on the circle so that I can use the screen distance as the screen radius.
I decided to create B with the same longitude but a different latitude from A. I change R to delta latitude.
But after drawing the circle I found it is not the right one since the red triangular should be in the circle according to their distance.
The main difficulty is exactly how to calculate screen radius according to kilometers.
public void calcThreatCircleOnScreen(UnfoldingMap map) {
float radius = 0;
float deltaLat=(float) (threatCircle()/6371/2/3.1415927*360);
Location centerLocation = this.getLocation();
Location upperLocation = new Location(centerLocation);
upperLocation.setLat(centerLocation.getLat() + deltaLat);
SimplePointMarker upperMarker = new SimplePointMarker(upperLocation);
ScreenPosition center = this.getScreenPosition(map);
ScreenPosition upper = upperMarker.getScreenPosition(map);
radius = Math.abs(upper.y - center.y);
setThreatCircleOnScreen(radius);
}
This is going to depend on two things: the zoom level of the map, and the projection you're using.
You need to unproject kilometers to pixels, and you can probably figure out how to do that using google and the Unfolding API.
For example, I found a MercatorProjection class that contains a constructor that takes a zoom level, and methods for projecting and unprojecting points between world coordinates and pixel coordinates.
That's just a starting point, since I'm not sure what units those methods are taking, but hopefully this is a direction for you to take your googling and experimenting.
I'd recommend trying to get something working and posting an MCVE if you get stuck. Good luck.
Now I have the answer for this question. Hope it will be helpful for others.
Earthquake threat circle on the map
My early solution to calculate radius in pixels from km is correct. I think it a simple and powerful idea (independent of projecting API)
The only problem is I should use diameter rather than radius in drawing the circle. I should draw with d=2r like this
float d = 2 * threatCircleRadius();
pg.noFill();
pg.ellipse(x,y,d,d);
I found another cleaner solution like below by consulting the author of UnfoldingMaps. (https://github.com/tillnagel/unfolding/issues/124)
My early solution first changes distance to delta latitude, then create new location by changing latitude.
The new solution use the API GeoUtils.getDestinationLocation(sourceLocation, compassBearingDegree, distanceKm) to directly get the new location!
In addition, I needn't create a new marker to find its screen position.
public void calcThreatCircleOnScreen(UnfoldingMap map) {
float radius = 0;
Location centerLocation = this.getLocation();
Location upperLocation = GeoUtils.getDestinationLocation(centerLocation, 0, threatCircle());
//SimplePointMarker upperMarker = new SimplePointMarker(upperLocation);
ScreenPosition center = map.getScreenPosition(centerLocation);
ScreenPosition upper = map.getScreenPosition(upperLocation);
radius = PApplet.dist(center.x, center.y, upper.x, upper.y);
setThreatCircleOnScreen(radius);
}

distanceBetween or other way to check if current position is in radius to marker

I want to check if the current location of the user is in a certain radius to a marker. Should I do it with distanceBetween()?
What arguments do I have to pass this function and what exactly does the result say?
LatLng markerPosition = marker.getPosition();
myLocation = locationManager.getLastKnownLocation(provider);
double latitude = myLocation.getLatitude();
double longitude = myLocation.getLongitude();
distanceBetween(???);
public static void distanceBetween (double startLatitude, double startLongitude, double endLatitude, double endLongitude, float[] results) {
}
you can simple use
distanceTo(Location location)
from Location class todo this, like:
Location makerLoc = new Location("marker");
markerLoc.setLatitude(markerPosition.latitude);
markerLoc.setLongitude(markerPosition.longtitude);
float meters = myLocation.distanceTo(markerLoc);
Please use Google Maps Utility Library
This library provide very simple distance calculating just like you need
In example:
computeDistanceBetween() – Returns the distance, in meters, between two
latitude/longitude coordinates.
computeHeading() – Returns the bearing, in degrees, between two
latitude/longitude coordinates.
computeArea() – Returns the area, in square meters, of a closed path on the
Earth.
interpolate() – Returns the latitude/longitude coordinates of a point that
lies a given fraction of the distance between two given points. You can use
this to animate a marker between two points, for example.

Make polygons perpendicular to bearing in google maps android

I'm creating polygons on a map
getMap().addPolygon(
getPolygonOptions(point1, point2, widthInMeters)
.fillColor(Color.YELLOW));
The position of the corners is calculated by:
public static PolygonOptions getPolygonOptions(LatLng point1, LatLng point2, double widthInMeters) {
double distance = SphericalUtil.computeDistanceBetween(point1, point2) + 6;
float bearing = location.getBearing();
double bears = bearing;
LatLng corner1 = SphericalUtil.computeOffset(point2, widthInMeters / 2, bears + 90);
LatLng corner2 = SphericalUtil.computeOffset(point2, widthInMeters / 2, bears - 90);
LatLng corner3 = SphericalUtil.computeOffset(corner2, distance, bears);
LatLng corner4 = SphericalUtil.computeOffset(corner1, distance, bears);
return new PolygonOptions().add(corner1, corner2, corner3, corner4);
}
The intent was for the polygons to be perpendicular to the bearing. Certain directions on the map the polygons are nearly that way, but as you can see in this picture, in some directions the polygons end up slanted compared to the current bearing. The current bearing is shown by the blue arrow and the magenta polyline. Can anyone identify whats wrong with my corner calculations?
It may be problem of units which are returning from two functions.
Here, SphericalUtil.computeDistanceBetween(point1, point2) returns the values in meters.
and
Google's location documentation suggests,
location.getBearing() returns values in degrees.
Get the bearing, in degrees.
Bearing is the horizontal direction of travel of this device, and is not related to the device orientation. It is guaranteed to be in the range (0.0, 360.0] if the device has a bearing.
If this location does not have a bearing then 0.0 is returned.
Please, check whether the location is heaving bearing pr not before using location.getBearing()
So, you have convert those degrees into meters and then implement it in your logic. It will solve your issue.

Calculate height above map given zoom level in google maps

I am trying to calculate the height above map (ignoring topography) given a zoom level. I know the equation for scale at a specific zoom level is 591657550.5/2^(level-1) (https://gis.stackexchange.com/questions/7430/google-maps-zoom-level-ratio), but I am unsure on how to use this information (or whether or not this is the right information) to solve for height above map. Any help is appreciated.
I set my google map size to 5cm selected a zoom level, and then re-found that location with that zoom in google earth to get a eye altitude level (the D value in the angular size equation http://en.wikipedia.org/wiki/Forced_perspective). I was able to find the h value in the angular size equation by first setting my map length on screen to 5cm, and then using the scale equation of 591657550.5/2^(level-1) *5cm to calculate the h value in the angular size equation. Knowing these two variables I was able to calculate the constant angle for which google maps displayed images when maps was at a 5cm width (85.36222058). From these pieces of information I was able to construct this method which calculates eye altitude above map from zoom level with relative accuracy
public float getAltitude(float mapzoom){
//this equation is a transformation of the angular size equation solving for D. See: http://en.wikipedia.org/wiki/Forced_perspective
float googleearthaltitude;
float firstPartOfEq= (float)(.05 * ((591657550.5/(Math.pow(2,(mapzoom-1))))/2));//amount displayed is .05 meters and map scale =591657550.5/(Math.pow(2,(mapzoom-1))))
//this bit ^ essentially gets the h value in the angular size eq then divides it by 2
googleearthaltitude =(firstPartOfEq) * ((float) (Math.cos(Math.toRadians(85.362/2)))/(float) (Math.sin(Math.toRadians(85.362/2))));//85.362 is angle which google maps displays on a 5cm wide screen
return googleearthaltitude;
}
Sorry if my explanation is poorly explained. If you guys want to use this method feel free to. Sorry for any poorly worded sentences.
I have basically converted Javascript code to Java. I hope this works.
public int convertRangeToZoom(double range) {
//see: google.maps.v3.all.debug.js
int zoom = (int) Math.round(Math.log(35200000 / range) / Math.log(2));
if (zoom < 0) zoom = 0;
else if (zoom > 19) zoom = 19;
return zoom;
}
public int convertZoomToRange(double zoom){
//see: google.maps.v3.all.debug.js
int range = (int) 35200000/(Math.pow(2, zoom));
if (range < 300) range = 300;
return range;
}
https://groups.google.com/forum/#!msg/google-earth-browser-plugin/eSL9GlAkWBk/T4mdToJz_FgJ

Categories