How to programmatically zoom to geometry / pins area

Feb 13, 2009 at 8:06 AM
Hi!

I would like to zoom programmatically to geometry/pins that were added through a geometry layer.  In other word, after zooming all pins should be visible on the map.

This is what I've done thus far:
  • Calculate GeoBounds rectangle for pins
  • Calculate PixelBounds retangle for pins
  • Inflate PixelBounds rectangle as to overcome area limit of 100 in Map.SetPixelBounds()
  • Set Map.PixelBounds to inflated PixelBounds rectangle
This does not seem to do the job.

How do I go about doing this?


Coordinator
Feb 13, 2009 at 6:37 PM
Something in your PixelBounds might be off.  Try stepping through the MouseControl.Events_MouseUp event on a Marquee zoom.  This calls the same code for what you are trying to do:

        private void Events_MapMouseUp(Map map, MouseButtonEventArgs args)
        {
            ReleaseMouseCapture();

            switch(MapInstance.DragMode)
            {
                case Map.DragBehavior.Select:
                    if(_Moved) MapInstance.PixelBounds = Bounds;
                    break;
                case Map.DragBehavior.Pan:
                    (map).ReleaseMouseCapture();
                    break;
            }
Feb 16, 2009 at 10:08 AM
Sorry, I still can't seem to get it to work as is should.  Here is what I have done.

        void Events_MapLoaded(Map map, DeepEarth.Events.MapEventArgs args)
        {
            // Add points to geometryLayer (object inheriting from PointBase similar to DevPin)
            // 27.1928, -26.6069
            // 28.4669, -25.8683
            // 29.4342, -26.5213

            Rect pixelBounds = GetPixelBounds(geometryLayer .Geometry);
            map.PixelBounds = pixelBounds;
        }

        private Rect GetPixelBounds(IEnumerable<GeometryBase> geometry)
        {
            // Get PointBase structures
            IEnumerable<PointBase> points =
                from pointBase in geometry
                select (PointBase)pointBase;

            // Get x-values
            IEnumerable<double> xValues =
                from point in points
                select point.Point.X;

            // Get y-values
            IEnumerable<double> yValues =
                from point in points
                select point.Point.Y;

            // Get bounding rectangle
            Rect geoBounds = new Rect(
                new Point(xValues.Min(), yValues.Min()),
                new Point(xValues.Max(), yValues.Max())
                );

            // Convert to pixel bounds
            Rect pixelBounds = GeoBoundsToPixelBounds(geoBounds);

            // We need to inflate for DeepEarth to zoom to area (this is dirty)
            if (pixelBounds.Area() < 100)
            {
                while (pixelBounds.Area() < 100)
                {
                    // Inflate is an extension method creates an inflated rectangle copy (which is calculated by adding the value to Right & Bottom and subtracting the value from Top & left)
                    pixelBounds = pixelBounds.Inflate(1);
                }
            }

            return pixelBounds;
        }

        private Rect GeoBoundsToPixelBounds(Rect geoBounds)
        {
            Point pixelBoxOrigin = map.CoordHelper.GeoToPixel(new Point(geoBounds.Left, geoBounds.Top));
            Point pixelBoxExtent = map.CoordHelper.GeoToPixel(new Point(geoBounds.Right, geoBounds.Bottom));
            
            return new Rect(pixelBoxOrigin, pixelBoxExtent);
        }

Also: When the DeepEarth control is set to Width=400px and Height=300px, this does not have any effect.

Any help appreciated!
Jun 25, 2009 at 2:44 PM

Your code is OK, except the initial bounding rectangle that is wrong.

// Get bounding rectangle

Rect geoBounds = new Rect(
         new Point(xValues.Min(), yValues.Min()),
         new Point(xValues.Max(), yValues.Max())
);

The correct points are Left/Top and Right/Button

Rect geoBounds = new Rect(
         new Point(xValues.Min(), yValues.Max()),
         new Point(xValues.Max(), yValues.Min())
);

 

Aug 12, 2010 at 4:32 PM

Hi there

I was wanting to do something similar, but was getting frustrated with the face that when you set the GeoBounds, it rounds off the zooming. I don't see why this is necessary when I am specifying the exact region to zoom to.

Anyway, I made a helper class to work around this (basically a copy of the code base and a conversion into a helper class). Here is the code:

 

public class MapHelper
{
    private readonly Map _map;

    public MapHelper(Map map)
    {
        _map = map;
    }

    private TileLayer BaseLayer { get { return _map.BaseLayer; } }
    private CoordTransform CoordHelper { get { return _map.CoordHelper; } }
    private Size MapViewPixelSize { get { return _map.MapViewPixelSize; } }

    public void SetGeoBounds(Rect value)
    {
        if (BaseLayer.Msi != null && BaseLayer.Source != null)
        {
            Point pixelBoxOrigin = CoordHelper.GeoToPixel(GetLocation(value.Left, value.Top));
            Point pixelBoxExtent = CoordHelper.GeoToPixel(GetLocation(value.Right, value.Bottom));
            var pixelBox = new Rect(pixelBoxOrigin, pixelBoxExtent);
            SetPixelBounds(pixelBox);
        }
    }

    private static Location GetLocation(double x, double y)
    {
        return new Location { Longitude = x, Latitude = y };
    }

    private void SetPixelBounds(Rect pixelBox)
    {
        //Check to make sure this wasn't a simple click by checking the distance moved by mouse
        var area = pixelBox.Width * pixelBox.Height;

        if (area > 100)
        {
            var pixelCenter = new Point
                {
                    X = pixelBox.X + pixelBox.Width / 2,
                    Y = pixelBox.Y + pixelBox.Height / 2
                };

            //Get the minimum of the bounding box proportions to the actual screen.
            var percWidth = pixelBox.Width / MapViewPixelSize.Width;
            var percHeight = pixelBox.Height / MapViewPixelSize.Height;
            var maxPerc = Math.Max(percWidth, percHeight);
            var targetWidth = BaseLayer.Msi.ViewportWidth * maxPerc;
            var targetZoom = CoordHelper.LogicalViewToZoomLevel(new Size(targetWidth, targetWidth));

            //Try to round the zoom level and redo the size off the new value;
            //var roundZoom = (int)Math.Round(targetZoom, 0);
            var roundZoom = targetZoom;
            var roundedView = CoordHelper.ZoomLevelToLogicalView(roundZoom);
            var logicalOrigin = new Point();
            var logicalCenter = CoordHelper.PixelToLogical(pixelCenter);

            logicalOrigin.X = logicalCenter.X - (roundedView.Width / 2);
            logicalOrigin.Y = logicalCenter.Y - (roundedView.Height / 2);

            BaseLayer.Msi.ViewportOrigin = logicalOrigin;
            BaseLayer.Msi.ViewportWidth = roundedView.Width;
        }
    }
}

Hope this helps someone...

PS You can also remove the area check here if you want.

All the best

-Mark