This project has moved. For the latest updates, please go here.

ZoomRect

Dec 15, 2012 at 2:23 PM

Good control used it 2 now, would be nice to zoom between 2 exents or rect.

ZoomRect(lat1, lon1, lat2, lon2);

Coordinator
Dec 15, 2012 at 4:21 PM

Zooming that way would enforce a certain aspect ratio which would usually conflict with the aspect ratio of the Map's viewport. And if the map is rotated (Heading != 0), such a zoom rectangle would be quite hard to calculate.

Apr 10, 2013 at 3:53 PM
Here is the code to do that .. add this into MapBase.cs:
        //MJP:
        #region Visible Boundary support

        public static readonly DependencyProperty VisibleBoundaryProperty = DependencyProperty.Register(
            "VisibleBoundary", typeof(Boundary), typeof(MapBase), new PropertyMetadata(new Boundary(),
            (o, e) => ((MapBase)o).VisibleBoundaryPropertyChanged((Boundary)e.NewValue)));

        /// <summary>
        /// Gets/Sets the current visible map boundary
        /// </summary>
        public Boundary Boundary
        {
            get { return (Boundary)GetValue(VisibleBoundaryProperty); }
            private set { SetValue(VisibleBoundaryProperty, value); }
        }

        private void VisibleBoundaryPropertyChanged(Boundary newBoundary)
        {
            // no action yet
        }

        public void ZoomToBoundary(Boundary bounds)
        {
            double cx = bounds.BottomLeft.Longitude + (bounds.TopRight.Longitude - bounds.BottomLeft.Longitude) / 2;
            double cy = bounds.BottomLeft.Latitude + (bounds.TopRight.Latitude - bounds.BottomLeft.Latitude) / 2;
            double zoom = GetBoundsZoomLevel(bounds);

            zoom = Math.Floor(zoom) - 1;
            
            TargetCenter = new Location(cy, cx);
            TargetZoomLevel = zoom;
        }


        /// <summary>
        /// recalculate the boundary. Updated when the viewport translation changes
        /// </summary>
        private void SetBoundaryFromViewport()
        {
            var bounds = ViewportTransform.Inverse.TransformBounds(new Rect(0d, 0d, RenderSize.Width, RenderSize.Height));
            Boundary.BottomLeft.Latitude = bounds.Bottom;
            Boundary.BottomLeft.Longitude = bounds.Left;
            Boundary.TopRight.Latitude = bounds.Top;
            Boundary.TopRight.Longitude = bounds.Right;            
        }

        /// <summary>
        /// calculates a suitable zoom level given a boundary
        /// </summary>
        /// <param name="bounds"></param>
        /// <returns></returns>
        double GetBoundsZoomLevel( Boundary bounds )
        {
            var GLOBE_HEIGHT = 256; // Height of a map that displays the entire world when zoomed all the way out
            var GLOBE_WIDTH = 256; // Width of a map that displays the entire world when zoomed all the way out

            var ne = bounds.TopRight;
            var sw = bounds.BottomLeft;

            var latAngle = ne.Latitude - sw.Latitude;
            if (latAngle < 0)
            {
                latAngle += 360;
            }

            var lngAngle = ne.Longitude - sw.Longitude;

            var latZoomLevel = Math.Floor(Math.Log(RenderSize.Height * 360 / latAngle / GLOBE_HEIGHT) / Math.Log(2));
            var lngZoomLevel = Math.Floor(Math.Log(RenderSize.Width * 360 / lngAngle / GLOBE_WIDTH) / Math.Log(2)); //0.6931471805599453 

            return (latZoomLevel < lngZoomLevel) ? latZoomLevel : lngZoomLevel;
        }

        #endregion
and add the SetBoundaryFromViewport in the TransformMap method..
        public void TransformMap(Point origin, Point translation, double rotation, double scale)
        {
            if (rotation != 0d || scale != 1d)
            {
                SetTransformOrigin(origin);
                SetProperty(HeadingProperty, CoerceHeading(Heading + rotation));
                SetProperty(ZoomLevelProperty, CoerceZoomLevel(ZoomLevel + Math.Log(scale, 2d)));
                UpdateTransform();
            }

            ResetTransformOrigin();
            TranslateMap(translation);


            //MJP: Boundary Support
            SetBoundaryFromViewport();
        }
and finally you need the Boundary class.
namespace MapControl
{
    public class Boundary
    {
        public Boundary()
        {
            BottomLeft = new Location();
            TopRight = new Location();
        }

        public Boundary(double t, double l, double b, double r) : this()
        {
            BottomLeft.Longitude = l;
            BottomLeft.Latitude = b;
            TopRight.Longitude = r;
            TopRight.Latitude = t;
        }

        public double Width
        {
            get
            {
                return TopRight.Longitude - BottomLeft.Longitude;
            }
        }

        public double Height
        {
            get
            {
                return TopRight.Latitude - BottomLeft.Latitude;
            }
        }

        public Boundary(Location bottomleft, Location topright)
        {
            BottomLeft = bottomleft;
            TopRight = topright;
        }

        public Location BottomLeft  { get; set; }
        public Location TopRight { get; set; }

    }
}
May 7, 2013 at 11:16 AM
This might be a bit premature, but I have reworked the mouse handling in the WPF (only) to demonstrate the use of Behaviours instead of direct handling by the map control. The fork is call SelectZoom .

This was driven by the desire to have a dragged rectangle zoom selector as raised in this thread.

Mouse/keyboard/touch handling is now achieved by adding behaviours to the map control as shown below. I have created 3 behaviors, 2 of them replace existing functionality of zooming and panning using the mouse. The 3rd one adds the draggable zoom rectangle (or your own shape if required). You can restrict zooming and panning by not including the behaviours as a child of the map xaml.

I find that using behaviours is a nice way to encapsulate specific behavior, allowing fine-grain control over users' interaction with the map.

I guess you could implement your own behaviours as desired very easily.

add this namespace..
xmlns:mapbehaviour="clr-namespace:MapControl.Behaviours;assembly=MapControl.WPF"

<map:Map ...>
<i:Interaction.Behaviors>
<!-- pan map using mouse --> <mapbehaviour:MousePan />
<!-- zoom using mouse wheel --> <mapbehaviour:Zoom />
<!-- rectangle selection, use Shift and left mouse down --> <mapbehaviour:RectangleZoom Modifier="Shift" />
</i:Interaction.Behaviors>
Apr 22, 2014 at 1:12 PM
@marcuspoulton,

I cannot find the fork that you mention, but I am interested in seeing your implementation of the RectangleZoom behaviour as this is something I too am looking to do with this control.

Is it possible for you to post the code?

Thanks.