ThinkGeo.com    |     Documentation    |     Premium Support

Map missing custom zoom levels when zooming in

 Guys, I populated the CustomZoomLevels collection as follows:



  void AddCustomZoomLevels()
        {
            var sc1 = map.ZoomLevelSet.ZoomLevel05.Scale;
            var sc2 = map.ZoomLevelSet.ZoomLevel06.Scale;

            map.ZoomLevelSet.CustomZoomLevels.Add(map.ZoomLevelSet.ZoomLevel01);
            map.ZoomLevelSet.CustomZoomLevels.Add(map.ZoomLevelSet.ZoomLevel02);
            map.ZoomLevelSet.CustomZoomLevels.Add(map.ZoomLevelSet.ZoomLevel03);
            map.ZoomLevelSet.CustomZoomLevels.Add(map.ZoomLevelSet.ZoomLevel04);
            map.ZoomLevelSet.CustomZoomLevels.Add(map.ZoomLevelSet.ZoomLevel05);
            map.ZoomLevelSet.CustomZoomLevels.Add(new ZoomLevel((sc1 + sc2)/2));
            map.ZoomLevelSet.CustomZoomLevels.Add(map.ZoomLevelSet.ZoomLevel06);
            map.ZoomLevelSet.CustomZoomLevels.Add(map.ZoomLevelSet.ZoomLevel07);
            map.ZoomLevelSet.CustomZoomLevels.Add(map.ZoomLevelSet.ZoomLevel08);
            map.ZoomLevelSet.CustomZoomLevels.Add(map.ZoomLevelSet.ZoomLevel09);
            map.ZoomLevelSet.CustomZoomLevels.Add(map.ZoomLevelSet.ZoomLevel10);
            map.ZoomLevelSet.CustomZoomLevels.Add(map.ZoomLevelSet.ZoomLevel11);
            map.ZoomLevelSet.CustomZoomLevels.Add(map.ZoomLevelSet.ZoomLevel12);
            map.ZoomLevelSet.CustomZoomLevels.Add(map.ZoomLevelSet.ZoomLevel13);
            map.ZoomLevelSet.CustomZoomLevels.Add(map.ZoomLevelSet.ZoomLevel14);
            map.ZoomLevelSet.CustomZoomLevels.Add(map.ZoomLevelSet.ZoomLevel15);
            map.ZoomLevelSet.CustomZoomLevels.Add(map.ZoomLevelSet.ZoomLevel16);
            map.ZoomLevelSet.CustomZoomLevels.Add(map.ZoomLevelSet.ZoomLevel17);
            map.ZoomLevelSet.CustomZoomLevels.Add(map.ZoomLevelSet.ZoomLevel18);
            map.ZoomLevelSet.CustomZoomLevels.Add(map.ZoomLevelSet.ZoomLevel19);
            map.ZoomLevelSet.CustomZoomLevels.Add(map.ZoomLevelSet.ZoomLevel20);
        }

 


 When I zoom in, map does hit my custom zoom level.  However, when zooming out, it does.  Is there an issue using the CustomZoomLevels?  


TIA.



Klaus, 
  
 Thank you for your post, sorry I didn’t understand it very well, is map hit or not hit when you zoom out? 
  
 Regards, 
  
 Gary

Gary, the map skips my custom zoom level, when I start zooming in from zoom level 1.  However, when zooming out from say zoom level 8, it does hit my custom zoom level.  



 Guys, any updates on this?  Were you able to reproduce?



Klaus, 
  
 Sorry for waiting, I’m still working on this, will let you know the result as soon as possible. 
  
 Thanks, 
  
 Gary

Gary, any updates?  This is a gating issue for me.

Klaus, 
  
 Sorry for waiting and thank you for your patience. 
  
 It’s almost done, I will post the result here tomorrow. 
  
 Regards, 
  
 Gary

Klaus, 
  
 This problem is caused by zoom in and zoom out , the way it works is scale up or scale down 20% per time not one zoom level to next, so that it miss your custom zoomlevel in sometimes. 
  
 There are two solutions to resolve this, one is override the zoom in, zoom out, double click to change the zoomlevel one by one. the other is use the scalechange event, every time it changes, get the next zoomlevel. 
  
 Regards, 
  
 Gary

 Gary, thanks for the response.  


I need more details on how to implement the first solution as WpfMap.ZoomIn or WpfMap.ZoomOut  are not virtual methods.  As to the second solution, I do not see how this would solve my problem.  


Each time a user zooms in using the mouse wheel, for example, it appears as though you zoom in one level, as indicated by the zoom level control.   Are you saying the map is doing one thing while the zoom level control is indicating another?



Klaus, 
  
 The map didn’t change it’s zoom level one by one, it changed by 20% scale. So the way I suggest is use the scalechange event to help the map change zoom level one by one to avoid to missing your custom zoomlevel. 
  
 The first way is more complex, you need to override interactiveOverlay to make it. 
  
 Regards, 
  
 Gary

 Gary, again, thanks for the response but you are being theoretical on this issue.  I simply cannot see how the ScaleChanged event, which fires AFTER the fact, will help me control which scale I should be zooming to!  


i guess, let us go back to first principles.  How exactly does your CustomZoomLevels work?  Also, if you can post some pseudo code on your proposed solution 2, that will help alot, while I browse this site related questions and comments.


 


 



This Wiki article may be what I am looking for: 
  
 wiki.thinkgeo.com/wiki/Map_Suite_Wpf_Desktop_Edition_Zooming_Panning_Moving_Samples#Zoom_Levels_Partitioning

Klaus, 
  
 I’m sorry I didn’t explain how it worked very well. 
  
 Did this sample help you resolved your problem?  
  
 Regards, 
  
 Gary

Gary, the article did provide some ideas on how to add custom zoom levels.  Also, it gave me a little more clarity on what I am trying to achieve:


If I do not alter the default zoom levels map tracks zoom levels if I scroll mouse wheel by one click, or if I use the +/- buttons of the zoom level control.   However, if I have add custom zoom levels map no longer tracks zoom levels if I scroll mouse wheel by one click. 


If I am zooming by rotating mouse wheel one click at a time, I want map to zoom through all levels, including my custom ones and not just the default.


 


 



 Gary, to solve this, I just overrode WpfMap.OnMouseWheel with snippet below.  This will work for now, till I figure out how ths mouse wheel number of 120 is generated, as it may depend on the mouse.



 protected override void OnMouseWheel(System.Windows.Input.MouseWheelEventArgs e)
        {
             double scaleToZoom = CurrentScale;
            if (e.Delta > 0)
                scaleToZoom = ZoomLevelSet.GetLowerZoomLevelScale(CurrentScale, ZoomLevelSet);
            else
                scaleToZoom = ZoomLevelSet.GetHigherZoomLevelScale(CurrentScale, ZoomLevelSet);

            ZoomToScale(scaleToZoom);
        }


Hi Klaus,
 
I think this one doesn’t work as expect and it always zooms in by the center of the current viewport. I’m not sure if it has some side effect in the future; so I think to rewrite the default ExtentInteractiveOverlay is a better option. Please check the code below:

public class ZoomByLevelExtentInteractiveOverlay : ExtentInteractiveOverlay
    {
        private const int dotsPerInch = 96;
        private const double feet = 12.0;
        private const double meter = 39.3701;
        private const double decimalDegree = 4374754;

        protected override InteractiveResult MouseWheelCore(InteractionArguments interactionArguments)
        {
            InteractiveResult interactiveResult = base.MouseWheelCore(interactionArguments);
            if (MouseWheelMode == MapMouseWheelMode.Disabled)
            {
                return interactiveResult;
            }

            int level = MapArguments.GetSnappedZoomLevelIndex(interactionArguments.CurrentExtent);
            if (interactionArguments.MouseWheelDelta <= 0 && level > 0)
            {
                ExtentChangedType = ExtentChangedType.MouseWheelZoomOut;
                level–;
            }
            else if(level < MapArguments.ZoomLevelScales.Count - 1)
            {
                ExtentChangedType = ExtentChangedType.MouseWheelZoomIn;
                level++;
            }

            var mousePosition = new Point(interactionArguments.ScreenX, interactionArguments.ScreenY);
            double deltaX = interactionArguments.MapWidth * .5 - mousePosition.X;
            double deltaY = mousePosition.Y - interactionArguments.MapHeight * .5;
            
            double newResolution = GetResolutionFromScale(MapArguments.ZoomLevelScales[level], interactionArguments.MapUnit);
            PointShape newWorldCenter = MapArguments.ToWorldCoordinate(new PointShape(mousePosition.X, mousePosition.Y));
            newWorldCenter.X += deltaX * newResolution;
            newWorldCenter.Y += deltaY * newResolution;
            interactiveResult.NewCurrentExtent = CalculateExtent(new Point(newWorldCenter.X, newWorldCenter.Y), MapArguments.ZoomLevelScales[level], interactionArguments.MapUnit, interactionArguments.MapWidth, interactionArguments.MapHeight);
            return interactiveResult;
        }

        public static RectangleShape CalculateExtent(Point center, double scale, GeographyUnit mapUnit, double mapWidth, double mapHeight)
        {
            if (Double.IsNaN(mapWidth) || Double.IsNaN(mapHeight))
            {
                return null;
            }

            double resolution = GetResolutionFromScale(scale, mapUnit);
            double widthInDegree = mapWidth * resolution;
            double heightInDegree = mapHeight * resolution;
            double left = center.X - widthInDegree * .5;
            double right = center.X + widthInDegree * .5;
            double top = center.Y + heightInDegree * .5;
            double bottom = center.Y - heightInDegree * .5;
            return new RectangleShape(left, top, right, bottom);
        }

        private static double GetResolutionFromScale(double scale, GeographyUnit unit)
        {
            return scale / (GetInchesByUnit(unit) * dotsPerInch);
        }

        private static double GetInchesByUnit(GeographyUnit unit)
        {
            switch (unit)
            {
                case GeographyUnit.Feet: return feet;
                case GeographyUnit.Meter: return meter;
                case GeographyUnit.DecimalDegree: return decimalDegree;
                default: return double.NaN;
            }
        }
    }

You just need to replace the default ExtentOverlay with this one like this code:
Map1.ExtentOverlay = new ZoomByLevelExtentInteractiveOverlay();
 
Now it allows you to zoom by the custom zoom levels you defined. Please give it a try and hope it helps.
 
Thanks,
Howard

Now, that is magical guys, thank you, thank you.  Alot complete and re-assuring than my hack.


BTW, Howard, please PM me when you get a chance.  Glad to see you are occassionally checking support threads.  



 Oh, guys, a problem:


Using the ZoomByLevelExtentInteractiveOverlay, map now has problem generating tiles for custom zoom levels, if the TileCache is being used.


In the sample you used to test this overlay, add usage of the FileBitmapTileCache.  Also, add the following methods to WpfMap:



 public ZoomLevelSet PartitionZoomLevelSet(int zoomLevelToStartStepping, int stepsBetweenZoomLevels)
        {
            return ZoomLevelSet = PartitionZoomLevelSet(zoomLevelToStartStepping, stepsBetweenZoomLevels, ZoomLevelSet);
        }

        public ZoomLevelSet PartitionZoomLevelSet(int zoomLevelToStartStepping,
            int stepsBetweenZoomLevels, ZoomLevelSet referenceZoomLevelSet)
        {
            ZoomLevelSet partitionedZoomLevelSet = new ZoomLevelSet();
            var zoomLevels = referenceZoomLevelSet.GetZoomLevels();
            partitionedZoomLevelSet.CustomZoomLevels.Add(new ZoomLevel(zoomLevels[0].Scale));
            ZoomLevel zoomLevel = null;
            for (int index = 0; index < zoomLevelToStartStepping; index++)
            {
                zoomLevel = zoomLevels[index];
                partitionedZoomLevelSet.CustomZoomLevels.Add(zoomLevel);
            }

            for (int index = zoomLevelToStartStepping; index < zoomLevels.Count - 1; index++)
            {
                zoomLevel = zoomLevels[index];
                double lowerScale = ZoomLevelSet.GetLowerZoomLevelScale(zoomLevel.Scale, referenceZoomLevelSet);
                if (lowerScale != zoomLevel.Scale)
                {
                    for (int x = 1; x < stepsBetweenZoomLevels + 2; x++)
                    {
                        double steppedScale = zoomLevel.Scale - (((zoomLevel.Scale - lowerScale) / (stepsBetweenZoomLevels + 1)) * x);
                        partitionedZoomLevelSet.CustomZoomLevels.Add(new ZoomLevel(steppedScale));
                    }
                }
            }
            return partitionedZoomLevelSet;
        }

and then call as follows to add custom zoom levels:



map.PartitionZoomLevelSet(3, 2);

 Observe that at the custom zoom levels, tiles are not being generated.



Hi Klaus,


Yeah, it's been a long time; I'm too busy on another project to attend to anything else you know; and my current project is not finished :( I think our support team can satisfy you when I'm not around. And I will also check these issues occasionally.


Well, let's get back to this topic. I tried to recreate this issue but the cache works properly, maybe there is some misunderstanding, so I upload the test project I used on my machine. I'm using the latest version, please check my project to see if we are in the same line.


Thanks,

Howard



Post9479.zip (10.8 KB)

 Howard, 


There is no issue after I cleaned the cache, deleting those tiles that were created for the custom zoom level scales.  For some strange reason, the cached images generated for these scales were originally blank, and my custom FileBitMapTileCache did not catch these.  I need to find a way to avoid writing blank images to the cache.  If you run into a solution on how to prevent writing blank images to the cache, please share.  I may create a seperate thread on this later.


Yes, your support folks are doing a great jobs helping us.  


Thanks again for helping out with this one and I am glad to know you are still around :)