mapbox / DEPRECATED-mapbox-ios-sdk

REPLACED – use https://www.mapbox.com/ios-sdk instead
https://github.com/mapbox/mapbox-gl-native
Other
323 stars 8 forks source link

RMMark, RMPath "vibrate" while the RMMapView is being scrolled #72

Open mouse4d opened 12 years ago

mouse4d commented 12 years ago

while scrolling the map the RMMarker and RMPath overlay "vibrates", it's like the overlay tries to "catch up" with it's normal position when the map is scrolled. It's just a few pixels, but when zoomed it looks pretty bad. this happens most probably because the -draw() method is called only when the map is moved more than just a pixel.

incanus commented 12 years ago

For RMPath, see #73.

For RMMarker, I haven't seen this. Do you still see it when not using a path/shape with your markers, and if so, when using RMShape instead of RMPath?

incanus commented 12 years ago

I at least can see this now. Will try to address soon.

incanus commented 12 years ago

Assigned to Tender discussion #259.

cga-F4 commented 12 years ago

Hi, I tried to see where this problem comes from. With high zoom level, we see that the tile layer moves pixel by pixel while the RMMarker moves only 4 by 4 pixel. I log some info in the function in RMMapView

I hope this can help you find a fix for this issue.

davidovitch1 commented 11 years ago

same problem here, any news ?

cga-F4 commented 11 years ago

Since last time, i tried using Alpstein Route Me, and we see the same problem with their example MarkerMurder. When you zoom to max, you see the marker vibrating (in this example, you can set the maxZoom to 22, and on maximum zoom, the markers vibrates much more). I saw the layer with annotations is not in the RMMapScrollView _mapScrollView. Would it be a bad idea or lots of work to have the RMMapOverlayView _overlayView and the _tiledLayersSuperview in the same scrollView ?

cga-F4 commented 11 years ago

I see the problem : when I am on level 19 zoom, and I look to Paris, France, my contentOffset has a value more than 60 000 000. When scrolling, we want to add 1, 2, 3 ... pixels to this value. But due to float precision, 60 000 000 + 1 returns 60 000 000 and 60 000 000 + 3 returns 60 000 004. We have a precision to 4 digit, so we see the annotions move only 4 pixels by 4 pixels. All because contentOffset is a float value (not double). [ By the way, I don't know how the _mapScrollView handles this by itself for the tileLayer which moves perfectly good, maybe the OS computation is in double but we can only access the contentOffset with float precision] One solution would be to ensure that our contentOffset does not go further than the float precision limit. From this post : http://stackoverflow.com/questions/1576126/iphone-and-floating-point-math, we can see that if we want a 1 pixel precision, we need to keep contentOffset under approximatively 8 000 000 (log2(8000000) = 22.93 < 23). This explains why we have the problem with big zoom level, and not average zoom level.

incanus commented 11 years ago

Interesting, @cga-F4. That's a great find & this makes sense. I will take a look.

incanus commented 11 years ago

@trasch, do you have any insight into this? I'm wondering if you ever tried the route of making the overlay layer size with the scroll view's content view, instead of making it a scroll layer with annotation corrections like it is now.

trasch commented 11 years ago

I did try it, but I really can't remember what the issue was. Maybe this is not an issue any more, since so many parts of route-me have changed in the meantime. It would certainly make lots of things easier if the overlay would have the size of the content view, maybe we could just try it.

Apart from that I honestly never thought that anyone would use route-me with zoom leves above 18... :)

psavich commented 11 years ago

Hello there. I'm experiencing the same problem on zoom levels 17 and above.

Here's a demo: https://dl.dropbox.com/u/586782/mapbox-marker-bug.mp4

The problem is definitely related to float precision of contentOffset – like cga-F4 I injected some logging into the value observer, and here's what I got (each line is a 1px movement):

2012-11-25 19:51:16.403 iGIS[98912:c07] ContentOffset: 162256000.000000 83907544.000000 2012-11-25 19:51:16.420 iGIS[98912:c07] ContentOffset: 162256000.000000 83907544.000000 2012-11-25 19:51:16.436 iGIS[98912:c07] ContentOffset: 162256000.000000 83907544.000000 2012-11-25 19:51:16.453 iGIS[98912:c07] ContentOffset: 162256016.000000 83907544.000000 2012-11-25 19:51:16.470 iGIS[98912:c07] ContentOffset: 162256016.000000 83907544.000000 2012-11-25 19:51:16.487 iGIS[98912:c07] ContentOffset: 162256016.000000 83907536.000000 2012-11-25 19:51:16.503 iGIS[98912:c07] ContentOffset: 162256016.000000 83907536.000000 2012-11-25 19:51:16.520 iGIS[98912:c07] ContentOffset: 162256016.000000 83907536.000000

As you can see, on 19 zoom we have a jump of 16px by x, and 8px by y. Each step of zooming out decreases the "jump distance" by factor of 2.

Unfortunately this issue is critical for our project, cause all RMPaths and RMMarkers vibrate really badly in some important use cases.

I see two solutions:

What do you guys think about this stuff?

zhm commented 11 years ago

+1 to seeing this one fixed. Once this gets fixed I can complete the conversion of 2 apps to this SDK :) This one is just kind of critical because users have maps that are often high resolution satellite imagery.

cga-F4 commented 11 years ago

Hi ! (and Happy New Year) Is there some news for this issue ? This is critical for us too.

incanus commented 11 years ago

I will be investigating this soon and working with upstream on it. Have a couple ideas here.

specialunderwear commented 11 years ago

This blog contains a proper solution to the problem: http://www.mlsite.net/blog/?p=1342 for more context, see https://github.com/Alpstein/route-me/issues/122#issuecomment-11965580

incanus commented 11 years ago

I'm not sure this does entirely solve the problem, though I have to investigate further. It says:

Note that the content view is set to the size of the entire rendered world; this does not cause out-of-memory problems because the content view does not render any content directly; only those subviews in its visible area are rendered.

Which leads me to believe that we will still have precision problems with higher zoom levels since the effective content view is still dealing in 1:1 pixel sizes with the current method.

specialunderwear commented 11 years ago

No, the problem you are seeing, is that on high zoom level you need to scroll 10 pixels for a contentOffset change to be fired, because it is assumed you are viewing a zoomed in bitmap. By resetting the zoomlevel back to 1 you will get back the fine grained scroll events.

incanus commented 11 years ago

Ah, interesting.

specialunderwear commented 11 years ago

Could you keep this ticket updated with progress? I was planning on working on this myself, but it's better to not waste 2 people's time working on the same thing. Or if you want some help, I'm more then happy to chip in :)

incanus commented 11 years ago

Yes, I will. I am working on this combined with improved tile load performance in a hopefully-major (if successful) overhaul of this subsystem.

incanus commented 11 years ago

Based on my testing, this is a limitation in UIScrollView. I'm currently working on some testing that uses a large (> z17) content view, nothing to do with the KVO calls, and its own content view vibrates in this manner on high zoom.

I'm working on this issue in combination with performance work from discussions in https://github.com/Alpstein/route-me/issues/35 and will probably end up doing something related to not actually having the scroll view content view be 2^zoom * 256 pixels to a side, but rather "faking out" the system in order to keep the content view within reasonable sizes.

specialunderwear commented 11 years ago

Yes, that is true, because again, it is assumed you are looking at a zoomed in bitmap, where it makes no sense to scroll less then 1 (hugely magnified) pixel. Now if you would have tested the kvo calls as well, you would have noticed the calls will only be triggered by scrolling for the magnified pixel (10 pixels instead of 1). Which still means that resetting the zoom level back to 1 after zooming has completed solved all problems. Also your problem IS the kvo calls because you are updating the position of the markers based on kvo events (thevmarkers are not even inside the scroll view). You can actually test this by zooming in heavily and scroll for less then 10 pixels and you will see no kvo events triggered untill you scroll enough pixels to cover an 'enlarged' pixel.

The markers are not inside the scroll view because they would be scaled while zooming. They are on an entirely different layer.

incanus commented 11 years ago

I am also resetting the zoom level back to 1 and also not drawing a bitmap, but rather using sublayers.

specialunderwear commented 11 years ago

Awesome, then in any case I'm positive the problem will be fixed. Not sure if having layers will change anything since cglayers are rendered to the pixel buffer as a bitmap in the end, at which point you can still do transformations on them.

So most likely you will be zooming and panning in the scrollview with the allready rendered layers. But yeah cglayer rendering is really fast.

Good luck!

incanus commented 11 years ago

Will keep at it. Thanks for the back-and-forth; will keep progress updated here.

specialunderwear commented 11 years ago

Hi how is it going? Any progress?

cliffrowley commented 11 years ago

Is there a workaround for this? Looking forward to a solution...

incanus commented 11 years ago

No workaround yet.

specialunderwear commented 11 years ago

Are you working on this in a branch or fork? I'd like to help.

incanus commented 11 years ago

I'm not actively working on it right now, as I'm preparing the next release of the SDK. After that, I'm targeting this and tile performance together since both will be somewhat disruptive.

If you'd like to work on it in the meantime, that's good. I can share some of the ideas I had, but I don't have code as of yet.

specialunderwear commented 11 years ago

Ok, I can work on it on the Alpstein fork. Please share your ideas. My email is in my github profile.

incanus commented 11 years ago

I'll just post them here. If you look at RMLoadingTileView, which is the gridded background behind loading map tiles, I'm thinking it could behave similarly. That view is a UIScrollView and it adjusts its content view after each pan and zoom to remain 3x the size of the super view and centered.

I'm thinking the actual tiled view(s) could behave like this, too. It could track the supposed offset so that it knows which tiles to display. The only thing I'm worried about is pans and zooms over large areas, as I'm not sure it will still look convincing and may look like the reuse that it is.

Does that make sense?

tkohout commented 11 years ago

I have found very dirty workaround for this. But I really needed it and it's working. The idea is to convert source from bigger zoom to smaller so the wiggle stops. Now it's working for only smaller areas and some small range of zoom. If anybody would be interested I can post the source codes.

incanus commented 11 years ago

I'd love to see it. I think that's basically what I'm going to try to do when I address this fully, effectively. The zoom will be legitimate but it won't zoom the scroll view as much as it does now. Feel free to gist the code or paste inline or whatever.

tkohout commented 11 years ago

Ok here it is, it is for alpstein fork but it should work for mapbox too I guess.

https://github.com/tkohout/route-me

There is sample Zoom Convert with some sample google map source.

Basic idea is to take base coordinate which will serve as zero point from where the map will stretch to every direction. Zoom steps is how much zoom levels you want to skip. Min zoom is there for converting base coordinate to proper base tile. Then every loaded tile is converted to proper tile from the bigger zoom.

All coordinates drawn on map (markers, boundaries, routes) have to be converted too.

I haven't tested it properly but it has basic idea.

lemak5 commented 11 years ago

@incanus : do you know when this bug will be fixed? Thank you !

incanus commented 11 years ago

No updates to report yet.

jcouture commented 11 years ago

Is there any known workaround or incoming fix for this issue?

tkohout commented 11 years ago

If you need to display only small area, for example one cite or state, it could help to move the city coordinates to the null coordinates where tile 0,0 is. The content offset would not get that high and the float issue would not appear. However, it requires change of map sources connectors code.

incanus commented 11 years ago

See also http://stackoverflow.com/questions/448285/how-do-i-reset-after-a-uiscrollview-zoom

gthompson333 commented 10 years ago

Hi All. It sounds like the best workaround to this issue is to dupe the scrollview into rendering tiles zoomed in, while it's zoomScale value is set to 1. This way, we still get the finely granular contentOffset property changes, which helps alleviate the twitchy pin movement. Is my nutshell solution description correct?

Any chance Mapbox might have this solution implemented soon? Thanks.

incanus commented 10 years ago

I have been working on a similar approach using a scroll view to drive a GL-based view, where the scroll view never leaves the 0.5 - 2.0 zoom scale range. I may be able to integrate this soon, but the implications throughout RMMapView are pretty broad. Many things use the scroll view values directly.

I've also spoken just yesterday with someone who fixed this internally a different way and am trying to get the solution contributed back.

gthompson333 commented 10 years ago

Hey incanus. Thanks much for the info.

It seems, from a high level perspective, what is happening is, the scrollview, by design, is adjusting the frequency of contentOffset events to be in proportion to the zoomLevel. This actually makes sense. At zoom level 1, a point of contentOffset would be different than at zoom level 10. That is, at zoom level 1, contentOffset events happen for every point of scroll, but at zoom level 10, one would expect contentOffset events to happen every 10 points of scroll.

Because pin markers are often not scaled, along with the zoom scale, the "every 10 points" of scroll, when contentOffset events are generated, is very pronounced. That is, twitchy pin marker movements.

I'm wondering if the ideal solution, albight not quickest, might be to eventually ditch the UIScrollview altogether, and implement your own scrolling image container.

I'm experimenting with some ideas. I'll be sure to keep you informed. Thanks much for your efforts in resolving this.

incanus commented 10 years ago

That's kind of what I was thinking -- use the scroll view for gestures and "bounciness", but maintain the world offset, bounds, and zoom independently of the scroll view's properties.

abetlehem commented 10 years ago

We had the same issue when overlaying layers with high-resolution tilesets, up to zoom level 22. When you zoomed, the tilesets would be very jumpy and stretched. To fix: Make sure all the overlay tilesets have alpha of 1.0. Instantly smooth scrolling. Not sure why but it worked for us.

cga-F4 commented 10 years ago

@incanus : now that v1.1.0 is released (we moved to this version for our project), do you plan to work on this bug ?

florianbuerger commented 10 years ago

Are there any updates for this issue? We are developing an applications for exhibitions and we are considering to use Mapbox & custom mbtiles for offline indoor maps.

Sadly this issue is a deal breaker :disappointed:

dpfaffenbauer commented 10 years ago

Are there any updates for this issue?

florianbuerger commented 10 years ago

@dpfaffenbauer I think since they released a completely new renderer written in OpenGL they focus on that instead.

incanus commented 10 years ago

Yes, and the goal is to merge the APIs making a clean upgrade path from the SDK to GL.