5

I add a number of pushpins to a map and then "rightsize" or "right zoom" it so that all the pushpins show but so that the ones on the far edges are barely within the boundaries, like so:

enter image description here

However, if there is only one pushpin (as in the following case with Iowa, which only had the one civil war battle), rather than zero in on that as close as possible, it zooms out all the way to Skylab, like so:

enter image description here

This is the code I use to "right size" the map:

private void RightsizeZoomLevelForAllPushpins()
{
    try
    {
        // Set the view so that all pushpins are displayed, with a bit of a margin around them
        // from https://stackoverflow.com/questions/65779504/how-to-set-the-zoom-level-of-bing-map-to-just-wide-enough-to-display-all-pushpin/65781319#65781319
        var map = this.userControl11.myMap;
        var locations = map.Children.OfType<Pushpin>().Select(x => x.Location);
        //Margin
        var w = new Pushpin().Width;
        var h = new Pushpin().Height;
        var margin = new Thickness(w / 2, h, w / 2, 0);

        //Set view
        map.SetView(locations, margin, 0);
        currentZoomLevel = Convert.ToInt32(map.ZoomLevel);
        UncheckAllZoomLevelToolstripMenuItems();
        SetZoomMenuItem();
    }
    catch (Exception ex)
    {
        System.Windows.Forms.MessageBox.Show(ex.Message);
    }
}

How can I get a one-pushpin map to not only center, but also zoom in as far as possible?

UPDATE

After adding parens to both references to Count, and adding an "if" in the else block so that it would compile, I still get two errors with this code:

private void RightsizeZoomLevelForAllPushpins()
{
    try
    {
        // Set the view so that all pushpins are displayed, with a bit of a margin around them
        var map = this.userControl11.myMap;
        var locations = map.Children.OfType<Pushpin>().Select(x => x.Location);

        if (locations.Count() == 1)
        {
            map.setView(locations[0], singlePinZoom);                
        }
        else if (locations.Count() > 1) 
        {
            //Margin
            var w = new Pushpin().Width;
            var h = new Pushpin().Height;
            var margin = new Thickness(w / 2, h, w / 2, 0);

            //Set view
            map.SetView(locations, margin, 0);
        }    
        currentZoomLevel = Convert.ToInt32(map.ZoomLevel);
        UncheckAllZoomLevelToolstripMenuItems();
        SetZoomMenuItem();
    }
    catch (Exception ex)
    {
        System.Windows.Forms.MessageBox.Show(ex.Message);
    }
}

They are:

1>C:\Users\bclay\source\repos\MyMaps\MyMaps\Form1.cs(155,33,155,45): error CS0021: Cannot apply indexing with [] to an expression of type 'IEnumerable<Location>'
1>C:\Users\bclay\source\repos\MyMaps\MyMaps\Form1.cs(155,25,155,32): error CS1061: 'Map' does not contain a definition for 'setView' and no accessible extension method 'setView' accepting a first argument of type 'Map' could be found (are you missing a using directive or an assembly reference?)

BTW, I'm not seeing any errors anymore in Visual Studio until I compile the project (it stopped finding errors as they were entered). I don't know why ... I changed no settings...

UPDATE 2

This is the code I'm using now, which does not take just one pushpin into account, but does work (note that "SetView" doesn't throw an exception here):

private void RightsizeZoomLevelForAllPushpins()
    {
        const int LEFT_TOP_RIGHT_MARGIN_ADDITION = 16;
        const int BOTTOM_MARGIN_ADDITION = 48;
        try
        {
            // Set the view so that all pushpins are displayed, with a bit of a margin around them
            // from https://stackoverflow.com/questions/65779504/how-to-set-the-zoom-level-of-bing-map-to-just-wide-enough-to-display-all-pushpin/65781319#65781319
            var map = this.userControl11.myMap;
            var locations = map.Children.OfType<Pushpin>().Select(x => x.Location);
            //Margin
            var w = new Pushpin().Width;
            var h = new Pushpin().Height;
            var margin = new Thickness((w / 2) + LEFT_TOP_RIGHT_MARGIN_ADDITION, 
                                        h + LEFT_TOP_RIGHT_MARGIN_ADDITION, 
                                        (w / 2) + LEFT_TOP_RIGHT_MARGIN_ADDITION, 
                                        0 + BOTTOM_MARGIN_ADDITION);
            //Set view
            map.SetView(locations, margin, 0);
            currentZoomLevel = Convert.ToInt32(map.ZoomLevel);
            UncheckAllZoomLevelToolstripMenuItems();
            SetZoomMenuItem();
        }
        catch (Exception ex)
        {
            System.Windows.Forms.MessageBox.Show(ex.Message);
        }
    }
Reza Aghaei
  • 120,393
  • 18
  • 203
  • 398
B. Clay Shannon-B. Crow Raven
  • 8,547
  • 144
  • 472
  • 862
  • How far is "possible" to you? To me it's "all the way in til the zoom in button is disabled" – Caius Jard Feb 27 '21 at 07:08
  • @CaiusJard "so that all the pushpins show but so that the ones on the far edges are barely within the boundaries" – B. Clay Shannon-B. Crow Raven Feb 27 '21 at 17:38
  • But if there's only one pushpin.. – Caius Jard Feb 27 '21 at 17:39
  • ...then I want to zoom in as much as possible. Is it as easy as simply setting the zoom level to the strongest magnification when there is 1 pushpin? – B. Clay Shannon-B. Crow Raven Feb 28 '21 at 15:37
  • 1
    You have learned bout `SetView` for a single point [here](https://stackoverflow.com/a/65513589/3110834), so you can check if there's a single point in locations, use the overload which accepts a single location by passing `locations.First()`, otherwise use the overload which uses the IEnumerable of locations by passing `locations`. Choosing the zoom level for a single point is up to you. – Reza Aghaei Feb 28 '21 at 16:51
  • Use this: `if(locations.Count()==1) { map.SetView(locations.First(), 12); } else if(locations.Count()>1) {map.SetView(locations, margin, 0);} else {MessageBox.Show("No point.");}` – Reza Aghaei Mar 06 '21 at 18:48
  • 1
    The answer below shares the idea, but unfortunately is has some syntax error also the logic of the if/else is wrong. To check the count as I mentioned, you need to use `locations.Count()`, to get the first, you cannot use `[]`, you need to use `locations.First()`, also the conditions should be `locations.Count() ==1`, `locations.Count() >1` – Reza Aghaei Mar 06 '21 at 18:51
  • @Reza: Yes, that's what I changed it to, as shown in the Update above. I'm still getting those err msgs with the "Update" code, though, so for now am using what is in Update 2. If you can give me another working answer (as you've always done in the past), I will give you the Bounty, but time is running out... – B. Clay Shannon-B. Crow Raven Mar 07 '21 at 15:49
  • @B.ClayShannon Maybe you missed the point, you cannot use `locations[0]` you should use `locations.First()`. You should also make sure you have the `using System.Linq;` – Reza Aghaei Mar 07 '21 at 16:08

2 Answers2

3

There is no single zoom level for a single pin, the best zoom level would depend on your scenario or what the pin is meant to represent, as well as the size of the map since both zoom and map size determine the viewable area. For example, if your pin represented a house you may want the map to be zoomed in more than if it represented a city.

That said, here is a modified version of your code that will handle the single pin scenario and use a static zoom level of your choosing:

private double singlePinZoom = 15;

private void RightsizeZoomLevelForAllPushpins()
{
    try
    {
        // Set the view so that all pushpins are displayed, with a bit of a margin around them
        var map = this.userControl11.myMap;
        var locations = map.Children.OfType<Pushpin>().Select(x => x.Location);

        if(locations.Count() > 0) {
            //Margin
            var w = new Pushpin().Width;
            var h = new Pushpin().Height;
            var margin = new Thickness(w / 2, h, w / 2, 0);

            //Set view
            map.SetView(locations, margin, 0);
        } else(locations.Count == 1) {
            map.setView(locations[0], singlePinZoom);
        }   
        
        currentZoomLevel = Convert.ToInt32(map.ZoomLevel);
        UncheckAllZoomLevelToolstripMenuItems();
        SetZoomMenuItem();
    }
    catch (Exception ex)
    {
        System.Windows.Forms.MessageBox.Show(ex.Message);
    }
}
rbrundritt
  • 16,570
  • 2
  • 21
  • 46
  • I get "error CS0019: Operator '>' cannot be applied to operands of type 'method group' and 'int'" on this line: if (locations.Count > 0) – B. Clay Shannon-B. Crow Raven Feb 28 '21 at 16:06
  • 1
    @B.ClayShannon Should be `Count()`. – Reza Aghaei Feb 28 '21 at 16:47
  • 1
    ...and because locations.Count being one is also satisfying condition for "if locations.Count > 0", you could check for count == 1 first and handle multiple locations in the else – jaro Mar 04 '21 at 15:16
  • @jaroslaw: Yes, but a count of only 1 will probably be extremely rare. – B. Clay Shannon-B. Crow Raven Mar 04 '21 at 21:43
  • 1
    If conditions provided In the order as in this answer will not work at all for "1". Also you want to make locations a list so it doesn't have to be enumerated again just to be counted. var locations = map.Children.OfType().Select(x => x.Location).ToList() <- and then in "if" statements go with Count without () – jaro Mar 05 '21 at 12:37
  • @jaroslaw: Oh yes, you're right; I should have noticed that - the "1" part would never get reached as-is. I have other problems with the code, though, that prevents me from proceeding (see Update to my question if interested). – B. Clay Shannon-B. Crow Raven Mar 05 '21 at 16:39
1

The answer in the other post shares the idea, but unfortunately is has(had) some syntax errors, and the logic of the if/else is wrong. As I mentioned in the comments, these are some points to which you should pay attention:

  1. locations in your code is IEnumerable<Location> and it doesn't have Count property, instead, you can use Count() extension method on it.
  2. locations doesn't have indexer like arrays and you cannot use locations[0] to get the first element, instead you need to use locations.First().
  3. If you first check for Count()>0, then Count()==1 will never hit. So you need to change the conditions, for example first locations.Count() ==1 and then locations.Count() >1.
  4. Make sure you add useing System.Linq; so you can use Count() and First() methods.

Here is the modified version of your code:

private void RightsizeZoomLevelForAllPushpins()
{
    try
    {
        var map = this.userControl11.myMap;
        var locations = map.Children.OfType<Pushpin>().Select(x => x.Location);
        if(locations.Count()==1)
        {
            //Your preferred zoom level
            var zoomLevel = 12; 

            //Set view
            map.SetView(locations.First(), zoomLevel );
        }
        else if (locations.Count()>1)
        {
            //Margin
            var w = new Pushpin().Width;
            var h = new Pushpin().Height;
            var margin = new Thickness(w / 2, h, w / 2, 0);

            //Set view
            map.SetView(locations, margin, 0);
        }
        else
        {
            //Not necessary
            System.Windows.Forms.MessageBox.Show("No pushpin.");
        }
        currentZoomLevel = Convert.ToInt32(map.ZoomLevel);
        UncheckAllZoomLevelToolstripMenuItems();
        SetZoomMenuItem();
    }
    catch (Exception ex)
    {
        //Not necessary
        System.Windows.Forms.MessageBox.Show(ex.Message);
    }
}
Reza Aghaei
  • 120,393
  • 18
  • 203
  • 398