public static NoFastRoutePostcodes GetThamesEstuary_Areas_SplitByWater()
        {
            NoFastRoutePostcodes farEastThamesNoBridge = new NoFastRoutePostcodes();

            farEastThamesNoBridge.OneSideDescription   = "North-Southend-on-Sea/Chelmsford";
            farEastThamesNoBridge.OneSide              = new string[] { "SS", "CM" };
            farEastThamesNoBridge.OtherSideDescription = "South-Canterbury";
            farEastThamesNoBridge.OtherSide            = new string[] { "CT" };
            return(farEastThamesNoBridge);
        }
        /// <summary>
        /// These are cross-land trips that can't take a 'crow flies' approach due
        /// to lack of bridges (e.g. across the bristol channel)
        /// </summary>
        /// <returns></returns>
        public static NoFastRoutePostcodes GetEngWalesPostcodes_Areas_SplitByWater()
        {
            // Bristol Channel / River severn
            NoFastRoutePostcodes bristolChannel = new NoFastRoutePostcodes();

            bristolChannel.OneSideDescription   = "England";
            bristolChannel.OneSide              = new string[] { "BS", "TA", "EX", "PL", "TR" }; // WARNING: GL is actually split into two, spanning both sides of the Severn, so not included here.
            bristolChannel.OtherSideDescription = "Wales";
            bristolChannel.OtherSide            = new string[] { "NP", "CF", "SA", };

            return(bristolChannel);
        }
        public static NoFastRoutePostcodes GetGloucester_AreaDistrics_SplitByWater()
        {
            // Gloucester (split by )
            NoFastRoutePostcodes gloucester = new NoFastRoutePostcodes();

            gloucester.IsSplitSamePostcodeArea = true;
            gloucester.OneSideDescription      = "England";
            gloucester.OneSide = new string[] { "GL1", "GL2", "GL3", "GL4", "GL5", "GL6", "GL7", "GL8", "GL9", "GL10", "GL11", "GL12", "GL13", "GL19", "GL20", "GL50", "GL51", "GL52", "GL53", "GL54", "GL55", "GL56" };
            gloucester.OtherSideDescription = "Wales";
            gloucester.OtherSide            = new string[] { "GL15", "GL14", "GL16", "GL17", "GL18" };

            return(gloucester);
        }
        public async Task <List <string> > FindPostcodesInRadiusKm(string startingPostcode, PostcodePartsType startingPostcodeType, double radiusInKm, bool excludeBoatRides, bool avoidClippingOtherSideBristolChannel)
        {
            if (_lookupData == null || !_lookupData.Any())
            {
                throw new InvalidOperationException("Must initialize the data first");
            }

            List <string> postcodesInRadius = new List <string>();

            // 1. Break the postcode down to the bit we need.
            PostcodeParts postcode        = new PostcodeParts(startingPostcode, startingPostcodeType);
            var           areaAndDistrict = postcode.GetAreaAndDistrict_10K();   // E.g. PO4


            // 2. Get lat long for the postcode entered.
            var startingLatLong = _lookupData.FirstOrDefault(_ => _.Identifier == areaAndDistrict);

            if (startingLatLong == null)
            {
                throw new InvalidOperationException($"Could not find location for postcode {startingPostcode} -> {areaAndDistrict}");
            }

            var startingLocation = new Coordinate(startingLatLong.Latitude, startingLatLong.Longitude);

            // 3. Loop through all the postcodes to find ones in the radius.
            foreach (var loc in _lookupData)
            {
                var compareLocation = new Coordinate(loc.Latitude, loc.Longitude);
                var distanceInKm    = GeoCalculator.GetDistance(startingLocation, compareLocation, 2, DistanceUnit.Kilometers);

                if (distanceInKm < radiusInKm)
                {
                    postcodesInRadius.Add(loc.Identifier);
                }
            }


            if (excludeBoatRides)
            {
                var islandAreaPostcodes         = NoFastRoutePostcodes.GetIslands_WithOwnAreaPostcodes();
                var islandAreaDistrictPostcodes = NoFastRoutePostcodes.GetIslands_WithAreaDistrictPostcodes();

                bool isStartPointAnIsland = false;

                var startingPostcodeParts        = new PostcodeParts(startingPostcode, startingPostcodeType);
                var startingPostcodeArea         = startingPostcodeParts.GetArea_250K();
                var startingPostcodeAreaDistrict = startingPostcodeParts.GetAreaAndDistrict_10K();

                var isIslandAreaMatch         = islandAreaPostcodes.FirstOrDefault(_ => _ == startingPostcodeArea) != null;
                var isIslandAreaDistrictmatch = islandAreaDistrictPostcodes.FirstOrDefault(_ => _ == startingPostcodeAreaDistrict) != null;

                bool isStartingPointAnIsland = isIslandAreaMatch || isIslandAreaDistrictmatch;

                // Exclude Water Crossings - Kept Simple
                if (isStartingPointAnIsland)
                {
                    // Only Keep postcodes in same Area code (cross island e.g. Jersey (JE)/Guernsey (GY))
                    postcodesInRadius = postcodesInRadius.Where(_ => _.StartsWith(startingPostcodeArea)).ToList();
                    // Then remove anything that isn't in the island postcode area/district list (.e.g. IoW -> PO33 OK, PO5 (pompey) not.
                    postcodesInRadius = postcodesInRadius.Where(_ => islandAreaDistrictPostcodes.Contains(_)).ToList();
                }
                else
                {
                    // Starting on the mainland - Remove any Island postcode area (e.g. starting at IV [inverness], excluding HS [hebrides])
                    List <string> goodPostcodes = new List <string>();
                    foreach (var row in postcodesInRadius)
                    {
                        var rowParts = new PostcodeParts(row, PostcodePartsType.AreaAndDistrict);
                        var area     = rowParts.GetArea_250K();
                        if (!islandAreaPostcodes.Contains(area))
                        {
                            goodPostcodes.Add(row);
                        }
                    }
                    postcodesInRadius = goodPostcodes;


                    // Remove anything that starts with islandAreaPostcodes or is in islandAreaDistrictPostcodes (can simplify as district in lookup data)
                    postcodesInRadius = postcodesInRadius.Where(_ => !islandAreaDistrictPostcodes.Contains(_)).ToList();
                }
            }


            if (avoidClippingOtherSideBristolChannel)
            {
                var startingPostcodeParts = new PostcodeParts(startingPostcode, startingPostcodeType);
                var startingPostcodeArea  = startingPostcodeParts.GetArea_250K();

                var englandSWalesPostcodeAreas = NoFastRoutePostcodes.GetEngWalesPostcodes_Areas_SplitByWater();
                var gloucester = NoFastRoutePostcodes.GetGloucester_AreaDistrics_SplitByWater();


                var isStartSWEngland = false;
                foreach (var row in englandSWalesPostcodeAreas.OneSide)                 // ONESIDE = ENG
                {
                    if (row == startingPostcodeArea)
                    {
                        isStartSWEngland = true;
                        break;
                    }
                }

                if (isStartSWEngland)
                {
                    // If we're starting in England, Remove any postcodes that have welsh starters (e.g. CF, NP)
                    List <string> goodPostcodes = new List <string>();
                    foreach (var row in postcodesInRadius)
                    {
                        var rowParts = new PostcodeParts(row, PostcodePartsType.AreaAndDistrict);
                        var area     = rowParts.GetArea_250K();
                        if (!englandSWalesPostcodeAreas.OtherSide.Contains(area))
                        {
                            goodPostcodes.Add(row);
                        }
                    }
                    postcodesInRadius = goodPostcodes;


                    // We're in England, so remove the Welsh Gloucester Postcodes (Area+District, so easy to handle).
                    postcodesInRadius = postcodesInRadius.Where(_ => !gloucester.OtherSide.Contains(_)).ToList();
                }



                var isStartSouthWales = false;
                foreach (var row in englandSWalesPostcodeAreas.OtherSide)                // OtherSide = WALES
                {
                    if (row == startingPostcodeArea)
                    {
                        isStartSouthWales = true;
                        break;
                    }
                }

                if (isStartSouthWales)
                {
                    // If we're starting in Wales, Remove any postcodes that have English starters (e.g. BS, TR)
                    List <string> goodPostcodes = new List <string>();
                    foreach (var row in postcodesInRadius)
                    {
                        var rowParts = new PostcodeParts(row, PostcodePartsType.AreaAndDistrict);
                        var area     = rowParts.GetArea_250K();
                        if (!englandSWalesPostcodeAreas.OneSide.Contains(area))
                        {
                            goodPostcodes.Add(row);
                        }
                    }
                    postcodesInRadius = goodPostcodes;

                    // We're in Wales, so remove the English Gloucester Postcodes (Area+District, so easy to handle).
                    postcodesInRadius = postcodesInRadius.Where(_ => !gloucester.OneSide.Contains(_)).ToList();
                }



                //var southWalesAreaPostcodes = NoFastRoutePostcodes.GetWales_ClippableAreaPostcodes();
                //var southWalesAreaDistrictPostcodes = NoFastRoutePostcodes.GetWales_ClippableAreaDistricts();


                //var startingPostcodeParts = new PostcodeParts(startingPostcode, startingPostcodeType);
                //var startingPostcodeArea = startingPostcodeParts.GetArea_250K();
                //var startingPostcodeAreaDistrict = startingPostcodeParts.GetAreaAndDistrict_10K();

                //var isSWalesAreaMatch = southWalesAreaPostcodes.FirstOrDefault(_ => _ == startingPostcodeArea) != null;
                //var isSWalesAreaDistrictmatch = southWalesAreaDistrictPostcodes.FirstOrDefault(_ => _ == startingPostcodeAreaDistrict) != null;
                //bool isStartingPointInWales = isSWalesAreaMatch || isSWalesAreaDistrictmatch;

                //// Avoid River Severn/Bristol Channel Crossings
                //if (isStartingPointInWales)
                //{
                //	// Only Keep postcodes in same Area code (cross island e.g. Jersey (JE)/Guernsey (GY))
                //	postcodesInRadius = postcodesInRadius.Where(_ => _.StartsWith(startingPostcodeArea)).ToList();
                //	// Then remove anything that isn't in the island postcode area/district list (.e.g. IoW -> PO33 OK, PO5 (pompey) not.
                //	postcodesInRadius = postcodesInRadius.Where(_ => southWalesAreaDistrictPostcodes.Contains(_)).ToList();
                //}
                //else
                //{
                //	// Starting on the mainland
                //	postcodesInRadius = postcodesInRadius.Where(_ => !islandAreaPostcodes.Contains(startingPostcodeArea)).ToList();
                //	// Remove anything that starts with islandAreaPostcodes or is in islandAreaDistrictPostcodes
                //	postcodesInRadius = postcodesInRadius.Where(_ => !islandAreaDistrictPostcodes.Contains(_)).ToList();
                //}
            }



            // 4. Return list
            return(postcodesInRadius);
        }