non integer zoom levels

Jul 21, 2009 at 8:14 PM

Synchronizing a background GMap and an overlay Canvas hosting a shape Map, I get a good match with integer zoom levels but a rather poor one with the intermediate non integer zoom levels (the GMap image is larger than it should be). I noticed that in those cases CurrentViewArea.Left does not match FromLocalToLatLng(1, 1), but maybe this is expected ?

Any explanation or hints ? If it helps, I can send a set of screenshots showing this effect.

Great Job anyway !

Coordinator
Jul 21, 2009 at 9:36 PM

yes it is know isue, any ideas?

Jul 21, 2009 at 10:30 PM

How/where are the intermediate zoom levels actually handeled ?

At first glance I would try to go for a canvas hosting GMap. This canvas could be scaled down between two integer zooms with no change in  the GMap zoom level, and then reset to its full scale when reaching the next integer zoom level.

Coordinator
Jul 21, 2009 at 10:42 PM

well i just apply ScaleTransform before rendering, so it must be used somehow to correct the local coordinates

Jul 22, 2009 at 3:05 PM

I managed to get it working

I embeded GMap in a Border that has a rendertransform (with renderorigin at the center 0.5,0.5), which in turn has a ScaleTransform (GScaleX, GScaleY).

The code used on mousewheel is as follow :

            double _ZoomStep = 0.1;
            double _ZoomFactor = Math.Pow(2,_ZoomStep);
            double _ZFactor;

            if (e.Delta > 0)
            {
                _RealZoom += _ZoomStep;
                _ZFactor = _ZoomFactor; 
            }
            else
            {
                _RealZoom -= _ZoomStep;
                _ZFactor = 1 / _ZoomFactor;
            }

            _GMap.Zoom = Math.Floor(_RealZoom);

            double test = Math.Round(_RealZoom - _GMap.Zoom, 1);
            if ((test == 1) || (test == 0))
            {
                if(test == 1) _GMap.Zoom += 1;
                this.GScaleX = 1;
                this.GScaleY = 1;
            }
            else if ((test == 1-_ZoomStep) && (Math.Round(GScaleX,0) == 1.0))
            {
                this.GScaleX = Math.Pow(_ZoomFactor, Math.Round(1 / _ZoomStep, 0)-1);
                this.GScaleY = Math.Pow(_ZoomFactor, Math.Round(1 / _ZoomStep, 0)-1);
            }
            else
            {
                this.GScaleX *= _ZFactor;
                this.GScaleY *= _ZFactor;
            }

Coordinator
Jul 22, 2009 at 3:27 PM

..but how it helps to sync geo-local coordinates?

Jul 22, 2009 at 5:05 PM

The Sync is done at initialization, first by setting the OverlayMap (called _MainMap) base scale and translate parameters, according to the size of the Map and the extent of the coordinates space that we want to view (BoundingBox being a rectangle of XY coordinates) :

 

                Double BestScale = Math.Min(_MainMap.ActualWidth / BoundingBox.Width, _MainMap.ActualHeight / BoundingBox.Height);
                ScaleX = BestScale;
                ScaleY = -BestScale;
                TranslateX = -BoundingBox.Left;
                TranslateY = -BoundingBox.Bottom;

 

 


then by adjusting the base scale to show the same longitude range as showed in the underlying GMap
            double deltaL = (_GMap.ActualWidth * 360) / (256 * Math.Pow(2, _RealZoom));
            double NewScale = _GMap.ActualWidth / ((EarthRadiusKm * 1000) * Math.Tan((deltaL / 180) * Math.PI));
            this.ScaleX = NewScale;
            this.ScaleY = -NewScale;

and finally by setting the GMap current position to match the _MainMap center (in this case _MainMap is in UTM40S coordinates) 

double Xc = _MainMap.RenderTransform.Inverse.Transform(new System.Windows.Point(0.5 * (_MainMap.ActualWidth - 1), 0.5 * (_MainMap.ActualHeight - 1))).X; double Yc = _MainMap.RenderTransform.Inverse.Transform(new System.Windows.Point(0.5 * (_MainMap.ActualWidth - 1), 0.5 * (_MainMap.ActualHeight - 1))).Y; _GMap.CurrentPosition = UTMtoLL(22, Yc, Xc, "40C");

These last 3 lines are reused on each pan or zoom event

 

 

Jul 22, 2009 at 5:27 PM

I forgot to mention that the _MainMap is obviously also rescaled on the MouseWheel event by applying the ZFactor as decribed previously :

            double NewScale = _ZFactor * ScaleX;
            this.ScaleX = NewScale;
            this.ScaleY = -NewScale;

Coordinator
Jul 22, 2009 at 8:25 PM

..but what we need to change here?

public GMap.NET.Point FromLatLngToLocal(PointLatLng point)
{
    return Core.FromLatLngToLocal(point);
}

 

Jul 22, 2009 at 9:21 PM

I don't know... seems OK

As far as I can see, the scaling problem for non integer zoom levels, as described in the first post, must originate in the way the scale transform parameters are defined.

With the short procedure in my 3rd message, we define the scaling factor that corresponds to the zoom+ step and the zoom- step. We then set the GMap zoom level to the integer part of the real zoom level and apply this scale factor to the embedding border.

Two special cases have to be accounted for :

1) when realzoom reaches an integer value, we eventually adjust GMap.Zoom and reset the embedding border scale factors to 1,

2) when realzoom gets one zoom step below an integer value, we set the scale parameters to their max value (GMap zoom has already been decreased by one.

Hope this helps

Coordinator
Jul 22, 2009 at 9:23 PM

not so much, still marker positions is out of sync in non int zoom level