I’m working on an application that uses the OpenStreetMap (OSM) API to display various points of interest on an offline map, composed of static tiles.
One of the features that I am currently implementing is having the map rotate in accordance with the bearing determined by the phone (via GPS). I was able to implement the actual rotation without too much effort, but since my code rotates the entire canvas — perhaps a rather naive approach — I now have blank corners on the screen where no new tiles are being loaded to compensate for the fact that the rotated tiles no longer fill these pixels.
After a bit of Googling, I found a few suggestions as to how I might be able to solve this issue, but so far no luck.
In one of Mr. Romain Guy’s posts, he mentions the following:
I have done this in the past and it requires to create a custom
ViewGroup that rotates the Canvas in the dispatchDraw() method. You
also need to increase the size of the MapView (so that it draws enough
pixels when rotated.) You will also need to rotate the touch events in
dispatchTouchEvent(). Or if you use Android 3.0 you can simply call
theMapView.rotate()
Taking his advice on map rotation, I have implemented dispatchDraw() and dispatchTouchEvent() as suggested, but I’m having trouble with the part where he mentions that I need to increase the size of the MapView?
Is this something that I do in the XML file, like suggested in this thread?
Or, can I somehow override the onMeasure() function in my subclassed RelativeLayout class that handles my map rotation?
Suggestions and hints are most welcome.
UPDATE:
In an attempt to find an acceptable solution to this problem, I tried to change the size of the canvas. The thinking was that with a canvas size that is bigger than the actual screen size, I might be able to move the blank corners off the screen entirely. Unfortunately, there does not appear to be an actual canvas.size() option; the best I found was canvas.scale().
Using canvas.scale(), I was able to increase the scale of the canvas by a factor of 2 in both the horizontal as well as vertical dimensions. This means, however, that the image is effectively zoomed in, causing unacceptable pixelation to the map tiles.
Does anyone know where the size of the canvas gets declared, and if changing the size of the canvas might actually solve my problem?
I ended up following Romain Guy’s advice (the same advice that I posted in my question). That is, I created my own custom
ViewGroupby extending theRelativeLayout, and I then increased the size of mymapViewto provide coverage of the entire screen. Extending theRelativeLayout ViewGroupwas necessary so that I could override thedispatchDraw(...), theonMeasure(...), as well as thedispatchTouchEvent(...)functions to enable the desired map rotation features for my application.The
dispatchDraw(...)function essentially intercepts calls to theonDraw(...)function, performs a few specific manipulations on the input to that function and then releases it to be processed. In our case, we’ll want to rotate themapViewcanvas before it makes its way to the actualonDraw(...)function. This is why we’ll need to override this function.Specifically, the
dispatchDraw(...)function takes as input a canvas object, which (in this case) represents the OSMmapViewobject (as defined in the XML file below). If a rotation is to be applied to the canvas, we’ll want to locate the center of the map, translate (i.e. move) the map so that the center of the map sits on the origin of the coordinate system, rotated the map about the origin of the coordinate system, and then, finally, we’ll want to dispatch this modified canvas to the next stage in the rendering pipeline.My code for this is below; note that
Manageris my own singleton creation that won’t exist in your implementation unless you write one yourself!Next we’ll need to override
dispatchTouchEvent(...), because any rotation of the OSMmapViewcanvas ends up rotating not only the graphical representation of the map, but also everything else related to that Activity (this occurs as a side effect of my specific implementation); that is, touch event coordinates remain relative to themapViewcanvas after being rotated and not relative to the actual phone. For example, if we imagine the canvas being rotated by 180 degrees, then if the user attempts to pan the map to the left, it will instead move to the map to the right, since everything is upside-down!In code, you might correct for this problem as follows:
Finally, the trick to getting the corresponding XML for the map activity to work properly is to utilize a
FrameLayoutas the parent to all other GUI elements in my layout. This allowed me to make themapViewdimensions considerably larger than the dimensions of the display on my Nexus One (480 by 800). This solution also allowed me to nest aRelativeLayoutinside myFrameLayoutwhile still respecting the device’s actual display dimensions when usingmatch_parentand similar parameters.The relevant portion of my XML layout looks like this:
I’d like to remark that this solution is by no means the best solution, but that it worked out fine for my proof-of-concept application!