private static Helper.Coordinate GetIntersection(
            Helper.Coordinate p1,
            Helper.Coordinate p2,
            Helper.Coordinate p3,
            Helper.Coordinate p4)
        {
            // http://csharphelper.com/blog/2014/08/determine-where-two-lines-intersect-in-c/

            // Get the segments' parameters.
            var dx12 = p2.X - p1.X;
            var dy12 = p2.Y - p1.Y;
            var dx34 = p4.X - p3.X;
            var dy34 = p4.Y - p3.Y;

            // Solve for t1 and t2
            var denominator = dy12 * dx34 - dx12 * dy34;

            var t1 = ((p1.X - p3.X) * dy34 + (p3.Y - p1.Y) * dx34) / denominator;

            if (double.IsInfinity(t1))
            {
                // The lines are parallel (or close enough to it).
                throw new ArgumentException("Lines are parallel");
            }

            // Find the point of intersection.
            return(new Helper.Coordinate(p1.X + dx12 * t1, p1.Y + dy12 * t1));
        }
        public static void AddCoordinatesToLineSf(Shapefile sfLines, Helper.Coordinate coordinate1,
                                                  Helper.Coordinate coordinate2)
        {
            var shp = new Shape();

            if (!shp.Create(ShpfileType.SHP_POLYLINE))
            {
                throw new Exception("Error in creating shape. Error: " + shp.ErrorMsg[shp.LastErrorCode]);
            }
            if (shp.AddPoint(coordinate1.X, coordinate1.Y) < 0)
            {
                throw new Exception("Error in adding point. Error: " + shp.ErrorMsg[shp.LastErrorCode]);
            }
            if (shp.AddPoint(coordinate2.X, coordinate2.Y) < 0)
            {
                throw new Exception("Error in adding point. Error: " + shp.ErrorMsg[shp.LastErrorCode]);
            }

            // Check if this line intersects other lines, if so shorten to intersection point:
            var numShapes = sfLines.NumShapes;

            for (var i = 0; i < numShapes; i++)
            {
                var shpTesting = sfLines.Shape[i];
                if (!shpTesting.Crosses(shp))
                {
                    continue;
                }

                var newCoordinate = GetIntersection(Helper.PointToCoordinate(shp.Point[0]),
                                                    Helper.PointToCoordinate(shp.Point[1]), Helper.PointToCoordinate(shpTesting.Point[0]),
                                                    Helper.PointToCoordinate(shpTesting.Point[1]));
                // Replace point:
                shp.Point[1] = Helper.CoordinateToPoint(newCoordinate);
            }

            if (sfLines.EditAddShape(shp) < 0)
            {
                throw new Exception("Error in adding shape. Error: " + sfLines.ErrorMsg[sfLines.LastErrorCode]);
            }
        }
        private static void AddToPolygonSf(IShapefile sfPolygons, Helper.Coordinate coordinate1_1,
                                           Helper.Coordinate coordinate1_2, Helper.Coordinate coordinate2_1, Helper.Coordinate coordinate2_2)
        {
            var shp = new Shape();

            if (!shp.Create(ShpfileType.SHP_POLYGON))
            {
                throw new Exception("Error in creating shape. Error: " + shp.ErrorMsg[shp.LastErrorCode]);
            }
            if (shp.AddPoint(coordinate1_1.X, coordinate1_1.Y) < 0)
            {
                throw new Exception("Error in adding point. Error: " + shp.ErrorMsg[shp.LastErrorCode]);
            }
            if (shp.AddPoint(coordinate1_2.X, coordinate1_2.Y) < 0)
            {
                throw new Exception("Error in adding point. Error: " + shp.ErrorMsg[shp.LastErrorCode]);
            }
            if (shp.AddPoint(coordinate2_1.X, coordinate2_1.Y) < 0)
            {
                throw new Exception("Error in adding point. Error: " + shp.ErrorMsg[shp.LastErrorCode]);
            }
            if (shp.AddPoint(coordinate2_2.X, coordinate2_2.Y) < 0)
            {
                throw new Exception("Error in adding point. Error: " + shp.ErrorMsg[shp.LastErrorCode]);
            }
            // Closing:
            if (shp.AddPoint(coordinate1_1.X, coordinate1_1.Y) < 0)
            {
                throw new Exception("Error in adding point. Error: " + shp.ErrorMsg[shp.LastErrorCode]);
            }
            if (!shp.PartIsClockWise[0])
            {
                shp.ReversePointsOrder(0);
            }
            if (!shp.IsValid)
            {
                shp = shp.FixUp2(tkUnitsOfMeasure.umMeters);
                if (shp == null)
                {
                    return;
                }
                if (!shp.IsValid)
                {
                    throw new Exception("Error: shape is not valid. " + shp.IsValidReason);
                }
            }

            // Check if this new shape is overlapping other shapes,
            // if so clip and add that version instead:
            var numShapes = sfPolygons.NumShapes;

            for (var i = 0; i < numShapes; i++)
            {
                var shpTesting = sfPolygons.Shape[i];
                // If within, don't add again:
                if (shp.Within(shpTesting))
                {
                    Debug.WriteLine("Shape is within " + i);
                    return;
                }
                // If overlaps, add only new part:
                if (shp.Overlaps(shpTesting))
                {
                    Debug.WriteLine(i + " overlaps. Touches: " + shp.Touches(shpTesting));
                    // TODO: Returns wrong part:
                    //shp = shpTesting.Clip(shp, tkClipOperation.clDifference);
                    //if (shp == null) return;
                }
            }

            if (shp.ShapeType2D != ShpfileType.SHP_POLYGON)
            {
                return;
            }

            if (!shp.PartIsClockWise[0])
            {
                shp.ReversePointsOrder(0);
            }
            if (!shp.IsValid)
            {
                shp = shp.FixUp2(tkUnitsOfMeasure.umMeters);
                if (shp == null)
                {
                    return;
                }
                if (!shp.IsValid)
                {
                    throw new Exception("Error: shape is not valid. " + shp.IsValidReason);
                }
            }


            if (shp.ShapeType2D != ShpfileType.SHP_POLYGON)
            {
                return;
            }
            if (sfPolygons.EditAddShape(shp) < 0)
            {
                throw new Exception("Error in adding shape. Error: " + sfPolygons.ErrorMsg[sfPolygons.LastErrorCode]);
            }
        }
        public void CreateSprayAreas()
        {
            var sfTracks   = Helper.OpenShapefile(Path.Combine(@"sf", "Tracks.shp"));
            var sfPoints   = Helper.CreateSf(ShpfileType.SHP_POINT);
            var sfLines    = Helper.CreateSf(ShpfileType.SHP_POLYLINE);
            var sfPolygons = Helper.CreateSf(ShpfileType.SHP_POLYGON);

            // Set projection:
            sfPoints.GeoProjection   = sfTracks.GeoProjection.Clone();
            sfLines.GeoProjection    = sfTracks.GeoProjection.Clone();
            sfPolygons.GeoProjection = sfTracks.GeoProjection.Clone();

            var tempPath = Path.GetTempPath();
            var utils    = new Utils();

            // Debug first track point:
            var debugPoint = sfTracks.Shape[0].Point[0];

            Debug.WriteLine(debugPoint.x);
            Debug.WriteLine(debugPoint.y);

            // Get the first shape:
            var shp         = sfTracks.Shape[0];
            var trackLength = shp.Length;
            // Get the first point:
            var firstPoint = shp.Point[0];

            // Save first point to result shapefile:
            Helper.AddPointToPointSf(sfPoints, firstPoint);
            var       segmentLength = 0d; // Total lenght between the points
            const int distance      = 10; // Distance between points
            const int length        = 15; // Length of perpendicular lines
            var       skip          = 1;

            while (trackLength > segmentLength)
            {
                // Create next point on line:
                var lastShape     = sfPoints.Shape[sfPoints.NumShapes - 1];
                var previousPoint = lastShape.Point[0];
                var nextPoint     = shp.InterpolatePoint(previousPoint, distance * skip);

                Helper.AddPointToPointSf(sfPoints, nextPoint);
                skip = 1; // reset

                // Create perpendicular line
                var angle = GetAngle(utils, previousPoint, nextPoint);
                var previousPointCoordinate  = new Helper.Coordinate(previousPoint.x, previousPoint.y);
                var perpendicularCoordinate1 = new Helper.Coordinate(Math.Cos(angle) * length + previousPoint.x,
                                                                     Math.Sin(angle) * length + previousPoint.y);
                AddCoordinatesToLineSf(sfLines, previousPointCoordinate, perpendicularCoordinate1);
                // Add line to other side:
                var perpendicularCoordinate2 = new Helper.Coordinate(Math.Cos(angle) * -1 * length + previousPoint.x,
                                                                     Math.Sin(angle) * -1 * length + previousPoint.y);
                AddCoordinatesToLineSf(sfLines, previousPointCoordinate, perpendicularCoordinate2);

                // Create polygon from two perpendicular lines:
                var numShapes = sfLines.NumShapes;
                if (numShapes >= 4)
                {
                    Debug.WriteLine("numShapes: " + numShapes);
                    // Get first perpendicular line:
                    var shpLine1 = sfLines.Shape[numShapes - 4];
                    var point1_1 = shpLine1.Point[0];
                    var point1_2 = shpLine1.Point[1];
                    var shpLine3 = sfLines.Shape[numShapes - 2];
                    var point3_1 = shpLine3.Point[0];
                    var point3_2 = shpLine3.Point[1];

                    AddToPolygonSf(sfPolygons,
                                   new Helper.Coordinate(point1_1.x, point1_1.y),
                                   new Helper.Coordinate(point1_2.x, point1_2.y),
                                   new Helper.Coordinate(point3_2.x, point3_2.y),
                                   new Helper.Coordinate(point3_1.x, point3_1.y));

                    // Other side:
                    var shpLine2 = sfLines.Shape[numShapes - 3];
                    var point2_1 = shpLine2.Point[0];
                    var point2_2 = shpLine2.Point[1];
                    var shpLine4 = sfLines.Shape[numShapes - 1];
                    var point4_1 = shpLine4.Point[0];
                    var point4_2 = shpLine4.Point[1];
                    AddToPolygonSf(sfPolygons,
                                   new Helper.Coordinate(point2_1.x, point2_1.y),
                                   new Helper.Coordinate(point2_2.x, point2_2.y),
                                   new Helper.Coordinate(point4_2.x, point4_2.y),
                                   new Helper.Coordinate(point4_1.x, point4_1.y));
                }

                segmentLength += distance;

                // Debug:
                if (sfPolygons.NumShapes >= 33)
                {
                    break;
                }
            }

            // TODO: Check last point

            sfTracks.Close();
            Helper.SaveAsShapefile(sfPoints, Path.Combine(tempPath, "InterpolatedPoints.shp"));
            Helper.SaveAsShapefile(sfLines, Path.Combine(tempPath, "PerpendicularLines.shp"));
            Helper.SaveAsShapefile(sfPolygons, Path.Combine(tempPath, "SprayAreas.shp"));
        }