Porting a Google Maps app to Osmdroid - problem with overlay - android

I'm porting a Google Maps based project to Osmdroid in order to use OpenStreetMaps. The port is working OK apart from adding my overlay which consists of a number of straight lines and some text. In both projects I add the Overlay by means of a timer thread and handler calling redrawOverlay.
In the OSM project my overlay is just a grey square completely hiding the map. If I remove the call to redrawOveraly, the OSM tiles are shown OK. I've reduced the overlay code to the bare minimum of a single diagonal line in the code samples below. It works fine in the Google app, overlaying the map tile. The com.google.android.maps.Overlay has a draw method, the OSM has an onDraw, so I have in the OSM version:
private MapView mv;
private MapOverlay mmapOverlay = null;
private void redrawOverlay() {
gPt = mv.getMapCenter();
if (mmapOverlay == null)
mmapOverlay = new MapOverlay(getApplicationContext());
List<Overlay> listOfOverlays = mv.getOverlays();
listOfOverlays.clear();
listOfOverlays.add(mmapOverlay);
mv.invalidate();
}
public class MapOverlay extends org.osmdroid.views.overlay.Overlay {
public MapOverlay(Context ctx) {
super(ctx);
// TODO Auto-generated constructor stub
}
#Override
public void onDraw(Canvas canvas, MapView mapView) {
Paint lp3;
lp3 = new Paint();
lp3.setColor(Color.RED);
lp3.setAntiAlias(true);
lp3.setStyle(Style.STROKE);
lp3.setStrokeWidth(1);
lp3.setTextAlign(Paint.Align.LEFT);
lp3.setTextSize(12);
canvas.drawLine(10, 10, 150, 150, lp3);
}
Whilst in the Google maps original I have the equivalent :
public class MapOverlay extends com.google.android.maps.Overlay {
#Override
public boolean draw(Canvas canvas, MapView mapView, boolean shadow,
long when) {
super.draw(canvas, mapView, shadow);
Paint lp3;
lp3 = new Paint();
.....etc.
redrawOverlay is the same except the instantiation of the overlay is just:
mmapOverlay = new MapOverlay();
All suggestions will be gratefully received
UPDATE Question for kurtzmarc:
Thanks for you help so far, I see that you are one of the authors of Osmdroid. I like what it's doing so far. I would like to suppress the 'jump to and zoom in' that you get on double tap. I'd like it to do nothing at all. I think it's probably hitting this bit in your source and doing the zoomInFixing:
private class MapViewDoubleClickListener implements GestureDetector.OnDoubleTapListener {
#Override
public boolean onDoubleTap(final MotionEvent e) {
for (int i = mOverlays.size() - 1; i >= 0; i--)
if (mOverlays.get(i).onDoubleTapUp(e, MapView.this))
return true;
final GeoPoint center = getProjection().fromPixels(e.getX(), e.getY());
return zoomInFixing(center);
}
It doesn't seem that I can override it. I'm using the 3.0.1 jar and the associated javadocs. I'm wondering if the Mapview's setTouchDelegate method would help, but there is no reference to it in the javadocs. Have you any suggestions please?

I'm not sure where you are calling redrawOverlay() from, but if you look at the MinimapOverlay you will see an example where something is drawn at a fixed location on the screen. In other words, you are drawing in screen coordinates not in map coordinates.
Example:
#Override
protected void onDraw(final Canvas pC, final MapView pOsmv) {
// Calculate the half-world size
final Rect viewportRect = new Rect();
final Projection projection = pOsmv.getProjection();
final int zoomLevel = projection.getZoomLevel();
final int tileZoom = projection.getTileMapZoom();
mWorldSize_2 = 1 << (zoomLevel + tileZoom - 1);
// Find what's on the screen
final BoundingBoxE6 boundingBox = projection.getBoundingBox();
final Point upperLeft = org.osmdroid.views.util.Mercator
.projectGeoPoint(boundingBox.getLatNorthE6(), boundingBox.getLonWestE6(),
zoomLevel + tileZoom, null);
final Point lowerRight = org.osmdroid.views.util.Mercator
.projectGeoPoint(boundingBox.getLatSouthE6(), boundingBox.getLonEastE6(), zoomLevel
+ tileZoom, null);
// Save the Mercator coordinates of what is on the screen
viewportRect.set(upperLeft.x, upperLeft.y, lowerRight.x, lowerRight.y);
// Offset into OSM coordinates
viewportRect.offset(-mWorldSize_2, -mWorldSize_2);
// Draw a line from one corner to the other
canvas.drawLine(viewportRect.left, viewportRect.top, viewportRect.right, viewportRect.bottom);
From here viewportRect represents the upper left to the lower right of the screen. You can use this to draw at any fixed points on the screen.
UPDATE:
To answer your second question - what you need to do is override onDoubleTap in your Overlay and return "true". Returning "true" indicates to the base class that you "consumed" the event and no further processing should take place. Take a look at the minimap overlay code for a good example:
http://code.google.com/p/osmdroid/source/browse/trunk/osmdroid-android/src/org/osmdroid/views/overlay/MinimapOverlay.java
We are right in the middle of overhauling the Overlays, so some of this will be handled a little better in the near future. For example, the getOverlays().clear() bug you ran into has also been reported elsewhere and we've since fixed it.

Related

OSMDroid MapView. Successfully added custom markers, but defaults also showing too?

My Code is as follows. Essentially, I am trying to replace each marker with my custom marker, namely my drawable beer_full.
This overridden draw function in my custom overlay is doing the job perfectly, BUT it leaves the default icon there too. So my markers are appearing on my map as my custom marker on top of the default marker.
Anyone know how I stop the default marker showing?
Cheers
#Override
public void draw(Canvas canvas, MapView mapview, boolean shadow) {
// TODO Auto-generated method stub
super.draw(canvas, mapview, shadow);
if(!shadow)
{
for (int ctr = 0; ctr < myOverlays.size(); ctr++)
{
GeoPoint in = myOverlays.get(ctr).getPoint();
//Toast.makeText(mapview.getContext(), ctr, Toast.LENGTH_SHORT).show();
Point out = new Point();
mapview.getProjection().toPixels(in, out);
Bitmap bm = BitmapFactory.decodeResource(mapview.getResources(),
R.drawable.beer_full);
canvas.drawBitmap(bm,
out.x - bm.getWidth()/2, //shift the bitmap center
out.y - bm.getHeight()/2, //shift the bitmap center
null);
}
}
}
Remove the line super.draw(canvas, mapview, shadow);. You are basically drawing the default icon (by calling the default implementation of the draw method), and then drawing your custom icon over it.

Android: How can get paint on mapviews?

I am getting the MapView of current location by passing longitude and latitude parameters correctly. But i want to draw paint just like free hand (i.e names or lines and so..on) on that MapView image. from that i want total image of MapViewimage on that paint by user.
How can I do this?
when user send his path to another user like shown below...
Use an overlay. See this example Overlays
Here's example code:
public class overlayGeoPoints extends Overlay {
private GeoPoint geoPoint;
private int radius = 20;
#Override
public boolean onTap(GeoPoint geoPoint, MapView mapView){
this.geoPoint = geoPoint;
}
#Override
public void draw(Canvas canvas, MapView mapView, boolean shadow) {
// put your logic here to draw on the overlay
// ie draw a circle
Projection projection = mapView.getProjection();
Point point = new Point();
projection.toPixels(geoPoint, point);
int px = point.x;
int py = point.y;
Paint paint = new Paint(paintSecondary.ANTI_ALIAS_FLAG);
hl.setColor(intColor);
canvas.drawCircle(px, py, radius, paint);
canvas.save();
}
}
Just add this overlay to your map.
Edit
Here is a step by step (generic):
AFTER your map object (map) is created
// get a handle to the map overlays
List<Overlay> overlays = mapView.getOverlays();
// reset overlays as necessary
//overlays.clear();
// create your custom overly class
overlayGeoPoints olay = new overlayGeoPoints();
// append overlay to map overlays
overlays.add(olay);
// tell the map object to redraw
mapView.postInvalidate();
If I correctly understood you question, you want to capture the drwaing movements and display then over the map.
Steps:
Create a new class extending Overlay
Override onTouchEvent() to get the positions (x,y) the user taps and move finger on, convert them to Geopoint and add the geopoints to a Path object. Call invalidate() to have the map redrwan.
Override onDraw() to draw the path build above with canvas.drawPath()
Finally, add the overlay to the end of mapView.getOverlays(), to ensure this overlay is the first getting touch events.
good luck.
// This file is MapViewDemoActivity.java
import android.os.Bundle;
import android.view.View;
import com.google.android.maps.MapActivity;
import com.google.android.maps.MapView;
public class MapViewDemoActivity extends MapActivity {
private MapView mapView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.mapview);
mapView = (MapView)findViewById(R.id.mapview);
mapView.setBuiltInZoomControls(true); //display zoom controls
}
#Override
protected boolean isLocationDisplayed() {
return false;
}
this like is greate to usefull to all http://n3vrax.wordpress.com/2011/08/13/drawing-overlays-on-android-map-view/

Android Overlay setting Boundary for ontap method?

Recently I have drew a custom polygon around my country by creating a overlay class and drew it using path.
I have many sector of a map, each sector is divided and filled with color with a overlay class. However when I use the ontap function, only the last overlay item ontap function get called.
I believe is because I did not set a boundary for the overlay? The following is my overlay code
public class SectorOverlay extends Overlay{
CustomPolygon customPolygon =null;
Context context;
public SectorOverlay(Context context, CustomPolygon customPolygon) {
this.context=context;
this.customPolygon =customPolygon;
}
#Override
public void draw(Canvas canvas, MapView mapView, boolean shadow)
{
shadow=false;
Paint paint = new Paint();
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setStrokeWidth(2);
paint.setColor(0x10000000);
paint.setStyle(Paint.Style.FILL_AND_STROKE);
paint.setAntiAlias(true);
Point point1_draw = new Point();
if(customPolygon!=null)
{
Path path = new Path();
path.setFillType(Path.FillType.EVEN_ODD);
for(int n=0;n<customPolygon.getCorrdinateList().size();n++)
{
GeoPoint sector1 = new GeoPoint((int)(customPolygon.getCorrdinateList().get(n).getLatitude()*1e6), (int)((customPolygon.getCorrdinateList().get(n).getLongitude())*1e6));
if(n==0){
mapView.getProjection().toPixels(sector1, point1_draw);
path.moveTo(point1_draw.x,point1_draw.y);
}else
{
mapView.getProjection().toPixels(sector1, point1_draw);
path.lineTo(point1_draw.x,point1_draw.y);
}
}
path.close();
canvas.drawPath(path, paint);
}
}
#Override
public boolean onTap(GeoPoint p, MapView mapView) {
new CommonOpearation().showToast(context, customPolygon.getName());
return true;
}
}
It is being called for the "last" Overlay, because it is the topmost. MapView draws its overlays from 0..end, but when there is an event, the last drawn is on top, so it gets the event first, and since you return true in Overlay.onTap (same applies to Overlay.onTouchEvent, and practically a lot of Android events) you're saying that the event is handled, therefore it doesn't bother calling the overlays down the line. So the event handlers are called in end..0 order.
I did not use onTap, but according to CommonsWare at Android - Map overlay onTouchEvent / onTap howto? if you use ItemizedOverlay you should get onTap only for your bounded/drawn area.
It is surely possible that common onTap and onTouchEvents are called for any touch point on screen. In this case you can find the GeoPoint with getProjection from MapView applying it reversly to (x,y) getting (lon,lat). Or using onTap (again, I haven't heard about this before).
If you need to do a hit-test with the drawn polygon of the overlay, here's one which helps zou determine whether the point is in the (not necessarily convex) polygon:
http://verkkoopetus.cs.utu.fi/vhanke/trakla/PointInPolygon.html
for 2. LStart.x = minXOfPolygon, LEnd.x = maxXOfPolygon, LStart.y = LEnd.y = 0
(you may need extend the line by 1 unit at LStart.x and LEnd.x just to be safe.)
for 4. use findIntersection from http://workshop.evolutionzone.com/2007/09/10/code-2d-line-intersection/

MapView with Overlay has performance issues

I have a MapView that will contain alot of overlays. For this i've extended the Overlay class as i've heard it has better performance than ItemizedOverlay (that was calling populate for every element).
When the map is moved or zoomed, in the MapActivity, I select only the visible drawables and I call:
protected void RefreshOverlay() {
Log.e("||||||||||||||||||||||||||", "RefrashOverlay()");
mapOverlays.clear();
mapOverlays.add(new OverlayElements(zonesP,getApplicationContext()));
}
The class OverlayElements extends Overlay and here i do all the drawings:
public void draw(Canvas canvas, MapView mapview, boolean shadow) {
super.draw(canvas, mapview, shadow);
int i;
int sz=zonesP.size();
int style;
ZoneProperties zone;
//paint.setAntiAlias(true);
pp = new Point();
int zoomLevelC=mapview.getZoomLevel();
Projection pr = mapview.getProjection();
for(i=0;i<sz;i++)
{
zone=zonesP.get(i);
if(zone.visible==true)
{
pr.toPixels(zone.point, pp);
xp = pp.x;
yp = pp.y;
if(zone.zoneSizePx<500)
{
//----------
Log.e("zone radious>>",zone.zoneSizePx+"<<");
int alfa;
style=(int)i%2;
Log.e("style>>",i+">>"+r[i]+" "+g[i]+" "+b[i]);
paint.setColor(Color.rgb(r[style],g[style],b[style]));
alfa=(18-zoomLevelC)*35;
if(alfa>255)
alfa = 200;
else
if(alfa<0) alfa = 20;
paint.setAlpha(alfa);
canvas.drawCircle(xp, yp, zone.zoneSizePx, paint);
}
if(zone.select!=0)
if(zone.vState==2)
canvas.drawBitmap(getDrawRes(zone.select,zone.eSizePx),xp,yp,null);
}
//----------------
}
}
Well, yeah this drawing isn't that fast :) .
Should i go back ItemizedOverlay, and if so how can i make the populate call only once? Any optimization idea? Thank you!! :)
You do not call populate() on an ItemizedOverlay until all of the items are ready, not for each item.
when i have similar drawables on the Map, i call only once populate() but mostly i have different one
That does not matter.
How could i add a new drawable without creating a new ItemizedOverlay?
Override getMarker() in a custom subclass of OverlayItem. Here is a sample project demonstrating this, where the drawable is toggled between different images at runtime.

ItemizedOverlay and custom marker on OsmDroid for Android, no more touch event

I'm curently using a class which extends from the Drawable on OsmDroid.
And on this class I draw a geometric form for each car I receive from internet.
I would like to add a listener on each car in order to give some informations when we touch the car.
With a OverlayItem we can do this :
ArrayList<OverlayItem> items = new ArrayList<OverlayItem>();
carOverlay.draw(new Canvas());
overlayItem = new OverlayItem("test", ""+i, geoPoint);
overlayItem.setMarker(carOverlay.getCurrent());
items.add(overlayItem);
ItemizedOverlay<OverlayItem> overlay = new ItemizedOverlayWithFocus<OverlayItem>(this.getApplicationContext(), items,
new ItemizedIconOverlay.OnItemGestureListener<OverlayItem>() {
#Override
public boolean onItemSingleTapUp(final int index, final OverlayItem item) {
Log.i("Touch","you just tap the car");
return true;
}
#Override
public boolean onItemLongPress(final int index, final OverlayItem item) {
return false;
}
});
With this piece of code, I can see the image I've just drawn, but no event when I touch a car, and if I remove the line :
overlayItem.setMarker(carOverlay.getCurrent());
to let the default marker being displayed instead of my custom marker, the touch events occure.
So what am I doing wrong ? must be the same no ?
This is not a 'proper' answer but as I am facing similar problem I resorted to using BOTH default draw and custom at the same time.
I just call super() at the end of my custom draw method on ItemizedIconOverlay and both my drawing and 'default' item marker are drawn.
Maybe it would be possible to tweak default marker to make it invisible (empty bitmap)... experiment. :)
#Override
public void draw(Canvas canvas, MapView mv, boolean shadow) {
if(!shadow) {
//my draw code
}
super.draw(canvas, mv, shadow);
}
tmouse is correct. If you're overriding the ItemizedOverlayWithFocus.draw() method to draw your car with canvas, you'll need to add:
super.draw(canvas, view, shadow);
The super will setup and use the OverlayItem's Hotspot which is (from what I can see in the source) used as the touchable area for gestures.
However you'll still see the original Markers AND your canvas drawn ones. PLUS if you move your cars, the touchable area won't move with it. (the Hotspot will not be for the new location you drew at)
There are only 2 real solutions.
1) You re-implement ItemizedOverlayWithFocus.draw() and ItemizedOverlayWithFocus.onDrawItem() in your own ItemizedOverlayWithFocus class. (only you'll call onDrawItem(), onDrawCar(), and call it instead from the overridden draw() method)
That's not worth it unless you want to change the offset of the Marker being drawn. e.g. I have a tower icon, and I want 10 pixels from the bottom to be the Y point so it looks like the tower is standing on the GeoPoint, not at the edge of it.
2) You create your own Markers from a Bitmap first, and set it as a Drawable Marker in the OverlayItem.setMarker() method. Try creating a a Bitmap using Canvas:
Bitmap bitmap = Bitmap.createBitmap(width, height, Config.RGB_565);
Canvas canvas = new Canvas(bitmap);
// Draw on your car on the canvas...
Then try assigning the Bitmap:
overlayItem.setMarker(new BitmapDrawable(bitmap));
Don't override draw() unless you want to draw some other stuff, and if you do ensure you super.draw() so the Marker and its Hotspot work with the gestures.
You can try also this code
Drawable newMarker = this.getResources().getDrawable(R.drawable.ic_marker);
this.mMyLocationOverlay = new ItemizedIconOverlay<OverlayItem>(items,
newMarker, "listener try null", new DefaultResourceProxyImpl(getActivity()));
mapView.getOverlays().add(mMyLocationOverlay);

Categories

Resources