void ExpandOverlayHandler()
        {
            var originalBufferSize = 0f;
            if (!string.IsNullOrEmpty(SelectedOverlayDescriptor.Metadata.OverlayFilename))
            {
                originalBufferSize = SelectedOverlayDescriptor.Metadata.BufferZoneSize;
                SelectedOverlayDescriptor = (NAEMOOverlayDescriptor)NAEMOOverlayDescriptors[SelectedOverlayDescriptor.Metadata.OverlayFilename];
            }
            if (!IsOverlayFileSelected) return;
            var vm = new OverlayExpandViewModel(SelectedOverlayDescriptor.Metadata) {BufferSize = originalBufferSize};
            var result = _visualizerService.ShowDialog("OverlayExpandView", vm);
            if ((!result.HasValue) || (!result.Value)) return;
            
            var curOverlay = SelectedOverlayDescriptor.Data;
            //var limits = (Limits)(new GeoRect(curOverlay.Shapes[0].EarthCoordinates));
            var limits = new Limits(ConvexHull.Create(curOverlay.Shapes[0].EarthCoordinates, true));
            var expandedLimits = limits.CreateExpandedLimit(vm.BufferSize);  //in km.
            var boundingBox = new GeoRect(expandedLimits.GeoPointList);
            var coordinateList = expandedLimits.GeoPointList.Select(geo => new EarthCoordinate(geo)).ToList();
            var testShape = new OverlayLineSegments(coordinateList.ToArray(), Colors.Black);

            if (!testShape.IsUsableAsPerimeter) coordinateList = ConvexHull.Create(coordinateList, true);

            NAEMOOverlayDescriptors.CreateNewOverlay(SelectedRangeComplexDescriptor.Data.Name, vm.OverlayName, coordinateList, boundingBox, vm.BufferSize, Path.GetFileNameWithoutExtension(SelectedOverlayDescriptor.DataFilename));
            SelectedOverlayDescriptor = (NAEMOOverlayDescriptor)NAEMOOverlayDescriptors[vm.OverlayName];
        }
        void ExpandOverlayHandler()
        {
            try
            {
                var vm = new OverlayExpandViewModel(RangeComplexes.SelectedRangeComplex, RangeComplexes.SelectedArea);
                var result = ESME.Globals.VisualizerService.ShowDialog("OverlayExpandView", vm);
                if ((!result.HasValue) || (!result.Value)) return;

                var curOverlay = RangeComplexes.SelectedArea.OverlayShape;
                var limits = new Limits(ConvexHull.Create(curOverlay.Geos, true));
                var expandedLimits = limits.CreateExpandedLimit(vm.BufferSize);  //in km.
                var coordinateList = expandedLimits.Geos;
                var testShape = new OverlayLineSegments(coordinateList, Colors.Black);

                if (!testShape.IsUsableAsPerimeter) coordinateList = ConvexHull.Create(coordinateList, true);

                RangeComplexes.SelectedArea = RangeComplexes.SelectedRangeComplex.CreateArea(vm.OverlayName, coordinateList);
            }
            catch (Exception e) { ESME.Globals.MessageBoxService.ShowError(e.Message); }
        }
        public static Limits CreateBoundingBoxLimit(List<Limits> areas, double rangeOutKm)
        {
            var result = new Limits
                         {
                             Name = "bounds-cw.ovr"
                         };

            foreach (var l in areas)
            {
                foreach (var p in l.Geos) result.Geos.Add(p);
            }

            foreach (var geo in result.Geos)
            {
                if (geo.Latitude < result._minLat)
                {
                    result._minLat = geo.Latitude;
                }
                if (geo.Latitude > result._maxLat)
                {
                    result._maxLat = geo.Latitude;
                }
                if (geo.Longitude < result._minLon)
                {
                    result._minLon = geo.Longitude;
                }
                if (geo.Longitude > result._maxLon)
                {
                    result._maxLon = geo.Longitude;
                }
            }

            result.Geos.Clear();

            result.Geos.Add(new Geo(result._minLat, result._minLon).Offset(Geo.KilometersToRadians(Math.Sqrt(2) * rangeOutKm), Geo.DegreesToRadians(225)));
            result.Geos.Add(new Geo(result._maxLat, result._minLon).Offset(Geo.KilometersToRadians(Math.Sqrt(2) * rangeOutKm), Geo.DegreesToRadians(315)));
            result.Geos.Add(new Geo(result._maxLat, result._maxLon).Offset(Geo.KilometersToRadians(Math.Sqrt(2) * rangeOutKm), Geo.DegreesToRadians(45)));
            result.Geos.Add(new Geo(result._minLat, result._maxLon).Offset(Geo.KilometersToRadians(Math.Sqrt(2) * rangeOutKm), Geo.DegreesToRadians(135)));
            result.Geos.Add(new Geo(result._minLat, result._minLon).Offset(Geo.KilometersToRadians(Math.Sqrt(2) * rangeOutKm), Geo.DegreesToRadians(225)));

            result.Initialize();

            return result;
        }
        void SetIsClockWise()
        {
            //Console.WriteLine(Name + " isClockWise check");
            var result = false;
            if (_region.Length > 0)
            {
                var segments = _region.Segments.ToList();
                for (int segmentIndex = 0; segmentIndex < segments.Count; segmentIndex++)
                {
                    var segment = segments[segmentIndex];
                    var seg1 = new Geo[2];
                    seg1[0] = new Geo(segment[0]);
                    seg1[1] = new Geo(segment[1]);

                    var seg2 = new Geo[2];
                    segmentIndex++;
                    if (segmentIndex >= segments.Count) throw new ApplicationException("Not enough segments!");
                    segment = segments[segmentIndex];
                    seg2[0] = new Geo(segment[0]);
                    seg2[1] = new Geo(segment[1]);

                    var pointInside = seg1[0].MidPoint(seg2[1]);
                    var s1 = seg1[0];
                    var s2 = seg1[1];
                    var p1 = s1.Cross(s2);
                    var p2 = s1.Cross(pointInside);
                    var p3 = p1.Cross(p2);

                    //Console.WriteLine("line seg: " + PrintGeo(s1) + " - to - " + PrintGeo(s2));
                    //Console.WriteLine("  --        inside: " + PrintGeo(pointInside));
                    //Console.WriteLine("  -- normal normal: " + PrintGeo(p3));

                    var sigPiLat = KillZeroSignum(pointInside.LatitudeRadians);
                    var sigPiLon = KillZeroSignum(pointInside.LongitudeRadians);
                    var sigP3Lat = KillZeroSignum(p3.LatitudeRadians);
                    var sigP3Lon = KillZeroSignum(p3.LongitudeRadians);

                    var pilatpos = (sigPiLat > 0);
                    var pilonpos = (sigPiLon > 0);
                    var p3Latpos = (sigP3Lat > 0);
                    var p3Lonpos = (sigP3Lon > 0);

                    // demorgan
                    // bool flag4 = !(pilatpos ^ p3latpos ) && !(p3lonpos ^ pilonpos);
                    // result = !flag4;

                    var flag4 = (pilatpos ^ p3Latpos) || (p3Lonpos ^ pilonpos);
                    result = flag4;
                }
            }
            //Console.WriteLine(" is clockwise? " + result);

            _isClockWise = result;
        }
        //
        // if the closure didn't work, then try to make a bounding circle and get the
        // point from that.
        // GeoRegion region = limit.getGeoRegion();
        // if(region != null)
        // {
        // BoundingCircle circle = region.getBoundingCircle();
        // Geo center = circle.getCenter();
        // Geo northW = center.offset(circle.getRadius(),
        // 0).offset(circle.getRadius(), 1.5*Math.PI);
        // Geo southE = center.offset(circle.getRadius(),
        // 1.0*Math.PI).offset(circle.getRadius(), 0.5*Math.PI);
        // }
        /**
    * checks to see if this object is inside the passed in limit and point
    * outside the list causes the test to fail
    * 
    * @param limit
    * @return
    */

        public bool IsInside(Limits limit)
        {
            // this has happened
            if (_region == null) throw new ApplicationException("Limits.isInside: region is null");

            return limit.Geos.All(point => _region.Contains(point));
        }
        public Limits CreateExpandedLimit(double rangeOutKm)
        {
            var result = new Limits();
            var geoList = new List<Geo>();

            if (Geos == null || Geos.Count == 0)
            {
                result.Name = Name + "-Undefined";
            }
            else
            {
                var newName = Name;
                if (newName != null)
                {
                    var suffix = "";
                    if (newName.EndsWith(".ovr"))
                    {
                        newName = newName.Substring(0, newName.IndexOf(".ovr"));
                        suffix = ".ovr";
                    }
                    result.Name = String.Format("{0}_{1}K{2}", newName, rangeOutKm, suffix);
                }

                Geo lastEndPt = null;
                GeoSegment segment = null;
                foreach (var curSegment in _region.Segments)
                {
                    var lineCourse = curSegment[0].Azimuth(curSegment[1]);
                    segment = curSegment;

                    // gives specular reflection with angle from center of region
                    // double ang = Geo.angle(centerOfRegion, segPts[1], segPts[0]);
                    // double azimuth = Math.toDegrees((lineCourse + (isClockWise ? 1 :
                    // -1) * ang)) % (360.0);
                    var azimuth = (lineCourse + (_isClockWise ? -1 : 1) * (Math.PI / 2)) % ((Math.PI * 2.0));

                    var newPt0 = curSegment[0].Offset(Geo.KilometersToRadians(rangeOutKm), azimuth);
                    var newPt1 = curSegment[1].Offset(Geo.KilometersToRadians(rangeOutKm), azimuth);

                    if (lastEndPt != null)
                    {
                        var left = (_isClockWise ? lastEndPt : newPt0);
                        var right = (_isClockWise ? newPt0 : lastEndPt);

                        var arc = curSegment[0].ApproximateArc(left, right, Geo.DegreesToRadians(5.0));
                        var arcList = new List<Geo>(arc.Length);
                        for (var i = 1; i < arc.Length - 1; i++)
                        {
                            var a = arc[i];
                            if (_isClockWise) arcList.Add(a);
                            else arcList.Insert(0, a);
                        }
                        geoList.AddRange(arcList);
                    }
                    geoList.Add(newPt0);
                    geoList.Add(newPt1);
                    for (var i = 0; i < geoList.Count - 1; i++)
                        if ((Math.Abs(geoList[i].Latitude - geoList[i + 1].Latitude) < .000001) && (Math.Abs(geoList[i].Longitude - geoList[i + 1].Longitude) < .000001)) Debugger.Break();

                    lastEndPt = newPt1;
                }

                if (lastEndPt != null)
                {
                    var left = (_isClockWise ? lastEndPt : geoList[0]);
                    var right = (_isClockWise ? geoList[0] : lastEndPt);
                    var arc = segment[1].ApproximateArc(left, right, Geo.DegreesToRadians(5.0));

                    var arcList = new List<Geo>(arc.Length);
                    for (var i = 1; i < arc.Length; i++)
                    {
                        var a = arc[i];
                        if (_isClockWise) arcList.Add(a);
                        else arcList.Insert(0, a);
                    }

                    geoList.AddRange(arcList);
                }
                for (var i = 0; i < geoList.Count - 1; i++)
                    if ((Math.Abs(geoList[i].Latitude - geoList[i + 1].Latitude) < .000001) && (Math.Abs(geoList[i].Longitude - geoList[i + 1].Longitude) < .000001)) Debugger.Break();
                result.Geos = geoList;
                result.Initialize();
            }

            return result;
        }
        public Limits CreateBoundingBoxLimit(double rangeOutKm)
        {
            var result = new Limits();

            if (Geos == null || Geos.Count == 0)
            {
                result.Name = Name + "-Undefined";
            }
            else
            {
                result.Name = Name + "-Bounding";

                foreach (var geo in Geos)
                {
                    var dot = _centerOfRegion.Azimuth(geo);

                    var point = geo.Offset(Geo.KilometersToRadians(rangeOutKm), dot);

                    result.Geos.Add(point);
                }

                result.Initialize();
            }

            return result;
        }