I use that Google Maps component MapView in an Android application. I can use the GPS location to show my location with a dot. But I would like to show an arrow instead, that points out the driving direction (bearing). I think that I can use the bearing value to get the angle of the arrow.
How can I do that?
Assuming you've got the Location then obtain the bearing by doing:
float myBearing = location.getBearing();
To implement the overlay you'll be using ItemizedOverlay and OverlayItem. You'll need to subclass OverlayItem to add the functionality to rotate the Drawable. Something like:
public BitmapDrawable rotateDrawable(float angle)
{
Bitmap arrowBitmap = BitmapFactory.decodeResource(context.getResources(),
R.drawable.map_pin);
// Create blank bitmap of equal size
Bitmap canvasBitmap = arrowBitmap.copy(Bitmap.Config.ARGB_8888, true);
canvasBitmap.eraseColor(0x00000000);
// Create canvas
Canvas canvas = new Canvas(canvasBitmap);
// Create rotation matrix
Matrix rotateMatrix = new Matrix();
rotateMatrix.setRotate(angle, canvas.getWidth()/2, canvas.getHeight()/2);
// Draw bitmap onto canvas using matrix
canvas.drawBitmap(arrowBitmap, rotateMatrix, null);
return new BitmapDrawable(canvasBitmap);
}
Then all that remains to be done is to apply this new Drawable to the OverlayItem. This is done using the setMarker() method.
Related
I want to make a custom image cropper for Android that supports rotation. Cropper works fine. The problem that i am facing while rotating the image (bitmap of the using using rotation matrix) the crop overlay area should also be rotated accordingly. For that I rotated the points of the crop shape by applying another rotation matrix. It map the points of the crop shape (specifically the bounding box pointed by different color circle in the image). The rotation matrix is applied for crop shape rotating is applied with the center of the crop shape as origin. But after rotating the crop shape the points of the shape should be translated to maintain the same position and aspect ratio. Can any one help me please to achieve this? [ I am sharing some code snippet to rotate the bitmap and crop shape points
private Bitmap rotateBitmap(Bitmap source, float angle) {
Matrix matrix = new Matrix();
matrix.postRotate(angle);
Bitmap rotatedBitmap = Bitmap.createBitmap(source, 0, 0, source.getWidth(), source.getHeight(), matrix, true);
rotateShapePoints(angle);
return rotatedBitmap;
}
Rotate Shape Points
private void rotateShapePoints(float angle)
{
PointF centerPoint = getCropShapeView().getCenterPoint(); // return the center of the shape
Matrix matrix = new Matrix();
matrix.postRotate(angle,centerPoint.x,centerPoint.y);
HashMap<Integer,PointF> points = (HashMap<Integer, PointF>) getCropShapeView().getPoints(); // map is used to track the point at different position
for(Map.Entry<Integer,PointF> entry : points.entrySet())
{
int key = entry.getKey();
PointF value = entry.getValue();
float[] pointArray = new float[] { value.x,value.y};
matrix.mapPoints(pointArray);
PointF rotatedPoint = new PointF(Math.abs(pointArray[0]),Math.abs(pointArray[1]));
points.put(key,rotatedPoint);
}
getCropShapeView().setPoints(points);
getCropShapeView().invalidate();
}
I am using the Projection class of Google maps to obtain the screen location for points in a polygon with the aim of creating a GroundOverlay and drawing a custom line style as a Path. The problem is when the camera is rotated the toScreenLocation method is returning incorrect results.
GroundOverlayOptions overlayOptions = new GroundOverlay();
Projection projection = map.getProjection();
LatLngBounds screenBounds = projection.getVisibleRegion().latLngBounds;
imageWidth = projection.toScreenLocation(screenBounds.northeast).x;
imageHeight = projection.toScreenLocation(screenBounds.southwest).y;
_overlayOptions.positionFromBounds(screenBounds);
Bitmap bmp = Bitmap.createBitmap(imageWidth, imageHeight, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bmp);
Paint paint = new Paint();
Path path = new Path();
List<LatLng> points = polygon.getPoints();
Point startPos = projection.toScreenLocation(points.get(0));
path.moveTo(startPos.x, startPos.y);
for (int i = 1; i < points.size(); i++) {
Point nextPos = projection.toScreenLocation(points.get(i));
path.lineTo(nextPos.x, nextPos.y);
}
path.lineTo(startX, startY);
canvas.drawPath(path, paint);
BitmapDescriptor bmpDesc = BitmapDescriptorFactory.fromBitmap(bmp);
overlayOptions.image(bmpDesc);
map.addGroundOverlay(overlayOptions);
The image below illustrates the problem. When the map is orientated North the blue dashed line renders where it is supposed to but when the bearing is changed the result of toScreenLocation gives me warped coordinates.
I've attempted to apply a matrix rotate transformation on the bitmap
Matrix rotationMatrix = new Matrix();
rotationMatrix.postRotate(map.getCameraPosition().bearing, imageWidth / 2, imageHeight / 2);
and I have also tried using trigonometry to rotate the points. Neither approach was successful.
So my question is how do you get the screen location of a LatLng that is independent of the camera orientation?
I think the screen locations are correct. But you do not use them to paint directly into the screen, but into a bitmap, which is by default oriented north up. When you apply this bitmap as GroundOverlay it is rotated together with the map and thus the locations do no longer fit.
See also the Documentation:
Bearing
The amount that the image should be rotated in a clockwise direction. The center of the rotation will be the image's anchor. This is optional and the default bearing is 0, i.e., the image is aligned so that up is north.
I am not sure, but I think you have to use the current rotation of the map as negative bearing for your GroundOverlay to compensate the effect. And I am also not sure, how a tilt of the map can or must be compensated. I would just try. The anchor point should most probably be the center of the image.
But in any case, I think it's important to realize that the screen locations are not really wrong. They just have the real screen as reference system, which is different from the bitmaps system after it is rotated.
I have layout in that it contain a horizontal scroll scale.
My question is how make this type of zoom effect on the center of the layout?
Is it possible?
i have draw the scale in canvas and zoom the canvas??
Please help me.
Thank's in advance.
I think you want a Transformation: http://developer.android.com/reference/android/view/animation/Transformation.html
The code in your onDraw method will look something like this:
canvas.save();
transformation.transform(canvas);
drawable.draw(canvas);
canvas.restore();
Step 1: At first draw the scale(drawing) on a canvas.
Step 2: get the zooming region, and apply this region as the clipRegion on the canvas ..
Step 3: get the bitmap from the canvas, and draw it on the canvas applying scale on it,scaling pivot point will be the center point of the area you want to zoom,
Bitmap.Config conf = Bitmap.Config.ARGB_8888;
Bitmap bitmap_object = Bitmap.createBitmap(width, height, conf);
Canvas canvas_object = new Canvas(bitmap_object);
/* now draw your drawing on canvas_object */
Bitmap temp = bitmap_object;
Matrix matrix_object = new Matrix();
/* now set transform and scale on matrix_object to zoom */
Paint paint_object = new Paint();
paint_object.setFilterBitmap(true);
Region reg= /* region of your zooming area */
canvas_object.clipRegion(reg);
canvas_object.drawBitmap(temp,matrix_object,paint_object);
I override the drawMyLocation of MyLocationOverlay to display an arrow as marker instead of the normal blue dot. However, I also need to change the direction to which the pointer is pointing depending on the sensor of the phone. I decided to call drawMyLocation inside onSensorChanged. But one of the parameters required to call drawMyLocation is a Canvas. How can I access the Canvas of the MyLocationOverlay? Or do I need to create a new Canvas and pass the new Canvas everytime I call drawMyLocation inside onSensorChanged?
Below is my code when I tried to override draw method. However, even though I can see that it was continuously being executed, it takes a while for the rotated bitmap to display.
**UPDATE: If I try to touch my map, I can see my arrow rotating as I touch the map. However, if I don't touch it, it takes a while for the arrow to update its direction.
public boolean draw(android.graphics.Canvas canvas, MapView mapView, boolean shadow, long when)
{
Log.v("Message", "Executing draw method...");
boolean result;
result = super.draw(canvas, mapView, shadow, when);
if(geoLastFix != null) //geoLastFix contains the GeoPoint of my Location.
{
Point screenPts = mapView.getProjection().toPixels(geoLastFix, null);
//rotate bitmap
Bitmap arrowBitmap = marker;
Matrix matrix = new Matrix();
matrix.postRotate(orientation);
Bitmap rotatedBmp = Bitmap.createBitmap(
arrowBitmap,
0, 0,
arrowBitmap.getWidth(),
arrowBitmap.getHeight(),
matrix,
true
);
//print bitmap to canvas
canvas.drawBitmap(
rotatedBmp,
screenPts.x - (rotatedBmp.getWidth() / 2),
screenPts.y - (rotatedBmp.getHeight() / 2),
null
);
}
return result;
}
You should not be accessing the canvas outside of the draw() method. If you find yourself needing to do drawing or use the canvas, you should override the draw() method.
Use the onSensonrChanged method to save the current orientation or sensor information into your classes state and then override the draw() method and use the saved state to augment your drawing routine.
I am trying to implement my own map overlay for osmdroid (but I assume it is fairly similar to Google map overlays).
What I am trying to do is draw a plane, rotate it according to bearing and draw a speed vector (line ahead of the plane in the flight direction that shows where it will soon be).
The idea is that I draw the plane (et all) on a canvas "facing North", then rotate it according to flight direction and "merge" it with the overlay canvas (I tried drawing directly to the overlay canvas, but on rotate, it was rotating the map as well).
I have created a subclass of Overlay and overiden the onDraw method as follows:
#Override
protected void draw(Canvas c, MapView mapView, boolean shadow) {
if (location != null) {
Point locPoint = new Point();
GeoPoint locGeoPoint = new GeoPoint(location);
final Projection pj = mapView.getProjection();
pj.toMapPixels(locGeoPoint, locPoint);
this.drawPlane(c, locPoint, location.getBearing());
}
}
private void drawPlane(Canvas cs, Point ctr, float bearing) {
Paint paint = new Paint();
paint.setColor(Color.RED);
paint.setAntiAlias(true);
Bitmap planeBM = Bitmap.createBitmap(cs.getWidth(), cs.getHeight(), Bitmap.Config.ARGB_8888);
planeBM.setDensity(cs.getDensity());
Canvas c = new Canvas(planeBM);
Rect r = new Rect();
//Point center = new Point(cs.getWidth() / 2, cs.getHeight() /2);
Point center = new Point(0, 0);
// Draw fuselage
r.left = center.x - PLANE_WIDTH / 2;
r.right = r.left + PLANE_WIDTH;
r.top = center.y - PLANE_SIZE / 3;
r.bottom = r.top + PLANE_SIZE;
c.drawRect(r, paint);
// Draw wing (REMOVED)
// Draw stabilizer (REMOVED)
// TODO Draw Speed vector
// "Merging" canvas
Matrix merge = new Matrix(cs.getMatrix());
//merge.setTranslate(0, 0);
//merge.setRotate(bearing, center.x, center.y);
cs.drawBitmap(planeBM, merge, paint);
cs.save();
}
Basically my plane never shows.
I assume this has to do with the matrix in the initial canvas which has large values (I assume these are sort of geographical coordinates).
It all seems to be consistent though (the plane location has large values as well consistent with the matrix).
I have tried a number of things :
drawing from the actual plane location (large values) : did not help;
setting the matrix of my new canvas with the overlay canvas matrix : did not help;
merging with a new "empty" matrix : did not help;
-...
I know that my image contains the plane (at least if I draw from 0,0 or the new canvas center as I saved it to the SD to check...
In case this is usefull to someone, I finally found a solution.
Basicaly I was trying to do too much at the same time.
Using setPostRotate instead of setRotate on the merge matrix did solve the issue (that and goig bac to the drawing board for the correct translation parameters).
Using the below as a merge matrix worked:
// "Merging" canvas
Matrix merge = new Matrix();
merge.setTranslate(loc.x - pCenter.x, loc.y - pCenter.y);
merge.postRotate(bearing, loc.x, loc.y);