# C# 7 - <s>414</s> <s>369</s> 327 bytes

<!-- language: lang-c# -->

___Edit__: switched to 1D looping, computing `i` and `j` on the fly_

___Edit__: changed input method, collapsed lookup table, and switched to well defined initial bounds ...and removed the pointless space in the last outer for-loop_

    using C=System.Console;class P{static void Main(){var D=C.In.ReadToEnd().Replace("\r","");int W=D.IndexOf('\n')+1,H=D.Length,z=H,k,q,c;int P()=>z%W*(k%3-1)+z/W*(k/3-1)+H;var B=new int[9];for(;z-->0;)for(k=9;k-->0&D[z]%7<1;)if(B[k]<=P())B[k]=P()+1;for(;++z<H;C.Write(q>9?'o':D[z]))for(q=k=9;k-->0;)q*=(c=P()-B[k])>0?0:c<0?1:2;}}

[Try It Online](https://tio.run/nexus/cs-core#lZJdT4MwFIbv9ysIZNKOD0EvzFbKkjETTDQuarKLbRcEukmYRT6cyrLfPimD6TaIWm7enue87wltt2@JTxechR8/k5S8qFZIk3BJkLt0koQbrZPUSX2XW4W@x905PgVwvXJibogt9YaqD8TxnsJr6gGY69el4xLAT2Ne5nmIfJpyYzzM@zzycT8H4pSKUNJlO6/dErpIn@UM23IgR7JbNI8AxGbWHndA0L5UdChl50yfF9pGbO4AU/LO5c2T7gzNwxigTFFMDUGmA9xFAdueDSfZrH1l6Aj6czCYBDMD5@GQKSYkfeeVpMywkaWOYz8lIDK7fTEUe8wMi8AI7yMRjDoYuMytsBhoan2t5xpaX@9doM1mu1UbVutvQKgHAvuEkp@CEv4AQrkaQQFLINSDKvmAtFjnUfEb1NR3UTX1esf@z@uB0OQQGof/x7EDJ9PVynFMKlAd7eEVtk4v9ZfH8AU)

Complete program, takes input to standard in, prints it to standard out, uses `#`, `.`, and `o`. For each cell, it computes a 'profile' (which is the distance over 8 directions (it appears to compute a ninth for convenience, but this is always `0`), and records a maximum of each of these. It then writes out the whole map again, and replaces any cell which is both on a boundary and not outside any with a 'o'. The commented code below explains how it all works.

As per my answer to https://codegolf.stackexchange.com/questions/50829/save-the-geese-from-extinction, this produces the smallest octagon (valid circumnavigation with largest area) which bounds the island.

___Note__: that for once in my life I'm using something from the current decade, and this code requires C# 7 to compile. If you do not have C# 7, there is one line that will need to be replaced, which is clearly marked in the code._

Example usage and output:

    type t7.txt | IslandGolf1.exe

    .........ooooooooooo....
    ........o....#......o...
    .......o...#.#.##...#o..
    ......o....#.#.###.##.o.
    .....o....########.##..o
    ....o.....############.o
    ...o.#....############.o
    ..o#.###.##############o
    .o##.##################o
    o.####################.o
    o..##################..o
    o.##################...o
    o...################...o
    o###################...o
    o#####################.o
    o.##################..o.
    o####################o..
    o#...##############.o...
    o##...#############o....
    o#.....###....#...o.....
    .o.....#.........o......
    ..ooooooooooooooo.......

Formatted and commented code:

<!-- language: lang-c# -->

    using C=System.Console;
    
    class P
    {
        static void Main()
        {
            // \n 10
            // # 35
            // . 46
            // o 111
            
    
            var D=C.In.ReadToEnd().Replace("\r",""); // map
    
            int W=D.IndexOf('\n')+1, // width
                H=D.Length, // length
                z=H, // position in map (decomposed into i and j by and for P)
                k, // bound index
                q, // bound distance, and later cell condition (0 -> outside, 8 -> inside, >8 -> on boudary)
                c; // (free), comparison store
            
            // 'indexes' into a profile for the point z at index k
            // effectively {i=z%W,j=z/W,-i,-j,i+j,j-i,-i-j,i-j,0}[k] (inside order is a bit different) (0 const is always treated as 'inside bounds')
            // each non-zero-const entry describes the distance in one of the 8 directions: we want to maximise these to find the 'outer bounds'
            // the non-zero-const bounds describe 8 lines, together an octogen
            int P()=>z%W*(k%3-1)+z/W*(k/3-1)+H; // new C#7 local method syntax (if you don't have C#7, you can test this code with the line below instead)
            //k=0;System.Func<int>P=()=>z%W*(k%3-1)+z/W*(k/3-1)+H; // old lambda syntax (must pre-assign k to make static checker happy)
            
            var B=new int[9]; // our current bounds, each is initially null (must only call P() when on a #)
            // B[k] starts off a 0, P() has a +H term, and W+(H/W)<H for W >= 3, so B[k] is assigned the first time we compare it (H-i-j always > 0)
    
            for(;z-->0;) // for each cell
                for(k=9;k-->0& // for each bound
                    D[z]%7<1;) // if this cell is #
                    if(B[k]<=P())B[k]=P()+1; // update bound if necessary (add one so that we define the bound _outside_ the hashes)
            // z=-1
            for(;++z<H; // for each cell
                    C.Write(q>9?'o':D[z])) // print the cell (if q > 9, then we are on the bounds, otherwise, spit out whatever we were before)
                // check we are not 'outside' any of the bounds, and that we are 'on' atleast one of them
                for(q=k=9;k-->0;) // for each bound
                    q*=(c=P()-B[k])>0?0: // outside bound (q=0)    (??0 is cheaper than (int) or .Value)
                        c<0?1: // inside (preserve q)
                        2; // on bound (if q != 0, then q becomes > 9)
        }
    }