I'm using OSMdroid to implement a mapping application.
I have implemented a custom MapTileProvider that uses a tile source that allows zoom levels up to 22.
The default MAPNIK provider only allows zooms to level 18.
The problem is that any PathOverlay instances draw perfectly until zoom level 19, but then
are not drawn properly at zoom level 20-22. it looks like someone's rubbed out the path with an eraser over 90% of the path length (see screenshots below).
I've stepped through the draw() method of PathOverlay and exerything seems to be calculating correctly (the intermediate points appear correct for ZoomLevel 22, and then the XY projections are dividing by 22-ZoomLevel to get current screen coordinates).
Can anyone provide some insight as to what the problem is, and how to resolve it?
The same thing happens if I invoke the MapView using Cloudmade small tiles, which allows zooms up until level 20 and is a 'built-in' osmDroid tile provider class.
//mMapTileProvider = new HighResMapTileProvider(this);
mMapTileProvider = new MapTileProviderBasic(this,TileSourceFactory.CLOUDMADESMALLTILES);
mMapView = new MapView(this, 256, mResourceProxy,mMapTileProvider);
So the problem does not appear to be with the tile source or provider but with the canvas drawing method. Any ideas on how to resolve this?
At zoomLevel 19 I can see my paths nicely:
But here is that same path at the next zoom level:
I've found a workaround. It's not ideal, but it does work.
Firstly, if I add a
canvas.drawCircle(screenPoint1.x, screenPoint1.y, 5, cpaint);
to PathOverlay's draw() method I see this, so I know the co-ordinates are at least being calculated correctly.
So the problem seems to be related to the underlying line draw method in Android.
After some trial and error, I found that setting the STROKE WIDTH to 0.0 for the PathOverlay's Paint object fixes the problem, but the line is obviously only 1 pixel wide.
Adding a check for the current zoom level in PathOverlay.draw() to set the stroke width will keep the current behaviour for levels <20 and draw a hairline path for higher zoom levels.
Some other things I noticed:
The circles become squares at zoom level 21&22. This strongly suggests that there's some floating point precision issues when passing very large (x,y) co-ordinates to Path.lineTo / canvas.drawCircle etc, e.g. mPath.lineTo(131000001,38000001)
Setting stroke width to say, 5 sort-of works up to zoom level 21, but the same problem crops up at level 22 again
Update: This has been fixed in osmdroid 3.0.9.
Original Answer:
This issue appears to be rooted in Android. I believe it is due to a rounding error that happens when you scroll a view to a large offset (possibly due to use of SKScalar). This bug can be isolated by creating a new android project with an activity and a view:
Begin with the view at the origin with no scroll offset yet.
Draw a circle on the canvas: canvas.drawCircle(screenCenterX, screenCenterY, 100, mPaint)
Scroll the view to a large number: mainView.scrollTo(536870912, 536870912)
Draw a circle on the canvas: canvas.drawCircle(newScreenCenterX, newScreenCenterY, 100, mPaint)
The first circle draws ok, the second one draws distorted. For further evidence, try to draw your path near 0 lat/0 long and zoom in - notice the distortion no longer appears.
I will update the osmdroid ticket with some possible solutions workarounds.
I am fairly sure this is because the default PathOverlay only draws the line between points in the view. If a the point is outside of the curent view the line segment is not drawen. At lower zoom levels you just don't see that the bit of the line going off the view is not showen as all the sections are small.
I think you have 2 options.
The easy but maybe not the best is to put more points into the path, then at least the problem will be less noticable. If this is a good idea or not will depend how many points you already have.
The corect solution will be to extend the class and do your own draw method then clip the line segmet that gose off the view to the view edge. Idealy you would contribute your code back to the source.
I have the same problem and I posted a bug on OSMDroid here:
http://code.google.com/p/osmdroid/issues/detail?can=2&start=0&num=100&q=221&colspec=ID%20Type%20Status%20Priority%20Milestone%20Owner%20Summary&groupby=&sort=&id=221
I'm not sure if it's a problem of OSMDroid or just a problem of too big canvas.
Other workaround would be drawing on a new, smaller canvas (size of current visible mapView) and using drawBitmap in the top left corner of a big canvas. Just remember not to create new bitmap each draw - because it's expensive.
All shapes are drawn perfectly fine but Im struggling with other problem with not smooth panning in levels > 18. You can see that when you pan the map it is not moving every pixel as it is in levels < 18, but it jumps over few pixels.
I agree that this is OSMDROID related bug because I got the same working DrawPath function (Zoom level 21 & 22) using Google MapView. I hope they will be able to address this issue.
To solve this problem, we need to implement the line clipping algorithm.
By clipping with the map viewport, the path is adding more vertexes.
Thus it will be drawed correctly at big zoom level.
Related
I have the lat/lon for the two BoundingBox corners
and I need to get the "Projection.toPixels()" for those two
points for use with OpenStreetMap.
I already have the basic functions, like getting the Tile
numbers for a lat/lon location, and given a lat/lon in a Tile
getting the X and Y pixels, but I can't find any code that
will help solve my BoundingBox problem.
It's a large BoundingBox, and usually, at zoom 8, the Tiles
with the BoundingBox points, are outside the normal display grid of Tiles
for the display, so the point on the "left" is something like X: -169 Y: -343
and the point on the "right" is like X: 841 Y: 849
So I need a formula, given the lat/lon of the BoundingBox corner
points, and end up with rectangle points like the above pixel offsets.
Currently I'm using Google Maps, and I can get it's version of the
BoundingBox points with "Projection.toPixels()" basically, but I'm
converting to use OpenStreetMap. They are both based on 256 X 256 tiles,
and use the same Projection, but I don't want to use any Google Maps API calls to do it.
I have seen some Google Maps like source in JavaScript that looks pretty good, but nothing about using BoundingBox.
I already have the display center lat/lon,
zoom level 8, and other needed data, but not the needed formula(s).
The goal is to call an Android draw() function like:
canvas.drawBitmap(
imageBitmap, // bitmap
null, // src rect
BBRect, // dest rect
paint); // paint
I'd like to see code that's similar to Java so I can understand it
enough to convert it if needed.
Thanks!
I think I finally have it "working".
This isn't the ideal way to do it, but it seems to "work".
The Projection has to take into account the display
size for it to work, for the proper solution.
But I was thinking about it from the point of view
of the grid of tiles it drew to make up the display.
For my display, it used a grid of three columns
and five rows of tiles for each, and you know what the X and Y it drew each at, and you can figure out where the
corner tiles of the BoundingBox from the lat/lon are,
and you can get the X/Y pixel offsets for the lat/lon in each corner tile.
So you can just "extend" the X/Y tile draw points out to the corner tiles, and then adjust for the pixel offsets in each corner tile, and you should have the rectangle points
for the BoundingBox.
My problem was that initially, when I tried to do that,
the image was a good ways off. I finally realized that
for the Android Canvas drawing coordinates, it used the
top left corner, but for whatever reason, I was thinking that it was from the bottom left, so once I re-calculated,
it finally looked like it drew the image in the right spot.
I'd like to be able to use the Projection, and properly
figure out how to get the toPixels(), but for now I think
this solution will work.
I got a question in my mind about how can I bound 2(or more) circle via android api.
The right side of the image is what I exactly want.
this is how can I creating my circle.
circle=mMap.addCircle(new CircleOptions()
.center(geoPoint
.radius(40)
.fillColor(0x12541252)
.strokeWidth(3)
.strokeColor(Color.RED));
I really will be glad, if you suggest me any way to solve my this problem..! thanks in advance!
The order in which this tile overlay is drawn with respect to other overlays (including GroundOverlays, TileOverlays, Polylines, and Polygons but not Markers). An overlay with a larger z-index is drawn over overlays with smaller z-indices. The order of overlays with the same z-index is arbitrary. The default zIndex is 0.
Overlays (such as circles) with higher zIndices are drawn above those with lower indices.
You should use the method mentioned below with Circle and the ZIndex values for both the Circle should be different
public void setZIndex (float zIndex)
To get more help follow this link:
https://developers.google.com/android/reference/com/google/android/gms/maps/model/Circle.html#setZIndex(float)
I am working with osmdroid and trying to display map tiles past the zoom level of 19. I have set the mapview max zoom to be 20, and told the tile source to use a max level of 20 as well. I can "zoom" to the level 20 (it just appears more pixelated as it doesn't seem that Mapnik actually provide them at that level) which at the minute is OK, but then when I pan across to outside of the current tile no new tiles get loaded
mMapView.setMaxZoomLevel(20);
mMapView.getTileProvider().setTileSource(new XYTileSource("Mapnik", null,
0, 20, 256, ".png", new String[] {
"http://a.tile.openstreetmap.org/",
"http://b.tile.openstreetmap.org/",
"http://c.tile.openstreetmap.org/" }));
Is there a way to get it to load these tiles whilst still zoomed in? I have read some answers that say there is an issue with zooming past 19 in osmdroid (Android OSM zoom level 19 and above), so I am not sure if this is something to do with that...
As of right now, no. As you zoom in past the available tiles, the animator for zooming in stretches the previous tile to fill the screen during animation. Once the new tile is loaded, it's redrawn with the correct tile.
It's possible to attempt to retrieve tile current zoom -1, then stretch and crop it to fit the screen, but that functionality doesn't exist right now. Feel free to write it and open a pull request.
I used https://github.com/ened/Android-Tiling-ScrollView library to display large tiled image. Everything is working fine. Only problem is that, I want to zoom only particular part which I touched(pinch-to-zoom as in Google map). Now, its always display tiles from beginning for all zoom levels irrespective of where I touched. Sometimes, it takes me near where I want to zoom but its not perfect as sometimes it takes me to different part. Following code does moving logic
mScroller.startScroll(getScrollX(), getScrollY(), newOffsetX, newOffsetY);
First 2 parameters indicate start scroll offset and last 2 indicates destination points to where scroll ends. I tried different combinations for last 2 parameters but ran out of luck.
If it's a ImageView, I would have done
canvas.translate(x, y);
to Zoom particular part but unfortunately its not ImageView. Even though I applied ImageView zoom logic as in https://github.com/jasonpolites/gesture-imageview to tile Zoom but had no luck.
Suggestions or clues are greatly appreciated.
Thanks.
Got a very nice library for custom mapView with almost all the features of google map. Its here.
I have to draw about 10000 line on Google Maps. So, it is spending too much time in draw() method. Moving on map becomes very laggy. Is there any way to cache drawing or can I draw just the part of map / canvas which is currently seen on screen?
drawing 10000 lines will never get lag free. I'm guessing you connect points.
Here is an implementation of point Clustering in mapView and also renders the visible ones if you want. So you can draw lines to the clustered points.
Now I can draw all 10000 lines without any lag. It is all about designing draw() method carefully. I moved some object creating operations (like Path, Point) out of draw(). I saw that especially projection.toPixels(geoPoint, point); is very expensive operation. Finally I set an alpha constant which holds the pixel value of finger movement. And it only draws when pixelX or pixelY movement is bigger than alpha.
Have a look at this post, it suggests drawing your lines into a shape then drawing that to the mapview.
Here: Cache whats being draw on MapView in Android
Just a suggestion on this one, you may want to try saving the MapView as a bitmap then render that instead (depending on your situation).
Here: Save MapView as a Bitmap