Rotate osmdroid map around arbitrary point - android

I need to rotate my map around current location marker. Current location marker will be placed at the boottom of map. Like on a picture. Map with location marker

I solved my task with this code:
//rotate map
map.setMapOrientation(angle);
//center to current location
GeoPoint point = mMyLocationNewOverlay.getMyLocation();
if (point == null) return;
mapController.setCenter(point);
//calculate translation
float bottomPadding = map.getHeight() * 0.1f;
float radius = map.getHeight()/2 - bottomPadding;
double deltaW = Math.sin(Math.toRadians(angle));
double deltaH = Math.cos(Math.toRadians(angle));
int width = map.getWidth()/2 - (int)(radius * deltaW);
int height = map.getHeight()/2 - (int)(radius * deltaH);
//Move current location marker to bottom of the map
Projection projection = map.getProjection();
GeoPoint centerPoint = (GeoPoint) projection.fromPixels(width, height);
mapController.setCenter(centerPoint);

Related

How to change a rectangle's area based on it's coordinates?

I'm developing an Android app. I have a polygon/rectangle with it's 4 bounds/coordinates on a Google Map. I have a variable named areaDifference which is set to an arbitrary value. I need to take the value of this variable and change the size of the rectangle. For instance, if areaDifference is set to 2, I need to make the rectangle twice as big. How do i change the size? I only have the 4 coordinates and the area variable.
Just convert LatLon coordinates of your polygon/rectangle to screen coordinates, multiply screen coords by areaDifference and convert it back to LatLon. Something like that:
Polygon polygon = mGoogleMap.addPolygon(new PolygonOptions()
.add(new LatLng(0, 0), new LatLng(0, 1), new LatLng(1, 1), new LatLng(1, 0))
.strokeColor(Color.RED)
.fillColor(Color.GREEN));
List<LatLng> points = polygon.getPoints();
List<LatLng> scaledPoints = new ArrayList(points.size());
Projection projection = mGoogleMap.getProjection();
for (int i=0; i<points.size(); i++) {
Point screenPosition = projection.toScreenLocation(points.get(i));
screenPosition.x = areaDifference * screenPosition.x;
screenPosition.y = areaDifference * screenPosition.y;
LatLng latLng = projection.fromScreenLocation(screenPosition);
scaledPoints.add(latLng);
}
polygon.setPoints(scaledPoints);
More details about scaling here.
UPDATE:
For scaling around center of polygon you should implement some more affine transformations: convert LatLon coordinates of your polygon/rectangle to screen coordinates, move center of polygon to screen cootds (0,0), multiply screen coords by areaDifference, move center of polygon to it's original point and convert it back to LatLon. You can use than methods to do that:
private static List<LatLng> scalePolygonPoints(List<LatLng> points, float scale, Projection projection) {
List<LatLng> scaledPoints = new ArrayList(points.size());
LatLng polygonCenter = getPolygonCenterPoint(points);
Point centerPoint = projection.toScreenLocation(polygonCenter);
for (int i=0; i < points.size(); i++) {
Point screenPosition = projection.toScreenLocation(points.get(i));
screenPosition.x = (int) (scale * (screenPosition.x - centerPoint.x) + centerPoint.x);
screenPosition.y = (int) (scale * (screenPosition.y - centerPoint.y) + centerPoint.y);
LatLng latLng = projection.fromScreenLocation(screenPosition);
scaledPoints.add(latLng);
}
return scaledPoints;
}
private static LatLng getPolygonCenterPoint(List<LatLng> polygonPointsList){
LatLng centerLatLng = null;
LatLngBounds.Builder builder = new LatLngBounds.Builder();
for(int i = 0; i < polygonPointsList.size() ; i++) {
builder.include(polygonPointsList.get(i));
}
LatLngBounds bounds = builder.build();
centerLatLng = bounds.getCenter();
return centerLatLng;
}
Usage:
...
areaDifference = 2.0f;
Projection projection = mGoogleMap.getProjection();
List<LatLng> scaledPoints = scalePolygonPoints(mPolygon.getPoints(), areaDifference, projection);
mPolygon.setPoints(scaledPoints);
...

Specific range of map in android by using mapView

how to show the mapBox map view in specific range in android just like i need to show only Asia pacific in mapView only ,not display full world in mapView android?
You can manually set the zoom level of the map (radius is the radius of the map in meter):
double radius = 500.0;
Circle circle = map.addCircle(new CircleOptions().center(latLng).radius(radius).strokeColor(Color.RED));
circle.setVisible(false);
int zoomLevel = getZoomLevel(circle);
map.animateCamera(CameraUpdateFactory.newLatLngZoom(latLng, zoomLevel));
private int getZoomLevel(Circle circle) {
int zoomLevel = 15;
if (circle != null) {
double radius = circle.getRadius();
double scale = radius / 500;
zoomLevel = (int) (16 - Math.log(scale) / Math.log(2));
}
return zoomLevel;
}

How to calculate the bounding box around the touched map position?

In my Android application I want to request data for the location where the user touches the map. GoogleMap.OnMapClickListener provides the touched position as latitude and longitude coordinates.
public abstract void onMapClick(LatLng point)
In order to pass an area instead of a point I need to calculate the coordinates for a bounding box centered at the touch position. The extend of the bounding box should not depend on the zoom level of the map.
I do not want to request the bounding box of the visible screen - just a small bounding box area around the touch position.
Is there any framework method I could use? Otherwise, how do I find a suitable distance value for the extend of the bounding box?
Isn't LatLngBounds what you need?
You can override onMarkerClick like below
#Override
public boolean onMarkerClick(Marker marker) {
if (markerClicked) {
if (polygon != null) {
polygon.remove();
polygon = null;
}
polygonOptions.add(marker.getPosition());
polygonOptions.strokeColor(ContextCompat.getColor(getContext(), R.color.colorRedTransparent));
polygonOptions.fillColor(ContextCompat.getColor(getContext(), R.color.colorBlueTransparent));
polygonOptions.strokeWidth(5.0f);
polygon = mMap.addPolygon(polygonOptions);
polygon.setClickable(true);
} else {
if (polygon != null) {
polygon.remove();
polygon = null;
}
polygonOptions = new PolygonOptions().add(marker.getPosition());
markerClicked = true;
}
return true;
}
Pass your polygon to generate bounding box
public static Rectangle getBoundingBox(Polygon polygon) {
double boundsMinX = Double.MAX_VALUE; // bottom south latitude of the bounding box.
double boundsMaxX = Double.MIN_VALUE; // top north latitude of bounding box.
double boundsMinY = Double.MAX_VALUE; // left longitude of bounding box (western bound).
double boundsMaxY = Double.MIN_VALUE; // right longitude of bounding box (eastern bound).
for (int i = 0; i < polygon.getPoints().size(); i++) {
double x = polygon.getPoints().get(i).latitude;
boundsMinX = Math.min(boundsMinX, x);
boundsMaxX = Math.max(boundsMaxX, x);
double y = polygon.getPoints().get(i).longitude;
boundsMinY = Math.min(boundsMinY, y);
boundsMaxY = Math.max(boundsMaxY, y);
}
//Rectangle(double left, double bottom, double right, double top)
return new Rectangle(boundsMinY, boundsMinX, boundsMaxY, boundsMaxX);
}
I'm not sure I fully understand your question, but usually screen-to-map coordinates are done using a few methods, probably something like that:
Projection projection = googleMaps.getProjection();
Point p = projection.toScreenLocation(point);
LatLng topLeft = projection.fromScreenLocation(new Point(p.x - halfWidth, p.y - halfHeight));
LatLng bottomRight = projection.fromScreenLocation(new Point(p.x + halfWidth, p.y + halfHeight));
Edit:
on the above
point is the LatLng you received on your onMapClick.
p is the on screen position of said click event
p.x - halfWidth, p.y - halfHeight, p.x + halfWidth, p.y + halfHeight is a bounding box around the click location with 2 * halfWidth of width and 2 * halfHeight of height.
topLeft and bottomRight is a bounding box around the click event in Latitude-Longitude coordinates.

Is it possible to set dimensions to a groundOverlay that is independent of the map zoom level?

I have a GroundOverlay on my GoogleMap and I want that its dimensions to not change when I zoom in/out on map. Exact like default map markers that always keep their dimensions. I have tried with both forms of the GroundOverlay.setDimensions() but the image is still resize on zoom. Here is my code:
Bitmap btm = BitmapFactory.decodeResource(getResources(), R.drawable.map_arrow);
BitmapDescriptor arrow = BitmapDescriptorFactory.fromBitmap(btm);
float w = btm.getWidth();
float h = btm.getHeight();
if (groundOverlay != null) groundOverlay.remove();
groundOverlay = mMap.addGroundOverlay(new GroundOverlayOptions()
.image(arrow).position(meLoc, w,h).bearing(bearAngle));
groundOverlay.setDimensions(1000);
you have your width and heigth of overlay and placed it on the map according to your zoom level. It seems good for that zoom level. Right? Now you can calculate radius and get meters of your map screen. Because map background overlay width and height values are meters. We have to go with meter not zoom level or anything else. Maybe someone can find a better solution but I have tried too many ways, and end up with this solution and it worked very well.
float zoomLevel=mMap.getCameraPosition().zoom;
//calculate meters*********************
myBounds = mMap.getProjection().getVisibleRegion().latLngBounds;
myCenter= mMap.getCameraPosition().target;
if (myCenter.latitude==0 || myCenter.longitude==0) {
myCenter=new LatLng(myLocation.getLatitude(),myLocation.getLongitude());
}
LatLng ne = myBounds.northeast;
// r = radius of the earth in statute miles
double r = 3963.0;
// Convert lat or lng from decimal degrees into radians (divide by 57.2958)
double lat1 = myCenter.latitude / 57.2958;
double lon1 = myCenter.longitude / 57.2958;
final double lat2 = ne.latitude / 57.2958;
final double lon2 = ne.longitude / 57.2958;
// distance = circle radius from center to Northeast corner of bounds
double dis = r * Math.acos(Math.sin(lat1) * Math.sin(lat2) +
Math.cos(lat1) * Math.cos(lat2) * Math.cos(lon2 - lon1));
//1 Meter = 0.000621371192237334 Miles
double meters_calc=dis/0.000621371192237334;
float factor=1;
if (zoomLevel==15) { // my default zoom level yours can be different
metersoverlay=meters_calc; // global variable metersoverlay set
}
else { // if my zoom level change then I have to calculate dimension scale factor
factor=(float) (meters_calc/metersoverlay);
}
//******************************* now we are ready to set dimension of background overlay
float dimensions=1000*factor;
loadingGroundOverlayBg.setDimensions(dimensions);
I hope it works for all of you :)
I had the same issue. If you want to place an image, you can use the default markers provided by Google maps API. You can set a custom image as the marker icon, which will remain the same size.
private final List<BitmapDescriptor> mImages = new ArrayList<BitmapDescriptor>();
mImages.add(BitmapDescriptorFactory.fromResource(R.drawable.YOUR_IMAGE));
Marker marker = map.addMarker(new MarkerOptions().icon(mImages.get(0)));
This example shows how to set the icon of the marker, from an element of an image list. Hope it helps.

toPixels() returns the pixels of the tiles, instead of the screen

I have an Android application that showing maps using OSMDroid.
I want to get the projection pixels of a GeoPoint on the screen, not on the tiles.
Consider the following piece of code:
Projection projection = getProjection();
GeoPoint geoPoint1 = (GeoPoint)projection.fromPixels(0, 0);
Point pixelsPoint = new Point();
projection.toPixels(geoPoint1, pixelsPoint);
GeoPoint geoPoint2 = (GeoPoint)projection.fromPixels(pixelsPoint.x, pixelsPoint.y);
I would like geoPoint1 to be equal to geoPoint2. Instead, I get 2 totally different `GeoPoint'.
In my opininion, the problem is in this line:
projection.toPixels(geoPoint1, pixelsPoint);
The out variable pixelsPoint get filled with values much higher than the screen dimensions (I get 10,000+ for the x and y) and I suspect that this are the pixels on the tile, rather than the screen pixels.
How can I get from GeoPoint to screen pixels back and forth?
You need to compensate for the top left offset, these methods should work:
/**
*
* #param x view coord relative to left
* #param y view coord relative to top
* #param vw MapView
* #return GeoPoint
*/
private GeoPoint geoPointFromScreenCoords(int x, int y, MapView vw){
if (x < 0 || y < 0 || x > vw.getWidth() || y > vw.getHeight()){
return null; // coord out of bounds
}
// Get the top left GeoPoint
Projection projection = vw.getProjection();
GeoPoint geoPointTopLeft = (GeoPoint) projection.fromPixels(0, 0);
Point topLeftPoint = new Point();
// Get the top left Point (includes osmdroid offsets)
projection.toPixels(geoPointTopLeft, topLeftPoint);
// get the GeoPoint of any point on screen
GeoPoint rtnGeoPoint = (GeoPoint) projection.fromPixels(x, y);
return rtnGeoPoint;
}
/**
*
* #param gp GeoPoint
* #param vw Mapview
* #return a 'Point' in screen coords relative to top left
*/
private Point pointFromGeoPoint(GeoPoint gp, MapView vw){
Point rtnPoint = new Point();
Projection projection = vw.getProjection();
projection.toPixels(gp, rtnPoint);
// Get the top left GeoPoint
GeoPoint geoPointTopLeft = (GeoPoint) projection.fromPixels(0, 0);
Point topLeftPoint = new Point();
// Get the top left Point (includes osmdroid offsets)
projection.toPixels(geoPointTopLeft, topLeftPoint);
rtnPoint.x-= topLeftPoint.x; // remove offsets
rtnPoint.y-= topLeftPoint.y;
if (rtnPoint.x > vw.getWidth() || rtnPoint.y > vw.getHeight() ||
rtnPoint.x < 0 || rtnPoint.y < 0){
return null; // gp must be off the screen
}
return rtnPoint;
}

Categories

Resources