Google Maps style zoom function

Blitz3D Forums/Blitz3D Programming/Google Maps style zoom function

Axel Wheeler(Posted 2015) [#1]
Hi all,

I'm looking for a formula to calculate the upper left corner x/y in a map after zooming in or out from the mouse pointer, just like Google Maps. What I have almost works, except when I move the mouse. Here's what I have now:

upperLeftX=mapMx-mapMx/scale
upperLeftY=mapMx-mapMy/scale


... where scale is 1.0 to start and increases as you zoom in, like a camera. And mapMx/mapMy is the map location under the mouse pointer. This works perfectly as long as I keep the mouse in the same place, which obviously isn't good enough.

I also tried a different approach based on the current upperLeftX/Y. Using this method the distance from mapMx/mapMy to the upperLeftX/Y (x and y separately of course) is scaled to the new scale and then the distance is used to recalculate upperLeftX/Y. This is inelegant, and also doesn't work at all. Here it is:

upperLeftX=mapMx-(mapMx-upperLeftX)/scale
upperLeftY=mapMy-(mapMy-upperLeftY)/scale


Both these methods should work I think. But they don't, so I'm doing something wrong. Any ideas? Thanks!


Matty(Posted 2015) [#2]
Easiest way is to start with the world coordinate of the point where your mouse is located.

(Typed on phone so excuse any typos)

Assuming zoompoint is at screen midpoint which we will assume is 0.5×screen width etc

Worldxatmousex = worldxatzoompoint + (mousex-screenwidth×0.5)/zoom

You should be able to rearrange from that..make sure everything is a float.


Axel Wheeler(Posted 2015) [#3]
Matty,

Thanks for the response. The world coordinate corresponding to the mouse location is mapMx/mapMy in my code above.

You've got it changing though; it should not change. So if the zoompoint (thanks for that term!) is over Times Square, it will still be over Times Square after zooming, so it didn't change. See what I mean? Everything else gets further away or closer in.

Here is a crude table of corresponding map/screen variables:

Screen . . . . Map
MouseX() . . mapMx
MouseY() . . mapMy
0 . . . . . . . . upperLeftX
0 . . . . . . . . upperLeftY

Interestingly there are several discussions about this on the web, but like so many things the discussion is about how to use some API to do it, not how the algorithm itself works, so it's no help.


Floyd(Posted 2015) [#4]
Suppose screen width is 800 and the mouse ( fixed point ) is at x = 200. This is 200/800 = 0.25 of the way from left to right.

What width of "subimage" will be drawn after scaling? This is just as you were doing: newWidth = originalWidth/scale. originalWidth is whatever width gets drawn when scale = 1.

Let's say this newly scaled width is 100. The fixed image point must still be 0.25 of the way from left to right, i.e. 0.25*100 = 25 pixels. If the image point (ix,iy) was drawn at the mouse location before, and now after, scaling then the new left edge must be at ix - 25.


Axel Wheeler(Posted 2015) [#5]
I think you're on to something though Matty ... the proportion across the screen can be used to determine the top left edge.

mouseX()/GraphicsWidth()

... but on our chart above we don't have the map value corresponding to GraphicsWidth(). In other words I don't have that value. But let's say I did... (and let's call it mapGW)

Then MouseX()/GraphicsWidth() = (mapMx-upperLeftX)/mapGW

Both are the ration of the distance to the left of the mouse compared to the total area displayed. upperLeftX is the only unknown, so we can solve for it:

mapGW*(MouseX()/GraphicsWidth())=(mapMx-upperLeftX)
upperLeftX+(mapGW*(MouseX()/GraphicsWidth()))=mapMx
upperLeftX=mapMx-(mapGW*(MouseX()/GraphicsWidth()))

Plus, I think mapGW should just be GraphicsWidth()/scale

Let's try it and see...


Axel Wheeler(Posted 2015) [#6]
Yess!!! It works.

Hi Floyd! Yes that's what I was just doing based on Matty's post. Here's the code:

mapGW#=Float(GW)/scale
mapGH#=Float(GH)/scale

upperLeftX=mapMx-(mapGW*(mx/Float(GW)))
upperLeftY=mapMy-(mapGH*(my/Float(GH)))


GH and GW are my consts for use in Graphics3D(). At first the zoom was just slightly off because they were ints, but by floating them it's now razor sharp.

This will be for an inference diagramming, or argument diagramming tool. By zooming out the user can create much larger diagrams and have different regions for related arguments. But obviously this would be perfect for any game with a large map into which the user can zoom in to see what's happening at street level.

Anyway, thanks so much both of you!


Matty(Posted 2015) [#7]
Glad you got it working.