Trouble keeping two RMMapViews in sync when zooming.

214 views
Skip to first unread message

Mark Danks

unread,
Oct 11, 2011, 9:33:37 AM10/11/11
to route-me
Hello,

I'm currently working on a mapping/logging application using Route-Me
and I'm having an issue with panning and zooming when displaying one
map overlaid onto another.

Problem:

I have a semi-transparent RMMapView that I have layered above another
RMMapView. Both need to move and zoom in sync with each other.
However, certain types of panning and zooming cause the two layers to
become out of sync.

Current Solution:

I have a custom subclass of RMMapView for the semi-transparent upper
layer. The top layer has a baseLayer property which holds the lower
unmodified RMMapView. This subclass simply overrides the movement and
zooming methods and for each one calls the super implementation and
also the base layer's implementation.

For example:

> -(void)moveBy:(CGSize)delta{
> [super moveBy:delta];
> [baseLayer moveBy:delta];
> }
>
> -(void)moveToProjectedPoint:(RMProjectedPoint)aPoint{
> [super moveToProjectedPoint:aPoint];
> [baseLayer moveToProjectedPoint:aPoint];
> }

This works fine when moving programmatically, panning by dragging, or
when zooming to fixed zoom levels (either programmatically or by
double-tapping). However this doesn't work when performing a pinch to
zoom motion. When this occurs, the two layers zoom at different rates.

Unable to work out what was causing this disparity, I instead overrode
the methods to handle touch gestures and passed the touches through to
the base layer when they involve two fingers (supposedly making them a
pinch or stretch) instead of overriding zoomByFactor:near: and
zoomByFactor:near:animated (the other movement and zooming methods
remained unchanged).

For example:

> - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
>
> [super touchesBegan:touches withEvent:event];
>
> // Only pass down pinch & stretch gestures (for zooming)
> if(lastGesture.numTouches > 1){
> [baseLayer touchesBegan:touches withEvent:event];
> }
> }

I do the same for touchesMoved:withEvent:, touchesEnded:touches
withEvent: and touchesCancelled:touches withEvent:

This appeared to solve the problem (maps now stay in sync when
pinching to zoom). However I recently realised that this meant that
two-fingered dragging (but not pinching or stretching) now causes the
previous issue to reoccur, as my method for determining whether a
gesture is a pinch (lastGesture.numTouches > 1) is too naive.

To see an example of this (which might help explain what I mean):
http://f.cl.ly/items/1p3E2B2e0g1x3U1l3b00/BadScrollAnimation.gif

The OpenStreetMap layer is the baseLayer. The stars and other icons
are from the top layer (OpenSeaMap). Watch, for instance, how the star
below Goat Island moves from the bottom to the top of the island
during the drag.

The possible solutions I can think of include:

* Modifying the touches{Began, Moved, Ended, Cancelled} methods to
correctly determine when a gesture is a pinch, which I'm not sure how
to do. This feels a little kludgey, but should solve the issue.

* Making the two maps zoom at the same rate during the pinch to zoom
gestures (this is the preferred solution, as I can use one style for
all interaction types). Unfortunately, I've been unable to work out
what determines the map's zooming speed. I assumed it was a function
of minimum zoom, maximum zoom and current zoom, but making these
values the same for both maps doesn't seem to have an effect.

I'd be grateful for any advice or suggestions on how to resolve this
issue.

Thanks,

-Mark

Alan Pew

unread,
May 23, 2012, 6:25:28 AM5/23/12
to route-...@googlegroups.com
I'm trying to work out the same problem and am going to try to implement your idea.  Do you think excluding movement for two-finger dragging, but not for zoom might solve your problem?  As long as movement is available with one finger it doesn't seem like you would necessarily be excluding functionality from the user.

Roman Belyakovsky

unread,
May 23, 2012, 8:36:34 AM5/23/12
to route-...@googlegroups.com
--
You received this message because you are subscribed to the Google Groups "route-me" group.
To view this discussion on the web visit https://groups.google.com/d/msg/route-me-map/-/VLKL8bkcSeIJ.
To post to this group, send email to route-...@googlegroups.com.
To unsubscribe from this group, send email to route-me-map...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/route-me-map?hl=en.

Mark Danks

unread,
May 23, 2012, 8:49:26 AM5/23/12
to route-me
On May 23, 11:25 am, Alan Pew <alan...@gmail.com> wrote:
> I'm trying to work out the same problem and am going to try to implement
> your idea.  Do you think excluding movement for two-finger dragging, but
> not for zoom might solve your problem?  As long as movement is available
> with one finger it doesn't seem like you would necessarily be excluding
> functionality from the user.

If you're curious, I found the solution to the problem I was having: I
accidentally was performing some of the operations twice. In RouteMe's
implementation of one of the method I was overriding, the other
method I'd overridden was being also being called, so the effect
occurred twice.

I've explained that terribly.

I found that it was enough to override (and in that implementation,
call the [super] and the other map's implementation of):

moveBy:
moveToProjectedPoint:
moveToLatLong:
zoomWithLatLngBoundsNorthEast:SouthWest:
zoomByFactor:near:animated
zoomInToNextNativeZoomAt:
zoomInToNextNativeZoomAt:animated:
zoomOutToNextNativeZoomAt:
zoomOutToNextNativeZoomAt:animated:
setZoom:

One complication was the implementation of zoomByFactor:near, where
each map seemed to zoom by different amounts for the same zoom factor.
So instead here, I called zoomByFactor:near: on one map, then set the
second map's zoom level directly to the first map's new zoom value.

Hope that helps. Let me know if that doesn't work for you, or if
something I said doesn't seem right.

-Mark

Mark Danks

unread,
May 23, 2012, 8:59:48 AM5/23/12
to route-me
On May 23, 11:25 am, Alan Pew <alan...@gmail.com> wrote:
> I'm trying to work out the same problem and am going to try to implement
> your idea.  Do you think excluding movement for two-finger dragging, but
> not for zoom might solve your problem?  As long as movement is available
> with one finger it doesn't seem like you would necessarily be excluding
> functionality from the user.

If it helps, I was able to find a solution to the problem: I'd
accidentally overridden a method which was merely a convenience
accessor to another one which I'd also overridden. This was causing
any zooming and panning to occur twice on one of the maps.

I found that it worked correctly if I provided implementations for
these methods (and in that implementation, called the super
implementation, and the other map's method) in the map that the user
interacts with:

moveBy:
moveToProjectedPoint:
moveTolatLong:
zoomWithLatLngBoundsNorthEast:SouthWest:
zoomByFactor:near:animated:
zoomInToNextNativeZoomAt:
zoomInToNextNativeZoomAt:animated:
zoomOutToNextNativeZoomAt:
zoomOutToNextNativeZoomAt:animated:
setZoom:

One complication was that zoomByFactor:near: would zoom by different
amounts for each map. So instead in that implementation, I called
super's zoomByFactor:near: method, then set the second map's zoom
directly to the first map's new zoom level.

I hope that helps. Let me know if anything's unclear, or if you have
any other questions.

-Mark

Mark Danks

unread,
May 23, 2012, 9:14:37 AM5/23/12
to route-me
(Incidentally, Google Groups gave me a 500 error when I first replied,
so I rewrote the message and sent it again. If two subtly different
messages appear, that's why).

hryamzik

unread,
May 23, 2012, 9:33:48 AM5/23/12
to route-me
Guys, come on, pay attention to the link I've posted. Multiple map
views is not a good idea!

Justin R. Miller

unread,
May 23, 2012, 12:36:02 PM5/23/12
to route-...@googlegroups.com
FWIW, for about two years I've used the approach of multiple master/slave RMMapViews to do multiple layers. I turned off interaction on the upper layers, as well as passed pan/zoom actions on the bottom one to the others to keep them in sync. Performance has not been great. There was also some administrative hassle in passing the overlay layer around to always be attached to the topmost tile layer so that it appeared on top. 

Currently I'm working on our fork of the Alpstein fork (https://github.com/mapbox/mapbox-ios-sdk) with an approach that creates an RMCompositeSource for Core Graphics compositing of tiles. However, I'm now favoring hryamzik's approach of multiple views (CATiledLayer-backed) within the scroll view. Thought this only really works on Alpstein fork derivatives. See my quick test of this procedure here: https://github.com/incanus/TileTest

-- 
Justin R. Miller
Development Seed / MapBox

Roman Belyakovsky

unread,
May 23, 2012, 12:43:25 PM5/23/12
to route-...@googlegroups.com
Well, Alpstein fork had the most up-to-date code and compiled with out warnings. It also had a better performance than the original one. That's why I've chosen it for my project.

Thanks,
Roman

--
You received this message because you are subscribed to the Google Groups "route-me" group.
To view this discussion on the web visit https://groups.google.com/d/msg/route-me-map/-/2WThS_vWRR0J.

Alan Pew

unread,
May 23, 2012, 6:56:05 PM5/23/12
to route-...@googlegroups.com
Thanks for taking time to post that clarification, Mark!  I'm excited to maybe get the multiple map views to work with some good performance and am going to give your instructions a shot.

For those looking for a good way to combine multiple tile sources into a single mapview, I've had good success using the RMTileSourceManager class from this fellow's blog where he posted a zip file of his source code: http://mappingdev.wordpress.com/2012/04/ .  I had to clean it up a bit to get it to work, but its been quite easy to implement.  Just add the tile sources to the tile source manager instance in the order you want them to appear, then add the tile source manager as your mapview tile source.  My biggest issue is that I've had trouble implementing the tile source manager with the most recent Route-Me code -- specifically the route-me code that Justin is using in his MapBox-ios-example that uses ARC. that has gotten rid of the RMMapContents class.  Would love to figure out how to do so though.

@hryamzik, I would love to avoid using multiple map views, but I don't know how I could adjust transparency of multiple tile sources otherwise.  Not sure how to implement transparency for multiple tile sources overlaid upon one another in a single map view.

Alan Pew

unread,
May 23, 2012, 11:58:03 PM5/23/12
to route-...@googlegroups.com

Mark,

Thank you so very very very much for your help with this.  I really appreciate you taking the time to share you did it so promptly when your original post was last year.  I really enjoyed reading everyones thoughts and inputs on this matter as I sat at my desk job waiting to jump on this code.  Well I started at 7p and I now have it fully implemented and the mapviews run very smoothly when dragging and zooming.  I can't emphasize how much you rule for sharing how you went about subclassing the rmmapview class.  Thanks again!!!!

Alan

BUDDAx2

unread,
Oct 30, 2012, 11:11:17 AM10/30/12
to route-...@googlegroups.com
How about horizontal endless scroll in MapBox SDK? This is the main reason why I chose route-me. I need this scrolling
Reply all
Reply to author
Forward
0 new messages