// ref: https://doi.org/10.3138/FM57-6770-U75U-7727
        public static List <T> SimplifyByDouglasPeucker <T>(List <T> pointList, SimplificationParamters parameters /*, double threshold, bool retain3Points = false*/) where T : IPoint
        {
            var result = new List <T>();

            if (pointList == null || pointList.Count < 2)
            {
                return(result);
            }

            if (pointList.Count == 2)
            {
                return(pointList);
            }

            //to handle lines with the same start and end
            if (pointList.First().Equals(pointList.Last()))
            {
                //return DivideForDouglasPeucker(pointList, threshold, pointList.Count / 2);
                return(DivideForDouglasPeucker(pointList, parameters, pointList.Count / 2));
            }

            var numberOfPoints = pointList.Count;

            //
            double maxSemiPerpendicularDistance = 0;
            int    maxIndex = 0;

            //1399.06.23
            //در این جا برای سرعت بیش‌تر مقدار فاصله استفاه
            //نمی‌شود بلکه توان دوم آن استفاده می‌شود. این
            //روش باعث می‌شود در محاسبات از تابع جزر استفاده
            //نشود
            //double effectiveThreshold = threshold * threshold;
            double effectiveThreshold = parameters.DistanceThreshold.Value /** parameters.DistanceThreshold.Value*/;

            for (int i = 1; i < numberOfPoints - 1; i++)
            {
                var semiPerpendicularDistance = SpatialUtility.GetPointToLineSegmentDistance(pointList[0], pointList[numberOfPoints - 1], pointList[i]);

                if (semiPerpendicularDistance > maxSemiPerpendicularDistance)
                {
                    maxIndex = i;
                    maxSemiPerpendicularDistance = semiPerpendicularDistance;
                }
            }

            if (maxSemiPerpendicularDistance > effectiveThreshold)
            {
                return(DivideForDouglasPeucker(pointList, parameters, maxIndex));
            }
            else
            {
                return(new List <T> {
                    pointList[0], pointList[numberOfPoints - 1]
                });
            }
        }
        // http://psimpl.sourceforge.net/reumann-witkam.html
        public static List <T> SimplifyByReumannWitkam <T>(List <T> pointList, SimplificationParamters parameters) where T : IPoint
        {
            var result = new List <T>();

            if (pointList == null || pointList.Count < 2)
            {
                return(result);
            }

            if (pointList.Count == 2)
            {
                return(pointList);
            }

            var numberOfPoints = pointList.Count;

            //1399.06.23
            //در این جا برای سرعت بیش‌تر مقدار فاصله استفاه
            //نمی‌شود بلکه توان دوم آن استفاده می‌شود. این
            //روش باعث می‌شود در محاسبات از تابع جزر استفاده
            //نشود
            //double effectiveThreshold = threshold * threshold;
            double effectiveThreshold = parameters.DistanceThreshold.Value /** parameters.DistanceThreshold.Value*/;

            result.Add(pointList[0]);

            int startIndex = 0;

            int middleIndex = 1;

            for (int i = 2; i < numberOfPoints; i++)
            {
                var semiPerpendicularDistance = SpatialUtility.GetPointToLineSegmentDistance(pointList[startIndex], pointList[middleIndex], pointList[i]);

                if (semiPerpendicularDistance > effectiveThreshold)
                {
                    result.Add(pointList[i - 1]);
                    startIndex  = i - 1;
                    middleIndex = i;
                }
            }

            if (parameters.Retain3Points && result.Count == 1)
            {
                result.Add(pointList[pointList.Count() / 2]);
            }

            result.Add(pointList.Last());

            return(result);
        }
        //private static List<T> DivideForDouglasPeucker<T>(List<T> pointList, double threshold, int divideIndex) where T : IPoint
        //{
        //    var result = new List<T>();

        //    var leftList = pointList.Take(divideIndex + 1).ToList();

        //    result = SimplifyByDouglasPeucker(leftList, threshold);

        //    var rightList = pointList.Skip(divideIndex).ToList();

        //    var rightResult = SimplifyByDouglasPeucker(rightList, threshold);

        //    result.AddRange(rightResult.Skip(1));

        //    return result;
        //}

        private static bool AnyPerpendicularDistanceExceedTolerance <T>(List <T> pointList, double threshold) where T : IPoint
        {
            for (int i = 1; i < pointList.Count - 1; i++)
            {
                var semiPerpendicularDistance = SpatialUtility.GetPointToLineSegmentDistance(pointList[0], pointList[pointList.Count - 1], pointList[i]);

                if (semiPerpendicularDistance >= threshold)
                {
                    return(true);
                }
            }

            return(false);
        }
        // 1400.05.20
        public static List <T> SimplifyByBeforeOpeningWindow <T>(List <T> points, SimplificationParamters parameters /*, double threshold, bool retain3Points = false*/) where T : IPoint
        {
            if (points == null || points.Count == 0)
            {
                return(null);
            }
            else if (points.Count == 2)
            {
                return(points);
            }

            List <T> result = new List <T>();

            result.Add(points.First());

            // 1400.05.20
            //در این جا برای سرعت بیش‌تر مقدار فاصله استفاه
            //نمی‌شود بلکه توان دوم آن استفاده می‌شود. این
            //روش باعث می‌شود در محاسبات از تابع جزر استفاده
            //نشود
            double effectiveThreshold = parameters.DistanceThreshold.Value /* * parameters.DistanceThreshold.Value*/;

            int startIndex = 0, middleIndex = 1, endIndex = 2;

            while (endIndex < points.Count)
            {
                while (middleIndex < endIndex)
                {
                    var semiDistance = SpatialUtility.GetPointToLineSegmentDistance(points[startIndex], points[endIndex], points[middleIndex]);

                    if (semiDistance > effectiveThreshold)
                    {
                        result.Add(points[endIndex - 1]);

                        startIndex  = endIndex - 1;
                        middleIndex = startIndex + 1;

                        // after breaking it will increment by 1 6 lines below
                        endIndex = middleIndex;

                        break;
                    }

                    middleIndex++;
                }

                endIndex++;
                middleIndex = startIndex + 1;
            }

            if (parameters.Retain3Points && result.Count == 1)
            {
                result.Add(points[points.Count() / 2]);
            }

            if (result.Last().DistanceTo(points.Last()) != 0)
            {
                result.Add(points.Last());
            }

            return(result);
        }
        // 1400.05.11
        // http://psimpl.sourceforge.net/perpendicular-distance.html
        // Ekdemir, S., Efficient Implementation of Polyline Simplification for Large Datasets and Usability Evaluation.
        // Master’s thesis, Uppsala University, Department of Information Technology, 2011
        public static List <T> SimplifyByPerpendicularDistance <T>(List <T> points, SimplificationParamters parameters /*, double threshold, bool retain3Points = false*/) where T : IPoint
        {
            if (points == null || points.Count == 0)
            {
                return(null);
            }
            else if (points.Count == 2)
            {
                return(points);
            }

            List <T> result = new List <T>();

            result.Add(points.First());


            // 1400.06.05
            // استفاده از مقدار توان دوم در مقایسه مشکل‌ساز
            // نخواهد بود چون در هر حال تابع توان دوم هم
            // اکیدا صعودی است و توان دوم هیچ مقداری از توان
            // دوم مقدار کم‌تر از خودش، بیش‌تر نخواهد شد. اما
            // برای این‌که مقایسه سرعت درست انجام شود
            // استفاده از توان دوم متوقف شد.

            // 1400.05.11
            //در این جا برای سرعت بیش‌تر مقدار فاصله استفاه
            //نمی‌شود بلکه توان دوم آن استفاده می‌شود. این
            //روش باعث می‌شود در محاسبات از تابع جزر استفاده
            //نشود

            //double effectiveThreshold = threshold * threshold;
            double effectiveThreshold = parameters.DistanceThreshold.Value /** parameters.DistanceThreshold.Value*/;

            for (int i = 0; i < points.Count - 2; i++)
            {
                var perpendicularDistance = SpatialUtility.GetPointToLineSegmentDistance(points[i], points[i + 2], points[i + 1]);

                if (perpendicularDistance > effectiveThreshold)
                {
                    result.Add(points[i + 1]);
                }

                // 1400.06.05
                // نقطه حذف شده به عنوان نقطه شروع استفاده نمی‌شود
                // این روش در بهترین حالت ۵۰ درصد فشرده سازی ایجاد
                // می کند
                else
                {
                    result.Add(points[i + 2]);
                    i++;
                }
            }

            if (parameters.Retain3Points && result.Count == 1)
            {
                result.Add(points[points.Count() / 2]);
            }

            if (result.Last().DistanceTo(points.Last()) != 0)
            {
                result.Add(points.Last());
            }

            return(result);
        }