Android GoogleMap V2 : How can I draw a line with specific length? - android

I have a GoogleMap in my project. It's set in zoom level 18. I want to draw a line that is 1 meter in length. I saw and use a code Like this:
googleMap.addCircle(new CircleOptions()
.center(latLng1)
.radius(5)
.fillColor(Color.BLUE));
I gave it's radius in meters. how can I do it with a line?(polyLine doesn't have this options) a line with specific LatLng and specific direction(for example: Heading from north) and specific length?
I can specify direction by sin and cos.. but what can I do for length of the line?

For given point there is only one circle with given radius. But with lines the situation is a bit different. For given point there are infinite number of lines starting from this points and given length. Therefore you can't simple draw such line.
One way to do it is to pick a point on the circle with radius 1 meter and center your point. Here is a good example how to calculate point on a circle with given radius. Than just draw a line between the two points.
UPDATE:
This may help you how to find the LatLng points on the circle LatLng Points on circle on Google Map V2 in Android

To compute line end I use:
SphericalUtil.computeOffset(LatLng,lenght,heading);
To compute width in meters I use this:
public Double calcw(GoogleMap map,int ancho,LatLng p) {
float tilt = map.getCameraPosition().tilt;
CameraPosition old=map.getCameraPosition();
if(tilt!=0){
CameraPosition cameraPosition = new CameraPosition.Builder()
.target(old.target) // Sets the center of the map to Mountain View
.zoom(old.zoom) // Sets the zoom
.bearing(old.bearing) // Sets the orientation of the camera to east
.tilt(0) // Sets the tilt of the camera to 30 degrees
.build();
map.moveCamera(CameraUpdateFactory.newCameraPosition(cameraPosition));
}
Point g1 =map.getProjection().toScreenLocation(p);
Point g2=map.getProjection().toScreenLocation(SphericalUtil.computeOffset(p,ancho/10,0));
Double result=(distance(g1,g2));
//Log.e("PROJ1",Double.toString(distance(g1,g2)));
map.moveCamera(CameraUpdateFactory.newCameraPosition(old));
return result;
}
public double distance(Point a, Point b)
{
double dx = a.x - b.x;
double dy = a.y - b.y;
return Math.sqrt(dx * dx + dy * dy);
}

Use polyline to draw line something like below,
private ArrayList<LatLng> mLatlngs ;
PolylineOptions mPolylineOptions = new PolylineOptions();
mPolylineOptions.color(Color.RED);
mPolylineOptions.width(3);
mPolylineOptions.addAll(mLatlngs);
mGoogleMap.addPolyline(mPolylineOptions);

Related

How to find if the lat.lng is within a certain radius circle in google maps

I have the sample entries for
lat: 12.962899
lng 77.622330
I have drawn a circle in the map of a radius of 10000
Circle circle = map.addCircle(new CircleOptions()
.center(new LatLng(lat, lng))
.radius(10000)
.strokeColor(Color.RED)
.fillColor(Color.BLUE));
What I am trying to do: If i am given a new lat,lng value, I want to find out if the new position is within the circle radius or not.
How to achieve this
You can find out distance between 2 lat longs using this function
private float calculateLocationDifference(LatLng lastLocation, LatLng firstLocation) {
float[] dist = new float[1];
Location.distanceBetween(lastLocation.latitude, lastLocation.longitude, firstLocation.latitude, firstLocation.longitude, dist);
return dist[0];
}
pass center point of circle and new point to it . if distance is < 10000 then location is with in the circle
You can look at it as a coordinate system, let's say that you are putting the first point on the map.
You are adding a new point and want to check if it's inside your circle, because your circle has a radius of 10000 you can compare the distance between your points (new and old) and if this distance is > 10000 your new point is not inside the circle.
If you are using google maps you can use distanceBetween method to find the distance:
Location.distanceBetween(FirstLatitude, FirstLongitude, SecondLatitude, SecondLongitude, results);
You can use the following function:
It will return a boolean true or false
point = {lat: 12.962899, lng: 77.622330}
or you can get the points from click event
function pointInCircle(point, radius, center) {
return (google.maps.geometry.spherical.computeDistanceBetween(point, center) <= radius)
}

Calculate radius of visible map user in Google map

What I want to do is calculate a circle's radius by which the user is viewing the map.
I've written the solution so far as follows (which is true):
mMap.setOnCameraChangeListener(new GoogleMap.OnCameraChangeListener() {
#Override
public void onCameraChange(CameraPosition cameraPosition) {
String TAG = AppController.TAG;
LatLngBounds bounds = mMap.getProjection().getVisibleRegion().latLngBounds;
LatLng target = cameraPosition.target;
LatLng northEast = bounds.northeast;
LatLng southEast = bounds.southwest;
float[] results1 = new float[1];
float[] results2 = new float[1];
Location.distanceBetween(target.latitude, target.longitude, northEast.latitude, northEast.longitude, results1);
Location.distanceBetween(target.latitude, target.longitude, southEast.latitude, southEast.longitude, results2);
double distance = results1[0] > results2[0] ? results1[0] : results2[0];
Log.d(TAG, "onCameraChange:" + results1[0] + " " + results2[0]);
}
});
I'm facing two questions here:
1- First of all why the distane between the center and north east isn't equal to south west?
2- Is there any built in method to achieve the same result?
as you can see from the map above, the horizontal line (latitude) gets shorter the further away you get from the equator.
on the screen, the map is 'distorted' to flatten the curved surface of the earth, with +/- the same amount of longitude and latitude from the center of the view.
so unless you are taking measurements directly at the equator, you will have slight differences in the radius. for small distances, this is negligible.
if accuracy is not important, you can try using the smaller radius (if you want to draw a circle on the view that will not be partially hidden), or use the bigger radius (if you want to do calculations for places within that cicle).

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

Google Maps SphericalUtil computeOffset

I'm using the computeOffset() from the SpericalUtil in the google maps android library. The computeOffset takes the parameters:
public static LatLng computeOffset(LatLng from, double distance, double heading)
with the original coordinates of the first two markers I can use the computeOffset to generate another line after inputing the distance and the heading. My goal is to make parallel lines at a certain distance apart. But as you can see below it looks pretty good when the lines are nearly north and south, but when you go towards east and west it looks to me like the distance between the two lines isn't the same value. The end points appear to be the right distance but offset. How can I make it so all the lines look like the first image no matter what direction?
public void onMarkerDragEnd(Marker marker) {
showDistance();
LatLng markerCPosition = SphericalUtil.computeOffset(mMarkerA.getPosition(), 50, 90);
mMarkerC = getMap().addMarker(new MarkerOptions().position(markerCPosition).draggable(true).visible(false));
LatLng markerDPosition = SphericalUtil.computeOffset(mMarkerB.getPosition(), 50, 90);
mMarkerD = getMap().addMarker(new MarkerOptions().position(markerDPosition).draggable(true).visible(false));
updatePolyline();
}
At the moment you're just shifting the points sideways using a constant angle (90 degrees). If I understand you correctly and you want both lines parallel to each other separated by some distance you need to take into account the heading of the original A-B line like so:
double heading = SphericalUtil.computeHeading(posA, posB);
LatLng markerCPosition = SphericalUtil.computeOffset(posA, 50, heading + 90);
LatLng markerDPosition = SphericalUtil.computeOffset(posB, 50, heading + 90);

Google Maps Circle radius alternative

At present I am using circles to display the path of an object of a certain width in meters on a map. This is problematic because at the rate of even 15 mph the circle method would have to be taken every few hundred milliseconds so that the circles overlap so you don't get gaps between the circles. After an hour you end up with countless circles and no memory.
CircleOptions circleOptions = new CircleOptions()
.center(new LatLng(location.getLatitude(), location.getLongitude()));
circleOptions.radius(radi); // In meters
Currently I can't seem to find where the google maps android api v2 supports using a polyline with set radius in meters, but only in pixel width. With varying zoom levels and screen conditions and the projection of the map this sounds rather complicated to basically highlight a path of certain width on the map with a polyline.
Has anyone seen any alternative to using the circle radius method in the api?
Using android-maps-utils this becomes relatively simple:
public static PolygonOptions getPolygonOptions(LatLng point1, LatLng point2, float widthInMeters) {
double heading = SphericalUtil.computeHeading(point1, point2);
double distance = SphericalUtil.computeDistanceBetween(point1, point2);
LatLng corner1 = SphericalUtil.computeOffset(point2, widthInMeters / 2, heading + 90);
LatLng corner2 = SphericalUtil.computeOffset(point2, widthInMeters / 2, heading - 90);
LatLng corner3 = SphericalUtil.computeOffset(corner2, distance, heading + 180);
LatLng corner4 = SphericalUtil.computeOffset(corner3, widthInMeters, heading + 90);
return new PolygonOptions().add(corner1, corner2, corner3, corner4);
}
And then add polygons connecting your points:
map.addPolygon(
getPolygonOptions(point1, point2, 50)
.fillColor(Color.BLACK)
);
map.addPolygon(
getPolygonOptions(point2, point3, 50)
.fillColor(Color.BLACK)
);

Categories

Resources