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.
Related
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.
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());
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);
}
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.
I have a 3D arrow drawn with OpenGL that points to the coordinates (0, 0, 0) and I want it to point to a specific GPS location depending on my GPS position and Orientation.
I've tried calculating the azimuth (with my phone's orientation) and adjusting it to be the real north (not the magnetic north).
SensorManager.getOrientation(remappedRotationMatrix, orientation);
// convert radians to degrees
float azimuth = orientation[0];
azimuth = azimuth * 360 / (2 * (float) Math.PI);
GeomagneticField geoField = new GeomagneticField(
Double.valueOf(loc.getLatitude()).floatValue(),
Double.valueOf(loc.getLongitude()).floatValue(),
Double.valueOf(loc.getAltitude()).floatValue(),
System.currentTimeMillis());
// converts magnetic north into true north
azimuth -= geoField.getDeclination();
Then getting the bearing from my Location to the Location I want to point.
target.setLatitude(42.806484);
target.setLongitude(-1.632482);
float bearing = loc.bearingTo(target); // (it's already in degrees)
if (bearing < 0) {
bearing = bearing + 360;
}
float degrees = bearing - azimuth;
if (degrees < 0) {
degrees = degrees + 360;
}
and calculating the degrees I have to rotate the arrow
gl.glRotatef(degrees, 0.0f, 1.0f, 0.0f);
arrow.draw(gl);
Is there someway to do it? Could another possibility be to convert the GPS position to the OpenGL coordinates and use GLU.gluLookAt to point to it?
Thanks.
This seem to be purely a math problem.
Your question is pretty vague, I don't think I can help you without understanding more precisely how your scene is set up and what you want.
Do you know how to use 3D rotation matrices? If not, you probably should learn how they work.
It shouldn't be complicated to calculate the bearing and then rotate the arrow by the degrees you get. I have done the same in 2D although not in OpenGL. I based my code on the Radar sample (http://apps-for-android.googlecode.com/svn/trunk/Radar/). Here is how I draw the 2D arrow:
double bearingToTarget = mBearing - mOrientation;
// Draw an arrow in direction of target
canvas.rotate((float) bearingToTarget, center, center);
final int tipX = center;
final int tipY = center-radius;
canvas.drawLine(center, center, tipX, tipY, mArrowPaint);
final int tipLen = 30;
final int tipWidth = 20;
Path path = new Path();
path.moveTo(tipX, tipY);
path.lineTo(tipX + tipWidth/2, tipY + tipLen);
path.lineTo(tipX - tipWidth/2, tipY + tipLen);
path.lineTo(tipX, tipY);
path.close();
canvas.drawPath(path, mArrowPaint);
canvas.restore();
mBearing is calculated using the method GeoUtils.bearing from the Radar sample which takes care of the complicated math. mOrientation is just the orientation from the sensor listener. So the idea is to compute the difference between the bearing of the GPS location you want to point to (mBearing) and the current orientation of the phone (mOrientation). This gives us the angle bearingToTarget. We then rotate the view about its center by that angle before drawing the arrow along the y axis. This is the same as drawing the arrow rotated by bearingToTarget degrees.
You should be able to apply the same logic in OpenGL by rotating the view about the center of the screen by bearingToTarget degrees before you draw the arrow. Exactly what point you rotate about depends on how your view is set up. To make it simple, make the starting point of your arrow at the origin. Then you can simply rotate about the origin using glRotatef. Otherwise you would first need to translate to the center of the rotation, rotate and then translate back again (this is the common OpenGL technique for rotation about a point).