/// <summary>
    /// Evaluates the similarity between two maps using the graph sampling evaluation of biagioni and eriksson.
    /// Parameters are hardcoded, based on the ones we used in the corresponding research.
    /// </summary>
    /// <param name="GT">The ground truth map</param>
    /// <param name="CT">the constructed map.</param>
    /// <param name="amount">the amount of neighbourhood evaluations we average over</param>
    /// <returns>A precision and recall value indicating the similarity between the maps.</returns>
    public (float, float) EvalMap(Map GT, Map CT, int amount)
    {
        float precision = 0;
        float recall    = 0;

        for (int k = 0; k < amount; k++)
        {
            Coordinate originGT = rand.GetRandomPointOnRoad(rand.GetWeightedRandomRoad(GT));
            Coordinate originCT = CT.GetClosestPoint(originGT.location);

            /* An extra step that is sometimes used in the literature when ground truth map is not pruned.
             * while (Vector3.Distance(originCT.location, originGT.location) > 50)
             * {
             *  originGT = rand.GetRandomCoordinate(rand.GetWeightedRandomRoad(GT));
             *  originCT = CT.GetClosestPoint(originGT.location);
             * }
             */

            List <Vector3> pointsGT = sampleNeighbourhood.GetNeighbourhood(GT, originGT, 500, 30);
            List <Vector3> pointsCT = sampleNeighbourhood.GetNeighbourhood(CT, originCT, 500, 30);

            MaxFlow flow = new MaxFlow(pointsGT.Count + pointsCT.Count + 2);

            for (int i = 0; i < pointsCT.Count; i++)
            {
                flow.AddEdge(0, i + 1, 1);
            }

            for (int i = 0; i < pointsCT.Count; i++)
            {
                Vector3 pointCT = pointsCT[i];

                for (int j = 0; j < pointsGT.Count; j++)
                {
                    Vector3 pointGT = pointsGT[j];
                    if (Vector3.Distance(pointCT, pointGT) < 20)
                    {
                        flow.AddEdge(i + 1, j + pointsCT.Count + 1, 1);
                    }
                }
            }

            for (int i = 0; i < pointsGT.Count; i++)
            {
                flow.AddEdge(i + pointsCT.Count + 1, 1 + pointsCT.Count + pointsGT.Count, 1);
            }

            float matching = flow.FindMaximumFlow(0, 1 + pointsCT.Count + pointsGT.Count).Item1;
            precision += pointsCT.Count > 0 ? matching / pointsCT.Count : 0;
            recall    += pointsGT.Count > 0 ? matching / pointsGT.Count : 0;
        }

        return(precision / amount, recall / amount);
    }
    /// <summary>
    /// Visualizes and evaluates a single random neighbourhood.
    /// </summary>
    /// <param name="GT">Ground truth map</param>
    /// <param name="CT">contructed map</param>
    /// <param name="originDistanceCondition"></param>
    /// <param name="matchDistance"></param>
    /// <returns></returns>
    public (float, float) EvalNeighbourhood(Map GT, Map CT)
    {
        Coordinate originGT = rand.GetRandomPointOnRoad(rand.GetWeightedRandomRoad(GT));
        Coordinate originCT = CT.GetClosestPoint(originGT.location);

        /* An extra step that is sometimes used in the literature when ground truth map is not pruned.
         * while (Vector3.Distance(originCT.location, originGT.location) > 50)
         * {
         *  originGT = rand.GetRandomCoordinate(rand.GetWeightedRandomRoad(GT));
         *  originCT = CT.GetClosestPoint(originGT.location);
         * }
         */

        List <Vector3> pointsGT = sampleNeighbourhood.GetNeighbourhood(GT, originGT, 150, 30);
        List <Vector3> pointsCT = sampleNeighbourhood.GetNeighbourhood(CT, originCT, 150, 30);

        MaxFlow flow = new MaxFlow(pointsGT.Count + pointsCT.Count + 2);

        for (int i = 0; i < pointsCT.Count; i++)
        {
            flow.AddEdge(0, i + 1, 1);           // source edges
        }
        for (int i = 0; i < pointsCT.Count; i++) // edges between CT and GT
        {
            Vector3 pointCT = pointsCT[i];

            for (int j = 0; j < pointsGT.Count; j++)
            {
                Vector3 pointGT = pointsGT[j];
                if (Vector3.Distance(pointCT, pointGT) < 20)
                {
                    flow.AddEdge(i + 1, j + pointsCT.Count + 1, 1);
                }
            }
        }

        for (int i = 0; i < pointsGT.Count; i++)
        {
            flow.AddEdge(i + pointsCT.Count + 1, 1 + pointsCT.Count + pointsGT.Count, 1);                                      // sink edges
        }
        float matching;

        int[,] graph;
        (matching, graph) = flow.FindMaximumFlow(0, 1 + pointsCT.Count + pointsGT.Count);

        float prec   = pointsCT.Count > 0 ? matching / pointsCT.Count : 0;
        float recall = pointsGT.Count > 0 ? matching / pointsGT.Count : 0;

        return(prec, recall);
    }